自修改代码有什么用?

时间:2009-02-05 16:34:25

标签: executable self-modifying

自修改代码是否真的有用?

我知道它们可以用于构建蠕虫/病毒,但我想知道程序员是否有必要使用自修改代码的充分理由。

有什么想法吗?也是受欢迎的假设情况。

15 个答案:

答案 0 :(得分:48)

事实证明,“self-modifying code”上的维基百科条目有一个很棒的列表:

  
      
  1. 状态相关循环的半自动优化
  2.   
  3. 运行时代码生成,或者算法的专业化   运行时或加载时间(很流行,   例如,在域中   实时图形)如一般   排序实用程序准备执行代码   a中描述的关键比较   特定的调用。
  4.   
  5. 更改对象的内联状态,或模拟高级别   建造封闭物。
  6.   
  7. 修补子程序地址调用,通常在加载时完成   动态库,或每个   修补子程序的调用   内部引用其参数   以便使用他们的实际地址。   这是否被视为   “自修改代码”与否是一种情况   术语。
  8.   
  9. 进化计算系统,例如遗传编程。
  10.   
  11. 将代码隐藏到阻止逆向工程,就像使用a一样   反汇编程序或调试程序。
  12.   
  13. 通过病毒/间谍软件扫描软件隐藏代码逃避检测   之类的。
  14.   
  15. 使用滚动模式填充100%的内存(在某些体系结构中)   重复操作码,擦除所有   程序和数据,或老化   硬件即可。
  16.   
  17. 压缩代码,在运行时解压缩并执行,   例如,当内存或磁盘空间是   有限的。
  18.   
  19. 一些非常有限的指令集别无选择,只能使用   自修改代码实现一定的   功能即可。例如,“一个   指令集计算机“机器   仅使用   减去和转移 - 如果阴性   “指令”不能间接做   复制(类似于   C编程中的“* a = ** b”   语言)而不使用自我修改   代码。
  20.   
  21. 更改容错
  22. 的说明   

关于阻止黑客使用自修改代码的观点:

在几次固件更新过程中,DirectTV在他们的智能卡上慢慢组装了一个程序,以销毁被黑客入侵非法接收未付费频道的卡片。有关详细信息,请参阅Black Sunday Hack上的Jeff's Coding Horror文章。

答案 1 :(得分:12)

我见过自修改代码用于:

  1. 速度优化,让程序动态编写更多代码

  2. 阻碍,使逆向工程更加困难

答案 2 :(得分:11)

在以前RAM有限的情况下,自修改代码用于节省内存。现在,例如UPX等应用程序压缩实用程序在加载应用程序的压缩映像后用于解压缩/修改自己的代码。

答案 3 :(得分:6)

因为Commodore 64没有很多寄存器并且具有1Mhz处理器。当您需要通过值读取内存地址偏移时,更容易修改源。

@Reader:
LDA $C000
STA $D020
INC Reader+1
JMP Reader

这是我最后一次编写自修改代码: - )

答案 4 :(得分:6)

因为它真的非常酷,有时这还不够。

答案 5 :(得分:6)

20世纪60年代的汇编语言使用自修改代码来实现没有堆栈的函数调用。

Knuth,v1,1ed p.182:

MAX100  STJ   EXIT   ;Subroutine linkage
        ENT3  100    ;M1. Initialize
        JMP   2F
1H      CMPA  X,3    ;M3. Compare
        JGE   *+3
2H      ENT2  0,3    ;M4. Change m
        LDA   X,3    ;(New maximum found)
        DEC3  1      ;M5. Decrease k
        J3P   1B     ;M2. All tested?
EXIT    JMP   *      ;Return to main program
  

在包含该编码作为子程序的较大程序中,单个指令“JMP MAX100”将使寄存器A被设置为位置X + 1到X + 100的当前最大值,并且最大位置将是出现在rI2中。在这种情况下,子程序链接通过指令“MAX100 STJ EXIT”和后面的“EXIT JMP *”来实现。由于J寄存器的工作方式,退出指令将跳转到原始引用MAX100的位置之后的位置。

编辑:可能很难看到发生了什么,即使这里有简短的解释。在行MAX100 STJ EXIT中,MAX100是指令的标签(因此作为整个过程),STJ表示存储跳转寄存器(我们刚从来自),EXIT表示标记为“EXIT”的内存位置是STORE的目标。 EXIT,我们后面会看到最后一条指令的标签。所以它覆盖了代码!但是,许多指令(包括此处STJ)隐式地仅覆盖指令字的操作数部分。所以JMP保持不变,*是一个虚拟标记,因为放在那里真的没什么意义,它只会被覆盖。


自修改代码也用于寄存器间接寻址不可用的地方,但您需要的地址就在寄存器中。 PDP-1 LISP:

dap .+1  ;deposit address part of accumulator in (IP+1)
lac xy   ;load accumulator with (ADDRESS) [xy is a dummy symbol, just like * above]

这两条指令通过修改加载指令的操作数来执行ACC := (ACC)

这些修改是相对安全的,在古董架构上,它们是必要的。

答案 6 :(得分:5)

很多原因。在我的头顶:

  • 运行时类构造和元编程。例如,拥有一个类工厂,它连接到一个SQL表,并生成一个专门用于该表的客户端类(具有列的访问器,查找方法等)。

  • 当然还有着名的bitblt示例和正则表达式类似物。

  • 基于RT信息动态优化跟踪JIT

  • 增生环境中ada样式泛型函数的子类型特化。

- MarkusQ

答案 7 :(得分:5)

人工智能?

答案 8 :(得分:4)

动态链接是一种自我修改(修补绝对和/或相对跳转位置)......通常由O / S的程序加载器完成。

答案 9 :(得分:3)

Neural networks是一种自我修改代码。

然后有evolutionary algorithms修改自己。

答案 10 :(得分:3)

LOL - 我曾两次写过自修改代码:

  1. 首次学习汇编语言时,在理解间接索引访问之前
  2. 意外地,因为汇编语言和C
  3. 中的指针错误

    我可以想象,有些情况下自我修改代码比替代代码更有效,但没有任何明显的想法。一般来说,这是要避免的 - 调试噩梦等等 - 除非你故意试图如上所述进行混淆。

答案 11 :(得分:2)

迈克·阿布拉什(Mike Abrash)在一段时间后为Dr. Dobb's Journal描述了Pixomatic代码生成器:http://www.ddj.com/architect/184405807。这是一个软件3d dx7(?)兼容的光栅化器。

答案 12 :(得分:1)

实现自己的脚本语言的应用程序通常会这样做。例如,数据库服务器通常以这种方式编译存储过程(或查询)。

答案 13 :(得分:0)

SwiftShader中的动态代码生成是一种自修改代码,使其能够在CPU上有效地实现Direct3D 9.

答案 14 :(得分:0)

现代示例: 我有一个需要JWT令牌才能工作的脚本。要请求令牌,需要交互式登录或使用随新JWT令牌发行的刷新令牌。最好将刷新令牌存储在脚本中,并在每次执行时对其进行更新