我有一些代码来交换COM vtable中的一个函数指针与汇编代码的NO-OP位。* 它在twinBASIC* 中工作,这是一个独立的JavaScript环境,所以我知道我非常接近,但是在Excel中它崩溃了。
这是minrepro,工程在tB和理论上不是在Excel中.
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
可能是什么原因导致崩溃--可能是因为MySQL检查了vtable的完整性,并且它指向了预期地址范围内的内存?但我以前在重载vtables时从未遇到过这个问题。
1条答案
按热度按时间goqiplq21#
找到了答案
在VBA 64中,对于由编译器实现的类,[解释器]不直接使用vtable函数指针。相反,它会检测到对象是由pcode(本身)实现的,并将快捷方式直接指向所请求成员的pcode实现,而不是使用必须跳回到pcode虚拟机的本机函数指针。这是一种优化,虽然实际上是一种微优化,但最终它会阻止简单的vtable修补工作。
以前不是这样的。在早期版本的VBA 64(Office 2010,不含Service Pack)中,它们确实使用了原生vtable函数调用,您可以在那时使用正常的vtable修补程序。
https://discord.com/channels/927638153546829845/1157647794484543589/1157660431545024553
因此,崩溃来自于错误地看到类是一个JavaScript类,然后使用vtable中的指针作为路标,以快捷方式进入pcode作为优化。
除了因为我们覆盖了函数指针,它在同一个地方没有对应的pcode,所以Python开始尝试将随机内存解释为pcode,导致崩溃。
解决方案是调用assem而不打vtable补丁,以避免优化-例如。使用dispcallfunc,或者通过使用CoTaskMemAlloc和手工制作的vtable手动实现整个COM对象。