Win64平台NASM x86汇编调用ReadConsoleA后自定义函数ret时崩溃
Windows x64汇编调用Win32 API崩溃问题分析
崩溃触发原因
问题核心是你的代码违反了Microsoft x64调用约定的强制要求,具体有两点:
- 未预留32字节影子空间:Windows x64体系下所有API调用都要求调用者提前在栈上为前4个寄存器传递的参数(rcx/rdx/r8/r9)预留32字节的影子存储空间,供被调用的API内部临时使用。你代码中调用
GetStdHandle、ReadConsoleA等函数时完全没有预留这部分空间,API执行时会直接覆盖栈上对应位置的内容,其中就包括getinput函数的返回地址,导致ret指令跳转到非法地址触发崩溃。 - 调用
ReadConsoleA后栈未平衡:你调用ReadConsoleA前通过push 0传递第五个参数,调用完成后没有执行add rsp, 8恢复栈指针,每调用一次ReadConsoleA栈就会偏移8字节,遇到换行/回车重试时栈偏移会持续累积,进一步破坏栈结构。
这也刚好匹配你观察到的现象:去掉ReadConsoleA调用后,没有API非法改写栈上的返回地址,函数可以正常返回。
修复方案
按照Microsoft x64调用约定调整栈操作即可,核心修改点:
- 进入函数后先预留32字节影子空间+额外8字节空间,保证调用API时栈满足16字节对齐要求
- 调用带栈传参的API后及时平衡栈指针
- 函数返回前恢复栈指针
修复后完整代码
section .bss temp_char resb 1 io_count_holder resq 1 section .text global _start extern GetStdHandle, WriteConsoleA, ReadConsoleA, ExitProcess getinput: ; 预留32字节影子空间 + 8字节对齐,保证后续API调用时栈16字节对齐 sub rsp, 40 .read_loop: mov rcx, -10 call GetStdHandle mov rcx, rax mov rdx, temp_char mov r8, 1 mov r9, io_count_holder push 0 call ReadConsoleA add rsp, 8 ; 平衡push的第五个参数 mov al, [temp_char] cmp al, 0xa je .read_loop cmp al, 0xd je .read_loop add rsp, 40 ; 恢复栈 ret _start: sub rsp, 40 ; 主函数同样提前预留栈空间保证对齐 call getinput mov rcx, -11 call GetStdHandle mov rcx, rax mov rdx, temp_char mov r8, 1 mov r9, io_count_holder push 0 call WriteConsoleA add rsp, 8 mov rcx, 0 call ExitProcess
汇编链接命令无需修改,仍使用原有命令即可:
nasm -f win64 printtest.s -o printtest.obj GoLink.exe /console /entry _start printtest.obj kernel32.dll
内容的提问来源于stack exchange,提问作者Porem5002
相关产品推荐
相关产品推荐

