Windows x64汇编调用CreateProcessA无输出问题求助
使用NASM和MinGW开发Windows x64平台的进程管理汇编程序,目标是通过CreateProcessA运行cmd.exe,等待其执行完成并获取退出码,但程序始终无任何输出,无法定位原因。
汇编代码
section .data ; Command line to execute cmd_line db "C:\Windows\System32\cmd.exe", 0 ; Log messages create_process_msg db "Creating process...", 10, 0 process_done_msg db "Process completed with exit code: %d", 10, 0 error_msg db "Error: Could not create process.", 10, 0 section .bss ; Reserve space for structures sin resb 68 ; STARTUPINFO structure (size 68 bytes) pi resb 24 ; PROCESS_INFORMATION structure (size 24 bytes) exit_code resd 1 ; to store the process exit code section .text extern CreateProcessA, WaitForSingleObject, GetExitCodeProcess, printf, ExitProcess, CloseHandle global main main: ; Zero out the STARTUPINFO structure lea rcx, [rel sin] xor rax, rax mov rdi, 68 ; size of STARTUPINFO rep stosb ; Set the cb member to the size of the structure mov dword [rel sin], 68 ; STARTUPINFO.cb = 68 ; Print message for creating the process lea rcx, [rel create_process_msg] call printf ; Preserve registers before calling CreateProcessA push r12 push r13 push r10 push r11 ; Call CreateProcessA lea rcx, [rel cmd_line] ; lpCommandLine xor rdx, rdx ; lpApplicationName (NULL) xor r8, r8 ; lpProcessAttributes (NULL) xor r9, r9 ; lpThreadAttributes (NULL) mov r10d, 1 ; bInheritHandles = TRUE mov r11d, 0 ; dwCreationFlags = 0 lea r12, [rel sin] ; lpStartupInfo lea r13, [rel pi] ; lpProcessInformation sub rsp, 40 ; Align stack for Windows x64 calling convention call CreateProcessA add rsp, 40 ; Restore stack alignment ; Restore preserved registers pop r11 pop r10 pop r13 pop r12 ; Check if process creation was successful test rax, rax jz process_creation_failed ; Wait for the process to complete mov rcx, [rel pi + 0] ; pi.hProcess mov rdx, -1 ; INFINITE timeout call WaitForSingleObject ; Get the exit code of the process mov rcx, [rel pi + 0] ; pi.hProcess lea rdx, [rel exit_code] ; lpExitCode call GetExitCodeProcess ; Print the exit code mov rsi, [rel exit_code] lea rdi, [rel process_done_msg] xor rax, rax call printf ; Close process and thread handles mov rcx, [rel pi + 0] ; pi.hProcess call CloseHandle mov rcx, [rel pi + 8] ; pi.hThread call CloseHandle ; Exit the program xor ecx, ecx call ExitProcess process_creation_failed: ; Print error message lea rcx, [rel error_msg] call printf ; Exit with error code mov ecx, 1 call ExitProcess
已尝试的调试步骤
- 重定位错误:最初遇到重定位错误,已通过移除
rel引用解决。 - 寄存器覆盖:怀疑
rcx被覆盖,添加了寄存器保护,但问题仍存在。 - 修改.bss段:尝试将.bss段的
resb替换为resq,未解决问题。 - 错误处理:添加错误消息以检测
CreateProcessA是否失败,但错误消息也未打印。 - 栈对齐:严格遵循Windows x64调用约定进行栈对齐,无效果。
观察结果
- 程序编译链接无错误,但控制台无任何输出。
- 即使刻意制造错误条件(如进程创建失败),错误消息也未显示。
- 程序静默执行,无输出导致调试困难。
编译命令与环境
编译命令
nasm -f win64 EX.asm -o EX.o gcc -m64 -o EX EX.o -lkernel32 -lmsvcrt .\EX.exe
环境
- 操作系统:Windows 10 x64
- 汇编器:NASM
- 链接器:Mingw-w64
问题根源与修正方案
你的代码存在多个违反Windows x64调用约定和结构初始化的错误,这些错误导致程序崩溃或API调用失败,进而无输出:
1. STARTUPINFO结构初始化错误
rep stosb指令的使用完全错误:该指令要求rdi指向目标内存地址,rcx是填充的字节数,rax是填充值。你的代码把目标地址放到rcx,计数放到rdi,导致内存操作完全混乱,STARTUPINFO结构未被正确清零,后续API调用可能失败。
修正代码:
; Zero out the STARTUPINFO structure lea rdi, [rel sin] ; rdi指向目标地址 xor rax, rax ; 填充值为0 mov rcx, 68 ; 填充68字节 rep stosb
2. CreateProcessA参数顺序完全颠倒
Windows x64下CreateProcessA的参数顺序为:lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation
你把lpCommandLine放在了第一个参数(rcx)的位置,而正确的第一个参数是lpApplicationName,这直接导致进程创建失败,且后续错误处理的printf调用也因其他问题无法执行。
修正后的CreateProcessA调用:
; Call CreateProcessA xor rcx, rcx ; lpApplicationName = NULL lea rdx, [rel cmd_line] ; lpCommandLine xor r8, r8 ; lpProcessAttributes = NULL xor r9, r9 ; lpThreadAttributes = NULL mov r10d, 1 ; bInheritHandles = TRUE mov r11d, 0 ; dwCreationFlags = 0 xor rbx, rbx ; lpEnvironment = NULL xor rbp, rbp ; lpCurrentDirectory = NULL lea r12, [rel sin] ; lpStartupInfo lea r13, [rel pi] ; lpProcessInformation sub rsp, 40 ; 预留32字节影子空间+8字节对齐+参数空间 mov [rsp+32], r12 ; 第9个参数(影子空间之后) mov [rsp+40], r13 ; 第10个参数 call CreateProcessA add rsp, 40 ; 恢复栈空间
3. printf调用违反Windows x64调用约定
Windows x64下printf的第一个参数(格式字符串)必须放在rcx,第二个参数(%d对应的值)放在rdx,你错误地使用了rsi和rdi,导致printf调用失败。此外,调用printf前必须保证栈是16字节对齐:main函数被C运行时调用时栈是16字节对齐,call指令会压入8字节的返回地址,所以需要先调整栈对齐。
修正后的printf调用:
; Print message for creating the process sub rsp, 8 ; 调整栈到16字节对齐 lea rcx, [rel create_process_msg] call printf add rsp, 8 ; 恢复栈 ; ... 中间代码省略 ... ; Print the exit code sub rsp, 8 lea rcx, [rel process_done_msg] mov edx, [rel exit_code] ; %d对应32位整数,用edx即可 xor rax, rax ; 无浮点数参数,rax置0 call printf add rsp, 8 ; ... 错误处理中的printf同样需要调整对齐 ... process_creation_failed: ; Print error message sub rsp, 8 lea rcx, [rel error_msg] call printf add rsp, 8
4. 栈对齐与影子空间处理
Windows x64调用约定要求:调用函数前,栈必须是16字节对齐,且要为被调用函数预留32字节的影子空间(即使函数不需要这么多参数)。你在调用CreateProcessA时预留了40字节(32字节影子+8字节对齐),这部分是对的,但参数传递时要把第9、10个参数放在影子空间之后。
修正后的完整代码
section .data ; Command line to execute cmd_line db "C:\Windows\System32\cmd.exe", 0 ; Log messages create_process_msg db "Creating process...", 10, 0 process_done_msg db "Process completed with exit code: %d", 10, 0 error_msg db "Error: Could not create process.", 10, 0 section .bss ; Reserve space for structures sin resb 68 ; STARTUPINFO structure (size 68 bytes) pi resb 24 ; PROCESS_INFORMATION structure (size 24 bytes) exit_code resd 1 ; to store the process exit code section .text extern CreateProcessA, WaitForSingleObject, GetExitCodeProcess, printf, ExitProcess, CloseHandle global main main: ; Zero out the STARTUPINFO structure lea rdi, [rel sin] xor rax, rax mov rcx, 68 rep stosb ; Set the cb member to the size of the structure mov dword [rel sin], 68 ; STARTUPINFO.cb = 68 ; Print message for creating the process sub rsp, 8 lea rcx, [rel create_process_msg] call printf add rsp, 8 ; Preserve non-volatile registers before calling CreateProcessA push r12 push r13 push rbx push rbp ; Call CreateProcessA xor rcx, rcx ; lpApplicationName = NULL lea rdx, [rel cmd_line] ; lpCommandLine xor r8, r8 ; lpProcessAttributes = NULL xor r9, r9 ; lpThreadAttributes = NULL mov r10d, 1 ; bInheritHandles = TRUE mov r11d, 0 ; dwCreationFlags = 0 xor rbx, rbx ; lpEnvironment = NULL xor rbp, rbp ; lpCurrentDirectory = NULL lea r12, [rel sin] ; lpStartupInfo lea r13, [rel pi] ; lpProcessInformation sub rsp, 40 ; 预留32字节影子空间+8字节对齐+参数空间 mov [rsp+32], r12 mov [rsp+40], r13 call CreateProcessA add rsp, 40 ; Restore non-volatile registers pop rbp pop rbx pop r13 pop r12 ; Check if process creation was successful test rax, rax jz process_creation_failed ; Wait for the process to complete mov rcx, [rel pi + 0] ; pi.hProcess mov rdx, -1 ; INFINITE timeout call WaitForSingleObject ; Get the exit code of the process mov rcx, [rel pi + 0] ; pi.hProcess lea rdx, [rel exit_code] ; lpExitCode call GetExitCodeProcess ; Print the exit code sub rsp, 8 lea rcx, [rel process_done_msg] mov edx, [rel exit_code] xor rax, rax call printf add rsp, 8 ; Close process and thread handles mov rcx, [rel pi + 0] ; pi.hProcess call CloseHandle mov rcx, [rel pi + 8] ; pi.hThread call CloseHandle ; Exit the program xor ecx, ecx call ExitProcess process_creation_failed: ; Print error message sub rsp, 8 lea rcx, [rel error_msg] call printf add rsp, 8 ; Exit with error code mov ecx, 1 call ExitProcess
内容的提问来源于stack exchange,提问作者kavi castelo

