我遇到一个错误,有时此代码可以工作,有时则不能:
48 8B 41 08 ; MOV RAX, [RCX + 0x08] gets the refcount
48 FF C8 ; DEC RAX ; decrement refCount
48 89 41 08 ; MOV [RCX + 0x08], RAX ; update refCount in obj
48 83 F8 00 ; CMP RAX, 0
74 01 ; JE +1 ; (skips early return if RAX is zero)
C3 ; RET ; (return from function)
51 ; PUSH RCX ; to cache it
48 8B 49 10 ; MOV RCX, [RCX + 0x10] ; Read the child object into RCX
48 83 F9 00 ; CMP RCX, 0 ; ensure the child object hasn't been set to Nothing
74 11 ; JE 0x11 ; jump the next 17 bytes to avoid calling release
48 8B 01 ; MOV RAX, [RCX] ; Get vtable pointer from [RCX] into RAX
48 8B 40 10 ; MOV RAX, [RAX + 0x10] ; Get IUnknown::Release function pointer from [RAX + 0x10] into RAX (QI + hex(ptr_size * 2) for Release)
48 83 EC 28 ; SUB RSP, 0x28 ; Allocate shadow space
FF D0 ; CALL RAX ; Call the function pointed to by RAX
48 83 C4 28 ; ADD RSP, 0x28 ; Deallocate shadow space
59 ; POP RCX ; to restore it to the parent object
48 B8 {addrCoTaskMemFree} ; MOV RAX, addrCoTaskMemFree
48 83 EC 28 ; SUB RSP, 0x28
FF D0 ; CALL RAX
48 83 C4 28 ; ADD RSP, 0x28
48 31 C0 ; XOR RAX, RAX
C3 ; RET
此代码的目的是对象的 IUnknown::Release 方法的 64 位 Windows Intel 实现。这里我有一个父对象和一个子对象,这是父对象的release方法。
IUnknown::发布应该:
作为参考,父对象的内存布局是这样的
偏移 | 价值 | 尺寸 |
---|---|---|
0x00 | 虚函数表指针 | 0x08 - 64 位 |
0x08 | 引用计数 | 0x08 - 64 位 |
0x10 | **孩子VTable | 0x08 - 64 位 |
我正在尝试追踪一个复杂的错误 - 当我让父对象将其自身的第二个实例作为子对象保存,并且该对象作为子对象不保存任何内容时,此代码工作正常。所以父->子->nullptr
但是,如果我让父级保存其他一些 COM 对象(例如 VBA.Collection)的实例,那么我会崩溃。
我尝试制作代码的每个步骤的图表,并记下寄存器,以确保流程正确,请参阅此处或下图。但我无法弄清楚。我认为这可能是影子堆栈空间的问题,因为有时我在函数调用之前使用堆栈,但我感觉我正在用头撞砖墙。调试这个非常繁琐,汇编代码是在运行时注入内存的VBA中的thunk,所以我必须使用x64dbg附加到Excel.exe,在为函数代码分配的内存处创建一个断点,注入它并等待要命的断点。 VBA 被解释,因此调用堆栈非常混乱。 我希望有人可以查看我的程序集并立即发现问题,我是一个汇编新手,尤其是我只为这个项目学习的 64 位。
PUSH ECX
和分配影子空间的方式的组合,您的堆栈未对齐。您也可以去掉相互抵消的
ADD
/SUB
对。从 JE 0x11
开始的代码可能是:SUB RSP, 0x20 ; Allocate shadow space
CALL RAX ; Call the function pointed to by RAX
MOV RCX, [RSP + 0x20] ; to restore it to the parent object
MOV RAX, addrCoTaskMemFree
CALL RAX
ADD RSP, 0x28 ; free shadow space and discard saved RCX
XOR EAX, EAX ; 32 bit ops zero extend no need for REX
RET