我手上有太多时间,开始想知道我是否可以编写一个自我修改程序。为此,我在C中编写了一个“Hello World”,然后使用十六进制编辑器在已编译的可执行文件中查找“Hello World”字符串的位置。是否可以修改此程序以打开自身并覆盖“Hello World”字符串?
char* str = "Hello World\n";
int main(int argc, char* argv) {
printf(str);
FILE * file = fopen(argv, "r+");
fseek(file, 0x1000, SEEK_SET);
fputs("Goodbyewrld\n", file);
fclose(file);
return 0;
}
这不起作用,我假设有一些东西阻止它打开自己,因为我可以把它分成两个独立的程序(一个“Hello World”和一些修改它),它工作正常。
编辑:我的理解是,当程序运行时,它会完全加载到ram中。因此,对于所有意图和目的,硬盘驱动器上的可执行文件是一个副本。为什么修改自己会有问题?
有解决方法吗?
由于
答案 0 :(得分:30)
在Windows上,运行程序时,使用memory-mapped-file functions in Windows将整个*.exe
文件映射到内存中。这意味着该文件不一定全部被加载,而是在访问文件的页面时按需加载。
当以这种方式映射文件时,另一个应用程序(包括其自身)无法写入同一文件以在其运行时进行更改。 (此外,在Windows上,无法重命名正在运行的可执行文件,但它可以在Linux和其他具有基于inode的文件系统的Unix系统上重命名。)
可以更改映射到内存中的位,但是如果这样做,操作系统会使用“copy-on-write”语义来执行此操作,这意味着底层文件不会在磁盘上更改,而是副本内存中的页面是根据您的修改而制作的。在被允许这样做之前,你通常必须在有问题的存储器上摆弄保护位(例如VirtualProtect
)。
曾经有一段时间,在非常有限的内存环境中使用自修改代码的低级汇编程序常见。但是,没有人这样做了,因为我们不是在相同的受约束环境中运行,而现代处理器有很长的管道,如果你开始从它们下面改变代码就会非常沮丧。
答案 1 :(得分:6)
如果您使用的是Windows,则可以执行以下操作:
分步示例:
VirtualProtect()
保护功能,在您要修改的代码页上调用PAGE_WRITECOPY
。VirtualProtect()
保护功能,在修改过的代码页上调用PAGE_EXECUTE
。FlushInstructionCache()
。有关详细信息,请参阅How to Modify Executable Code in Memory(已存档:2010年8月)
答案 2 :(得分:4)
它非常依赖于操作系统。有些操作系统会锁定文件,因此您可以尝试通过在某处创建新副本来作弊,但是您只是运行该程序的另一个组合。
其他操作系统对文件进行安全检查,例如iPhone,所以写它将是很多工作,加上它作为只读文件。
对于其他系统,您甚至可能不知道文件的位置。
答案 3 :(得分:4)
所有现在的答案或多或少都围绕着这样一个事实,即今天你不能轻易地进行自修改机器码。我同意这对今天的PC来说基本上是正确的。
但是,如果您真的希望看到自己的自我修改代码,您可以使用以下方法:
试用微控制器,较简单的微控制器没有高级流水线。我找到的最便宜,最快捷的选择是MSP430 USB-Stick
如果您可以使用仿真,则可以为较旧的非流水线平台运行仿真器。
如果您想要自我修改代码只是为了它的乐趣,您可以在Corewars处使用自毁代码(更确切地说是敌人摧毁)获得更多乐趣。
< / LI>如果你愿意从C转到说Lisp方言,编写代码的代码就很自然了。我建议Scheme故意保持小。
答案 4 :(得分:2)
如果我们在x86环境中谈论这样做,那就不可能了。但是应该谨慎使用,因为x86指令是可变长度的。长指令可能会覆盖以下指令,较短的指令会从覆盖的指令中留下残余数据(NOP指令)。
当x86首次受到保护时,intel参考手册建议使用以下方法来调试对XO(仅执行)区域的访问:
所以问题的答案就在最后一步。如果您希望能够插入调试器所执行的断点指令,则RW是必需的。比80286更现代的处理器具有内部调试寄存器,以启用非侵入式监视功能,这可能导致发出断点。
从Win16开始,Windows提供了构建块。它们可能仍然存在。我认为微软称这类指针操作&#34; thunking。&#34;
我曾经在PL / M-86中为DOS编写了一个非常快速的16位数据库引擎。当Windows 3.1到达(在80386s上运行)时,我将它移植到Win16环境。我想利用32位内存,但没有PL / M-32可用(或Win32)。
解决我的程序以下列方式使用thunking的问题
一旦机制无bug,它就可以顺利运行。我的程序使用的最大内存区域是2304 * 2304双精度,大约40MB。即使在今天,我也称之为“#34;大&#34;记忆块。 1995年,它是典型SDRAM棒(128 MB PC100)的30%。
答案 5 :(得分:1)
在许多平台上都有不可移植的方法。例如,在Windows中,您可以使用WriteProcessMemory()
执行此操作。然而,在2010年,这通常是一个非常糟糕的主意。这不是你在汇编代码的DOS时代,为了节省空间这样做。这很难做到,你基本上要求稳定性和安全性问题。除非你像调试人那样做一些非常低级的事情,我会说不要为此烦恼,你所引入的问题并不值得你获得任何收益。
答案 6 :(得分:1)
自修改代码用于内存中的修改,而不是文件中的修改(如UPX那样的运行时解包器)。此外,程序的文件表示更难以操作,因为相对虚拟地址,可能的重定位和对大多数更新所需的标头的修改(例如,通过将Hello world!
更改为longer Hello World
您将需要扩展文件中的数据段。
我建议你先学会在记忆中去做。对于文件更新,最简单和更通用的方法是运行程序的副本,以便修改原始文件。
编辑:不要忘记使用自修改代码的主要原因:
1)混淆,以便实际执行的代码不是您通过文件的简单静态分析看到的代码。
2)表现,像JIT。
他们都不会从修改可执行文件中受益。
答案 7 :(得分:0)
如果您在Windows上运行,我相信它会锁定该文件,以防止它在运行时被修改。这就是为什么你经常需要退出程序才能安装更新。在Linux系统上也是如此。
答案 8 :(得分:0)
在较新版本的Windows CE(至少5.x更新版本)中,应用程序在用户空间中运行(与早期版本相比,所有应用程序都以管理员模式运行),应用程序甚至无法读取它自己的可执行文件。