如何获取x86-64汇编中Windows API函数的寄存器传参信息?
背景
在x86-64汇编中实现Windows平台功能,最佳方式是调用Windows API而非直接调用系统调用(比如打开控制台、打印文本等操作)。但开发时很难找到API的寄存器传参规则,比如RCX、RDX、R8、R9这些寄存器需要传入什么数据/数值。
以我找到的HelloWorld示例为例,代码使用了GetStdHandle和WriteConsoleA函数,我理解它们的作用,但不清楚代码里的数值和寄存器操作的含义,以及对API调用的影响。
示例汇编代码
extern GetStdHandle extern WriteConsoleA section .data msg: db "HelloWorld" msglen: equ $-msg section .bss section .text start: mov ECX, -11 call GetStdHandle mov RCX, RAX mov RDX, msg mov R8, msglen lea R9, [RSP-16] call WriteConsoleA mov EAX, 0 ret
编译链接脚本(Makefile)
{NasmPath} -f win64 HelloWorld.asm {GoLinkPath} HelloWorld.obj /console kernel32.dll
注:{NasmPath}和{GoLinkPath}为路径占位符,已做脱敏处理。
核心问题
哪里可以找到向Windows API函数的RCX、RDX、R8、R9寄存器传入数据的相关信息?
解答
1. 微软官方文档
Windows API的所有函数签名、参数顺序、寄存器传参规则都在微软官方文档中。x86-64平台下Windows采用fastcall调用约定:
- 前4个参数依次放入RCX、RDX、R8、R9寄存器
- 后续参数从右往左压入栈中
- 调用者需负责调用后清理栈上的参数
比如你示例里的GetStdHandle,唯一参数是DWORD nStdHandle,对应x86-64里放入RCX寄存器。-11是预定义常量STD_OUTPUT_HANDLE(值为(DWORD)-11),用来获取标准输出句柄。
WriteConsoleA的参数顺序对应寄存器规则:
BOOL WriteConsoleA( HANDLE hConsoleOutput, // 对应RCX const VOID *lpBuffer, // 对应RDX DWORD nNumberOfCharsToWrite, // 对应R8 LPDWORD lpNumberOfCharsWritten, // 对应R9 LPVOID lpReserved // 第5个参数,需压栈(示例中传NULL可省略) );
2. Windows SDK头文件
安装Windows SDK后,头文件中定义了所有API的参数、常量。比如winbase.h包含GetStdHandle的声明,consoleapi.h包含WriteConsoleA的声明,wincon.h定义了STD_OUTPUT_HANDLE这类常量。直接查看这些头文件,能明确参数类型和顺序,再对应到寄存器传参规则。
3. 汇编工具文档与示例
NASM、GoLink这类工具的官方文档中,会有Windows x86-64汇编的调用约定说明,还有大量示例代码可参考,能直观看到寄存器传参的实际用法。
对示例代码的补充解释
mov ECX, -11:将STD_OUTPUT_HANDLE常量放入RCX,作为GetStdHandle的参数,获取标准输出句柄,返回值存在RAX中。mov RCX, RAX:把获取到的输出句柄作为WriteConsoleA的第一个参数。mov RDX, msg:将字符串指针作为WriteConsoleA的缓冲区参数。mov R8, msglen:把字符串长度作为要写入的字符数参数。lea R9, [RSP-16]:在栈上分配16字节空间(满足x86-64栈16字节对齐要求),用来接收实际写入的字符数,作为第四个参数。mov EAX, 0:设置程序返回值为0,表示控制台程序正常退出。
内容的提问来源于stack exchange,提问作者Owen

