自修改代码是否真的有用?
我知道它们可以用于构建蠕虫/病毒,但我想知道程序员是否有必要使用自修改代码的充分理由。
有什么想法吗?也是受欢迎的假设情况。
答案 0 :(得分:48)
事实证明,“self-modifying code”上的维基百科条目有一个很棒的列表:
- 状态相关循环的半自动优化。
- 运行时代码生成,或者算法的专业化 运行时或加载时间(很流行, 例如,在域中 实时图形)如一般 排序实用程序准备执行代码 a中描述的关键比较 特定的调用。
- 更改对象的内联状态,或模拟高级别 建造封闭物。
- 修补子程序地址调用,通常在加载时完成 动态库,或每个 修补子程序的调用 内部引用其参数 以便使用他们的实际地址。 这是否被视为 “自修改代码”与否是一种情况 术语。
- 进化计算系统,例如遗传编程。
- 将代码隐藏到阻止逆向工程,就像使用a一样 反汇编程序或调试程序。
- 通过病毒/间谍软件扫描软件隐藏代码逃避检测 之类的。
- 使用滚动模式填充100%的内存(在某些体系结构中) 重复操作码,擦除所有 程序和数据,或老化 硬件即可。
- 压缩代码,在运行时解压缩并执行, 例如,当内存或磁盘空间是 有限的。
- 一些非常有限的指令集别无选择,只能使用 自修改代码实现一定的 功能即可。例如,“一个 指令集计算机“机器 仅使用 减去和转移 - 如果阴性 “指令”不能间接做 复制(类似于 C编程中的“* a = ** b” 语言)而不使用自我修改 代码。
- 更改容错
的说明 醇>
关于阻止黑客使用自修改代码的观点:
在几次固件更新过程中,DirectTV在他们的智能卡上慢慢组装了一个程序,以销毁被黑客入侵非法接收未付费频道的卡片。有关详细信息,请参阅Black Sunday Hack上的Jeff's Coding Horror文章。
答案 1 :(得分:12)
我见过自修改代码用于:
速度优化,让程序动态编写更多代码
阻碍,使逆向工程更加困难
答案 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 - 我曾两次写过自修改代码:
我可以想象,有些情况下自我修改代码比替代代码更有效,但没有任何明显的想法。一般来说,这是要避免的 - 调试噩梦等等 - 除非你故意试图如上所述进行混淆。
答案 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令牌发行的刷新令牌。最好将刷新令牌存储在脚本中,并在每次执行时对其进行更新