Windows平台下C/C++自修改代码实现异常问题求助
我有一些疑问:是否可以在Windows系统中编写自修改程序?我编写了一段使用_asm{}的C++代码,但无法正常运行,不清楚原因,希望得到帮助。
原代码
#include <iostream> using namespace std; void print() //FUNCTION TO MODIFY { cout << "Hello, World!\n"; } void Crypt(int adr) { _asm { mov eax, adr xor_loop: xor byte ptr [eax], 0x24 inc eax cmp eax, 0xC3 //compare ret opcode jne xor_loop } } int main() { print(); void *ptr = print; //address example : 003812E4 cout << "Address : " << ptr << "\n"; int mem; _asm { lea eax, print mov mem, eax call eax } cout << "EAX : " << mem << "\n"; Crypt(mem); return 0; }
伪代码
print function: mov eax, 0x80 add eax, 0x02 call eax ret modify: lea eax, print xor byte ptr [eax], 0x24 //xor for modify opcode inc eax //increase eax 1 byte cmp eax, 0xC3 //compare ret opcode jne modify --------------------------------------------- modify print function example: mul eax mov eax, edx jnz ebx ret
问题分析与解决
Windows下完全可以编写自修改程序,你的代码主要存在以下几个关键问题:
1. 内存权限限制
Windows默认将代码段(.text)设置为只读+执行权限,直接写入会触发访问违规(Access Violation)。必须先调用VirtualProtect函数修改目标内存页的权限为可读可写可执行(PAGE_EXECUTE_READWRITE),修改完成后可按需恢复原权限。
2. 循环终止条件错误
你汇编循环里的cmp eax, 0xC3是把寄存器EAX的地址值和0xC3比较,而不是检查当前地址指向的字节是否是ret指令的0xC3 opcode。正确写法应该读取[eax]的内容进行判断:
xor_loop: xor byte ptr [eax], 0x24 inc eax cmp byte ptr [eax], 0xC3 ; 读取当前eax指向的字节,判断是否为ret指令 jne xor_loop
另外,这种基于ret指令的循环可能会越界,建议提前计算函数的大致长度,或者使用明确的结束标记,避免意外修改其他内存区域。
3. 地址存储类型不兼容
用int adr存储函数地址在32位系统可行,但64位系统下指针是8字节,int仅4字节会导致地址截断,引发错误。应该用uintptr_t(来自<cstdint>头文件)或void*来存储地址,确保兼容32/64位环境。
4. 编译器优化干扰
编译器可能对print函数进行内联优化,导致你获取的地址并非实际函数代码的起始地址,或者代码被重排。可以给函数添加__declspec(noinline)属性禁止内联,确保函数有独立的代码段。
修改后的示例代码
#include <iostream> #include <cstdint> #include <windows.h> using namespace std; __declspec(noinline) void print() // 禁止内联,保证函数地址稳定 { cout << "Hello, World!\n"; } void Crypt(uintptr_t adr) { DWORD oldProtect; // 修改目标内存页权限为可读可写可执行 VirtualProtect(reinterpret_cast<void*>(adr), 0x100, PAGE_EXECUTE_READWRITE, &oldProtect); __asm { mov eax, adr xor_loop: xor byte ptr [eax], 0x24 inc eax cmp byte ptr [eax], 0xC3 ; 检查当前字节是否为ret指令的0xC3 jne xor_loop } // 恢复原内存权限(可选,建议恢复以保证安全性) VirtualProtect(reinterpret_cast<void*>(adr), 0x100, oldProtect, &oldProtect); } int main() { print(); uintptr_t mem = reinterpret_cast<uintptr_t>(print); cout << "Address : " << reinterpret_cast<void*>(mem) << "\n"; // 调用原函数 __asm { lea eax, print call eax } cout << "EAX : " << reinterpret_cast<void*>(mem) << "\n"; Crypt(mem); // 注:修改后的print函数指令已被篡改,直接调用可能触发非法指令异常,需确保修改后的指令序列合法 // print(); return 0; }
额外提示
自修改后的指令必须是合法的x86/x64指令,否则执行时会触发非法指令异常。比如你伪代码中修改后的指令序列,要确保每一条指令都是完整且合法的,避免出现截断的指令。
内容的提问来源于stack exchange,提问作者hacktooth

