在 VBA 中直接执行汇编代码 - 仅在 Excel 中失败

Gre*_*edo 3 com assembly vba x86-64 shellcode

我有一些 VBA 代码,用于将 COM vtable 中的函数指针之一与汇编代码的 NO-OP 位交换。它在 twinBASIC 中工作,这是一个独立的 VBA 环境,所以我知道我已经非常接近了,但是在 Excel 中它崩溃了。

这是 minrepro,可以在 tB 中运行,理论上不能在 Excel VBA 中运行。

Class DummyThing
    Sub DoNothing()
    End Sub
End Class

Module VBA
    Private Const MEM_COMMIT As Long = &H1000
    Private Const MEM_RESERVE As Long = &H2000
    Private Const PAGE_EXECUTE_READWRITE  As Long = &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

    Sub Main()
        Dim code(1 To 4) As Byte
        code(1) = CByte(&h48)    
        code(2) = CByte(&h31)
        code(3) = CByte(&hC0) 'xor rax, rax to clear it - this is like setting the hresult to 0
        code(4) = CByte(&hC3) 'ret

        Dim buffer As LongPtr
        buffer = VirtualAlloc(0&, UBound(code) - LBound(code) + 1, MEM_COMMIT Or MEM_RESERVE, PAGE_EXECUTE_READWRITE)

        If buffer = 0 Then
            Debug.Print "VirtualAlloc() failed (Err:" ; Err.LastDllError ; ")."
            Exit Sub
        End If

        CopyMemory ByVal buffer, code(1), UBound(code) - LBound(code) + 1

        Dim base As DummyThing
        Set base = New DummyThing

        Dim vtable As LongPtr
        vtable = MemLongPtr(ObjPtr(base))

        MemLongPtr(vtable + PTR_SIZE * 7) = buffer

        base.DoNothing 'Excel VBA crashes here, tB prints the message below
        Debug.Print vbNewLine ; "Done!"
    End Sub

End Module
Run Code Online (Sandbox Code Playgroud)

内存 api 来自这里https://github.com/cristianbuse/VBA-MemoryTools/blob/master/src/LibMemory.bas

它是一个超级简单的 64 位汇编代码,通过交换 out 的 vtableSub DoNothing()并将其替换为指向一些可执行操作码的指针来运行。汇编代码无非就是

48 31 C0          xor    rax, rax                ; Set return value to 0
C3                ret                            ; Return
Run Code Online (Sandbox Code Playgroud)

可能是什么原因导致崩溃 - 也许 VBA 检查 vtable 完整性并且它指向预期地址范围内的内存?但我以前从未遇到过重载 vtable 的问题。

Gre*_*edo 7

找到了答案

在VBA64中,对于VBA实现的类,[解释器]不直接使用vtable函数指针。相反,它检测到该对象是由 VBA(本身)实现的,并且是所请求成员的直接 pcode 实现的快捷方式,而不是使用必须跳回 pcode 虚拟机的本机函数指针。这是一种优化,虽然实际上是一种微观优化,但最终它会阻止简单的 vtable 修补工作。

情况并非总是如此。在 VBA64 的早期版本(没有服务包的 Office 2010)中,它们确实使用了本机 vtable 函数调用,当时您可以使用普通的 vtable 修补。

https://discord.com/channels/927638153546829845/1157647794484543589/1157660431545024553

因此,崩溃来自于 VBA 看到该类是一个 VBA 类,然后使用 vtable 中的指针作为指向 pcode 的快捷方式的路标作为优化。

除非我们覆盖了函数指针,否则它在同一位置没有相应的 pcode,因此查找失败或 VBA 开始尝试将随机内存解释为 pcode,从而导致崩溃。

解决方案是在不进行 vtable 修补的情况下调用 assem,以避免优化 - 例如使用 dispcallfunc,或通过使用 CoTaskMemAlloc 和手工制作的 vtable 手动实现整个 COM 对象。