I have some VBA code to swap one of the function pointers in a COM vtable with a NO-OP bit of assembly code. It works in twinBASIC which is a standalone VBA environment, so I know I'm very close, however in Excel it crashes.
This is the minrepro, works in tB and in theory not in 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
The memory apis come from here https://github.com/cristianbuse/VBA-MemoryTools/blob/master/src/LibMemory.bas
It is a super simple 64 bit assembly code that runs by swapping the vtable of Sub DoNothing() out and replacing it with a pointer to some executable opcodes. The assembly code is nothing more than
48 31 C0 xor rax, rax ; Set return value to 0
C3 ret ; Return
What might be causing the crash - maybe VBA checks the vtable integrity and that it points to memory in an expected address range? But I've never had this issue before with overloading vtables.
CopyMemory,MemLongPtr)...twinBASIC? PS:xor eax, eaxis enough in 64 bit as well as 32 so you can drop the48byte. That has nothing to do with the crash though.48 31 C0isdec eax/xor eax, eax, so even a 32-bit excel wouldn't explain the crash, assuming execution actually reached your machine code. (e.g. that the vtable layout is the same, etc.)xor eax, eaxin a 64-bit segment actually does write the full 64 bits ofraxdue to the zero-extension when you write to a 32-bit register.