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

问题描述 投票:0回答:1

我有一些 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

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

它是一个超级简单的 64 位汇编代码,通过交换

Sub DoNothing()
的 vtable 并用指向一些可执行操作码的指针替换它来运行。汇编代码无非就是

48 31 C0          xor    rax, rax                ; Set return value to 0
C3                ret                            ; Return

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

vba assembly com x86-64 shellcode
1个回答
0
投票

找到答案

在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 修补的组件以避免优化 - 例如使用 dispcallfunc,或者使用 CoTaskMemAlloc 和手工制作的 vtable 手动实现整个 COM 对象。

© www.soinside.com 2019 - 2024. All rights reserved.