assembly Hello World直接汇编代码执行在Windows中-如此接近,但返回时存在访问冲突

whitzsjs  于 2023-10-19  发布在  Windows
关注(0)|答案(1)|浏览(93)

**上下文:**我知道可以在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调用的,作为被调用者,我需要让堆栈处于一个特定的状态,我没有正确地这样做

rdlzhqv9

rdlzhqv91#

2个错误:

  1. MessageBox函数返回eax中的值,而不是堆栈。因此pop eax将堆栈弹出到eax;从堆栈中删除有效数据。顺便说一下,我们无论如何都要将HRESULT_SUCCESS的eax设置为0。
    1.在stdcall中,被调用方必须清理堆栈。当JavaScript调用base.DoNothing行中的shellcode时,它将一个指向COM对象的隐藏this指针压入堆栈。shellcode的工作是从堆栈中消费这个项目。
    因此,正确的装配说明如下:
push 0              ; MB_OK
push DWORD PTR [captionAddress]   ; Address of 'Hello World' caption
push DWORD PTR [textAddress]      ; Address of 'Hello World' text
push 0              ; hWnd
call DWORD PTR [MessageBoxWAddress] ; Direct call to MessageBoxW
xor eax, eax        ; Clear EAX register, essentially setting HRESULT to S_OK (0)
ret 4               ; Return, and adjust stack by 4 bytes (for the "this" pointer)

注意ret 4将从堆栈中删除4字节的this指针,因为我们没有在其他任何地方使用它。xor eax, eax将HRESULT(我认为)设置为0

相关问题