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的行为:
- 循环内调用的
gen_randi函数修改了cx(ecx的低16位):mov cx, CHAR_LEN,直接覆盖了循环计数器的值。 - 调用
printf时,按照Microsoft x64调用约定,rcx属于易失性寄存器,printf会修改rcx,进一步破坏循环计数器。
修复步骤
- 改用非易失性寄存器保存循环计数:将循环次数存入
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
- 额外优化:随机数重复问题
当前gen_randi使用GetTickCount64获取毫秒级时间戳,循环执行速度极快时,多次调用会得到相同时间值,导致生成重复字符。可以在每次生成后加入微小延迟,或者改用更可靠的随机数源(如RtlGenRandom)。
内容的提问来源于stack exchange,提问作者Mart
相关产品推荐
相关产品推荐

