作者:nine8
QQ :279933462
杂记:http://hi.baidu.com/tapeout
上篇:http://bbs.pediy.com/showthread.php?t=131294
接着上篇下面是MS11-006补丁的分析, 如果哪里有问题或大家觉得很别扭,请大家帮忙指出
对比补丁发现,几处函数做了修改,其中原来出现问题CreateSizeDIBSECTION函数已经将之前的有符号比较改成了无符号比较,
这样biClrUsed为负数也不会有问题了. 下面提到的IsValidClipData函数中也加入了相同的判断。
另外在进行DIB转Thumbnail的时候,加入了BOOL IsValidClipData(CLIPDATA * pClipdata)函数,这个函数对BITMAPHEADER信息作
了很细致的检查,不过大小是否有超过可用值,相乘的结果是否有溢出等,相当于,不管之前函数是否有问题,先在其前面加个检查
器,如果检查出问题,则不进行后面的转换。以免出现后面函数考虑不全再出现类似的漏洞。
.text:5CB06731 push eax ; struct tagCLIPDATA *
.text:5CB06732 call ?_IsValidClipData@@YGHPAUtagCLIPDATA@@@Z ; _IsValidClipData(tagCLIPDATA *)
.text:5CB06737 test eax, eax
.text:5CB06739 jz loc_5CB0691B
来判断clip中的DIB数据是否为有效且合法格式。下面对这个函数简要分析:
text:5CB0641F pClipdata = dword ptr 8
.text:5CB0641F
.text:5CB0641F mov edi, edi
.text:5CB06421 push ebp
.text:5CB06422 mov ebp, esp
.text:5CB06424 push ebx
.text:5CB06425 push esi
.text:5CB06426 mov esi, [ebp+pClipdata]
.text:5CB06429 lea eax, [ebp+pClipdata]
.text:5CB0642C push eax ; unsigned __int32 *
.text:5CB0642D push 4 ; unsigned __int32
.text:5CB0642F push dword ptr [esi] ; unsigned __int32
.text:5CB06431 xor ebx, ebx ; ebx = 0
.text:5CB06433 call ?ULongSub@@YGJKKPAK@Z ; ULongSub(ulong,ulong,ulong *) ; 检查结构体大小参数
.text:5CB06438 test eax, eax
.text:5CB0643A jl short Return
.text:5CB0643C cmp [ebp+pClipdata], 4
.text:5CB06440 jbe short Return
.text:5CB06442 mov esi, [esi+8] ; esi = pClipdata
.text:5CB06445 mov eax, [esi]
.text:5CB06447 cmp eax, 3 ; CF_METAFILEPICT
.text:5CB0644A jz short loc_5CB06480 ; 判断clip中是否为图元文件格式
.text:5CB0644C cmp eax, 8 ; CF_DIB
.text:5CB0644F jz short loc_5CB06454 ; 判断clip中是否为DIB位图格式
.text:5CB06451 inc ebx
.text:5CB06452 jmp short Return
.....
loc_5CB06454:
.text:5CB06454 lea eax, [ebp+pClipdata]
.text:5CB06457 push eax ; unsigned __int32 *
.text:5CB06458 push 4 ; unsigned __int32
.text:5CB0645A push [ebp+pClipdata] ; unsigned __int32
.text:5CB0645D call ?ULongSub@@YGJKKPAK@Z ; ULongSub(ulong,ulong,ulong *)
.text:5CB06462 test eax, eax
.text:5CB06464 jl short Return
.text:5CB06466 cmp [ebp+pClipdata], 28h
.text:5CB0646A jb short Return
.text:5CB0646C lea eax, [esi+4] ; eax = pclipdata->pclipdata + sizeof(DWORD) = pbmi
.text:5CB0646F test eax, eax ; 跳过文件类型标识的那个DWORD
.text:5CB06471 jz short Return
.text:5CB06473 push [ebp+pClipdata] ; unsigned __int32
.text:5CB06476 push eax ; struct tagBITMAPINFO *
.text:5CB06477 call ?_ValidateBitmapInfoAndPixelData@@YGHPBUtagBITMAPINFO@@K@Z ; 这个函数会对DIP的BITMAPHEADER信息详细的判断
.text:5CB0647C mov ebx, eax
.text:5CB0647E jmp short Return
.....
函数 ULongSub(ULONG num1, ULONG num2, ULONG * pulSubRes)
如果 ulNum1 >= ulNum2, *pulSubResult = ulNum1 - ulNum2, return 0 ;
如果 ulNum1 < ulNum2, *pulSubResult = 0xffffffff, return 0x80070216.
?ULongSub@@YGJKKPAK@Z proc near
.text:5CB06175
.text:5CB06175
.text:5CB06175 arg_0 = dword ptr 8
.text:5CB06175 arg_4 = dword ptr 0Ch
.text:5CB06175 arg_8 = dword ptr 10h
.text:5CB06175
.text:5CB06175 mov edi, edi
.text:5CB06177 push ebp
.text:5CB06178 mov ebp, esp
.text:5CB0617A mov edx, [ebp+arg_8] ; edx = arg8
.text:5CB0617D mov ecx, [ebp+arg_0] ; ecx = arg0
.text:5CB06180 or dword ptr [edx], 0FFFFFFFFh ; [arg8] = -1
.text:5CB06183 cmp ecx, [ebp+arg_4] ; ecx = arg0, cmp arg0, arg4
.text:5CB06186 mov eax, 80070216h
.text:5CB0618B jb short return
.text:5CB0618D sub ecx, [ebp+arg_4] ; arg0 - arg4
.text:5CB06190 xor eax, eax ; eax = 0
.text:5CB06192 mov [edx], ecx ; [arg8] = arg0 - arg4
.text:5CB06194
.text:5CB06194 return:
.text:5CB06194 pop ebp
.text:5CB06195 retn 0Ch
函数: ValidateBitmapInfoHeader(BITMAPINFO * pbmi, ULONG ulSize)
.text:5CB061E2 mov esi, [ebp+pbmi]
.text:5CB061E5 mov eax, [esi] ; pbmi->bmiHead.biSize
.text:5CB061E7 cmp eax, 28h ; 检查header中的结构体大小字段
.text:5CB061EA mov [ebp+biSize], eax
.text:5CB061ED jb Return_FALSE
.text:5CB061F3 cmp eax, 1000h
.text:5CB061F8 ja Return_FALSE
text:5CB061FE mov eax, [esi+4] ; pbmi->bmiHeader.biWidth
.text:5CB06201 test eax, eax ; 检查DIB宽度(这里单位为像素)
.text:5CB06203 mov [ebp+biWidth], eax
.text:5CB06206 jz Return_FALSE
.text:5CB0620C mov ebx, [esi+8] ; pbmi->bmiHeader.biHeight
.text:5CB0620F test ebx, ebx ; 检查DIB高度(单位也是像素)
.text:5CB06211 jz Return_FALSE
.text:5CB06217 movzx edi, word ptr [esi+0Eh]
.text:5CB0621B cmp edi, 20h ; pbmi->bmiHeader.biBitCount
.text:5CB0621B ; 检查像素的BitCount,是否超出最大的32bit
.text:5CB0621E ja Return_FALSE
.text:5CB06224 lea ecx, [ebp+pbmi] ; pbmi is a mul res tmp register here, not the argument any more!!!
.text:5CB06227 push ecx
.text:5CB06228 push eax ; eax = biWidth
.text:5CB06229 push 20h
.text:5CB0622B call _MultiplyCheckOverflow@12 ; 检查 0x20 * biWidth 是否overflow(应该是为了计算Row Bytes做检测, 看是否有overflow)
; 因为: row bits = bicount * biwidth
; 其中, bitcount取值为 1, 4, 8, 16, 24, 32, 所以取32最大值
.text:5CB06230 test eax, eax
.text:5CB06232 jz Return_FALSE
.text:5CB06238 mov eax, ebx ; ebx = biHeight
.text:5CB0623A cdq ; check biHeight <0 or >= 0
.text:5CB0623B mov ebx, eax
.text:5CB0623D lea eax, [ebp+pbmi]
.text:5CB06240 push eax ; arg3
.text:5CB06241 mov eax, [ebp+pbmi]
.text:5CB06244 shr eax, 3
.text:5CB06247 xor ebx, edx ; biHeight < 0, ebx = ~ebx; else ebx = ebx
.text:5CB06249 sub ebx, edx
.text:5CB0624B add eax, 3
.text:5CB0624E push ebx ; arg2
.text:5CB0624F and eax, 0FFFFFFFCh
.text:5CB06252 push eax ; arg1
.text:5CB06253 call _MultiplyCheckOverflow@12 ; 检查 检查SizeImage是否有溢出
; 因为: SizeImage = row bytes * biHeight
; 其中: row bytes = (row bits / 8 + 3) & 0xffffffffc;
; +3 是为了当相除有余数,即不是整好够一个4byte,来通过+3使其4byte对齐
; 图像是4byte对齐操作
.text:5CB06258 test eax, eax
.text:5CB0625A jz short Return_FALSE
.text:5CB0625C mov eax, 40000000h
.text:5CB06261 cmp [ebp+pbmi], eax ; 判断计算出来的图片的尺寸大小是否有超过0x40000000 bytes
.text:5CB06264 ja short Return
.text:5CB06266 mov ecx, [esi+14h] ; pbmi->bmiHeader.biSizeImage
.text:5CB06269 cmp ecx, eax ; 判断biSizeImage是否有超过0x40000000 bytes
.text:5CB0626B mov [ebp+pbmi], ecx
.text:5CB0626E ja short Return_FA
.text:5CB06270 mov eax, [esi+20h] ; biClrUsed
.text:5CB06273 cmp eax, 100h
.text:5CB06278 ja short Return_FALSE ; 这里就是对问题函数中的再次判断,用的是ja不是jg, 问题中修补后也改成了ja
...... ; 找到了biClrUsed后。下面就没有再分析了
对应的C描述:
#include <windows.h>
#include <wingdi.h>
#include <propidl.h>
BOOL IsValidClipData(CLIPDATA * pClipdata)
{
ULONG ulSubResult
DWORD dwPicType;
BITMAPINFO * pbmi;
// 检查结构体大小
if (ULongSub( pClipdata->cbSize, 4, &ulSubResult) < 0 )
{
return FALSE;
}
if (ulSubResult <= 4)
{
return FALSE;
}
dwPicType = *(pClipdata->pClipData);
if (dwPicType == CF_METAFILEPICT)
{
}
else if (dwPicType == CF_DIB)
{
if ((ULongSub(ulSubResult, 4, &ulSubResult)) < 0)
{
return FALSE;
}
if (ulSubResult < 0x28)
{
return FALSE;
}
pbmi = (BITMAPINFO *)(pClipdata->pClipData + sizeof(DWORD))
if (!pbmi)
{
return FALSE;
}
// 验证pbmi结构信息是否有效
return( ValidateBitmapInfoAndPixelData(pbmi, ulSubResult) );
}
else
{
return TRUE;
}
}
// if ulNum1 >= ulNum2, *pulSubResult = ulNum1 - ulNum2, return 0 ;
// if ulNum1 < ulNum2, *pulSubResult = 0xffffffff, return 0x80070216.
INT ULongSub(ULONG ulNum1, ULONG ulNum2, ULONG * pulSubResult)
{
*pulSubResult = 0xffffffff;
if (ulNum1 < ulNum2)
{
return 0x80070216;
}
*pulSubResult = ulNum1 - ulNum2;
return 0;
}
INT ULongAdd(ULONG ulNum1, ULONG ulNum2, ULONG * pulAddResult)
{
*pulAddResult = 0xffffffff;
// add msb bit overflow, since unsigned
if (ulNum1 + ulNum2 < ulNum1)
{
return 0x80070216;
}
*pulAddResult = ulNum1 + ulNum2;
return 0;
}
INT ValidateBitmapInfoAndPixelData(
BITMAPINFO * pbmi,
ULONG ulSize)
{
ULONG ulAddResult;
//
if ( ValidateBitmapInfoHeader(pbmi, ulSize) == FALSE )
{
return FALSE;
}
if ( ComputeColorTableSize(pbmi, &ulSize) < 0 )
{
return FALSE;
}
if (ULongAdd(0x28, ulSize, &ulAddResult) < 0)
{
return FALSE;
}
if (ULongAdd(ulAddResult, CalcBitmapSize(pbmi, &ulAddResult), &ulAddResult) < 0)
{
return FALSE;
}
if (ulSize < ulAddResult)
{
return FALSE;
}
return TRUE;
}
ValidateBitmapInfoHeader(BITMAPINFO * pbmi, ULONG ulSize)
{
DWORD biSize;
DWORD dwMulRes;
if (ulSize < 0x28)
{
return FALSE;
}
// check biSize
biSize = pbmi->bmiHeader.biSize;
if ((biSize < 0x28) || (biSize > 0x1000))
{
return FALSE;
}
// check biWidth
biWidth = pbmi->bmiHeader.biWidth;
if (!biWidth)
{
return FALSE;
}
// check biHeight
if( !(pbmi->bmiHeader.biHeight) )
{
return FALSE;
}
// check biBitCount
if ((pbmi->bmiHeader.biBitCount) > 0x20)
{
return FALSE;
}
// 检查 0x20 * biWidth 是否overflow(应该是为了计算Row Bytes做检测, 看是否有overflow)
// 因为: row bits = bicount * biwidth
// 其中, bitcount取值为 1, 4, 8, 16, 24, 32, 所以取32最大值
if ( !(MultiplyCheckOverflow(0x20, biWidth, &dwMulRes)) )
{
return FALSE;
}
// 检查 检查SizeImage是否有溢出
// 因为: SizeImage = row bytes * biHeight
// 其中: row bytes = (row bits / 8 + 3) & 0xffffffffc;
// +3 是为了当相除有余数,即不是整好够一个4byte,来通过+3使其4byte对齐
// 图像是4byte对齐操作
if (!(MultiPlyCheckOverFlow(
( (dwMulRes / 8 + 3) & 0xfffffffc ),
((biHeight < 0) ? (~biHeight + 1): biHeight),
&dwMulRes )))
{
return FALSE;
}
// 判断计算出来的图片的尺寸大小是否有超过0x40000000 bytes
if (dwMulRes > 0x40000000)
{
return FALSE;
}
// 判断biSizeImage是否有超过0x40000000 bytes
biSize = pbmi->bmiHeader.biSizeImage;
if (biSize > 0x40000000)
{
return FALSE;
}
// 判断biClrUsed, 注意补丁这里换成了无符号的比较!!!
// 从而修复了之前,有符号比较,当其为负数,然后复制数据
// 当做无符号的漏洞,造成stack overflow.
if (pbmi->bmiHeader.biClrUsed > (DWORD)0x100)
{
return FALSE;
}
}
// 检查num1与num2相乘是否有溢出, 相乘结果存入pdwMulRes地址
// 如果有溢出, 返回0; 无溢出,则返回1;
INT MultiplyCheckOverflow(DWORD dwNum1, DWORD dwNum2, DWORD * pdwMulRes)
{
*pdwMulRes = dwNum1 * dwNum2;
if (dwNum1 == 0)
{
return 0;
}
// 如果之前的乘机结果,除以dwNum1还等于dwNum2,表示相乘的时候结果无溢出
if ((*pdwMulRes / dwNum1) == dwNum2)
{
return 1;
}
else
{
return 0;
}
}
上传的附件: