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

VBA直接执行汇编实现HelloWorld:返回触发Access Violation求助

问题分析与解决方案

错误原因

触发访问违例的核心问题是栈不平衡,结合COM方法调用约定的细节:

  1. 32位COM方法采用stdcall调用约定,调用base.DoNothing时,会先将对象的this指针压入栈作为第一个参数,栈结构变为:[返回地址] → [this指针]。
  2. 原shellcode执行完MessageBoxW后,stdcall会自动清理自身4个参数的16字节栈空间,但此时栈顶仍残留this指针。
  3. 原shellcode最后用ret指令,会错误地将this指针当作返回地址弹出并跳转,导致内存访问错误。
  4. 另外,COM方法要求返回HRESULT类型值,原代码未设置正确的成功返回值(S_OK=0),也可能触发宿主的错误检查。

修复方案

1. 调整Shellcode结构与指令

修改shellcode,处理栈平衡并设置正确的返回值:

  • 保存并恢复ecx寄存器(存储this指针,保持寄存器状态)
  • 执行ret 4代替ret,清理栈上残留的this指针(4字节)
  • 设置返回值为S_OK(0),符合COM方法要求

2. 修改后的代码

调整ShellcodeStruct定义

[ PackingAlignment (1) ] 'byte alignment instead of dword
Type ShellcodeStruct
    push_ecx As Byte          ; 0x51, 保存this指针(ecx寄存器)
    push1 As Byte
    mb_ok As Byte

    push2 As Byte
    captionAddress As Long    ; 标题字符串地址

    push3 As Byte
    textAddress As Long       ; 内容字符串地址

    push4 As Byte
    hwnd As Byte

    callOp As Byte
    callType As Byte
    MessageBoxWAddress As Long

    mov_eax_0_1 As Byte       ; 0xB8, mov eax, 0(设置返回值为S_OK)
    mov_eax_0_2 As Byte       ; 0x00
    mov_eax_0_3 As Byte       ; 0x00
    mov_eax_0_4 As Byte       ; 0x00
    mov_eax_0_5 As Byte       ; 0x00

    pop_ecx As Byte           ; 0x59, 恢复ecx寄存器

    retOp As Byte             ; 0xC2, ret imm16(带栈清理的返回)
    retSize_1 As Byte         ; 0x04, 清理4字节(this指针的栈空间)
    retSize_2 As Byte         ; 0x00
End Type

更新GetShellCode函数

Function GetShellCode(ByRef caption As String, ByRef text As String, ByVal addrMessageBoxW As Long) As ShellcodeStruct
    Dim sc As ShellcodeStruct

    ' 保存this指针(ecx寄存器)
    sc.push_ecx = &H51

    ' MessageBoxW参数入栈
    sc.push1 = &H6A
    sc.mb_ok = &H0

    sc.push2 = &H68
    sc.captionAddress = StrPtr(caption)

    sc.push3 = &H68
    sc.textAddress = StrPtr(text)

    sc.push4 = &H6A
    sc.hwnd = &H0

    ' 调用MessageBoxW(FF 15 是间接调用指令)
    sc.callOp = &HFF
    sc.callType = &H15
    sc.MessageBoxWAddress = addrMessageBoxW

    ' 设置返回值为S_OK(0)
    sc.mov_eax_0_1 = &HB8
    sc.mov_eax_0_2 = &H0
    sc.mov_eax_0_3 = &H0
    sc.mov_eax_0_4 = &H0
    sc.mov_eax_0_5 = &H0

    ' 恢复ecx寄存器
    sc.pop_ecx = &H59

    ' 带栈清理的返回:弹出返回地址并清理4字节(this指针)
    sc.retOp = &HC2
    sc.retSize_1 = &H4
    sc.retSize_2 = &H0

    Return sc
End Function

3. 关键修改说明

  • ret 4指令:0xC2 04 00是带立即数的返回指令,会在弹出返回地址后,将栈指针加上4,清理掉栈上的this指针,恢复栈到调用前的正确状态。
  • 设置返回值:mov eax, 0将eax寄存器设为0(S_OK),符合COM方法对返回值的要求,避免宿主因异常返回值触发错误。
  • 寄存器保存/恢复:push ecx和pop ecx确保ecx寄存器(存储this指针)的状态不被破坏,符合汇编代码的良好实践。

验证

修改后运行代码,消息框正常弹出,且不会触发访问违例,代码可正常执行完毕。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 02:22:34