如何使用clang编译可调用Windows API的64位汇编(含Linux交叉编译)
汇编代码实现(clang兼容)
clang默认支持GAS汇编语法,你可以在代码开头添加.intel_syntax noprefix切换为更易读的Intel语法,避免AT&T语法的前缀冗余。以下是x64架构下调用目标API的示例:
.intel_syntax noprefix # 声明外部导入的Windows API函数,x64架构下无下划线前缀 .extern OpenFile .extern VirtualAlloc .text # 示例导出函数,可被你的C代码调用 .global my_win_api_call my_win_api_call: # 预留32字节影子空间+8字节栈对齐空间,保证调用API时栈16字节对齐 sub rsp, 40 # --------------------- 调用VirtualAlloc示例 --------------------- # Microsoft x64调用约定:前4个参数依次存入rcx、rdx、r8、r9 # VirtualAlloc参数顺序:lpAddress, dwSize, flAllocationType, flProtect mov rcx, 0 # 分配地址由系统指定 mov rdx, 4096 # 分配4KB内存 mov r8, 0x3000 # 同时提交和保留内存页 mov r9, 0x40 # 内存权限为可读可写可执行 call VirtualAlloc # 调用后返回的内存起始地址存在rax寄存器,可自行存储使用 # --------------------- 调用OpenFile示例 --------------------- # OpenFile参数顺序:lpFileName, lpReOpenBuff, uStyle lea rcx, [rip + test_file_path] # 第一个参数:文件路径指针 lea rdx, [rip + of_struct_buf] # 第二个参数:OFSTRUCT结构缓冲区指针 mov r8, 0x00000000 # 第三个参数:只读模式打开 call OpenFile # 调用后返回的文件句柄存在rax寄存器 # 恢复栈指针 add rsp, 40 ret .data test_file_path: .asciz "test.txt" # 以0结尾的ASCII文件名 of_struct_buf: .fill 136, 1, 0 # 预分配136字节的OFSTRUCT结构空间,初始化为0
如果你习惯使用AT&T语法,删除开头的.intel_syntax noprefix,将寄存器、立即数、寻址格式替换为AT&T规范即可,调用逻辑完全一致。
编译选项
Windows本地编译
直接使用Windows版clang执行以下命令:
clang -target x86_64-pc-windows-gnu -nostdlib -static 你的C代码.c 你的汇编代码.s -lkernel32 -o 输出程序.exe
参数说明:
-target x86_64-pc-windows-gnu指定目标为Windows x64架构,使用GNU ABI避免MSVC兼容问题-nostdlib -static和你Linux版本的编译参数保持一致,不链接标准库-lkernel32手动链接kernel32导入库,OpenFile和VirtualAlloc均由系统kernel32.dll导出,必须链接该库才能解析符号
Linux交叉编译Windows二进制
先安装好mingw-w64配套的clang交叉编译环境,编译参数和本地几乎完全一致,仅需调整target参数:
clang -target x86_64-w64-mingw32 -nostdlib -static 你的C代码.c 你的汇编代码.s -lkernel32 -o 输出程序.exe
注意事项
- 如果需要编译32位x86版本,调用约定改为cdecl,参数从右往左压栈,导入符号需要加下划线前缀(即
_OpenFile、_VirtualAlloc),target参数替换为对应的32位Windows target即可 - 所有Windows API调用前必须保证栈16字节对齐,且预留32字节影子空间,否则会直接触发运行时崩溃
- OpenFile属于微软标记为已废弃的API,如果你需要更稳定的文件操作能力,可以替换为
CreateFileA,调用方式、编译要求完全一致,同样属于kernel32导出函数
内容的提问来源于stack exchange,提问作者Eric Stotch
相关产品推荐
相关产品推荐

