使用栈字符串调用WinAPI GetUserNameA时程序挂起问题求助
问题分析与修复
你的程序挂起的核心原因是栈操作错误、API参数传递错误和违反x64 Windows调用约定,以下是具体问题和修复方案:
核心错误点
栈字符串位置错误
原代码中sub rsp, 0x60后,通过add rdx,0x60让缓冲区指针指向了sub rsp之前的栈顶位置,这会覆盖栈上的关键数据(比如函数返回地址),导致程序执行流程混乱。变量类型不匹配
stdout和bytes_written使用16位的dw定义,但Windows API返回的句柄和写入字节数都是32/64位值,会导致数据截断,引发未知错误。GetUserNameA参数错误
- 第二个参数需要指向一个
DWORD变量,输入值应为缓冲区的总大小(需容纳最大用户名255字符+终止符,即256字节),原代码仅传入0xff且未正确分配对齐的栈空间存储该变量。 - 缓冲区大小设置不足,不符合API要求。
- 第二个参数需要指向一个
栈对齐与调用约定违反
x64 Windows要求调用任何API前,rsp必须保持16字节对齐,原代码中调用GetUserNameA前的栈操作破坏了对齐规则,导致API调用失败。栈操作不平衡
原代码中sub rsp,0x28、push rdx、add rsp,0x08的组合导致栈状态混乱,后续恢复操作无法正确还原栈布局,影响后续API调用。
修复后的代码
extern GetStdHandle extern WriteFile extern ExitProcess extern GetUserNameA section .rodata stdout_query equ -11 section .data stdout dq 0 ; 64位句柄,替换原16位dw bytes_written dq 0 ; 64位变量,适配API要求 section .text global start start: ; 获取标准输出句柄 mov rcx, stdout_query call GetStdHandle mov [rel stdout], rax ; 分配栈空间:256字节用户名缓冲区 + 8字节大小变量 + 对齐空间,总计288字节(0x120,16的倍数) sub rsp, 0x120 ; 准备GetUserNameA参数 mov rcx, rsp ; 第一个参数:栈缓冲区指针 mov dword [rsp+0x100], 0x100 ; 缓冲区大小设为256字节 mov rdx, rsp+0x100 ; 第二个参数:指向大小变量的指针 ; 调用GetUserNameA,此时rsp保持16字节对齐 call GetUserNameA ; 准备WriteFile参数 mov rcx, [rel stdout] ; 第一个参数:标准输出句柄 mov rdx, rsp ; 第二个参数:用户名缓冲区指针 mov r8d, [rsp+0x100] ; 第三个参数:实际写入的字符数(API返回后更新的值) mov r9, rel bytes_written ; 第四个参数:写入字节数的指针 push qword 0 ; 第五个参数:重叠结构设为NULL call WriteFile ; 恢复栈空间 add rsp, 0x120 exit: xor rcx, rcx call ExitProcess
修复说明
- 栈空间管理:统一分配288字节(16的倍数)的栈空间,保证
rsp始终对齐到16字节边界,同时预留足够的缓冲区和参数存储位置。 - API参数修正:正确传递GetUserNameA的缓冲区指针和大小变量,缓冲区大小设为符合要求的256字节。
- 变量类型修正:将
stdout和bytes_written改为64位的dq,避免数据截断。 - 栈平衡保证:通过统一的
sub rsp和add rsp操作维护栈平衡,避免布局混乱。
链接时需确保链接kernel32.lib和advapi32.lib。
内容的提问来源于stack exchange,提问作者jord
相关产品推荐
相关产品推荐

