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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
ThreadStackSpoofer — تزوير مكدس الخيط - إثبات مفهوم لتقنية متقدمة للتهرب داخل الذاكرة تسمح بإخفاء تخصيص الذاكرة للشيل كود المحقون بشكل أفضل من الماسحات الضوئية والمحللين. | Kitploit
أدوات/GitHubGitHub/mgeeky/threadstackspoofer
أطر اختبار الاختراقأطر الاستغلالتحليل الذاكرة الجنائيشيل كودتحليل البرمجيات الخبيثةالفريق الأحمرتطوير الحمولات
GitHubmgeeky/threadstackspoofer

ThreadStackSpoofer

تزوير مكدس الخيط - إثبات مفهوم لتقنية متقدمة للتهرب داخل الذاكرة تسمح بإخفاء تخصيص الذاكرة للشيل كود المحقون بشكل أفضل من الماسحات الضوئية والمحللين.

عرض المستودع
1.2k1924منذ 4 سنواتتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
مشاركة

إثبات المفهوم لتزوير مكدس الخيط / تزوير مكدس الاستدعاء

تنفيذ إثبات مفهوم لتقنية متقدمة للتهرب من الذاكرة تقوم بتزوير مكدس استدعاء الخيط. تسمح هذه التقنية بتجاوز قواعد فحص الذاكرة القائمة على الخيوط وإخفاء الشيلكود بشكل أفضل أثناء وجودها في ذاكرة العملية.

مقدمة

هذا مثال تنفيذ لتقنية تزوير مكدس الخيط بهدف التهرب من محللي البرامج الضارة وبرامج مكافحة الفيروسات وحلول الكشف والاستجابة الطرفية (EDR) التي تبحث عن مراجع لإطارات الشيلكود في مكدس استدعاء الخيط قيد الفحص. الفكرة هي إخفاء المراجع إلى الشيلكود في مكدس استدعاء الخيط وبالتالي إخفاء التخصيصات التي تحتوي على كود البرامج الضارة.

التنفيذ مع مشروعي ShellcodeFluctuation يقدم لمجتمع الأمن الهجومي تطبيقات نموذجية للحاق بالعروض التي تقدمها منتجات C2 التجارية، حتى نتمكن من تحقيق نتائج لا تقل عنها في أدوات الفريق الأحمر لدينا. 💪

لقد تغير التنفيذ

يختلف التنفيذ الحالي اختلافاً كبيراً عما تم نشره أصلاً. وذلك لأنني أدركت أن هناك نهجاً أبسط لإنهاء معالجة مكدس استدعاء الخيط وإخفاء الإطارات المتعلقة بالشيلكود عن طريق كتابة 0 على عنوان الإرجاع لأول إطار نتحكم به:

root@kitploit:~
void WINAPI MySleep(DWORD _dwMilliseconds)
{
    [...]
    auto overwrite = (PULONG_PTR)_AddressOfReturnAddress();
    const auto origReturnAddress = *overwrite;
    *overwrite = 0;

    [...]
    *overwrite = origReturnAddress;
}

يمكن الوصول إلى التنفيذ السابق، الذي يستخدم StackWalk64، في هذا الالتزام c250724.

هذا التنفيذ أكثر استقراراً بكثير ويعمل بشكل جيد على كل من Debug و Release تحت معماريتين - x64 و x86.

عرض توضيحي

هذا ما قد يبدو عليه مكدس الاستدعاء عندما لا يكون مزوراً:

not-spoofed

وهذا بدوره، عندما يكون تزوير مكدس الخيط ممكّناً:

spoofed

أعلاه يمكننا رؤية أن الإطار الأخير في مكدس الاستدعاء لدينا هو رد النداء MySleep الخاص بنا. قد يتساءل المرء عما إذا كان هذا يتيح على الفور مؤشرات اكتشاف جديدة (IOCs)؟ يمكن لقواعد الصيد البحث عن خيوط ذات مكدسات استدعاء لا تتفكك إلى نقاط إدخال الخيط المتوقعة التالية الموجودة داخل المكتبات النظامية:

root@kitploit:~
kernel32!BaseThreadInitThunk+0x14
ntdll!RtlUserThreadStart+0x21

ومع ذلك، قد يبدو مكدس استدعاء الخيط المزور غريباً بعض الشيء في البداية، لكن فحصاً موجزاً لنظامي أظهر وجود خيوط أخرى لا تتفكك إلى نقاط الإدخال المذكورة أعلاه أيضاً:

مكدس استدعاء شرعي

تُظهر لقطة الشاشة أعلاه خيطاً من برنامج Total Commander x64 غير المعدل. كما نرى، يشبه مكدس استدعائه إلى حد كبير مكدسنا الخاص من حيث إطارات مكدس الاستدعاء الأولية.

لماذا يجب أن نهتم بتزييف مكدس الاستدعاء الخاص بنا بعناية عندما تكون هناك عمليات تظهر خصائص يمكننا ببساطة تقليدها؟

كيف يعمل؟

الخوارزمية التقريبية هي كما يلي:

  1. قراءة محتويات الشيلكود من ملف.
  2. الحصول على جميع مؤشرات الدوال الضرورية من dbghelp.dll، واستدعاء SymInitialize.
  3. ربط kernel32!Sleep للإشارة مرة أخرى إلى رد النداء الخاص بنا.
  4. حقن وتشغيل الشيلكود عبر VirtualAlloc + memcpy + CreateThread. يجب أن يبدأ الخيط من الدالة runShellcode الخاصة بنا لتجنب أن يشير عنوان بداية الخيط إلى مكان غير متوقع وشاذ (مثل ntdll!RtlUserThreadStart+0x21).
  5. بمجرد أن يحاول البيكون (Beacon) السكون، يتم استدعاء رد النداء MySleep الخاص بنا.
  6. نقوم بعد ذلك بالكتابة فوق آخر عنوان إرجاع على المكدس إلى 0 مما ينهي مكدس الاستدعاء بشكل فعال.
  7. أخيراً، يتم إجراء استدعاء لـ ::SleepEx للسماح للبيكون بالسكون أثناء انتظار مزيد من الاتصالات.
  8. بعد انتهاء السكون، نستعيد عناوين إرجاع الدوال الأصلية المحفوظة مسبقاً ويستأنف التنفيذ.

تنتشر عناوين إرجاع الدوال في جميع أنحاء منطقة ذاكرة مكدس الخيط، المشار إليها بواسطة سجل RBP/EBP. للعثور عليها على المكدس، نحتاج أولاً إلى جمع مؤشرات الإطارات، ثم إلغاء الإشارة إليها للكتابة فوقها:

إطار المكدس

(الصورة أعلاه مقتبسة من منشور Eli Bendersky بعنوان تخطيط إطار المكدس على x86-64)

root@kitploit:~
*(PULONG_PTR)(frameAddr + sizeof(void*)) = Fake_Return_Address;

قام التنفيذ الأولي لـ ThreadStackSpoofer بذلك في دالتي walkCallStack و spoofCallStack، لكن التنفيذ الحالي يظهر أن هذه الجهود ليست مطلوبة للحفاظ على مكدس استدعاء متخفي.

مثال على التشغيل

حالة الاستخدام:

root@kitploit:~
C:\> ThreadStackSpoofer.exe <shellcode> <spoof>

حيث:

  • <shellcode> هو مسار إلى ملف الشيلكود
  • <spoof> عندما 1 أو true سيمكن تزوير مكدس الخيط وأي شيء آخر يعطله.

مثال تشغيل يقوم بتزوير مكدس استدعاء خيط البيكون:

root@kitploit:~
PS D:\dev2\ThreadStackSpoofer> .\x64\Release\ThreadStackSpoofer.exe .\tests\beacon64.bin 1
[.] قراءة بايتات الشيلكود...
[.] ربط kernel32!Sleep...
[.] حقن الشيلكود...
[+] الشيلكود يعمل الآن.
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...

===> MySleep(5000)

[<] استعادة عنوان الإرجاع الأصلي...
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...

===> MySleep(5000)

[<] استعادة عنوان الإرجاع الأصلي...
[>] عنوان الإرجاع الأصلي: 0x1926747bd51. إنهاء مكدس الاستدعاء...

كيف أستخدمه؟

انظر إلى الكود وتنفيذه، وافهم المفهوم وأعد تنفيذه ضمن أدوات تحميل الشيلكود الخاصة بك التي تستخدمها لتوصيل مشاركات فريقك الأحمر. هذه تقنية أخرى للتهرب المتقدم في الذاكرة تزيد من فرص فريقك في عدم الوقوع بواسطة برامج مكافحة الفيروسات وحلول الكشف والاستجابة الطرفية (EDR) ومحللي البرامج الضارة الذين يفحصون برامجك الضارة.

أثناء تطوير أداة تحميل الشيلكود المتقدمة الخاصة بك، قد ترغب أيضاً في تنفيذ:

  • تشفير كومة العملية - استلهم من منشور المدونة هذا: Hook Heaps and Live Free - والذي يمكن أن يسمح لك بالتهرب من مستخرجات تكوين البيكون مثل BeaconEye
  • تغيير حماية صفحات ذاكرة البيكون الخاصة بك إلى RW (من RX/RWX) وتشفير محتوياتها - باستخدام تقنية Shellcode Fluctuation - قبل السكون مباشرة (مما قد يسمح بالتهرب من الماسحات الضوئية مثل Moneta أو pe-sieve)
  • مسح أي بقايا من المحمل الانعكاسي (Reflective Loader) لتجنب عمليات الكشف القائمة على التوقيع في الذاكرة
  • إلغاء ربط كل ما ربما قمت بربطه (مثل AMSI, ETW, WLDP) قبل السومن ثم إعادة الربط بعد ذلك.

في الواقع هذا ليس (حتى الآن) تزويراً حقيقياً للمكدس

كما أشير إلي، فإن التقنية هنا ليست بعد حقيقية تستحق اسمها كـ مزور مكدس. نظراً لأننا نكتفي بالكتابة فوق عناوين الإرجاع على مكدس الخيط، فإننا لا نقوم بتزوير المناطق المتبقية من المكدس نفسه. علاوة على ذلك، فإننا نترك مكدس الاستدعاء الخاص بنا غير قابل للتفكك مما يجعله يبدو شاذاً لأن النظام لن يكون قادراً على اجتياز سلسلة إطارات مكدس الاستدعاء بأكملها بشكل صحيح.

ومع ذلك، أنا على دراية بهذه القصور، وقد تركته كما هو في الوقت الحالي لأنني اهتممت بشكل أساسي بالتهرب من الماسحات الضوئية الآلية التي يمكنها التكرار على العمليات، وتعداد خيوطها، واجتياز مكدسات تلك الخيوط والتقاط أي عنوان إرجاع يشير إلى ذاكرة غير مصورة (مثل SEC_PRIVATE - الذاكرة المخصصة ديناميكياً بواسطة VirtualAlloc وما شابه ذلك). سيتمكن محلل البرامج الضارة المركز على الفور من ملاحظة الشذوذ واعتبار الخيط غير عادي إلى حد ما، متتبعاً برنامجنا الضار. أنا متأكد من ذلك أكثر من كافي. ومع ذلك، لا أعتقد أن الماسحات الضوئية الآلية في الوقت الحالي مثل AV/EDR لديها أنواع من الاستدلالات المطبقة التي تقوم بالفعل باجتياز مكدس كل خيط للتحقق مما إذا كان غير قابل للتفكك ¯\_(ツ)_/¯.

بالتأكيد، هذا المشروع (والتنفيذ التجاري الموجود في أطر عمل C2) يعطي لبائعي AV و EDR حججاً للنظر في تنفيذ استدلالات مناسبة تغطي مثل هذه التقنية الجديدة للتهرب.

من أجل تحسين هذه التقنية، يمكن للمرء أن يهدف إلى الحصول على مزور مكدس خيط حقيقي عن طريق إدراج إطارات مكدس مزورة بعناية تم إنشاؤها في عملية تفكك عكسي. اقرأ المزيد حول هذه الفكرة أدناه.

تنفيذ مزور مكدس خيط حقيقي

علمتني محادثة استغرقت ساعات مع namazso، أنه من أجل الحصول على مزور مكدس خيط مناسب، سنحتاج إلى عكس عملية تفكك مكدس الاستدعاء x64. أولاً، يحتاج المرء إلى الاعتراف بعملية تفكك المكدس الموضحة في (أ) المرتبط أدناه. عندما يجتاز النظام مكدس استدعاء الخيط على بنية x64، لن يعتمد ببساطة على عناوين الإرجاع المنتشرة حول مكدس الخيط، بل:

  1. يأخذ عنوان الإرجاع
  2. يحاول تحديد الدالة التي تحتوي على هذا العنوان (مع RtlLookupFunctionEntry)
  3. ترجع هذه الدالة هياكل RUNTIME_FUNCTION و UNWIND_INFO و UNWIND_CODE. تصف هذه الهياكل مكان عنوان بداية الدالة وعنوان نهايتها، وأين توجد جميع تسلسلات الكود التي تعدل RBP أو RSP.
  4. يحتاج النظام إلى معرفة جميع تعديلات مؤشرات المكدس والإطار التي حدثت في كل دالة عبر مكدس الاستدعاء ثم التراجع افتراضياً عن هذه التغييرات واستعادة مؤشرات مكدس الاستدعاء افتراضياً عند حدوث استدعاء إلى إطار مكدس الاستدعاء المعالج (يتم تنفيذ ذلك في RtlVirtualUnwind)
  5. يقوم النظام بمعالجة جميع UNWIND_CODEs التي تظهرها الدالة المفحوصة لحساب موقع عنوان إرجاع ذلك الإطار وقيمة مؤشر المكدس بدقة.
  6. من خلال هذه المحاكاة، يكون النظام قادراً على النزول لأسفل سلسلة مكدسات الاستدعاء و "فك" مكدس الاستدعاء بشكل فعال.

من أجل التدخل في هذه العملية، سنحتاج إلى عكسها من خلال الحصول على نموذجنا المعكوس من RtlVirtualUnwind. سنحتاج إلى التكرار على الدوال المحددة في وحدة (لتكن kernel32)، ومسح رموز UNWIND_CODE لكل دالة ومحاكاتها بشكل وثيق للخلف (مقارنة بـ RtlVirtualUnwind وبالتحديد RtlpUnwindPrologue) من أجل العثور على مواقع على المكدس، حيث نضع عناوين الإرجاع المزورة لدينا.

يذكر namazso ضرورة إدخال 3 إطارات مكدس مزورة لربط مكدس الاستدعاء بشكل جيد:

  1. إطار "عدم التزامن" (اعتبره إطارًا مصغرًا أو gadget-frame) الذي يفك بشكل مختلف مقارنة بمتصل MySleep الخاص بنا (له UWOP مختلف - رمز عملية التفكك). نقوم بذلك من خلال النظر في جميع الدوال من وحدة، والبحث في UWOPs الخاصة بها، وحساب حجم الإطار المزور. يجب أن يكون لهذا الإطار UWOPS مختلفة عن متصل MySleep الخاص بنا.
  2. الإطار التالي الذي نريد العثور عليه هو دالة تفك عن طريق الدفع إلى RBP من المكدس - بشكل أساسي من خلال كود UWOP_PUSH_NONVOL.
  3. الإطار الثالث نحتاج إلى دالة تستعيد RSP من RBP من خلال الكود UWOP_SET_FPREG.

يجب تعيين RSP المستعاد باستخدام RSP المأخوذ من أينما دخل تدفق التحكم إلى MySleep الخاص بنا بحيث تصبح جميع إطاراتنا مخفية، نتيجة لتفكك الأداة الثالثة هناك.

من أجل بدء العملية، يمكن للمرء التكرار على .pdata للملف القابل للتنفيذ عن طريق إلغاء الإشارة إلى إدخال دليل البيانات IMAGE_DIRECTORY_ENTRY_EXCEPTION. ضع في اعتبارك المثال أدناه:

root@kitploit:~
    ULONG_PTR imageBase = (ULONG_PTR)GetModuleHandleA("kernel32");
    PIMAGE_NT_HEADERS64 pNthdrs = PIMAGE_NT_HEADERS64(imageBase + PIMAGE_DOS_HEADER(imageBase)->e_lfanew);

    auto excdir = pNthdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
    if (excdir.Size == 0 || excdir.VirtualAddress == 0)
        return;

    auto begin = PRUNTIME_FUNCTION(excdir.VirtualAddress + imageBase);
    auto end = PRUNTIME_FUNCTION(excdir.VirtualAddress + imageBase + excdir.Size);

    UNWIND_HISTORY_TABLE mshist = { 0 };
    DWORD64 imageBase2 = 0;

    PRUNTIME_FUNCTION currFrame = RtlLookupFunctionEntry(
        (DWORD64)caller,
        &imageBase2,
        &mshist
    );

    UNWIND_INFO *mySleep = (UNWIND_INFO*)(currFrame->UnwindData + imageBase);
    UNWIND_CODE myFrameUwop = (UNWIND_CODE)(mySleep->UnwindCodes[0]);

    log("1. MySleep RIP UWOP: ", myFrameUwop.UnwindOpcode);

    for (PRUNTIME_FUNCTION it = begin; it < end; ++it)
    {
        UNWIND_INFO* unwindData = (UNWIND_INFO*)(it->UnwindData + imageBase);
        UNWIND_CODE frameUwop = (UNWIND_CODE)(unwindData->UnwindCodes[0]);

        if (frameUwop.UnwindOpcode != myFrameUwop.UnwindOpcode)
        {
            // تم العثور على دالة مرشحة لإطار أداة عدم التزامن

        }
    }

العملية معقدة بعض الشيء، لكنها تختزل في عكس عملية تفكك مكدس استدعاء الخيط عن طريق استبدال إطارات مكدس عشوائية بإطارات أخرى مختارة بعناية، بطريقة تشبه ROP.

لا يتبع إثبات المفهوم هذا تكرار هذه الخوارزمية، لأن فهمي الحالي يسمح لي بقبول مكدس الاستدعاء المنتهي على إطار مكدس قائم على EXE ولا أريد تعقيد أدوات تحميل الشيلكود الخاصة بي ولا إثبات المفهوم هذا. أترك تمرين تنفيذ هذا ومشاركته علناً للقارئ المهتم. أو ربما سأجلس وأجرب القيام بذلك بنفسي إذا توفر لدي المزيد من وقت الفراغ :)

مزيد من المعلومات:

  • أ) معالجة الاستثناءات في x64 - شرح عملية تفكك المكدس
  • ب) تنفيذ عينة لـ RtlpUnwindPrologue و RtlVirtualUnwind
  • ج) قسم .pdata
  • د) تنفيذ عينة آخر لـ RtlpUnwindPrologue

كلمة تحذير

إذا كنت تخطط لإضافة هذه الوظيفة إلى أدوات تحميل الشيلكود / الأدوات الخاصة بك، فتأكد من تجنب إلغاء ربط kernel32.dll. أي محاولة لإلغاء ربط kernel32 ستستعيد وظيفة Sleep الأصلية مما يمنع استدعاء رد النداء الخاص بنا. إذا لم يتم استدعاء رد النداء الخاص بنا، فلن يتمكن الخيط من تزوير مكدس الاستدعاء الخاص به بنفسه.

إذا كان هذا هو ما تريده، فقد تحتاج إلى تشغيل خيط مراقب آخر، للتأكد من أن خيط البيكون سيتم تزويره كلما نام.

إذا كنت تستخدم Cobalt Strike و BOF unhook-bof بواسطة Raphael Mudge، فتأكد من مراجعة طلب السحب الخاص بي الذي يضيف معلمة اختيارية إلى BOF تحدد المكتبات التي لا ينبغي إلغاء ربطها.

بهذه الطريقة يمكنك الحفاظ على روابطك في kernel32:

root@kitploit:~
beacon> unhook kernel32
[*] تشغيل unhook.
    سيتم تخطي هذه الوحدات: wmp.dll, kernel32.dll
[+] استضاف host called home, sent: 9475 bytes
[+] received output:
ntdll.dll            <.text>
تم إلغاء الربط.

تم تعديل unhook-bof مع خيار تجاهل الوحدات المحددة


ملاحظة ختامية

تم تصميم إثبات المفهوم هذا للعمل مع شيلكودات البيكون الخاصة بـ Cobalt Strike. من المعروف أن البيكون يستدعي kernel32!Sleep لانتظار تعليمات إضافية من C2 الخاص به. يستفيد هذا المحمل من هذه الحقيقة عن طريق ربط Sleep من أجل القيام بمهام الصيانة الداخلية.

قد لا يعمل هذا التنفيذ مع شيلكودات أخرى في السوق (مثل Meterpreter) إذا كانت لا تستخدم Sleep للتهدئة. نظراً لأن هذا مجرد إثبات مفهوم يوضح التقنية، فلا أنوي إضافة دعم لأي إطار عمل C2 آخر.

عندما تفهم المفهوم، ستتمكن بالتأكيد من ترجمته إلى متطلبات الشيلكود الخاصة بك وتكييف الحل لصالحك.

يرجى عدم فتح مشكلات Github المتعلقة بـ "هذا الكود لا يعمل مع شيلكود XYZ"، سيتم إغلاقها على الفور.


☕ إظهار الدعم ☕

هذا المشروع ومشاريع أخرى هي نتاج ليالٍ بلا نوم وكثير من العمل الشاق. إذا أعجبك ما أفعله وتقدر أنني دائمًا أعود للمجتمع، فكر في شراء قهوة لي (أو الأفضل بيرة) فقط لتقول شكراً! 💪


المؤلف

root@kitploit:~
   Mariusz Banach / mgeeky, 21
   <mb [at] binary-offensive.com>
   (https://github.com/mgeeky)
تنزيل الأداة