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

Windows下x64汇编反汇编工具的函数调用与栈对齐问题

Windows x64汇编-反汇编工具崩溃问题求助

问题背景

我在Windows平台开发一款x64汇编语言的汇编-反汇编工具,需支持MOV、ADD、SUB、JMP等基础汇编指令转机器码功能,但核心功能实现后,程序出现崩溃或静默失败问题,尤其在执行特定函数调用后。

代码概况

以下是聚焦问题区域的简化代码:

section .data
    input_msg db "Enter assembly instruction: ", 0
    output_msg db "Machine code: ", 0
    invalid_msg db "Invalid instruction or operands!", 10, 0

    ; Opcode definitions and other data...

section .bss
    instruction resb 50     ; Buffer for user input
    opcode resb 5           ; Buffer for generated opcode
    op_len resd 1           ; Length of opcode
    operand1 resb 10        ; Buffer for operand1
    operand2 resb 10        ; Buffer for operand2

section .text
    extern printf, scanf, ExitProcess, sscanf, atoi, strcmp
    global main

main:
    sub rsp, 40

    lea rcx, [rel input_msg]
    call printf

    lea rcx, [rel fmt_string]
    lea rdx, [rel instruction]
    call scanf

    lea rcx, [rel instruction]
    lea rdx, [rel operand1]
    lea r8, [rel operand2]
    call parse_instruction

    cmp dword [rel op_len], 0
    je invalid_instruction

    lea rcx, [rel output_msg]
    call printf

    ; More code...

invalid_instruction:
    lea rcx, [rel invalid_msg]
    call printf
    add rsp, 40
    xor ecx, ecx
    call ExitProcess

parse_instruction:
    sub rsp, 32      ; Allocate shadow space

    ; Initialize op_len to 0
    mov dword [rel op_len], 0

    ; Extract operation and operands
    lea r9, [rel operand1]
    lea r10, [rel operand2]
    lea r11, [rel operation]
    lea rax, [rel fmt_string]
    mov rcx, rax
    mov rdx, r9
    mov r8, r10
    lea rax, [rel instruction]
    call sscanf_wrapper

    ; Compare operation
    lea rax, [rel operation]
    call str_compare_mov
    cmp al, 1
    je handle_mov

    call str_compare_add
    cmp al, 1
    je handle_add

    call str_compare_sub
    cmp al, 1
    je handle_sub

    call str_compare_jmp
    cmp al, 1
    je handle_jmp

    ; Invalid instruction
    jmp parse_end

handle_mov:
    ; MOV reg, imm
    ; opcode: 0xB8 + reg_code | imm (4 bytes)
    mov rdi, [rel operand1]
    call get_register_code
    cmp al, 0xFF
    je parse_end
    mov bl, [rel opcode_mov]
    add bl, al             ; opcode = 0xB8 + reg_code
    mov [rel opcode], bl
    ; Convert immediate value
    mov rdi, [rel operand2]
    call atoi_wrapper
    mov eax, [rel atoi_result]
    lea rbx, [rel opcode]
    mov [rbx + 1], eax
    mov dword [rel op_len], 5  ; 1 byte opcode + 4 bytes immediate
    jmp parse_end

完整代码可在GitHub仓库获取。问题疑似出在parse_instruction函数,该函数负责解析输入汇编指令并生成对应机器码,通过字符串比较判断指令类型后生成操作码。

已执行的调试步骤

  • 影子空间分配:按照Windows x64调用约定为每个函数分配32字节影子空间,未解决问题。
  • 栈对齐检查:确保函数调用前栈对齐,main函数通过sub rsp,40对齐栈,各函数也分配了影子空间,问题仍存在。
  • 简化函数调用:简化代码复杂度、聚焦核心功能,问题依旧。
  • 隔离功能模块:注释代码片段、隔离特定函数,程序仍在特定函数调用后失败,说明问题不局限于单一逻辑。
  • 复查调用约定:反复核对Windows x64调用约定的寄存器使用及影子空间要求,未解决问题。

具体问题

完成上述调试后,程序仍崩溃或无法输出正确结果,问题疑似与函数调用及栈管理相关,但无法定位具体原因。

编译环境与步骤

  • 系统:Windows 10 x64
  • 汇编器:NASM
  • 链接器:Mingw-w64
  • IDE:IntelliJ

汇编命令:

nasm -f win64 ASM.asm -o ASM.o

链接命令:

gcc -m64 -o ASM ASM.o -lkernel32 -lmsvcrt

运行命令:

.\ASM.exe

求助需求

可能原因

  1. 封装函数实现错误:sscanf_wrapper、atoi_wrapper、str_compare_mov等自定义封装函数可能未遵循调用约定,比如未正确处理影子空间、栈对齐,或者返回值处理错误。
  2. 全局变量访问问题:使用[rel ...]访问全局变量时,若符号定义缺失(比如operation、fmt_string在简化代码中未定义),会导致内存访问错误。
  3. 寄存器未保存:Windows x64调用约定中,RBX、RBP、RDI、RSI、R12-R15属于非volatile寄存器,若函数中修改了这些寄存器但未保存恢复,会破坏调用者的上下文。
  4. 栈对齐细节错误:函数调用前栈需对齐到16字节边界,parse_instruction中sub rsp,32后,后续调用外部函数前是否保持栈对齐?
  5. 内存越界:operand1、operand2等缓冲区大小不足,导致输入溢出破坏栈结构。

最佳实践

  1. 严格遵循栈对齐规则:所有外部函数调用前,RSP必须是16字节的整数倍。函数入口处应先分配影子空间+局部变量空间,确保栈对齐。
  2. 保存非volatile寄存器:若函数中使用了RBX、RBP、RDI、RSI、R12-R15,必须在函数开头将其压栈保存,结尾恢复。
  3. 影子空间管理:调用外部函数前必须分配32字节影子空间,即使函数参数少于4个,也需保留该空间;函数返回后要及时释放影子空间。
  4. 避免全局变量滥用:尽量减少全局变量使用,若必须使用,确保所有符号都正确定义,且访问时[rel ...]的偏移计算正确。
  5. 封装函数的一致性:自定义封装函数需完全遵循Windows x64调用约定,参数传递、返回值、栈管理要和标准C函数一致。

进一步调试步骤

  1. 使用WinDbg调试:附加进程,设置断点在parse_instruction入口及崩溃点,查看RSP值是否对齐、寄存器状态、内存访问错误的地址。
  2. 添加栈状态打印:在关键函数入口/调用外部函数前,打印RSP的值,确认是否符合16字节对齐要求。
  3. 最小化可复现例子:逐步删除无关代码,直到只剩能触发崩溃的最小代码块,缩小问题范围。
  4. 检查封装函数实现:单独测试sscanf_wrapper、atoi_wrapper等函数,验证其是否能正确处理输入、返回正确结果,且栈管理无误。
  5. 验证寄存器保存:在parse_instruction开头添加push rbx; push rdi; push rsi,结尾添加pop rsi; pop rdi; pop rbx,测试是否解决崩溃问题。
  6. 使用NASM调试选项:用nasm -f win64 -g ASM.asm -o ASM.o生成带调试信息的目标文件,配合调试器单步执行。

内容的提问来源于stack exchange,提问作者kavi castelo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:10:56