You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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调用约定调整栈操作即可,核心修改点:

  1. 进入函数后先预留32字节影子空间+额外8字节空间,保证调用API时栈满足16字节对齐要求
  2. 调用带栈传参的API后及时平衡栈指针
  3. 函数返回前恢复栈指针

修复后完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 11:57:02