
تقنية تهرب متقدمة في الذاكرة تعمل على تذبذب حماية ذاكرة الشيلكود بين RW/NoAccess و RX، ومن ثم تشفير/فك تشفير محتوياته
تنفيذ PoC لتقنية مراوغة أخرى في الذاكرة تقوم بتشفير وفك تشفير محتويات الشيل كود بشكل دوري لجعله يتأرجح بين حماية الذاكرة RW (أو NoAccess) و RX.
عندما تقيم شيل كود الخاص بنا في صفحات ذاكرة RW أو NoAccess، فإن الماسحات الضوئية مثل Moneta أو pe-sieve لن تتمكن من تتبعه وتفريغه لتحليله لاحقًا.
بعد إصدار ThreadStackSpoofer تلقيت بعض الأسئلة حول النقطة التالية من ملف README:
غيّر حماية صفحات ذاكرة الـ Beacon الخاص بك إلى RW (من RX/RWX) وقم بتشفير محتوياتها قبل النوم (قد يؤدي ذلك إلى مراوغة ماسحات ضوئية مثل Moneta أو pe-sieve)
في السابق كنت واثقًا تمامًا من أن المجتمع يعرف بالفعل كيفية تشفير/فك تشفير الحمولات وقلب حمايات الذاكرة الخاصة بها لمجرد مراوغة ماسحات الذاكرة التي تبحث عن مناطق تنفيذ غير طبيعية. لكن الأسئلة أثبتت عكس ذلك، لذا قررت إصدار هذا الـ PoC غير المسلّح لتوثيق استراتيجية مراوغة أخرى وتقديم تنفيذ نموذجي ليعمل معه المجتمع.
هذا الـ PoC هو عرض لتقنية بسيطة نسبيًا، معروفة بالفعل لدى مجتمع الهجوم (لذا لا أحضر أي شيء جديد هنا حقًا) على أمل كشف السر وراء السحر الذي تُظهره بعض الأطر التجارية التي تعرض قدراتها على المراوغة مستهدفةً الماسحين الضوئيين المذكورين أعلاه.
فيما يلي مقارنة عند التأرجح إلى RW (خيار آخر هو التأرجح إلى PAGE_NOACCESS - موصوف أدناه):

هذا التنفيذ إلى جانب ThreadStackSpoofer الخاص بي يقدم للمجتمع الأمني الهجومي تطبيقات نموذجية للحاق بالعروض المقدمة من منتجات C2 التجارية، حتى لا نكون أسوأ حالًا في أدوات Red Team الخاصة بنا. 💪
يقوم هذا البرنامج بتنفيذ حقن ذاتي للشيل كود (تقريبًا عبر VirtualAlloc الكلاسيكية + memcpy + CreateThread).
عند تشغيل الشيل كود (يستهدف هذا التنفيذ تحديدًا حمولات Cobalt Strike Beacon) سيتم ربط دالة ويندوز لاعتراض اللحظة التي ينام فيها الـ Beacon عبر kernel32!Sleep.
عندما يتم استدعاء دالة MySleep المربوطة، ستحدد حدود تخصيص الذاكرة الخاص بها، وتقلب حمايتها إلى RW وتقوم بعمل xor32 على جميع البايتات المخزنة هناك.
بعد الانتظار للمدة الزمنية المتوقعة، عندما يعود الشيل كود إلى معالج MySleep الخاص بنا، سنقوم بفك تشفير بيانات الشيل كود وقلب الحماية مرة أخرى إلى RX.
PAGE_READWRITE يعمل على النحو التاليkernel32!Sleep بحيث يشير إلى دالة الاستدعاء الخاصة بنا.VirtualAlloc + memcpy + CreateThread. على عكس ما كان لدينا في ThreadStackSpoofer، لا نقوم هنا بربط أي شيء في ntdll لتشغيل الشيل كود الخاص بنا بل نقفز إليه من داخل الدالة الخاصة بنا. تحاول هذه الطريقة تجنب ترك مؤشرات IOC بسيطة في الذاكرة تشير إلى ذاكرة ntdll معدّلة.MySleep الخاصة بنا.RWkernel32!Sleep الأصلية لتجنب ترك مؤشر IOC بسيط في الذاكرة يشير إلى أن Sleep قد تم تعديلها بـ trampoline (ربط داخلي inline).::Sleep الأصلي للسماح للـ Beacon بالنوم أثناء انتظار التواصل اللاحق.RX ثم نعيد ربط لضمان اعتراض النوم اللاحق.PAGE_NOACCESS يعمل على النحو التاليkernel32!Sleep بحيث يشير إلى دالة الاستدعاء الخاصة بنا.VirtualAlloc + memcpy + CreateThread ...MySleep الخاصة بنا.PAGE_NOACCESSkernel32!Sleep الأصلية لتجنب ترك مؤشر IOC بسيط في الذاكرة يشير إلى أن Sleep قد تم تعديلها بـ trampoline (ربط داخلي inline).::Sleep الأصلي للسماح للـ Beacon بالنوم أثناء انتظار التواصل اللاحق.kernel32!Sleep لضمان اعتراض النوم اللاحق.RX ويُستأنف الشيل كود.التقنية ليست جديدة كليًا، وليست شيئًا ابتكرته بنفسي. إنها مجرد تنفيذ يعرض المفهوم واستخدامه العملي للسماح لمجتمعنا الأمني الهجومي باللحاق بالعروض المقدمة من أطر C2 التجارية.
في الواقع، تم تعريفّي بفكرة قلب حماية ذاكرة الشيل كود قبل عدة سنوات من خلال عمل Josh Lospinoso في مشروعه المذهل Gargoyle.
فيما يلي مزيد من الخلفية:
Gargoyle يأخذ مفهوم الشيل كود الواعي بذاته والمتأرجح بذاته إلى أبعد من ذلك، من خلال الاستفادة من تسلسل ROP الذي يستدعي VirtualProtect.
ومع ذلك، التقنية مثيرة للإعجاب، لكن من الصعب بنفس القدر استخدامها مع Cobalt Strike's Beacon دون الحاجة إلى قتل الخيط الخاص به والاستمرار في إعادة تهيئة الـ Beacon أثناء تواجده في الذاكرة.
هذا بعيد عن المثالية، لكن نظرًا لأننا نعمل بالفعل من أرضية عملية تحميل ذاتي خاصة بنا، فنحن قادرون على فعل ما نشاء بالبيئة التي يعمل فيها الشيل كود وإخفائه بالطريقة التي نريدها. تُظهر هذه التقنية (والتقنية السابقة ThreadStackSpoofer) مزايا تشغيل الشيل كود الخاص بنا بهذه الطريقة.
تنفيذ التأرجح إلى PAGE_NOACCESS مستوحى من عمل ORCA666 المعروض في https://github.com/ORCA666/0x41 injector.
لقد أظهر أنه:
يحتوي هذا التنفيذ على هذه الفكرة مطبقة، وهي متاحة مع الخيار 2 في <fluctuate>.
تأكد من الاطلاع على مشاريعه الأخرى أيضًا.
تقبل الأداة ShellcodeFluctuation ثلاثة معاملات: الأول هو المسار إلى الشيل كود والثاني هو معدّل وظيفتنا.```
Usage: ShellcodeFluctuation.exe
:
-1 - Read shellcode but dont inject it. Run in an infinite loop.
0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything
1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE.
2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
### إيجابية كاذبة (على ما يبدو) من Moneta```
C:\> ShellcodeFluctuation.exe beacon64.bin -1
لذا أولاً سنرى ما يراه ماسح Moneta64 حول عملية لا تفعل أي شيء مريب وتكتفي ببساطة بتشغيل حلقة لا نهائية:

كما نرى هناك بعض الإيجابيات الكاذبة (على الأقل كما أعتبرها) التي يُزعم أنها تكتشف Mismatching PEB module / Phantom image.
حدود الذاكرة تشير إلى وحدة ShellcodeFluctuate.exe نفسها وقد تشير إلى أن هذه الوحدة، رغم كونها من نوع MEM_IMAGE، غير مرتبطة في PEB الخاصة بالعملية - وهو أمر غير معتاد ويبدو غريباً إلى حد ما.
سبب هذا الـ IOC غير معروف بالنسبة لي ولم أحاول فهمه بشكل أفضل، لكنه ليس شيئاً يجب أن نقلق بشأنه حقاً.
إذا كان أي شخص يعرف سبب هذا الاكتشاف، فسأكون فضولياً جداً لسماعه! يرجى التواصل معي.
C:> ShellcodeFluctuation.exe beacon64.bin 0
الحالة الاستخدامية الثانية تعرض مؤشرات ذاكرة (Memory IOCs) لـ Beacon يعمل داخل عمليتنا، ولا يستخدم أي أنواع من `Artifact Kits` المخصصة، أو `User-Defined Reflective Loaders` (مثل [`ElusiveMice`](https://github.com/mgeeky/ElusiveMice))، ولا أي إجراءات أولية من شأنها أن تفسد نتائجنا.

يمكننا أن نرى أن `Moneta64` يتعرف بشكل صحيح على `Abnormal private executable memory` مشيراً إلى الموقع الذي توجد فيه شيلكود (shellcode) الخاصة بنا.
هذا مؤشر ذاكرة قوي جداً يكشف الشيلكود الخاصة بنا ليقوم الماسحات الآلية بتفريغها وتحليلها. ليس أمراً جيداً.
### Beacon مشفر مع حمايات RW```
C:\> ShellcodeFluctuation.exe beacon64.bin 1
الآن، حالة الاستخدام الثالثة، والأكثر إثارة للاهتمام من منظور هذا التنفيذ، هي Beacon المتقلب.

بصرف النظر عن أول IOC، الذي يُعتبر إلى حد ما إيجابية كاذبة، نرى مؤشرًا جديدًا يشير إلى أن ذاكرة kernel32.dll تم تعديلها.
ومع ذلك، لا يوجد مؤشر Abnormal private executable memory هذه المرة. تقلبنا (التشفير/فك التشفير المتكرر وتبديل حمايات الذاكرة) نشط.
وللتسجيل، فإن pe-sieve يكتشف أيضًا الـ PE المزروع عند استخدامه مع خيار /data 3 (ما لم يتم إعطاء هذا الخيار، لن يتم إجراء أي اكتشاف):

افتراضي الحالي هو أن PE-Sieve يلتقط نفس السمات التي يلتقطها Moneta (الموصوفة أدناه في الكود المعدّل في kernel32.dll) - حقيقة أن الوحدة النمطية المعيّنة من PE تحتوي على Working set غير فارغ، وهي دليل واضح على حقن كود من نوع ما. يُصنَّف هذا على أنه Implanted PE / Implanted. إذا كان الأمر كذلك، فإن الاستنتاج مشابه لملاحظة Moneta. لا أعتقد أننا يجب أن نهتم كثيرًا بذلك المؤشر من ناحية الاكتشاف.
حاليًا، لم أفكر في خيار أفضل لاعتراض تنفيذ الـ shellcode في المنتصف (وأنا الآن أتحدث عن Cobalt Strike) سوى ربط kernel32!Sleep. وبالتالي، نحن مضطرون لترك هذه الأنواع من مؤشرات IOC.
لكن مهلاً، ما زالت البايتات كما هي تمامًا مقارنةً بما هو موجود على نظام الملفات (C:\Windows\System32\kernel32.dll) ولا توجد أي دالة مربوطة، فما الأمر؟ 😉
C:> ShellcodeFluctuation.exe beacon64.bin 2

سيؤدي ذلك إلى جعل الـ shellcode يتأرجح بين صفحات `RX` و `NA` بشكل فعّال.
في الوقت الحالي، لست متأكدًا من فوائد التحويل إلى `PAGE_NOACCESS` بدلاً من `PAGE_READWRITE`.
### الكود المعدَّل في kernel32.dll
إذًا، ما الأمر بالنسبة إلى مؤشر الاختراق (IOC) الخاص بـ `kernel32` المعدَّل؟
الآن، دعونا نحاول الوصول إلى جذور هذا الـ IOC ومعرفة ما هي القصة.
أولاً، سنقوم بتفريغ منطقة الذاكرة المذكورة - وهي قسم `.text` (الكود) في `kernel32.dll`. دعونا نستخدم `ProcessHacker` لهذا الغرض للاستفادة من أدوات معروفة ومستقرة للعموم:

نقوم بتفريغ قسم الكود من kernel32 الذي يُفترض أنه معدَّل، ثم نقوم بالشيء نفسه بالنسبة إلى kernel32 العامل في عملية لم تعدّل تلك المنطقة.
بعد الحصول على تفريغين، يمكننا مقارنتهما بايتًا ببايت (باستخدام [expdevBadChars](https://github.com/mgeeky/expdevBadChars) الخاص بي) للبحث عن أي تناقضات:

فقط لنرى أنهما متطابقان تمامًا. من الواضح أنه لا يوجد بايت واحد معدَّل في `kernel32.dll`، والسبب في ذلك هو أننا نقوم بإزالة الخطاف من `kernel32!Sleep` قبل استدعائه:
`main.cpp:31:````
HookTrampolineBuffers buffers = { 0 };
buffers.originalBytes = g_hookedSleep.sleepStub;
buffers.originalBytesSize = sizeof(g_hookedSleep.sleepStub);
//
// Unhook kernel32!Sleep to evade hooked Sleep IOC.
// We leverage the fact that the return address left on the stack will make the thread
// get back to our handler anyway.
//
fastTrampoline(false, (BYTE*)::Sleep, &MySleep, &buffers);
// Perform sleep emulating originally hooked functionality.
::Sleep(dwMilliseconds);
إذًا ما الذي يسبب تفعيل IOC؟ دعونا نفحص Moneta عن كثب:

عند كسرنا في ملف Ioc.cpp الخاص بـ Moneta قرب السطر 104 حيث يُبلِّغ عن IOC من نوع MODIFIED_CODE، يمكننا تعديل الكود قليلًا لكشف اللحظة الدقيقة التي يحلّل فيها تجمع kernel32 بشكل أفضل.
الآن:
a = truekernel32 يحتوي على b = 0x1000 بايت خاص. كيف حدث ذلك؟ من المفترض أن يكون العدد 0.a && b) فسيتم الإبلاغ عن IOCعندما يقوم مُحمّل الصور في Windows (Windows Image Loader) بتعيين وحدة DLL في مساحة ذاكرة العملية، سيتم تصنيف صفحات الذاكرة الأساسية على أنها MEM_MAPPED أو MEM_IMAGE حسب السيناريو.
عندما نعدّل ولو بايتًا واحدًا من تخصيص MEM_MAPPED/MEM_IMAGE، سيفصل النظام صفحة ذاكرة واحدة (بافتراض أننا عدّلنا أقل من PAGE_SIZE بايت ولم نتجاوز حدود الصفحة) للإشارة إلى جزء لا يعود إلى الصورة الأصلية.
تُستخدم هذه الملاحظة بعد ذلك كـ IOC - لا ينبغي أن تحتوي الصورة على تخصيصات MEM_PRIVATE داخل منطقة الذاكرة الخاصة بها (بداخلها) لأن ذلك سيشير إلى أن بعض البايتات قد عُدّلت في يوم من الأيام داخل تلك المنطقة. يعمل Moneta بشكل صحيح على التقاط تعديل الكود حتى وإن كانت البايتات مطابقة لبايتات الوحدة الأصلية في وقت المقارنة.
للحصول على شرح شامل لكيفية عمل Moneta وتنفيذ حقن العملية (process injection) وIOC المرتبط بها تحت الغطاء، اقرأ المقالات التالية عالية الجودة بقلم Forrest Orr:
هذا بحث وتوثيق رائع حقًا من Forrest، عمل رائع يا صديقي!
خاصةً المقال الثاني يوضح الأساس المنطقي لهذا الاكتشاف، كما نقرأ ما يعلّمه إيانا Forrest:
في حال كانت الوحدة قد حُمّلت بشكل شرعي وأُضيفت إلى PEB، لكان حِقن الشيلكود (shellcode implant) ما يزال قابِلًا للاكتشاف بسبب الـ 0x1000 بايت (صفحة واحدة) من الذاكرة المُعيَّنة بشكل خاص في مساحة العنوان والتي استرجعها Moneta عبر الاستعلام عن مجموعة العمل (working set) الخاصة به - مما يؤدي إلى IOC لتعديل الكود كما رأينا أعلاه.
باختصار، نترك وراءنا IOC، لكن هل يجب أن نقلق بشأن ذلك؟ حتى لو وُجد IOC، فلا توجد بايتات مسروقة مرئية، لذا لا يوجد مرجع مباشر يعيد إلى الشيلكود الخاص بنا أو يميّز تقنيتنا عن غيرها من التقنيات.
باختصار شديد - لا ينبغي لنا أن نقلق كثيرًا بشأن ذلك الـ IOC. :-)
يمكن للمرء أن يقول إن هذا التنفيذ بعيد عن الكمال لأنه يترك شيئًا، فما تزال هناك مؤشرات IOC وتُظهر المنتجات التجارية أنها لا تمتلك سمات مشابهة.
عندما يُطرَح هذا الجدال على الطاولة، يجب أن أذكّر بأن الأطر التجارية تمتلك سيطرة كاملة على الكود المصدري لحِقنها (implants) ومحمّلات الشيلكود الخاصة بها، وبالتالي يمكنها دمج أحدهما مع الآخر بشكل جيد لتجنب الحاجة إلى الربط (hooking) والالتفاف حول الشيلكود الخاص بها بنفسها. هنا، نحتاج إلى ربط kernel32!Sleep لاعتراض تنفيذ Beacon من Cobalt Strike قبل أن يدخل في وضع السكون مباشرةً لنتمكن من المتابعة في مهام الصيانة الخاصة بنا. لو وُجدت آلية أفضل لنا للتدخل دون الحاجة إلى ربط دالة sleep لكان ذلك مثاليًا.
ومع ذلك، هناك مفهوم قناع النوم (Sleep Mask) الذي قُدِّم إلى Cobalt Strike، وقيود الحجم كونه مئات البايتات تجعلنا غير قادرين تمامًا على إدخال هذا المنطق في القناع نفسه (وإلا لكنا قادرين على عدم ربط Sleep أيضًا، دون ترك أي IOC تمامًا كما تفعل المنتجات التجارية).
قد تكون حجة أخرى أن الأطر التجارية تدمج هذا النوع من المنطق في محمّلات الانعكاس (Reflective Loaders) الخاصة بها، بينما نتركه هنا في أداة الاستضافة EXE بدلًا من ذلك. هذا صحيح، لكن السبب وراء هذا القرار مزدوج:
أحتاج إلى توخي الحذر الشديد عند إصدار هذا النوع من التقنيات لتجنب خطر المساعدة في تسليح مجرمين حقيقيين بتنفيذ سيعود ليطاردنا عبر شيء آخر مثل Petya. وبهذا الأسلوب قررت تخطي بعض التفاصيل المعقدة التي أستخدمها في أدواتي الاحترافية المستخدمة لتقديم تمارين محاكاة الخصم (Adversary Simulation) التجارية المتعاقد عليها. تقديم البذرة آمل أن يُقابَل بمتخصصين من المجتمع قادرين على تنمية المفهوم في أدواتهم الخاصة، بافتراض أنهم يتمتعون بالمهارات المناسبة.
كنت أفضل كثيرًا نقل هذا المنطق بالكامل إلى المحمّل الانعكاسي المعرّف من قبل المستخدم (User-Defined Reflective Loader) الخاص بـ Cobalt Strike، مما يمنح فرق الاختراق (Red Team) فرصًا أعلى في مرحلة التسليم. لكن أولًا، انظر إلى النقطة (1)، وثانيًا هذه التقنية محدودة حاليًا بحجم 5 كيلوبايتات لملفات RDLL الخاصة بها، مما يجعلني غير قادر تمامًا على تنفيذها هناك أيضًا. بالنسبة لمن يبنون C2 وحِقنًا مخصصة لعمليات محاكاة الخصم الداخلية - فقد تلقوا الآن تنفيذًا نموذجيًا سيساعدهم بالتأكيد في تحسين أدواتهم وفقًا لذلك.
انظر إلى الكود وتنفيذه، وافهم المفهوم وأعد تنفيذه داخل محمّلات الشيلكود الخاصة بك التي تستخدمها لتقديم عمليات Red Team. هذه تقنية أخرى من تقنيات المراوغة المتقدمة في الذاكرة تزيد من فرص فريقك في عدم اكتشافه من قبل برامج مكافحة الفيروسات، وأنظمة EDR، ومحللي البرمجيات الخبيثة الذين يفحصون حِقنك.
أثناء تطوير محمّل الشيلكود المتقدم الخاص بك، قد ترغب أيضًا في تنفيذ ما يلي:
BeaconEyeMEM_PRIVATE التي تشير إليها هذه الخيوط)حالة الاستخدام:``` Usage: ShellcodeFluctuation.exe : -1 - Read shellcode but dont inject it. Run in an infinite loop. 0 - Inject the shellcode but don't hook kernel32!Sleep and don't encrypt anything 1 - Inject shellcode and start fluctuating its memory with standard PAGE_READWRITE. 2 - Inject shellcode and start fluctuating its memory with ORCA666's PAGE_NOACCESS.
حيث:
- `<shellcode>` هو مسار إلى ملف shellcode
- `<fluctuate>` كما هو موضح أعلاه، يأخذ `-1` أو `0` أو `1`
مثال على تشغيل يزيّف مكدس استدعاءات الخيط الخاص بـ beacon:```
C:\> ShellcodeFluctuation.exe ..\..\tests\beacon64.bin 1
[.] Reading shellcode bytes...
[.] Hooking kernel32!Sleep...
[.] Injecting shellcode...
[+] Shellcode is now running. PID = 9456
[+] Fluctuation initialized.
Shellcode resides at 0x000002210C091000 and occupies 176128 bytes. XOR32 key: 0x1e602f0d
[>] Flipped to RW. Encoding...
===> MySleep(5000)
[.] Decoding...
[>] Flipped to RX.
[>] Flipped to RW. Encoding...
===> MySleep(5000)
إذا كنت تخطط لإضافة هذه الوظيفة إلى أدوات تحميل الشيل كود / الأدوات الخاصة بك، فتأكد من تجنب إزالة الخطاف (unhook) من kernel32.dll.
أي محاولة لإزالة خطاف kernel32 ستؤدي إلى استعادة وظيفة Sleep الأصلية مما يمنع استدعاء الـ callback الخاص بنا.
إذا لم يتم استدعاء الـ callback، فلن يتمكن الخيط من تزييف مكدس الاستدعاءات الخاص به بنفسه.
إذا كان هذا ما تريده، فقد تحتاج إلى تشغيل خيط مراقب (watchdog) آخر، للتأكد من أن خيط Beacons يتم تزييف مكدسه كلما نام.
إذا كنت تستخدم Cobalt Strike و BOF unhook-bof من Raphael's Mudge، فتأكد من الاطّلاع على Pull Request الخاص بي الذي يضيف معاملًا اختياريًا إلى BOF يحدد المكتبات التي يجب عدم إزالة خطافاتها.
بهذه الطريقة يمكنك الحفاظ على خطافاتك في kernel32:
---``` beacon> unhook kernel32 [*] Running unhook. Will skip these modules: wmp.dll, kernel32.dll [+] host called home, sent: 9475 bytes [+] received output: ntdll.dll <.text> Unhook is done.
[نسخة معدّلة من `unhook-bof` مع خيار لتجاهل الوحدات المحددة](https://github.com/mgeeky/unhook-bof)
---
## ملاحظة ختامية
صُمم هذا الإثبات المفاهيمي ليعمل مع شيلكودات Beacon الخاصة بـ Cobalt Strike. من المعروف أن الـ Beacon يستدعي `kernel32!Sleep` لانتظار تعليمات أخرى من خادم التحكم (C2).
يستغل هذا المحمّل هذه الحقيقة من خلال تعليق خطاف على `Sleep` لتنفيذ مهامه الداخلية.
قد لا يعمل هذا التنفيذ مع شيلكودات أخرى في السوق (مثل _Meterpreter_) إذا كانت لا تستخدم `Sleep` لفترات التهدئة.
وبما أن هذا مجرد _إثبات مفاهيمي_ يعرض التقنية، فإنني لا أنوي إضافة دعم لأي إطار C2 آخر.
عندما تفهم الفكرة، ستحتاج بالتأكيد إلى ترجمتها إلى متطلبات الشيلكود الخاص بك وتكييف الحل لصالحك.
يُرجى عدم فتح مشكلات على GitHub تتعلق بـ "هذا الكود لا يعمل مع شيلكود XYZ"، فسيتم إغلاقها فورًا.
---
### ☕ أظهر دعمك ☕
هذا المشروع وغيره من المشاريع هي ثمرة ليالٍ بلا نوم و**الكثير من العمل الشاق**. إذا أعجبك ما أقدمه وتقدّر أنني دائمًا أعود بالفائدة على المجتمع،
[فكّر في شراء قهوة لي](https://github.com/sponsors/mgeeky) _(أو الأفضل بيرة)_ فقط لقول شكرًا! 💪
---
## المؤلف```
Mariusz Banach / mgeeky, 21
<mb [at] binary-offensive.com>
(https://github.com/mgeeky)
kernel32!Sleep