0x00
在公司的crash平台上发现每天都存在调用ClipBoard函数时发生崩溃。
函数最终崩溃在 ole32! RemoveClipboardDataObject 中。简易流程如下:
pData = (IDataObject *)GetPropW(hClipWnd, L"ClipboardDataObjectInterface");
__guard_check_icall_fptr(pData->vfptr->Release);
由此可见 pData对应的对象应该是已经被释放到,导致调用 Release 函数出错。
0x01
我们知道,对于虚函数表来说,当image被编译出来以后,它的内容就存在于image的某处。当image被加载到进程地址空间以后,new出的对象会动态更新其虚函数表的指针,指向这些虚表。
由此我们可以根据访问的地址(pData->vfptr 的地址)定位到对应的image与对应的虚表内容。
1、 假设pData->vfptr地址为 x;
2、 Windbg中执行 lm 命令,查看x落在哪个module地址空间内(这里最好不要直接使用ln命令,以为module可能已经被卸载了);
3、 找到对应的module后,假设起base addr为y;
4、 通过 x – y可以得到虚表在Image内的偏移;
5、 打开IDA,分析对应的image,找到实际的虚表内容,确定当前pData指向的对象的实际类型。
0x02
在本例中,问题的原因是当我们使用richedit并且编辑复制时,ole32可能会将riched20.dll中的对象通过 SetPropW 保存到 ClipWnd 中。当外部调用EmptyClipBoard或者其他函数导致ole32的RemoveClipboardDataObject被调用时,ole32会取出当前ClipWnd中保存着的riched20.dll的对象,并调用其Release函数。如果riched20.dll被提前卸载,则客户端崩溃。
检查了下代码,发现某个模块会在特定情况下加载卸载 riched20.dll,试了几把,可以重现问题。
0x03
系统模块间的关系看起来比我们想象的复杂的多,对待系统模块的加载卸载还是要谨慎一点。