您能想到运行时代码修改(程序在运行时修改自己的代码)的任何合法(智能)用途吗?
现代操作系统似乎对执行此操作的程序不满意,因为病毒已使用此技术来避免检测。
我所能想到的就是某种运行时优化,通过在运行时知道一些在编译时无法知道的内容,可以删除或添加一些代码。
您能想到运行时代码修改(程序在运行时修改自己的代码)的任何合法(智能)用途吗?
现代操作系统似乎对执行此操作的程序不满意,因为病毒已使用此技术来避免检测。
我所能想到的就是某种运行时优化,通过在运行时知道一些在编译时无法知道的内容,可以删除或添加一些代码。
Answers:
有许多有效的代码修改案例。在运行时生成代码可用于:
有时,代码会在运行时转换为代码(这称为动态二进制转换):
代码修改可用于解决指令集的限制:
更多的代码修改案例:
这已经在计算机图形学中完成,特别是出于优化目的的软件渲染器中。在运行时,将检查许多参数的状态,并生成光栅化器代码的优化版本(可能消除许多条件),从而使渲染图元(例如三角形)的速度更快。
一个有效的原因是因为asm指令集缺少一些必要的指令,您可以自己构建。示例:在x86上,无法对寄存器中的变量创建中断(例如,使用ax中的中断号进行中断)。仅允许将编码为操作码的const数字。使用自我修改的代码,人们可以模仿这种行为。
一些编译器过去将其用于静态变量初始化,从而避免了后续访问有条件的代价。换句话说,他们通过在第一次执行该代码时不执行操作来覆盖该代码,从而实现了“仅执行该代码一次”。
有很多情况:
某些操作系统的安全模型意味着没有root / admin特权就无法运行自修改代码,这对于通用用途而言是不切实际的。
从维基百科:
在具有严格W ^ X安全性的操作系统下运行的应用程序软件无法在允许其写入的页面中执行指令-仅允许操作系统本身将指令写入内存并在以后执行这些指令。
在这样的操作系统上,甚至Java VM之类的程序也需要root / admin特权才能执行其JIT代码。(有关更多详细信息,请参见http://en.wikipedia.org/wiki/W%5EX)
该合成OS基本上部分相对于API调用评估程序,并更换OS代码的结果。主要的好处是,许多错误检查都消失了(因为如果您的程序不要求操作系统做一些愚蠢的事情,则不需要检查)。
是的,这是运行时优化的一个示例。
许多年前,我花了一个早晨尝试调试一些自修改代码,一条指令更改了下一条指令的目标地址,即我正在计算分支地址。它是用汇编语言编写的,并且当我一次单步执行该程序时,效果很好。但是当我运行程序时,它失败了。最终,我意识到机器正在从内存中提取2条指令,并且(因为指令已放置在内存中)已经修改了我正在修改的指令,因此机器正在执行该指令的未修改(错误)版本。当然,当我调试时,它一次只执行一条指令。
我的观点是,自我修改的代码非常难以测试/调试,并且通常对机器(无论是硬件还是虚拟)的行为都具有隐含的假设。而且,系统永远无法在(现在)多核机器上执行的各种线程/进程之间共享代码页。这使虚拟内存等的许多优点无法实现。它还会使在硬件级别完成的分支优化无效。
(注意-我未将JIT包括在自修改代码的类别中。JIT正在将代码的一种表示形式转换为另一种表示形式,它没有在修改代码)
总而言之,这只是一个坏主意-真的很整洁,很晦涩,但是真的很糟糕。
当然-如果您只有8080和〜512字节的内存,则可能必须采取这种做法。
您知道古老的栗子,硬件和软件之间没有逻辑上的区别...也可以说代码和数据之间没有逻辑上的区别。
什么是自修改代码?将值放入执行流中的代码,以便可以将其解释为不是命令而是数据。当然,功能语言中存在理论上的观点,实际上没有区别。我是说e可以在命令性语言和编译器/解释器中以简单的方式做到这一点,而无需假定地位平等。
我指的是实际意义上的数据可以更改程序执行路径(在某种意义上,这是非常明显的)。我想到的是像编译器这样的编译器,它创建一个表(数据数组)供解析时遍历,从一个状态移动到另一个状态(并修改其他变量),就像程序从一个命令移到另一个命令一样,在此过程中修改变量。
因此,即使在通常的情况下,编译器会创建代码空间并引用完全独立的数据空间(堆),人们仍然可以修改数据以显式更改执行路径。
一个用例是EICAR测试文件,它是用于测试防病毒程序的合法DOS可执行COM文件。
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
它必须使用自我代码修改,因为可执行文件必须仅包含[21h-60h,7Bh-7Dh]范围内的可打印/可键入的ASCII字符,这大大限制了可编码指令的数量。
详细说明在这里
它也用于DOS中的浮点操作分派
一些编译器会CD xx
在x87浮点指令的位置发出xx,范围从0x34-0x3B。由于CD
是int
指令的操作码,如果x87协处理器不可用,它将跳入中断34h-3Bh并在软件中模拟该指令。否则,中断处理程序将用替换这2个字节,9B Dx
以便以后的执行将直接由x87处理而不进行仿真。
可以使用它的场景是一个学习程序。响应用户输入,程序学习了一种新算法:
有一个问题在Java中如何实现:自我修改Java代码的可能性是什么?
最好的版本可能是Lisp宏。与仅作为预处理器的C宏不同,Lisp使您可以随时访问整个编程语言。这是Lisp中最强大的功能,其他任何语言都不存在。
我绝不是专家,而是让其中一个轻率的家伙谈论它!他们说Lisp是周围最强大的语言,而聪明的人则没有理由说他们可能是对的。