【发布时间】:2009-02-05 16:34:24
【问题描述】:
自修改代码有什么实际用途吗?
我知道它们可用于构建蠕虫/病毒,但我想知道程序员可能不得不使用自修改代码是否有充分的理由。
有什么想法吗?也欢迎假设情况。
【问题讨论】:
自修改代码有什么实际用途吗?
我知道它们可用于构建蠕虫/病毒,但我想知道程序员可能不得不使用自修改代码是否有充分的理由。
有什么想法吗?也欢迎假设情况。
【问题讨论】:
原来“self-modifying code”上的维基百科条目有一个很棒的列表:
- 状态相关循环的半自动优化。
- 运行时代码生成,或在 运行时或加载时间(很流行, 例如,在 实时图形),如一般 排序实用程序准备要执行的代码 a中描述的关键比较 特定的调用。
- 改变对象的内联状态,或模拟高层 建造闭包。
- 子程序地址调用的修补,通常在加载时完成 动态库,或者,在每个 调用修补子例程的 对其参数的内部引用 以便使用他们的实际地址。 这是否被视为 '自修改代码'与否是一个案例 术语。
- 进化计算系统,例如基因编程。
- 隐藏代码以防止逆向工程,如通过使用 反汇编器或调试器。
- 隐藏代码以逃避检测病毒/间谍软件扫描软件和 之类的。
- 使用滚动模式填充 100% 的内存(在某些体系结构中) 重复操作码,擦除所有 程序和数据,或老化 硬件。
- 压缩代码在运行时解压并执行, 例如,当内存或磁盘空间不足时 有限。
- 一些非常有限的指令集别无选择,只能使用 自修改代码以达到一定的 功能。例如,“一 指令集计算机”机器 仅使用 如果为负,则减并分支 “指令”不能做间接的 复制(类似于 C 编程中的“*a = **b” 语言)不使用自修改 代码。
- 更改容错的说明
关于使用自我修改代码挫败黑客的问题:
在几次固件更新过程中,DirectTV 在他们的智能卡上慢慢组装了一个程序,以销毁被黑客入侵以非法接收未付费频道的卡。有关更多信息,请参阅 Black Sunday Hack 上 Jeff 的 Coding Horror 文章。
【讨论】:
我见过用于以下用途的自修改代码:
速度优化,让程序在运行中为自己编写更多代码
混淆,使逆向工程更加困难
【讨论】:
在以前 RAM 有限的情况下,使用自修改代码来节省内存。如今,例如UPX 等应用程序压缩实用程序用于在加载应用程序的压缩图像后解压缩/修改自己的代码。
【讨论】:
因为 Commodore 64 没有很多寄存器并且有一个 1Mhz 处理器。当您需要读取一个偏移值的内存地址时,修改源更容易。
@Reader:
LDA $C000
STA $D020
INC Reader+1
JMP Reader
那是我最后一次写自修改代码了 :-)
【讨论】:
人工智能?
【讨论】:
因为它真的很酷,有时这就是足够的理由。
【讨论】:
1960 年代的汇编语言使用自修改代码来实现没有堆栈的函数调用。
Knuth,v1,第 1 版,第 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 寄存器的操作方式,exit 指令将跳转到最初引用 MAX100 的位置之后的位置。
编辑:可能很难看出发生了什么,即使这里有简短的解释。在MAX100 STJ EXIT 行中,MAX100 是指令的标签(因此也是整个过程的标签),STJ 表示存储跳转寄存器(我们刚刚来自的地方), EXIT 表示标记为“EXIT”的内存位置是 STORE 的目标。 EXIT,我们稍后会看到是最后一条指令的标签。所以它正在覆盖代码!但是,许多指令(包括此处的STJ)隐式地仅覆盖指令字的操作数部分。所以JMP 保持不变,* 是一个虚拟标记,因为放在那里真的没有任何意义,它只会被覆盖。
自修改代码也用于寄存器间接寻址不可用的情况,但您需要的地址就在寄存器中。 PDP-1 语言:
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)。
这样的修改是相对安全的,在古董建筑上,它们是必要的。
【讨论】:
原因很多。在我脑海中浮现:
运行时类构造和元编程。例如,拥有一个与 SQL 表建立连接并生成专门用于该表的客户端类的类工厂(具有列的访问器、查找方法等)。
当然还有著名的 bitblt 示例和正则表达式类似物。
基于 RT 信息动态优化和跟踪 JIT
ada 风格泛型函数在增值环境中的子类型特化。
-- 马库斯Q
【讨论】:
动态链接是一种自我修改(修补绝对和/或相对跳转位置)......不过,这通常由操作系统的程序加载器完成。
【讨论】:
Neural networks 是一种自修改代码。
然后有evolutionary algorithms自己修改。
【讨论】:
Mike Abrash 不久前为 Dobb 博士的日记描述了 Pixomatic 代码生成器:http://www.ddj.com/architect/184405807。那是软件 3d dx7(?) 兼容的光栅化器。
【讨论】:
大声笑 - 我曾两次编写过自修改代码:
我可以想象在某些情况下,自修改代码会比替代代码更有效,但没有什么明显的飞跃。一般来说,这是要避免的——调试噩梦等——除非你像上面提到的那样故意试图混淆。
【讨论】:
实现自己的脚本语言的应用程序经常这样做。例如,数据库服务器经常以这种方式编译存储过程(或查询)。
【讨论】:
SwiftShader 中的动态代码生成是一种自我修改代码的形式,可以让它在 CPU 上高效地实现 Direct3D 9。
【讨论】:
现代例子: 我有一个需要 JWT 令牌才能工作的脚本。要请求令牌,需要交互式登录或使用新 JWT 令牌发出的刷新令牌。最好在脚本中存储刷新令牌并在每次执行时更新它
【讨论】: