Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
HallWatch — كاشف وضع المستخدم الذي يلتقط استدعاءات النظام غير المباشرة. يحاصر Hell's Hall وTartarus' Gate وRecycledGate وVEH syscalls والعديد غيرها. | Kitploit
أدوات/GitHubGitHub/zypherion-technologies/hallwatch
أدوات دفاعيةتحليل البرمجيات الخبيثةتحليل الملفات الثنائيةاستخبارات التهديداتالاستجابة للحوادث
GitHubzypherion-technologies/hallwatch

HallWatch

كاشف وضع المستخدم الذي يلتقط استدعاءات النظام غير المباشرة. يحاصر Hell's Hall وTartarus' Gate وRecycledGate وVEH syscalls والعديد غيرها.

عرض المستودع

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
8710منذ 2 أشهرتمت المراجعة من قبل Kitploit

HallWatch

License: GPL v3 Website Discord Telegram X

حقوق الطبع والنشر (C) 2026 Adam Zypherion <[email protected]> — مرخص تحت GPL-3.0.


الاستدعاءات النظامية غير المباشرة (Indirect syscalls) بسيطة بمجرد رؤية واحدة. المُحمِّل (loader) يجتاز ntdll، ويقرأ رقم SSN من مقطع Nt* التمهيدي، ويجد البايتين 0F 05 بعد ذلك بقليل، ويضبط r10/rax/rdx/r8/r9 بنفسه، ثم يقفز مباشرةً إلى تلك البايتين. يحدث الاستدعاء النظامي من داخل ntdll. لم يتم لمس خطافك (hook) على تصدير kernel32. لم يتم لمس خطافك على تصدير ntdll. يشير [RSP] في لحظة SYSCALL إلى صفحة RWX الخاصة بالمُحمِّل، لكن لا شيء في الاستدعاء يبدو غير معتاد من الخارج.

root@kitploit:~
HellHall proc
    mov  r10, rcx
    mov  eax, dwSSN
    jmp  qword ptr [qAddr]
    ret
HellHall endp

للاختلافات أسماء. يستخدم Tartarus' Gate و RecycledGate تعليمة syscall الخاصة بأحد المؤشرات (stubs) لكن SSN لآخر، لذا حتى لو سجلت اسم المؤشر الذي يُبلغ عنه خطافك، فهو كذب. يستخدم VEH syscall انتهاك وصول (access violation) عمدًا ويستخدم VEH الخاص به لإعادة كتابة السياق بحيث يهبط RIP على تعليمة syscall الخاصة بـ ntdll مع إعداد السجلات مسبقًا. لا يستخدم Hell's Gate ntdll على الإطلاق. يكتب المُحمِّل 0F 05 في صفحة RWX الخاصة به ويعمل من هناك.

الجواب على مستوى وضع kernel (KM) هو برنامج تشغيل (driver)، قد أصل إليه بالفعل مع الوقت وأصدر مشروعًا، لكن هذا لن يحدث قريبًا :D


يبدو PAGE_GUARD كما لو كان يجب أن يعمل. قم بتعليم الصفحة التي تحتوي على بايتات syscall بـ PAGE_GUARD، والتقط استثناء STATUS_GUARD_PAGE_VIOLATION في VEH، وافحص، وأعد التوجيه إلى ترامبولين خاص (private trampoline)، واضبط علامة المصيدة (trap flag)، وتخطَّ بخطوة واحدة (single-step out)، وأعد تسليح الصفحة. مصيدة واحدة لكل استدعاء Nt، بغض النظر عن كيفية وصول المُحمِّل إلى هناك. يعمل ضد كل شيء باستثناء Hell's Gate.

المشكلة أن نظام التشغيل لا يتعاون. PAGE_GUARD هو طلقة واحدة، ماذا يعني ذلك؟ في كل مرة يتم إطلاقها، يتم مسح البت وعليك إعادته. معالجك (handler) يقوم باستدعاءات نظامية (NtProtect لإعادة تعيين الحارس، NtContinue للاستئناف) وهذه الاستدعاءات النظامية لها مؤشرات stubs وتلك المؤشرات موجودة على الصفحة التي قمت بحراستها للتو. لقد عملت حول معظم ذلك باستخدام مؤشرات استدعاء نظامي خاصة (private syscall stubs) مبنية على صفحة منفصلة نملكها (خصص RWX، اكتب mov r10,rcx; mov eax,SSN; syscall; ret، قفل RX، لا تلمس ntdll's)، لكنها كانت هشة. كل إصدار ثانوي من Windows غيَّر التوقيت. كل خيط (thread) ينتجه النموذج كان سباقًا آخر ضد عامل النزاهة (integrity worker) الذي كان يعيد بناء الحارس. كان نجاح العرض التوضيحي حوالي 30 إلى 50 بالمائة عبر عشر مرات تشغيل على نفس الجهاز.

في النهاية توقفت عن محاولة إقناع Windows بأن PAGE_GUARD يجب أن يتصرف بالطريقة التي أريدها، وحاولت استبدال البايت بدلاً من ذلك.


إذا كانت لديك مشاكل في الفهم، شاهد هذا الفيديو :)

https://github.com/user-attachments/assets/0cb670fd-e51b-413e-bf00-08f9297888ed


البايت في تعليمة syscall هو 0F 05. البايت الأول وحده (0F) هو البادئة لمجموعة من ثنائيات البايت opcode بما في ذلك SYSCALL و CPUID و RDTSC. بمفرده ليس قابلاً للتنفيذ؛ تحتاج وحدة المعالجة المركزية (CPU) إلى البايت الثاني لفك التشفير. إذا استبدلت البايت الأول بـ 0xCC (INT3)، يصبح الزوج CC 05، والذي تفك تشفيره وحدة المعالجة المركزية على أنه INT3 متبوعًا ببايت عشوائي لا تصل إليه أبدًا. أي مسار كود يهبط على ذلك العنوان يتسبب في EXCEPTION_BREAKPOINT.

هذه هي الآلية بأكملها. يكتشف VEH الخاص بنا نقطة التوقف (breakpoint)، ويبحث عن المؤشر الذي ينتمي إليه العنوان (نبني الخريطة في وقت التهيئة عن طريق تعداد تصديرات ntdll)، ويجري ثلاث فحوصات على من ظهر، ويضبط Context->Rip على ترامبولين خاص يقوم بالاستدعاء النظامي الحقيقي ويعود. تظل البايت CC. يضربها المستدعي التالي بنفس الطريقة، لذلك لا يوجد تبديل لحراسة الصفحة ذهابًا وإيابًا.

للتوضيح: نعم، هذا هو التعليق (hooking) عن طريق استبدال البايت. لكنه ليس في البايت الذي يعنيه الناس عادةً بـ "خطاف ntdll". الخطاف الكلاسيكي لـ EDR يستبدل أول بايتات المؤشر بـ JMP <my_func>:

root@kitploit:~
ntdll!NtAllocateVirtualMemory:
  E9 ?? ?? ?? ??       jmp my_hook     ; overwrites "mov r10, rcx"
  ...
  0F 05                syscall
  C3                   ret

هذا هو بالضبط ما تتجنبه الاستدعاءات النظامية غير المباشرة. يقرأ المُحمِّل SSN بنفسه ويقفز مباشرة إلى 0F 05، ولا يتم تشغيل المقطع التمهيدي (ولا قفزتك) أبدًا، ولا يتم تشغيل الخطاف أبدًا. ما نفعله هو استبدال بايت syscall نفسه:

root@kitploit:~
ntdll!NtAllocateVirtualMemory:
  4C 8B D1             mov r10, rcx     ; untouched
  B8 18 00 00 00       mov eax, 18h     ; untouched
  CC 05                int3 / 05        ; was 0F 05, we wrote CC over the 0F
  C3                   ret

الآن لا يهم كيف وصلت إلى هذا العنوان. من خلال المقطع التمهيدي، أو من خلال قفزة غير مباشرة تتجاوز المقطع التمهيدي، أو من خلال إعادة كتابة السياق VEH-syscall التي تضع RIP هناك، أيًا كان. إذا نفذت وحدة المعالجة المركزية تلك البايتة، فإنها تتعثر. نفس عائلة التقنية مثل الخطاف الكلاسيكي، موقع مختلف، تغطية مختلفة تمامًا.

يبدو الترامبولين كما يلي:

root@kitploit:~
F3 0F 1E FA              endbr64
49 89 CA                 mov r10, rcx
B8 <stub SSN>            mov eax, ssn
0F 05                    syscall
C3                       ret

الفحوصات الثلاثة لم تتغير من نسخة PAGE_GUARD لأنها كانت الفحوصات الصحيحة الثلاثة؛ لقد احتاجت فقط إلى مكان موثوق للعيش فيه.

عنوان العودة. [RSP] هو المكان الذي كان سيعود إليه الاستدعاء النظامي. بالنسبة لاستدعاء حقيقي فهو داخل ntdll أو kernel32 أو kernelbase أو أحد مكتبات وقت التشغيل ذات الصلة. بالنسبة لـ Hell's Hall فهو داخل صفحة RWX التي يعمل منها المُحمِّل. لدينا قائمة قصيرة من أهداف العودة الموثوقة المبنية في وقت التهيئة عن طريق استدعاء GetModuleHandle لتلك الأسماء وقراءة نطاقات .text من رؤوس PE الخاصة بهم.

SSN. لقد تم تشغيل المقطع التمهيدي للمؤشر بالفعل قبل أن يصل إلى بايت syscall، لذلك يحمل eax أي قيمة تم تحميلها فيه. إذا قام المُحمِّل بتبديل Tartarus، فلن تتطابق تلك القيمة مع SSN الذي قرأناه من نفس هذا المؤشر في وقت التعداد. نسجل عدم التطابق، ويكتب الترامبولين SSN الصحيح قبل استدعائه النظامي الخاص، لذلك فإن وظيفة kernel التي يتم تشغيلها هي التي تنتمي إلى البايت بدلاً من تلك التي أرادها المُحمِّل. يتم تسجيل التقنية وتحييدها في نفس الخطوة.

تجوال المكدس (Stack walk). RtlVirtualUnwind من السياق الحالي، خمس إطارات لأعلى. يجب أن يكون RIP لكل إطار داخل وحدة معروفة ويجب أن يحتوي على إدخال RUNTIME_FUNCTION. يفشل الكود البرمجي (shellcode) وأدوات ROP في هذا حتى عندما يبدو [RSP] نفسه موثوقًا به، وهو ما يمكن للمُحمِّل تزويره (يمكنه التنبؤ تقريبًا بمكان وجود المستدعي الخاص به في الذاكرة وإنشاء عنوان عودة مزيف معقول هناك).

إذا فشل أي فحص، فإننا ندفع بنية صغيرة إلى حلقة خالية من القفل (lock free ring) ويقوم خيط التصريف (drain thread) بطباعتها في المرة التالية التي يستيقظ فيها

root@kitploit:~
[!! hallwatch !!] استدعاء نظامي غير مباشر (مستدع غير موثوق، SSN خاطئ لهذا المؤشر)
    syscall      : NtAllocateVirtualMemory
    syscall rip  : 0x00007FF827660372
    return addr  : 0x00007FF7EED719D6
    rax (ssn)    : 0x0000000F (stub encodes 0x00000018)
    thread       : 26388

Hell's Gate هو المكان الذي يتوقف فيه INT3 عن المساعدة. لم نقم أبدًا بتصحيح صفحة RWX الخاصة بالمُحمِّل لأننا لم نكن نعرف بوجودها أبدًا.

ما نفعله بدلاً من ذلك هو أبسط ماسح ضوئي يمكن أن يعمل. كل 250 مللي ثانية (أساسًا متكرر جدًا، يستهلك دورات CPU لكنه من أجل إثبات المفهوم) يقوم عامل النزاهة (integrity worker) بجتاز مساحة العناوين باستخدام VirtualQuery، بحثًا عن أي منطقة MEM_COMMIT مع حماية صفحة قابلة للتنفيذ. إذا كانت المنطقة داخل وحدة محملة، يتم تخطيها. إذا كانت داخل مجموعة الترامبولين الخاصة بنا، يتم تخطيها. ما تبقى هو ذاكرة قابلة للتنفيذ أجنبية. نقوم بمسح ما يصل إلى 64 كيلوبايت منها بحثًا عن أزواج بايت 0F 05، ونزيل التكرار حسب العنوان، ونسجل كل نتيجة فريدة مرة واحدة. أعلم أنها طريقة غبية وأنت أيضًا تهدر دورات CPU لكنه إثبات مفهوم لا ينبغي أن يقلل من الموقف لكنه يفعل حاليًا.

هذا يمسك Hell's Gate في تخصيص RWX جديد ويمسك أيضًا بـ shadow ntdll (مُحمِّل يقوم بـ NtMapViewOfSection على ntdll.dll للحصول على نسخة خاصة في عنوان جديد). حالة الظل ممتعة. التعيين (mapping) ليس في لقطة الوحدة الخاصة بنا، لذلك تظهر تعليمات syscall الخاصة به كبايتات قابلة للتنفيذ أجنبية، حتى لو كانت من DLL موقعة بشكل قانوني على القرص.

متغير آخر: يدمج المهاجم 0F 05 في .text الخاص بالثنائي الخاص به بحيث يعيش syscall داخل وحدة محملة بدلاً من صفحة RWX. يتخطى Foreign-RWX الوحدات المحملة، لذلك كان هذا يتسرب من خلاله سابقًا. لذا لدينا ماسح ضوئي آخر يجتاز البايتات القابلة للتنفيذ لكل وحدة، ويتخطى ntdll و win32u و wow64cpu (الوحدات الثلاث التي تحتوي بشكل شرعي على تعليمات syscall)، ويبلغ عن 0F 05 فقط إذا كان هناك C3 خلال البايتات الثلاثة التالية. فحص ret هو ما يجعله قابلاً للاستخدام. يظهر 0F 05 بمفرده في مخرجات المترجم كأجزاء من تعليمات أكبر طوال الوقت لكنه أبدًا لا يجاور C3. مؤشرات syscall الحقيقية تكون دائمًا كذلك. سجل:

root@kitploit:~
[hallwatch] scan: module-text syscall stub at 0x00007FF7FFC9227C in HellsHall-Demo.exe -- Hell's Gate inside loaded module .text

لدى عامل النزاهة مهمتان صغيرتان أخريان. يقرأ البايت في كل مؤصل مُصحَّح (patched stub) كل دورة ويكتب CC مرة أخرى إذا لم يكن CC. هذا هو أبسط إزالة خطاف (unhook) ممكنة: العينة تقرأ 0F الأصلي من مكان ما (تعيين ntdll جديد، PE على القرص، جدول بايتات معروف الجودة) وتكتبها مرة أخرى فوق التصحيح الخاص بنا. نلاحظ ذلك في الدورة التالية ونصحح مرة أخرى.

كل خمسين دورة (حوالي 12.5 ثانية) يقوم العامل بتثبيت VEH الخاص بنا مرة أخرى في رأس السلسلة ويزيل المقبض السابق. يمكن لعينة أن تجتاز LdrpVectorHandlerList وتلغي تسجيلنا؛ يحدد العامل المدة التي تظل فيها تلك الفجوة مفتوحة. اثنتا عشرة ثانية ونصف هي نافذة طويلة إذا كنت تتعرض لهجوم وقصيرة إذا كنت تدير برامج ضارة في صندوق رمل (sandbox). سأقوم على الأرجح بتقصيرها بمجرد أن أقيس تكلفة القيام بذلك أكثر.

يعمل خيط العامل مع علامة TLS مضبوطة يتحقق منها VEH. استدعاءات العامل النظامية الخاصة (NtProtect للتصحيح، NtQuery للمسح) تطلق نقاط التوقف الخاصة بنا مثل أي شيء آخر، لكن العلامة تخبر VEH بتخطي منطق الكشف وإعادة التوجيه بصمت عبر الترامبولين.


يقوم القسم الحرج (critical section) بتهيئة نفسه في المرة الأولى التي يستدعي فيها أي شخص، عبر تبادل مقارنة ثلاثي الحالات (0 = غير مهيأ، 2 = مشغول، 1 = جاهز). إنه قبيح لكنه يتجنب الحاجة إلى مُنشئ ثابت داخل DLL، والذي يمثل على Windows مجموعة منفصلة من المشكلات التي تتضمن قفل المُحمِّل (loader lock) وما إلى ذلك.

شيء واحد يستحق الإشارة إليه من ABI. INT3 هو فخ (trap)، مما يعني أن Context->Rip يشير إلى التعليمة التالية عندما يتم استدعاء VEH الخاص بنا، وليس إلى الفخ نفسه. إذا قمنا بتصحيح البايت عند 0x7FF827660372، يصل Context->Rip إلى VEH كـ 0x7FF827660373. يشير Record->ExceptionAddress إلى الفخ، لكن يجب علينا إعادة تعيين Context->Rip = ExceptionAddress قبل إعادة توجيهه إلى الترامبولين، وإلا فإن الترامبولين يبدأ ببايت متأخر ولا يفعل الاستدعاء النظامي شيئًا مفيدًا. أخبرك بهذا لأنه كان خطأ استغرق مني الكثير من الوقت للعثور عليه


هناك أربعة تصديرات. IscInitialize يُسَلِّح المكتشف؛ يقوم DllMain باستدعائه تلقائيًا لكن يمكنك استدعاؤه من عملية مضيفة إذا كنت تريد قيمة إرجاع. IscGetDetectionCount يعيد عدادًا متزايدًا بشكل رتيب. IscShutdown ينتظر المعالجات قيد التشغيل ويستعيد بايتات 0F. IscFlush يُفَرِّغ الحلقة بشكل متزامن، مفيدًا إذا كنت تدمج المكتشف في صندوق رمل يحتاج إلى الأحداث قبل خروج العينة.

الحد الأدنى للتكامل هو LoadLibrary. يتولى DllMain التهيئة ويبدأ كلا الخيطين الخلفيين من هناك.

Isc = استدعاءات نظامية غير مباشرة (Indirect Syscalls)


الأشياء التي لم نتمكن من التقاطها بعد.

عينة تقوم بفحوصات النزاهة (integrity checks)

عينة تستخدم مؤشرات لم نقم بتصحيحها. القائمة المسموح بها الحالية تضم حوالي 40 اسمًا تغطي أوليات الهجوم (offensive primitives) للذاكرة والعملية والخيط والقسم والرمز المميز (token) والملف. إضافة المزيد هو أمر تدريجي طالما أن DllMain ينتهي في الوقت المناسب. تصحيح جميع المؤشرات الـ 488 من داخل قفل المُحمِّل (loader lock) هو أمر غير محبب...

عينة تعمل بامتيازات kernel. ليست مشكلة وضع المستخدم.


INT3 هو ما اخترناه في النهاية، لكن آلية الفخ ليست الجزء المثير للاهتمام. السبب في أنه يعمل بشكل أفضل من PAGE_GUARD هو أن الفخ لا يتطلب تفاوضًا مستمرًا مع نظام التشغيل. البايت هو CC. يظل CC. ليس لدى نظام التشغيل رأي حول أي بايتات تعيش في ntdll.

تنزيل الأداة