首先说明一下为什么虽然Explorer.exe本身并没有直接调用StretchDIBits但还是会因为这个BUG受到影响。
Explorer.exe在显示目录时会调用到shell32.dll中的代码,并间接调用到user32.dll中的代码,在user32.dll区间内会设置SEH处理异常,这也是虽然StretchDIBits出错了,但Explorer.exe会因此进入一个无限循环并不会显示错误消息的原因
StretchDIBits是通过LookupIconIdFromDirectoryEx被调用到的。当参数lpBits的最低两位不都为0时,会跳转到77F2C6BE执行一段额外的处理构造一块新的内存空间重新排列对齐并将lpBits指向的内容复制过去,这是因为在win32k.sys中的NtGdiStretchDIBitsInternal函数中,如果lpBits的最低两位为0时会调用ExRaiseDatatypeMisalignment引发一个异常
77F26BE处的代码如下,这段代码的作用是从堆当中分配一块内存,然后将长度为bitmap_file_size,lpBits指向的数据复制到新分 配的内存中,虽然目标地址是安全的,但是这里有个问题是lpBits指向的数据未必有bitmap_file_size个字节,也就是说有可能读到非法地 址
现在回过头来在看看bitmap_file_size是怎么计算出来的
很明显bitmap_file_size的计算公式是
((((BITMAPINFO.bmiHeader.biBitCount * BITMAPINFO.bmiHeader.biPlanes * BITMAPINFO.bmiHeader.biWidth) + 0x1F) & 0xFFFFFFE0) / 8) * BITMAPINFO.bmiHeader.biHeight
所以要利用这个BUG需要满足两个条件:
1. 参数lpBits的最低两位不都为0。这一点可以通过修改可执行文件图标资源的指针来实现,缺省情况下,资源起始地址被排列对齐过,最低两位是0。这里的处理方法是修改指针+1并将图标头及数据相应的向后移1位
2. bitmap_file_size足够大,这一点可以通过调整BITMAPINFO.bmiHeader.biHeight来完成
另外我是用OD+IDA分析这个漏洞的
先IDA看GDI32.DLL,弄明白为什么会出错,然后用OllyDbg attach到Explorer.exe进程上调试
注:附件请不要解压到桌面上测试,否则就算重启了explorer.exe还是会挂掉