VBA直接执行汇编实现HelloWorld:返回触发Access Violation求助
问题分析与解决方案
错误原因
触发访问违例的核心问题是栈不平衡,结合COM方法调用约定的细节:
- 32位COM方法采用
stdcall调用约定,调用base.DoNothing时,会先将对象的this指针压入栈作为第一个参数,栈结构变为:[返回地址] → [this指针]。 - 原shellcode执行完
MessageBoxW后,stdcall会自动清理自身4个参数的16字节栈空间,但此时栈顶仍残留this指针。 - 原shellcode最后用
ret指令,会错误地将this指针当作返回地址弹出并跳转,导致内存访问错误。 - 另外,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
相关产品推荐
相关产品推荐

