通过 EXCEPTION_ACCESS_VIOLATION
可以判断异常类型是非法内存访问。
触发异常的指令地址位于 EIP=A95A3399
,指向非法内存区域。
栈帧底部地址位于 EBP=1127F7C4
,可以帮助恢复函数调用栈的结构。
Type: EXCEPTION_ACCESS_VIOLATION Error: Execute address 0xA95A3399 Address: A95A3399 CallStack: 0xA95A3399<unknown module> msf + D602 xpng_dll + 3A3B2 xpng_dll + 3A514 xpng_dll + 39989 xpng_dll + 3B161 xpng_dll + 31374 ... Regs: EAX=10D6DE00, EBX=10D490E0, ECX=10D521E0, EDX=00000000 ESI=10D52250, EDI=10A4AA20, EBP=1127F7C4, ESP=1127F760, EIP=A95A3399
通过日志记录的调用栈可以恢复函数调用链:
xpng::PacketStreamSocket::NotifyOnClose() msf::TCPChannelConnector::OnChannelClose() msf::TCPChannelConnector::HandleConnectFailed() delete msf::MSFChannelTCP msf::MSFChannelTCP::Close()
当网络连接因为异常原因而中止时,OnChannelClose
函数首先会调用 HandleConnectFailed
函数,其中 HandleConnectFailed
函数会把 MSFChannelTCP
类给释放掉,然后再调用 MSFChannelTCP
类中的 Close
虚函数。
由于此时 MSFChannelTCP
类已经被释放掉,结构体中第一项的虚表指针被 msvcrt(ucrt)
堆管理机制重新指向了之前被释放的一个 Chunk
,所以 Chunk
中的第三项会被当作 Close
虚函数的地址进行调用,导致程序崩溃并抛出 EXCEPTION_ACCESS_VIOLATION
异常。
上面那个虚表指针指向的 Chunk
实际是由相邻的两个小 Chunk
释放之后被 RtlpHpVsChunkCoalesce
函数合并得到的,其中第三项正好是高地址的小 chunk
中被内核 RtlpHpHeapGlobals
异或保护的 Header
,所以只要不重启系统每一次取出的虚函数指针都是相同的,即触发非法执行的 EIP
保持不变。
调试脚本如下,可以在断点处自动打印调用栈和对象地址:
from __future__ import print_function import ida_dbg import ida_ida import ida_lines from idc import * class MyDbgHook(ida_dbg.DBG_Hooks): """ Own debug hook class that implementd the callback functions """ def __init__(self): ida_dbg.DBG_Hooks.__init__(self) # important self.steps = 0 def dbg_stack_trace(self): tmp_ea = get_reg_value('eip') print('#### 0x%X %s' % (tmp_ea, get_func_off_str(tmp_ea))) ebp = get_reg_value('ebp') dep = 1 while 1: tmp_ea = read_dbg_dword(ebp+4) if tmp_ea == 0: break print('#' * dep + '#### 0x%X %s' % (tmp_ea, get_func_off_str(tmp_ea))) ebp = read_dbg_dword(ebp) dep += 1 def dbg_bpt(self, tid, ea): # Object trace print('### 0x%X %s 0x%X' % (ea, get_func_off_str(ea), get_reg_value('ecx'))) # Stack trace self.dbg_stack_trace() # ida_dbg.continue_process() return 0 # Remove an existing debug hook try: if debughook: print("Removing previous hook ...") debughook.unhook() except: pass # Install the debug hook debughook = MyDbgHook() debughook.hook()
https://www.blackhat.com/docs/us-16/materials/us-16-Yason-Windows-10-Segment-Heap-Internals-wp.pdf