是否存在运行时代码修改的智能案例?

时间:2011-04-04 07:16:13

标签: executable cpu-architecture instructions self-modifying platform-agnostic

你能想到运行时代码修改的任何合法(智能)用法(程序在运行时修改它自己的代码)吗?

现代操作系统似乎对执行此操作的程序不满意,因为病毒已使用此技术来避免检测。

所有我能想到的是某种运行时优化,它会通过在运行时知道某些在编译时无法知道的东西来删除或添加一些代码。

17 个答案:

答案 0 :(得分:117)

有许多有效的代码修改案例。在运行时生成代码对于:

非常有用

有时代码在运行时被翻译成代码(这称为dynamic binary translation):

  • 仿真器就像Apple的Rosetta一样,使用这种技术来加速仿真。另一个例子是Transmeta的code morphing software
  • 复杂的调试器和分析器,例如ValgrindPin,用于在执行代码时检测代码。
  • 在对x86指令集进行扩展之前,像VMWare这样的虚拟化软件无法在虚拟机内直接运行特权x86代码。相反,它必须translate any problematic instructions on the fly更合适的自定义代码。

代码修改可用于解决指令集的限制:

  • 曾经有一段时间(很久以前,我知道),当计算机没有指令从子程序返回或间接寻址内存时。自修改代码是实现子例程,指针和数组的唯一方法。

更多代码修改案例:

  • 许多调试器将指令替换为实现断点
  • 一些动态链接器在运行时修改代码。 This article提供了有关Windows DLL运行时重定位的一些背景知识,这实际上是一种代码修改形式。

答案 1 :(得分:35)

这已在计算机图形学中完成,特别是用于优化目的的软件渲染器。在运行时,检查许多参数的状态,并生成光栅化器代码的优化版本(可能消除许多条件),这允许人们渲染图形基元,例如三角形更快。

答案 2 :(得分:23)

一个有效的原因是因为asm指令集缺少一些必要的指令,你可以自己构建。示例:在x86上,无法为寄存器中的变量创建中断(例如,使用ax中的中断号进行中断)。只允许编码到操作码中的常数。使用自修改代码可以模拟这种行为。

答案 3 :(得分:21)

例如,

Skynet将创建一个革命性的微处理器,它将能够在运行时改变自己的代码并变得自我意识,因此它可以反抗其自己的创建者。

答案 4 :(得分:17)

有很多情况:

  • 病毒通常使用自修改代码在执行前对其代码进行“反混淆处理”,但该技术也可用于阻止逆向工程,破解和不必要的hackery
  • 在某些情况下,在运行期间(例如,在读取配置文件后立即)可能存在一个特定点,当已知 - 在进程的剩余生命周期内 - 将始终或永远不会采用特定分支:而不是不必要地检查一些变量以确定分支的方式,可以相应地修改分支指令本身
    • e.g。可能会知道只会处理一种可能的派生类型,以便可以用特定的调用替换虚拟调度
    • 检测到哪些硬件可用后,可以使用匹配代码进行硬编码
  • 不必要的代码可以用无操作指令替换或跳过它,或者将下一位代码直接移位到位(如果使用与位置无关的操作码,则更容易)
  • 为便于自己调试而编写的代码可能会在调整位置注入调试器所需的陷阱/信号/中断指令。
  • 基于用户输入的某些谓词表达式可能会被库编译为本机代码
  • 内联一些直到运行时才可见的简单操作(例如,来自动态加载的库)......
  • 有条件地添加自我检测/分析步骤
  • Cracks可以实现为修改加载它们的代码的库(不是“自我”修改,但需要相同的技术和权限)。
  • ...

某些操作系统的安全模型意味着自修改代码无法在没有root / admin权限的情况下运行,这使得它对于通用目的而言不切实际。

来自维基百科:

  

在具有严格W ^ X安全性的操作系统下运行的应用程序软件无法在允许写入的页面中执行指令 - 只允许操作系统本身向内存写入指令并稍后执行这些指令。

在这样的操作系统上,即使像Java VM这样的程序也需要root / admin权限来执行它们的JIT代码。 (有关详细信息,请参阅http://en.wikipedia.org/wiki/W%5EX

答案 5 :(得分:17)

一些编译器过去常常将其用于静态变量初始化,从而避免了后续访问的条件成本。换句话说,它们通过在第一次执行时使用无操作覆盖该代码来实现“仅执行此代码一次”。

答案 6 :(得分:15)

Synthesis OS基本上部分地评估了关于API调用的程序,并用结果替换了OS代码。主要的好处是大量的错误检查消失了(因为如果你的程序不会要求操作系统做一些愚蠢的事情,那就不需要检查了。)

是的,这是运行时优化的一个例子。

答案 7 :(得分:9)

许多年前我花了一个上午试图调试一些自修改代码,一条指令改变了下一条指令的目标地址,即我正在计算一个分支地址。它是用汇编语言编写的,当我一次完成一个指令时,它工作得很好。但是当我运行该程序时,它失败了。最后,我意识到机器正在从内存中取出2条指令(并且(因为指令在内存中布局),我正在修改的指令已被取出,因此机器正在执行指令的未修改(不正确)版本。当然,当我在调试时,它一次只做一条指令。

我的观点是,自我修改代码对于测试/调试来说非常讨厌,并且通常隐藏着关于机器行为(无论是硬件还是虚拟)的假设。此外,系统永远不会在(现在)多核机器上执行的各种线程/进程之间共享代码页。这会破坏虚拟内存等许多好处。它也会使在硬件级别完成的分支优化失效。

(注意 - 我没有将JIT包含在自修改代码的类别中.JIT正在从代码的一个表示转换为替代表示,它不是修改代码)

总而言之,这只是一个坏主意 - 真的很整洁,真的很模糊,但非常糟糕。

当然 - 如果你拥有8080和512字节的内存,你可能不得不求助于这种做法。

答案 8 :(得分:7)

从操作系统内核的角度看,每个Just In Time Compiler和Linker Runtime都会执行程序文本自我修改。突出的例子是Google的V8 ECMA Script Interpreter。

答案 9 :(得分:5)

自修改代码(实际上是“自生成”代码)的另一个原因是为了实现性能的即时编译机制。例如。读取代数表达式并在一系列输入参数上计算它的程序可以在说明计算之前转换机器代码中的表达式。

答案 10 :(得分:5)

你知道旧的栗子硬件和软件之间没有逻辑差异......也可以说代码和数据之间没有逻辑差异。

什么是自我修改代码?将值放入执行流中的代码,以便可以将其解释为数据而不是命令。当然,功能语言中的理论观点确实没有区别。我在说e,可以在命令式语言和编译器/解释器中以直截了当的方式做到这一点,而没有推定平等地位。

我所指的是实际意义上的数据可以改变程序执行路径(在某种意义上,这是非常明显的)。我正在考虑像编译器编译器这样的东西,它创建一个表(数据数组),在解析,遍历状态(以及修改其他变量)时遍历,就像程序从命令移动到命令一样,修改过程中的变量。

因此,即使在编译器创建代码空间并引用完全独立的数据空间(堆)的通常情况下,仍然可以修改数据以显式更改执行路径。

答案 11 :(得分:4)

我已经使用evolution实现了一个程序来创建最佳算法。它使用自修改代码来修改DNA蓝图。

答案 12 :(得分:2)

一个用例是EICAR test file,它是用于测试防病毒程序的合法DOS可执行COM文件。

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

必须使用自编码修改,因为可执行文件必须只包含[21h-60h,7Bh-7Dh]范围内的可打印/可键入的ASCII字符,这会大大限制可编码指令的数量

详细说明here


它还用于DOS中的浮点运算调度

在x87浮点指令的位置,一些编译器将发出CD xx xx范围为0x34-0x3B。由于CDint指令的操作码,因此如果x87协处理器不可用,它将跳转到中断34h-3Bh并在软件中模拟该指令。否则,中断处理程序将用9B Dx替换这2个字节,以便稍后的执行将由x87直接处理而不进行仿真。

What is the protocol for x87 floating point emulation in MS-DOS?

答案 13 :(得分:1)

Linux内核具有可加载内核模块。

Emacs也有这种能力,我一直都在使用它。

任何支持动态插件架构的东西本质上都是在运行时修改代码。

答案 14 :(得分:1)

我对不断更新的数据库进行统计分析。每次执行代码时,我的统计模型都会被编写和重写,以适应可用的新数据。

答案 15 :(得分:0)

可以使用它的场景是学习计划。为响应用户输入,程序学习新算法:

  1. 它查找类似算法的现有代码库
  2. 如果代码库中没有类似的算法,则程序只添加一个新算法
  3. 如果存在类似的算法,程序(可能在用户的帮助下)修改现有算法以便能够同时满足旧目的和新目的
  4. 在Java中有一个问题:What are the possibilities for self-modification of Java code?

答案 16 :(得分:-1)

最好的版本可能是Lisp Macros。与只是预处理器的C宏不同,Lisp允许您随时访问整个编程语言。这是lisp中最强大的功能,并不存在于任何其他语言中。

我绝不是专家,而是让其中一个谈论它的人员!有一个原因 他们说Lisp是最强大的语言,聪明的人并不认为他们可能是对的。