x86_64汇编调用Win32 API出现0xC0000005访问违例
Win32汇编中RegisterClassA调用崩溃问题排查
问题描述
将一段调用Win32 API的C代码改写为x86_64 NASM汇编后,程序运行时调用RegisterClassA函数崩溃,抛出退出码0xC0000005(访问违例)。已确认WNDCLASSA结构体定义符合C ABI,栈空间预留了足够的结构体空间与32字节阴影空间,但无法定位问题原因。
原C代码
#include <windows.h> __attribute__((section(".rdata"))) volatile LPCSTR lpClassName = "WindowClass"; __attribute__((section(".rdata"))) volatile LPCSTR lpText_RegisterClassAError = "Failed to register window class."; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int uExitCode; LPCSTR lpText; WNDCLASSA wc = {0}; wc.lpfnWndProc = DefWindowProcA; wc.hInstance = hInstance; wc.lpszClassName = lpClassName; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // 0x6 if (!RegisterClassA(&wc)) { lpText = lpText_RegisterClassAError; goto exit_error_msgbox; } uExitCode = 0; exit_error_msgbox: MessageBoxA(NULL, lpText, NULL, MB_ICONERROR); uExitCode = 1; exit: ExitProcess(uExitCode); return uExitCode; }
汇编代码
default rel global WinMain extern DefWindowProcA, RegisterClassA, MessageBoxA, ExitProcess ; WNDCLASSA 结构体定义(x64 C ABI对齐) struc WNDCLASSA .style resd 1 ; UINT style; 4字节 .padding resd 1 ; 对齐填充,使下一个成员对齐到8字节边界 .lpfnWndProc resq 1 ; WNDPROC lpfnWndProc; 8字节 .cbClsExtra resd 1 ; int cbClsExtra; 4字节 .cbWndExtra resd 1 ; int cbWndExtra; 4字节 .hInstance resq 1 ; HINSTANCE hInstance; 8字节 .hIcon resq 1 ; HICON hIcon; 8字节 .hCursor resq 1 ; HCURSOR hCursor; 8字节 .hbrBackground resq 1 ; HBRUSH hbrBackground; 8字节 .lpszMenuName resq 1 ; LPCSTR lpszMenuName; 8字节 .lpszClassName resq 1 ; LPCSTR lpszClassName; 8字节 endstruc section .rdata lpClassName: db "WindowClass", 0 lpText_RegisterClassAError: db "Failed to register window class.", 0 section .text ; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) WinMain: push rbp mov rbp, rsp sub rsp, ((WNDCLASSA_size + 15) & -16) + 32 ; 预留结构体空间(对齐到16字节)+ 32字节阴影空间 mov r12, rcx ; 保存hInstance ; 清零WNDCLASSA结构体 lea rdi, [rsp + 32] xor rax, rax mov rcx, WNDCLASSA_size cld rep stosb ; 初始化结构体成员 mov qword [rsp + 32 + WNDCLASSA.lpfnWndProc], DefWindowProcA mov qword [rsp + 32 + WNDCLASSA.hInstance], r12 mov qword [rsp + 32 + WNDCLASSA.hbrBackground], 0x6 ; COLOR_WINDOW + 1 = 0x6 mov qword [rsp + 32 + WNDCLASSA.lpszClassName], lpClassName ; 调用RegisterClassA lea rcx, [rsp + 32] ; lpWndClass参数 call RegisterClassA lea rdx, lpText_RegisterClassAError ; 预存MessageBoxA的lpText参数 test rax, rax ; 检查RegisterClassA返回值 jz exit_error_msgbox xor rcx, rcx ; uExitCode = 0 jmp exit exit_error_msgbox: ; 调用MessageBoxA xor rcx, rcx ; hWnd = NULL xor r8, r8 ; lpCaption = NULL mov r9, 0x10 ; uType = MB_ICONERROR call MessageBoxA mov rcx, 1 ; uExitCode = 1 exit: add rsp, ((WNDCLASSA_size + 15) & -16) + 32 mov rsp, rbp pop rbp call ExitProcess mov rax, rcx ret
链接命令
nasm -f win64 .\main.s -o .\main.o link /LARGEADDRESSAWARE:NO /subsystem:windows /entry:WinMain .\main.o kernel32.lib user32.lib
问题分析与修复
1. 原C代码的逻辑缺陷
原C代码存在严重问题:当RegisterClassA调用成功时,未初始化局部变量lpText就执行MessageBoxA,此时lpText是野指针,必然触发0xC0000005访问违例。你的汇编代码修改了这个逻辑,成功时直接调用ExitProcess退出,避免了该问题,但原C代码的错误可能误导你对崩溃来源的判断。
2. 汇编代码中RegisterClassA崩溃的排查方向
你的汇编代码整体逻辑符合Win64调用约定,结构体定义与初始化也无明显错误,建议通过以下方式定位问题:
- 调试器跟踪:使用x64dbg等调试器,在
call RegisterClassA处下断点,查看结构体的内存内容:- 确认
lpszClassName是否指向.rdata段中有效的"WindowClass"字符串; - 确认
hInstance是否为非空的有效句柄; - 确认
lpfnWndProc是否指向DefWindowProcA的有效地址。
- 确认
- 检查链接依赖:确保链接时正确导入了
user32.lib,RegisterClassA属于user32.dll导出函数,缺少该库会导致函数地址无效。 - 栈对齐验证:调用
RegisterClassA前,确认rsp处于16字节对齐状态(Win64调用约定强制要求)。
内容的提问来源于stack exchange,提问作者123
相关产品推荐
相关产品推荐

