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

Windows x86_64 NASM汇编循环无输出问题排查

问题:纯汇编密码生成器循环模块无法正常运行

现象

  • 预期输出:
    Welcome to Some Password Required
    Here is your generated password:
    <类似!qj@%k的密码>
    
  • 实际输出:
    Welcome to Some Password Required
    Here is your generated password:
    <无内容,等待一段时间后程序返回>
    
  • 无错误提示,怀疑是无限循环,但循环计数设置看起来正确。

代码

; 纯汇编编写的简易密码生成器
; 版权 © 2025 Mart (@ThatFrogDev) on GitHub.

; .data 段存储只读变量
section .data
    WELCOME_MESSAGE: db "Welcome to Some Password Required", 10, "Here is your generated password:", 10, 0 ; db 将字符串的ASCII码存入内存,10是换行符
    PASSWD_LEN: equ 6 ; equ 定义数值常量
    PASSWD: times PASSWD_LEN db 0 ; times 创建长度为PASSWD_LEN的内存块,填充0

    ; TODO: 将符号、字母、数字分开成数组,让用户可以选择生成规则
    CHARACTERS: db "abcdefghijklmnopqrstuvwxyz123456789~`!@#$%^&*();:-+,<.>/?"    
    CHAR_LEN: equ $ - CHARACTERS
    PRINTF_FORMAT: db "%c", 0

; .bss 段存储未初始化的数据结构
section .bss
    result resq 1

; 所有可执行代码放在 .text 段
section .text
    global _start
    extern printf, GetTickCount64, ExitProcess

_start:
    ; 为函数调用分配栈空间。调用函数前必须保证栈是16字节对齐的。
    ; rsp 默认是16字节对齐,但函数调用会占用栈上8字节空间。
    ; 为了保持栈16字节对齐,我们从栈指针中减去28h(40字节,十六进制0x28)。
    sub      rsp, 28h 
    mov      rcx, WELCOME_MESSAGE ; rcx 是 printf 函数的参数
    call     printf ; 调用 printf,参数在rcx中
    mov      ecx, PASSWD_LEN ; 将循环计数器设为 PASSWD_LEN
gen_passwd_loop:
    call     gen_randi
    call     gen_passwd
    loop     gen_passwd_loop
    add      rsp, 28h ; 函数调用完成,清理栈空间,把之前减去的值加回栈指针
    mov      ecx, 0
    call     ExitProcess
    ret

; 生成随机整数
gen_randi:
    call    GetTickCount64
    mov     rax, rax
    mov     dx, ax
    xor     ax, ax
    mov     cx, CHAR_LEN
    div     cx
    lea     rbx, [rel result] 
    mov     [rbx], dx 
    ret

gen_passwd:
    lea      rbx, [rel result]
    mov      rax, [rbx] 
    lea      rbx, [rel CHARACTERS] 
    movzx    rax, byte [rbx + rax]
    mov      rcx, PRINTF_FORMAT
    mov      rdx, rax
    call     printf
    ret

问题根源与修复方案

核心问题:loop指令被破坏

loop指令依赖ecx寄存器作为循环计数器,每次执行时会自动将ecx减1,直到ecx为0时退出循环。但代码中存在两个破坏ecx的行为:

  1. 循环内调用的gen_randi函数修改了cx(ecx的低16位):mov cx, CHAR_LEN,直接覆盖了循环计数器的值。
  2. 调用printf时,按照Microsoft x64调用约定,rcx属于易失性寄存器,printf会修改rcx,进一步破坏循环计数器。

修复步骤

  1. 改用非易失性寄存器保存循环计数:将循环次数存入ebx(非易失性寄存器,函数调用不会修改它),手动实现循环逻辑,避免依赖易被修改的ecx。

修改后的_start段代码:

_start:
    sub      rsp, 28h 
    mov      rcx, WELCOME_MESSAGE
    call     printf 
    mov      ebx, PASSWD_LEN ; 用ebx保存循环次数
gen_passwd_loop:
    call     gen_randi
    call     gen_passwd
    dec      ebx ; 计数器减1
    jnz      gen_passwd_loop ; 不为0则继续循环
    add      rsp, 28h 
    mov      ecx, 0
    call     ExitProcess
    ret
  1. 额外优化:随机数重复问题
    当前gen_randi使用GetTickCount64获取毫秒级时间戳,循环执行速度极快时,多次调用会得到相同时间值,导致生成重复字符。可以在每次生成后加入微小延迟,或者改用更可靠的随机数源(如RtlGenRandom)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:55:19