You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用WinAPI的InternetOpenA函数时触发访问违例(Access Violation)

解决x86-64汇编调用Windows HTTP API时的0xC0000005访问违例问题

核心问题排查与修复

x86-64 Windows下调用API触发访问违例,绝大多数是违反了调用约定或内存访问规则,针对你用wininet/winhttp都出错的情况,按以下步骤逐一排查:

1. 严格遵守x86-64 Windows调用约定(Fastcall)

Windows x64的调用规则是硬性要求,违反必出问题:

  • 前4个参数依次放入 RCX, RDX, R8, R9
  • 第5个及以后参数从右往左压栈
  • 调用前必须保证栈是16字节对齐(call指令会压入8字节返回地址,因此调用前RSP的低4位必须为0)
  • 无论参数数量多少,都要预留0x20字节的shadow register空间给API使用

错误示例(未对齐栈):

call InternetOpenA ; 此时RSP未对齐,直接触发访问违例

正确做法:

sub rsp, 0x20 ; 分配shadow空间并保证栈对齐(假设初始RSP已对齐)
call InternetOpenA
add rsp, 0x20 ; 调用后恢复栈空间

2. 确认API函数名的正确性

静态链接时必须使用Windows API的导出原名,不能省略后缀:

  • ANSI版本:InternetOpenA, WinHttpOpenA
  • Unicode版本:InternetOpenW, WinHttpOpenW
    如果写错函数名(比如漏了A/W后缀),链接时可能不报错,但运行时会调用非法地址,触发访问违例。

3. 确保字符串参数的内存合法性

汇编中字符串必须放在可读写的数据段(.data或.data?),不能放在只读的.text段:
错误示例:

section .text
agent db "MyAgent/1.0", 0 ; 放在.text只读段,API若尝试修改会触发访问违例

正确做法:

section .data
agent db "MyAgent/1.0", 0 ; 放在可读写数据段

同时要保证传递的字符串指针指向有效内存,不能使用未初始化的寄存器或非法地址。

4. 正确处理栈帧与局部变量

如果用栈存储局部变量(比如保存API返回的句柄),要确保分配了足够的栈空间,且访问偏移正确:

sub rsp, 0x30 ; 0x20 shadow空间 + 0x10 局部变量存储区
mov [rsp+0x20], rax ; 将InternetOpenA返回的句柄保存到栈上的局部变量

5. 静态链接库的正确引用

Makefile中必须明确指定链接所需的库文件:

  • 使用wininet时,链接wininet.lib
  • 使用winhttp时,链接winhttp.lib

示例Makefile片段:

LDFLAGS = -lwininet -lkernel32 -luser32

修正后的汇编示例(基于wininet的HTTP初始化)

以下是对应C参考代码的正确汇编实现:

bits 64
default rel

section .data
    user_agent db "Win32-Assembly-HTTP/1.0", 0
    server_name db "example.com", 0

section .text
    global main
    extern InternetOpenA, InternetConnectA, HttpOpenRequestA, HttpSendRequestA, InternetCloseHandle, GetLastError

main:
    ; 栈对齐 + 分配shadow空间
    sub rsp, 0x20

    ; 调用InternetOpenA
    mov rcx, user_agent       ; 参数1: User Agent
    mov rdx, 0                ; 参数2: INTERNET_OPEN_TYPE_DIRECT
    mov r8, 0                 ; 参数3: 代理地址(空)
    mov r9, 0                 ; 参数4: 代理绕过列表(空)
    mov qword [rsp+0x20], 0   ; 参数5: 标志位(0)
    call InternetOpenA
    test rax, rax
    jz exit_fail              ; 句柄创建失败,退出

    ; 保存session句柄到栈
    mov [rsp+0x18], rax

    ; 调用InternetConnectA
    mov rcx, [rsp+0x18]       ; 参数1: Session句柄
    mov rdx, server_name      ; 参数2: 服务器名
    mov r8, 80                ; 参数3: HTTP端口
    mov r9, 0                 ; 参数4: 用户名(空)
    mov qword [rsp+0x20], 0   ; 参数5: 密码(空)
    mov qword [rsp+0x28], 3   ; 参数6: INTERNET_SERVICE_HTTP
    mov qword [rsp+0x30], 0   ; 参数7: 标志位(0)
    mov qword [rsp+0x38], 0   ; 参数8: 上下文(0)
    sub rsp, 0x20             ; 为额外参数分配栈空间
    call InternetConnectA
    add rsp, 0x20
    test rax, rax
    jz close_session

    ; 后续HttpOpenRequestA、HttpSendRequestA等调用同理,严格遵守调用约定
    ; ...

close_session:
    mov rcx, [rsp+0x18]
    call InternetCloseHandle

exit_fail:
    call GetLastError         ; 可获取具体错误码用于调试
exit:
    add rsp, 0x20
    xor eax, eax
    ret

调试建议

  1. 使用x64dbg调试,在API调用处下断点,查看寄存器值是否符合预期
  2. 检查调用API前RSP的值是否为16字节对齐(RSP & 0xF == 0)
  3. 若API返回NULL,调用GetLastError获取具体错误码(汇编中调用后错误码存在RAX中)

内容的提问来源于stack exchange,提问作者Wrench56

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:38:10