调用VirtualAllocEx返回MEM_FREE/PAGE_NO_ACCESS内存的问题排查
问题分析与解决方案
你的核心问题是未严格遵循x64 Windows调用约定,导致VirtualAllocEx的第5个参数flProtect传递错误,进而出现内存属性不符、VirtualProtectEx调用失败的异常。
关键错误点
x64 Windows调用约定明确要求:
- 调用任何API前,RSP必须保持16字节对齐(
call指令会压入8字节返回地址,调用后RSP会变为8字节对齐,被调用函数会自行调整回16字节对齐)。 - 无论参数数量多少,调用者必须为被调用者预留32字节的阴影空间(Shadow Space),供被调用函数临时保存RCX/RDX/R8/R9寄存器。
- 第5个及以后的参数,需放在阴影空间之后的栈位置(按8字节对齐传递,即使参数是DWORD类型)。
你的代码直接操作[rsp+20h]传递flProtect,既未预留阴影空间,也未保证RSP对齐,导致VirtualAllocEx读取到错误的参数值——实际传递的可能不是0x40,而是栈上的随机数据,最终导致内存页属性不符合预期。
修复后的汇编代码
以下是符合调用约定的正确实现,使用栈帧管理栈空间避免计算错误:
push rbp mov rbp, rsp ; 构建栈帧,确保RSP对齐16字节 sub rsp, 48 ; 预留32字节阴影空间 + 8字节第5个参数空间(48是16的倍数,保持对齐) mov rcx, rax ; 1st参数:hProcess(OpenProcess返回的句柄) xor rdx, rdx ; 2nd参数:lpAddress = NULL mov r8, 01000h ; 3rd参数:dwSize = 0x1000 mov r9, 03000h ; 4th参数:flAllocationType = MEM_COMMIT | MEM_RESERVE mov qword ptr [rbp-48], 040h ; 5th参数:flProtect = PAGE_EXECUTE_READWRITE(放在阴影空间后) mov rax, [r15+90h] ; 获取VirtualAllocEx函数指针 call rax ; 调用API,返回地址存入RAX mov r13, rax ; 保存分配的内存地址 add rsp, 48 ; 恢复栈空间 pop rbp ; 销毁栈帧
额外验证步骤
- 确认函数指针有效性:用Windbg查看
[r15+90h]指向的地址,是否为kernel32!VirtualAllocEx的正确入口。 - 检查栈对齐:调用
VirtualAllocEx前,执行r rsp命令确认RSP值的最后4位为0(16字节对齐)。 - 验证参数传递:调用前用
dq rsp L10查看栈空间,确认flProtect参数(0x40)出现在正确位置。
关于VirtualProtectEx的补充说明
之前的内存分配参数错误,导致实际分配的内存可能处于未正确提交或权限不符的状态,进而触发VirtualProtectEx的访问拒绝错误。修复VirtualAllocEx的调用后,该问题会随之解决。
内容的提问来源于stack exchange,提问作者ab ab
相关产品推荐
相关产品推荐

