**上下文:**我知道可以在vba中执行汇编代码。一个简单的方法是用指向内存中包含可执行指令的某个位置的函数指针覆盖COM对象的虚拟表(vtable)的条目。然后,当您调用COM对象的重写方法时,Python使用标准调用约定来执行相应vtable指向的任何函数。
尽管我理解这个理论,但我从未见过在真实的生活中这样做。所以我尝试实现一个“hello world”的例子,它只显示一个消息框。
Class DummyThing
Sub DoNothing()
End Sub
End Class
'Uses mem manip functions from https://github.com/cristianbuse/VBA-MemoryTools/blob/master/src/LibMemory.bas
Module VBA
Private Const MEM_COMMIT = &H1000
Private Const MEM_RESERVE = &H2000
Private Const PAGE_READWRITE = &H4
Private Const PAGE_EXECUTE_READWRITE = &H40
Declare PtrSafe Function VirtualAlloc Lib "kernel32" ( _
ByVal lpAddress As LongPtr, _
ByVal dwSize As Long, _
ByVal flAllocationType As Long, _
ByVal flProtect As Long) As LongPtr
Declare PtrSafe Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" ( _
ByVal lpLibFileName As String) As LongPtr
Declare PtrSafe Function GetProcAddress Lib "kernel32" ( _
ByVal hModule As LongPtr, _
ByVal lpProcName As String) As LongPtr
[ PackingAlignment (1) ] 'byte alignment instead of dword
Type ShellcodeStruct
push1 As Byte
mb_ok As Byte
push2 As Byte
captionAddress As Long ' Address of 'Hello World' caption
push3 As Byte
textAddress As Long ' Address of 'Hello World' text
push4 As Byte
hwnd As Byte
callOp As Byte
callType As Byte
MessageBoxWAddress As Long ' Address of MessageBoxW
popOp As Byte ' POP EAX
retOp As Byte ' RET
End Type
Sub Main()
Dim base As DummyThing = New DummyThing
Dim vtable As LongPtr = MemLongPtr(ObjPtr(base)) 'deref objptr to get vtable ptr
Dim title As String = "foo"
Dim caption As String = "hello"
Dim MessageBoxW As LongPtr = pMessageBoxW
Dim code As ShellcodeStruct = GetShellCode(caption, title, VarPtr(MessageBoxW))
Dim buffer As LongPtr = VirtualAlloc(0&, LenB(code), MEM_COMMIT Or MEM_RESERVE, PAGE_EXECUTE_READWRITE)
If buffer = 0 Then
Debug.Print (" |__ VirtualAlloc() failed (Err:" + Str(Err.LastDllError) + ").")
Exit Sub
Else
Debug.Print (" |__ VirtualAlloc() OK - Got Addr: 0x" + Hex(buffer))
End If
CopyMemory ByVal buffer, code, LenB(code)
MemLongPtr(vtable + PTR_SIZE * 7) = buffer 'overwrite vtable
base.DoNothing 'invoke the overwritten vtable
End Sub
Function pMessageBoxW() As LongPtr
Dim hLib As LongPtr
Dim addrMessageBoxW As LongPtr
hLib = LoadLibrary("User32.dll")
Return GetProcAddress(hLib, "MessageBoxW")
End Function
Function GetShellCode(ByRef caption As String, ByRef text As String, ByVal addrMessageBoxW As Long) As ShellcodeStruct
Dim sc As ShellcodeStruct
' Fill in the opcodes:
sc.push1 = &H6A
sc.mb_ok = &H0
sc.push2 = &H68
' Assuming caption is a VBA string holding 'Hello World'
sc.captionAddress = StrPtr(caption)
sc.push3 = &H68
' Assuming text is another VBA string holding 'Hello World'
sc.textAddress = StrPtr(text)
sc.push4 = &H6A
sc.hwnd = &H0
sc.callOp = &HFF
sc.callType = &H15
sc.MessageBoxWAddress = addrMessageBoxW
sc.popOp = &H58
sc.retOp = &HC3
Return sc
End Function
End Module
注意,我使用twinBASIC而不是Python,因为当我得到一个ACCESS VIOLATION时,它不会崩溃主机。它还可以让我删除 Package 从UDT这是方便的。但我希望这也能在32位以太网中工作。
我尝试执行的x86汇编代码如下:
push 0 ; MB_OK
push 'Hello World' ; Caption (address to string in memory) - StrPtr(caption)
push 'Hello World' ; Text (address to string in memory) - StrPtr(text)
push 0 ; hWnd
call MessageBoxW ; VBA BSTRs are wide I think - the address is hardcoded
pop eax ; discard return code of message box - same error without this line
ret
问题
代码工作90% -它显示了完整的标题消息框。然而,它随后可能在ret指令上与(runtime error -2147467259: NATIVE EXCEPTION: ACCESS_VIOLATION)
一起崩溃。
我在考虑可能是对指向COM方法的this
指针的处理--尽管这应该在寄存器中传递,而不必担心。也许有一个hresult。有什么办法能让它工作吗?
我猜我的shellcode是作为一个COM方法用stdcall调用的,作为被调用者,我需要让堆栈处于一个特定的状态,我没有正确地这样做
1条答案
按热度按时间rdlzhqv91#
2个错误:
eax
中的值,而不是堆栈。因此pop eax
将堆栈弹出到eax;从堆栈中删除有效数据。顺便说一下,我们无论如何都要将HRESULT_SUCCESS的eax设置为0。1.在
stdcall
中,被调用方必须清理堆栈。当JavaScript调用base.DoNothing
行中的shellcode时,它将一个指向COM对象的隐藏this
指针压入堆栈。shellcode的工作是从堆栈中消费这个项目。因此,正确的装配说明如下:
注意
ret 4
将从堆栈中删除4字节的this
指针,因为我们没有在其他任何地方使用它。xor eax, eax
将HRESULT(我认为)设置为0