我手上有点太多时间,开始想知道我是否可以写一个自修改程序.为此,我在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;
}
Run Code Online (Sandbox Code Playgroud)
这不起作用,我假设有一些东西阻止它打开自己,因为我可以将它分成两个单独的程序(一个"Hello World"和一些修改它),它工作正常.
编辑:我的理解是,当程序运行时,它被完全加载到ram中.因此,对于所有意图和目的,硬盘驱动器上的可执行文件是一个副本.为什么修改自己会有问题?
有解决方法吗?
谢谢
我不知道任何自我改进的编译器,但我再也不是一个编译器人.
那里有任何自我改进的编译器吗?
请注意,我所说的是一个可以改进自身的编译器 - 而不是一个改进编译代码的编译器.
任何指针赞赏!
旁注:如果你想知道为什么我要问看看这篇文章.即使我同意大多数论点,我也不太了解以下内容:
我们的程序可以在没有人工输入的情况下改进代码 - 它们被称为编译器.
...因此我的问题.
language-agnostic compiler-construction artificial-intelligence self-modifying
我正在尝试加速可变位宽整数压缩方案,我有兴趣在运行中生成和执行汇编代码.目前,大量时间花在错误预测的间接分支上,并且基于所发现的一系列位宽生成代码似乎是避免这种惩罚的唯一方法.
一般技术被称为"子程序线程"(或"调用线程",尽管这也有其他定义).目标是利用处理器有效的呼叫/返回预测,以避免停顿.这种方法在这里有详细描述:http: //webdocs.cs.ualberta.ca/~amaral/cascon/CDP05/slides/CDP05-berndl.pdf
生成的代码只是一系列调用,后跟返回.如果有5个"块"的宽度[4,8,8,4,16],它看起来像:
call $decode_4
call $decode_8
call $decode_8
call $decode_4
call $decode_16
ret
Run Code Online (Sandbox Code Playgroud)
在实际使用中,它将是一个较长的一系列调用,具有足够的长度,每个系列可能是唯一的,只调用一次.无论是在这里还是在其他地方,生成和调用代码都有详细记录.但是,除了简单的"不要做"或者考虑周全的"有龙"之外,我还没有找到很多关于效率的讨论.即使是英特尔文档也主要说明了一般性:
8.1.3处理自我修改和交叉修改代码
处理器将数据写入当前正在执行的代码段以将该数据作为代码执行的动作称为自修改代码.IA-32处理器在执行自修改代码时表现出特定于模型的行为,具体取决于当前执行指针在代码被修改之前的距离....自修改代码将以比非自修改或普通代码更低的性能级别执行.性能恶化的程度取决于修改的频率和代码的特定特征.
11.6自修改代码
对当前在处理器中高速缓存的代码段中的存储器位置的写入导致相关联的高速缓存行(或多个行)无效.此检查基于指令的物理地址.此外,P6系列和奔腾处理器检查对代码段的写入是否可以修改已经预取执行的指令.如果写入影响预取指令,则预取队列无效.后一种检查基于指令的线性地址.对于Pentium 4和Intel Xeon处理器,代码段中的指令的写入或窥探(其中目标指令已经被解码并驻留在跟踪高速缓存中)使整个跟踪高速缓存无效.后一种行为意味着在Pentium 4和Intel Xeon处理器上运行时,自我修改代码的程序可能会导致性能严重下降.
虽然有一个性能计数器来确定是否发生了不好的事情(C3 04 MACHINE_CLEARS.SMC:检测到自修改代码机器清除的数量)我想知道更多细节,特别是Haswell.我的印象是,只要我能够提前编写生成的代码,指令预取还没有到达那里,并且只要我不通过修改同一页面上的代码来触发SMC检测器(四分之一 - 页面?)作为当前正在执行的任何内容,那么我应该获得良好的性能.但所有的细节看起来都非常模糊:距离过近有多近?到目前为止还远吗?
试图将这些问题转化为具体问题:
Haswell预取器运行的当前指令前面的最大距离是多少?
Haswell"跟踪缓存"可能包含的当前指令后面的最大距离是多少?
Haswell上MACHINE_CLEARS.SMC事件的周期实际惩罚是多少?
如何在预测循环中运行生成/执行循环,同时防止预取程序吃掉自己的尾部?
我如何安排流程,以便每个生成的代码始终"第一次看到",而不是踩到已经缓存的指令?
出于好奇,我想知道在执行程序期间是否可以重新定位一段代码.例如,我有一个函数,每次执行后都应该在内存中替换这个函数.我们想到的一个想法是使用自修改代码来做到这一点.根据一些在线资源,自修改代码可以在Linux上执行,但我仍然不确定是否可以进行这样的动态重定位.有人有经验吗?
我发现了一篇关于自修改代码的文章,并试图做一些例子,但我总是得到分段错误.就像我能理解的那样,内存权限存在违规行为.代码段是(r)ead/e(x)ecute,因此写入的尝试导致此错误.有没有办法通过在运行时或之前更改内存权限来测试程序?我正在使用linux,这个例子是用GAS汇编编写的.
.extern memcpy
.section .data
string:
.asciz "whatever"
string_end:
.section .bss
.lcomm buf, string_end-string
.section .text
.globl main
main:
call changer
mov $string, %edx
label:
push string_end-string
push $buf
push $string
call memcpy
changer:
mov $offset_to_write, %esi
mov $label, %edi
mov $0xb, %ecx
loop1:
lodsb
stosb
loop loop1
ret
offset_to_write:
push 0
call exit
end:
Run Code Online (Sandbox Code Playgroud)
所以在osgx建议的修改后,这是一个工作代码.(实际上,如果你组装并链接并运行它崩溃,但如果你看着使用gdb,它确实修改了它的代码!)
.extern memcpy
.section .data
string:
.asciz "Giorgos"
string_end:
.section .bss
.lcomm buf, string_end-string
.section .text
.globl main
main:
lea (main), %esi # get the …Run Code Online (Sandbox Code Playgroud) 我试图在Android上制作一些自我修改的本机代码并在模拟器中运行它.我的示例基于android-ndk中的HelloJNI示例.它看起来像这样:
#define NOPE_LENGTH 4
typedef void (*FUNC) (void);
// 00000be4 <nope>:
// be4: 46c0 nop (mov r8, r8)
// be6: 4770 bx lr
void nope(void) {
__asm__ __volatile__ ("nop");
}
void execute(void){
void *code = mmap(NULL, NOPE_LENGTH, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (code != MAP_FAILED) {
memcpy(code, nope, NOPE_LENGTH);
((FUNC)code)();
}
}
Run Code Online (Sandbox Code Playgroud)
问题是这段代码崩溃了.怎么了?
我一直想知道 - 如何拆解自修改二进制文件?我猜你不能使用olly或IDA,因为它们是静态反汇编程序,对吗?如果你跳到指令的中间,反汇编程序会发生什么?如何分析变质引擎?
这是我之前的问题的后续跟进.我不相信Lisp代码与Von Neumann架构上的机器代码一样是Homoiconic.对我来说似乎很明显,在这两种情况下代码都表示为数据,但似乎很明显,你可以在机器代码中比在Lisp中更自由地利用这个属性.
在使用机器代码时,自我修改代码非常容易,它总是偶然发生,通常是偶然的,并且(根据我的经验)是搞笑的结果.在编写一个简单的"打印数字0-15"程序时,我的一个指针可能会出现"off by one"错误.我最终会意外地将寄存器1中的任何内容转储到包含下一条指令的内存中的地址中,而是执行随机指令.(当它是某种"goto"时总是很棒.上帝知道它会在哪里结束以及在那之后会发生什么)
代码和数据之间确实没有分离.一切都是一条指令(即使它只是一个NOP),一个指针和一个普通的旧数字.并且代码可能会在您眼前改变.
请帮我搞一个Lisp场景我一直在摸不着头脑.说我有以下程序:
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
; -- Demonstrate the output of factorial --
; -- The part that does the Self modifying goes here –
; -- Demonstrate the changed output of factorial
Run Code Online (Sandbox Code Playgroud)
现在我想要发生的是在这个程序中添加一些Lisp代码,它会将*更改为+,将<=更改为a> =,在那里的某处粘贴一个(+ 1 2 3),并且通常会对函数进行操作起来.然后我希望程序执行结果的绝对混乱.
关键点:除非我在示例代码中犯了一些致命错误,否则您只能更改该-– More code goes here –-部分.你在上面看到的是代码.我不希望你引用整个列表并将其存储在变量中,以便可以将其作为具有相同名称的单独函数进行操作和吐出; 我不希望将阶乘的标准重新定义视为完全不同的东西.我想要那些代码,就在那里,我可以在屏幕上看到我在眼前改变自己,就像机器代码一样.
如果这是一个不可能/不合理的请求,那么它只会在我的脑海中进一步巩固Homoiconicity不是语言有或没有的离散属性的想法,它是一个谱,而Lisp并不处于最前沿.(或者Lisp就像他们来的同性恋,我正在寻找一些其他术语来描述机器代码式的自我修改)
通常说一个static变量初始化被包装在一个中if以防止它被多次初始化。
对于这种情况和其他一次性条件,在第一次通过自我修改后让代码删除条件会更有效。
C++ 编译器是否允许生成这样的代码,如果不允许,为什么?我听说它可能会对缓存产生负面影响,但我不知道详细信息。
self-modifying ×10
assembly ×4
linux ×3
c ×2
64-bit ×1
android ×1
android-ndk ×1
arm ×1
c++ ×1
disassembly ×1
dispatch ×1
ida ×1
intel ×1
java ×1
lisp ×1
machine-code ×1
ollydbg ×1
relocation ×1
x86 ×1