Windows x86-64下机器码调用C++函数触发段错误求助
故障原因及修复方案
故障原因
Windows x86-64平台的官方调用约定(__fastcall)明确要求:调用者必须在栈上预留32字节的影子空间,供被调用函数使用(即使函数参数少于4个)。你的机器码没有分配这个空间,导致test函数内部调用std::cout时,尝试访问栈上的非法地址,最终触发段错误。
修复方法
在调用test函数前,先通过sub rsp, 0x20指令分配32字节的影子空间;函数调用完成后,再用add rsp, 0x20恢复栈指针平衡。
修改后的完整代码如下:
#include <windows.h> #include <iostream> #include <cstring> void test(long long int val) { std::cout << "Calling test function:" << val << std::endl; } int main() { unsigned char code[] = { // 分配32字节影子空间 0x48, 0x83, 0xEC, 0x20, // sub rsp, 0x20 // 将目标函数地址加载到RAX寄存器 0x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, imm64 // 将参数加载到RCX寄存器(x64 Windows下第一个参数用RCX传递) 0x48, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov rcx, imm64 // 调用目标函数 0xFF, 0xD0, // call rax // 恢复栈指针 0x48, 0x83, 0xC4, 0x20, // add rsp, 0x20 0xC3 // ret }; // 模拟调用test(12345); *((void **) &code[4]) = (void *) &test; // 写入函数地址(偏移因新增指令调整) *((long long int *) &code[14]) = 12345L; // 写入参数值(偏移相应后移) // 准备可执行内存 SYSTEM_INFO sys_info; GetSystemInfo(&sys_info); auto const page_size = sys_info.dwPageSize; auto const code_buffer = VirtualAlloc(nullptr, page_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); ::memcpy(code_buffer, code, sizeof(code)); using func_ptr_t = void (*)(); auto const func_ptr = reinterpret_cast<func_ptr_t>(code_buffer); func_ptr(); VirtualFree(code_buffer, 0, MEM_RELEASE); return 0; }
关键修改说明
- 增加栈操作指令:调用前分配影子空间,调用后恢复栈平衡,保证被调用函数的栈访问合法。
- 调整地址和参数的写入偏移:因新增了4字节的栈操作指令,函数地址和参数的写入位置需相应后移。
内容的提问来源于stack exchange,提问作者Alphaharrius
相关产品推荐
相关产品推荐

