调用64位DLL中rundll函数触发访问违例,栈需如何调整?
解决x64 DLL函数调用的访问违例问题
先看你给出的汇编代码片段:
invoke LoadLibraryA, $("64.dll") invoke GetProcAddress, rax, $("rundll") call rax
出现访问违例的核心原因是x64 Windows平台的调用约定对栈有严格要求,你的代码没满足这些规则,导致函数调用时栈状态非法,触发内存访问错误。下面是具体需要调整的栈操作:
1. 强制保证调用前栈16字节对齐
在x64 Windows环境下,任何函数调用执行前,RSP寄存器的值必须是16字节的整数倍。invoke宏通常会自动处理部分栈对齐,但手动执行call指令时,需要自己检查并修正:
- 你可以在
call rax前添加指令调整栈,比如如果当前RSP是8字节偏移的状态,就执行:
实际调试时,可以直接查看调试器里的sub rsp, 8 ; 调整后RSP回到16字节对齐状态RSP值,按需调整。
2. 必须预留32字节的影子空间(Shadow Space)
x64 Windows调用约定强制要求:无论被调用函数有没有参数,调用者都要在栈上预留32字节的空间,供被调用者用来保存RCX/RDX/R8/R9这四个寄存器的值。哪怕你的rundll函数没有参数,这个空间也不能少。
修改后的完整代码示例:
invoke LoadLibraryA, $("64.dll") invoke GetProcAddress, rax, $("rundll") ; 预留影子空间+保证栈对齐(如果之前栈未对齐的话) sub rsp, 32 + 8 call rax ; 调用完成后恢复栈空间 add rsp, 32 + 8
如果确认调用前栈已经是16字节对齐状态,只需要单独预留32字节影子空间即可:
sub rsp, 32 call rax add rsp, 32
3. 带参数的函数额外处理(如果rundll有参数)
如果rundll函数需要传入参数:
- 前4个参数依次放入
RCX、RDX、R8、R9寄存器 - 第5个及以后的参数,按从右到左的顺序压入栈中(注意要先预留影子空间,再压入参数)
- 调用完成后,调用者不需要清理栈上的参数(被调用者负责清理),但必须恢复之前预留的影子空间和对齐用的栈空间
调试验证小技巧
在调试器里执行call rax前,先检查两个关键点:
- 确认
RSP的值最后一位十六进制是0(即16字节对齐) - 确认已经预留了足够的影子空间
如果调整后还是出错,可以检查rundll的参数传递是否正确,或者确认DLL和你的EXE都是64位(位数不匹配也会导致这类错误)
内容的提问来源于stack exchange,提问作者h goe
相关产品推荐
相关产品推荐

