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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
SILVERPICK — إطار تطوير شيلكود وضع المستخدم لنظام ويندوز (WUMSDF) | Kitploit
أدوات/GitHubGitHub/winterknife/silverpick
الاستغلالشيل كودالفريق الأحمرتوليد شيفرة القشرةتطوير الحمولات
GitHubwinterknife/silverpick

SILVERPICK

إطار تطوير شيلكود وضع المستخدم لنظام ويندوز (WUMSDF)

عرض المستودع
15817منذ شهر واحدتمت المراجعة من قبل Kitploit

الأكثر شعبية

عرض الكل →

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

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

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

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

SILVERPICK

الإصدار

  • v1.1

نبذة

مشروع SILVERPICK هو إطار عمل تطوير شيل كود في وضع المستخدم لنظام ويندوز (WUMSDF)، والغرض الوحيد منه هو تمكين مطوّري القدرات من بناء كتل كود مستقل عن العنوان (PIC) لنظام Windows بمعمارية x64 باستخدام C/C++ بطريقة سهلة، وذلك لتقليل تكاليف التطوير لمثل هذا المسعى.

إنه مشتق من مشروع WILDBEAST، وبالتالي يعتمد على:

  1. Visual Studio Code كمحرر أكواد
  2. سلسلة أدوات MinGW-w64 كسلسلة أدوات الترجمة
  3. GNU Make كنظام بناء

الإعداد

يمكنك العثور على تعليمات الإعداد من هنا: GCC-Clang-Setup-Windows

يُرجى ملاحظة أن هذا المشروع يستخدم MSYS2.

الميزات

كتابة الشيل كود بلغات برمجة عالية المستوى ليس بالأمر الجديد، فقد نُشرت عدد لا يحصى من التدوينات والأوراق البحثية حول هذا الموضوع منذ عام 2010. إذن، ما الجديد في SILVERPICK؟

حسنًا، يسعدني أنك سألت.

SILVERPICK يملك حقيبة صغيرة لطيفة من الحيل المخبأة في جعبته، لكن الأهم من ذلك كله، أن هذه هي رؤيتي الشخصية للموضوع.

لذا، دون مزيد من اللغط، أقدم لكم حيلتي الأولى.

الحيلة 01

منذ أن روّج مات غريبر (Matt Graeber) لكتابة الشيل كود بلغة C، كان معظم الناس يستخدمون كعب محاذاة المكدس ذا الـ 16 بايت المكتوب بلغة Assembly.

ورغم أن هذا ليس مشكلة، وبما أننا لسنا IKEA، فإن لغة التجميع لا ينبغي أن تكون ضرورية، وهي في الحقيقة ليست كذلك.

توجد سمة دالة في GCC من شأنها إصدار كعب محاذاة المكدس نيابةً عنك.

تعرّف على سمة الدالة force_align_arg_pointer في صورة ماكرو مفيد يُدعى ALIGN_STACK، والذي يولّد لغة التجميع التالية:

root@kitploit:~
Disassembly of section .init:

<PicEntry>:
	push   rbp
	mov    rbp,rsp
	and    rsp,0xfffffffffffffff0
	sub    rsp,0x20
	call   <PicEntry+0x11>	IMAGE_REL_AMD64_REL32	.text$payload
	leave
	ret

ما هو قسم .init، تتساءل؟ حسنًا، هذا يقودنا بشكل جميل إلى حيلتي الثانية.

الحيلة 02

ربما يكون مات غريبر قد روّج لكتابة الشيل كود بلغة C في وقت ما، لكن في الحقيقة، كان بول أونغور (Paul Ungur) هو من أحيا هذا الفن الأسود مع مشروع Stardust.

الآن، يستخدم Stardust سكربت ربط خاص بأدوات Binutils للتحكم في وضع الدوال والبيانات داخل قسم PE المناسب بالترتيب الصحيح. هذه التقنية نفسها مشتقة من عمل أوستن هدسون (Austin Hudson)، وكثير من الناس يستخدمون نسخة مختلفة من سكربتات الربط الخاصة به.

في حين أن سكربتات الربط رائعة لترتيب الأقسام في الرابط، إلا أنها غير ضرورية إذا كان كل ما تحتاجه هو وضع دالة معينة في بداية قسم الكود.

هنا تأتي سمة الدالة section مع اسم قسم خاص يُدعى .init، والذي يخبر الرابط بأن الدالة تحتوي على كود تهيئة زمن التشغيل الذي يسبق main()، ويجب أن تكون أولًا في ترتيب الربط.

من أجل هذا الغرض، تم إنشاء ماكرو CODE_BEGIN.

الحيلة 03

أما بالنسبة لحيلتي الثالثة، فأقدم لكم ماكرو STACK_STRING.

في C، يمكنك إنشاء سلسلة نصية على المكدس (سلسلة تُبنى ديناميكيًا على المكدس) عن طريق تعريف حرفية السلسلة كمصفوفة من أحرف ANSI:

root@kitploit:~
char charrHelloKitty[] = { 'H', 'e', 'l', 'l', 'o', 'K', 'i', 't', 't', 'y', '\0' };

في C++، يمكنك إنشاء سلسلة نصية على المكدس بمجرد تعريف مصفوفة char على أنها constexpr:

root@kitploit:~
constexpr char charrHelloKitty[]{ "HelloKitty" };

ومع ذلك، فإن كلتا التقنيتين تصبحان عديمتي الفائدة في مواجهة تحسينات المترجم إذا كانت الحروف النصية كبيرة بما يكفي، على عكس حلّنا الذي سيعمل بغض النظر عن طول السلسلة ومستوى تحسينات المترجم، وذلك بفضل خدعة برمجة قوالب C++ الذكية التي تفضلنا بها جان بولو (Can Bölük).

استخدام هذا الماكرو سهل للغاية:

root@kitploit:~
STACK_STRING(sstrText, "an extra long hello world!");
STACK_STRING(sstrCaption, "Demo");

MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK);

سيولّد هذا لغة التجميع التالية:

root@kitploit:~
mov     [rsp+58h+var_23], 61h ; 'a'
mov     [rsp+58h+var_22], 6Eh ; 'n'
mov     [rsp+58h+var_21], 20h ; ' '
mov     [rsp+58h+var_20], 65h ; 'e'
mov     [rsp+58h+var_1F], 78h ; 'x'
mov     [rsp+58h+var_1E], 74h ; 't'
mov     [rsp+58h+var_1D], 72h ; 'r'
mov     [rsp+58h+var_1C], 61h ; 'a'
mov     [rsp+58h+var_1B], 20h ; ' '
mov     [rsp+58h+var_1A], 6Ch ; 'l'
mov     [rsp+58h+var_19], 6Fh ; 'o'
mov     [rsp+58h+var_18], 6Eh ; 'n'
mov     [rsp+58h+var_17], 67h ; 'g'
mov     [rsp+58h+var_16], 20h ; ' '
mov     [rsp+58h+var_15], 68h ; 'h'
mov     [rsp+58h+var_14], 65h ; 'e'
mov     [rsp+58h+var_13], 6Ch ; 'l'
mov     [rsp+58h+var_12], 6Ch ; 'l'
mov     [rsp+58h+var_11], 6Fh ; 'o'
mov     [rsp+58h+var_10], 20h ; ' '
mov     [rsp+58h+var_2F], 0
mov     [rsp+58h+var_F], 77h ; 'w'
mov     [rsp+58h+var_E], 6Fh ; 'o'
mov     [rsp+58h+var_D], 72h ; 'r'
mov     [rsp+58h+var_C], 6Ch ; 'l'
mov     [rsp+58h+var_B], 64h ; 'd'
mov     [rsp+58h+var_A], 21h ; '!'
mov     [rsp+58h+var_33], 44h ; 'D'
mov     [rsp+58h+var_32], 65h ; 'e'
mov     [rsp+58h+var_31], 6Dh ; 'm'
mov     [rsp+58h+var_30], 6Fh ; 'o'

الحيلة 04

وبالحديث عن C++، أقدم لكم في حيلتي الرابعة التجزئة النصية في زمن الترجمة (compile-time string hashing).

ورغم أن هذا ليس مفهومًا جديدًا، فإن SILVERPICK يقدم بعض التحسينات على التنفيذات العامة الموجودة.

أولًا، نستخدم المتغير 64-بت من دالة التجزئة غير المشفرة الشهيرة FNV-1a لتقليل احتمالية نجاح هجوم تصادم التجزئة.

ثانيًا، نستخدم معاملًا معدلًا لدالة التجزئة للدفاع ضد عمليات البحث في جداول التجزئة المحسوبة مسبقًا، مثل HashDB. والأهم من ذلك، أن هذا لا يغير خصائص دالة التجزئة.

لتجزئة سلسلة قصيرة في زمن التشغيل، ما عليك سوى استخدام ماكرو HASH_STRING_RUN_TIME.

لتجزئة حرفية نصية قصيرة في زمن الترجمة، ما عليك سوى استخدام ماكرو HASH_STRING_COMPILE_TIME. يتم ضمان التقييم في زمن الترجمة فقط عبر consteval.

الحيلة 05

اتضح أنه يمكنك تنفيذ عدد لا بأس به من دوال مكتبة وقت التشغيل للغة C (CRT) باستخدام تعليمات السلاسل في معمارية x86. لذا، بالطبع، كان عليّ تنفيذها باستخدام مزيج من الدوال الجوهرية للمترجم (compiler intrinsics) ولغة التجميع المضمّنة (inline assembly).

هل تريد استخدام دالة msvcrt!memset في كودك؟ استخدم بدلًا منها ماكرو ZERO_MEMORY، والذي يستخدم تعليمة rep stosb الصادرة عبر دالة جوهرية للمترجم.

ماذا عن دالة msvcrt!memcpy أو دالة msvcrt!memmove، تسأل؟ تعرّف على ماكرو COPY_MEMORY كبديل، والذي يستخدم تعليمة rep movsb الصادرة عبر دالة جوهرية للمترجم.

ولكن ماذا عن بديل لدالة msvcrt!memcmp؟ اتضح أنه لا توجد بالضبط دالة جوهرية للمترجم متاحة لإصدار تعليمة repe cmpsb. لذا، نكتب دالة compare_memory باستخدام لغة التجميع المضمّنة بدلًا من ذلك.

أخيرًا، إذا كنت تبحث عن بديل لدالة msvcrt!memchr، تعرّف على دالة scan_memory، والتي تستخدم مرة أخرى لغة التجميع المضمّنة نظرًا لعدم وجود دالة جوهرية للمترجم متاحة لإصدار تعليمة repne scasb.

أوه، وهل نسيت أن أذكر أنه يمكنك كتابة نسختك الأكثر أمانًا من دالة msvcrt!strlen باستخدام روتين scan_memory على النحو التالي:

root@kitploit:~
DWORD_PTR dwptrExportNameLength = std::min(BIT_CAST(DWORD_PTR, scan_memory(strExportName, 0x00, MAX_EXPORTED_SYMBOL_NAME_LEN)) - BIT_CAST(DWORD_PTR, strExportName), MAX_EXPORTED_SYMBOL_NAME_LEN);

يُرجى ملاحظة أن هذه التنفيذات قد لا تنتج الكود الأكثر أداءً، اعتمادًا على البنية الدقيقة لوحدة المعالجة المركزية CPU المستهدفة. ومع ذلك، فهي مضمونة لإتمام المهمة.

الحيلة 06

هل تهتم بمزيد من الحيل الساحرة؟

هناك الكثير من الماكرو الصغيرة الأخرى في Common.h والتي وُجدت لتجريد بعض التعقيدات المتعلقة بترويض المترجم.

يتم توفير تنفيذ خالٍ من الاعتماديات لدالة GetModuleHandle في UserModuleBase.cpp. ولتبسيط سهولة الاستخدام، تم إنشاء ماكرو مفيد باسم GET_USER_MODULE_BASE.

وبالمثل، يتم توفير تنفيذ خالٍ من الاعتماديات لدالة GetProcAddress في PEParse.cpp، وهو ملفوف في ماكرو عملي باسم مناسب هو GET_EXPORTED_SYMBOL_ADDRESS. علاوة على ذلك، تم توفير ماكروين إضافيين للمساعدة في الربط الديناميكي في زمن التشغيل - INITIALIZE_FUNCTION_POINTER لتعريف وتهيئة مؤشر دالة إلى 0، وRESOLVE_FUNCTION_POINTER لحل مؤشر الدالة المذكور.

تكامل Visual Studio Code مدمج في المشروع حتى يتمكن المطورون من استخدام اختصار لوحة المفاتيح Ctrl+Shift+B لعملية بناء خالية من المتاعب.

تكامل GitHub Actions مدمج أيضًا في المشروع لتمكين بناءات CI.

يتباهى المشروع إلى حد ما بهيكله المنظم جيدًا، بالإضافة إلى الكود المعلق عليه جيدًا والنظيف نسبيًا.

أخيرًا، ألقِ نظرة على Makefile الخاص بالمشروع، والذي يحتوي على أفضل مجموعة مختارة من أعلام المترجم والرابط التي ستولّد كودًا صغيرًا وآمنًا وودودًا لـ OPSEC. وفي الوقت نفسه، ستوفر السجلات المفصلة وملف خريطة الرابط المُولَّد رؤية واضحة لعملية البناء لتسهيل فهم أعمق لسلسلة الأدوات. بالإضافة إلى ذلك، تنتج كل وحدة ترجمة أيضًا ملف تفكيك، وغالبًا ما يجعلك تفكيكه تقول أشياء مثل "ماذا فعل المترجم الآن؟"، وما إلى ذلك.

الاستخدام

إذا كنت مقتنعًا بهذا الإطار، فسيصف هذا القسم كيفية استخدامه.

فيما يلي مقتطف مأخوذ من PicMain.cpp:

root@kitploit:~
/// @brief PIC start function
/// @param None
/// @return None
EXTERN_C NO_INLINE VOID __stdcall payload(
    VOID
) {
    // Init local variables
    PVOID pKernel32 = nullptr;
    INITIALIZE_FUNCTION_POINTER(LoadLibraryA);
    HMODULE hUser32 = nullptr;
    STACK_STRING(sstrUser32, "user32.dll");
    INITIALIZE_FUNCTION_POINTER(MessageBoxA);
    STACK_STRING(sstrText, "an extra long hello world!");
    STACK_STRING(sstrCaption, "Demo");

    // Get the image base address of kernel32.dll
    pKernel32 = GET_USER_MODULE_BASE("kernel32.dll");
    if (pKernel32 == nullptr)
        goto cleanup;

    // Resolve kernel32!LoadLibraryA
    RESOLVE_FUNCTION_POINTER(pKernel32, LoadLibraryA);
    if (LoadLibraryA == nullptr)
        goto cleanup;

    // Load User32.dll into the process VAS
    hUser32 = LoadLibraryA(sstrUser32.data());
    if (hUser32 == nullptr)
        goto cleanup;

    // Resolve user32!MessageBoxA
    RESOLVE_FUNCTION_POINTER(hUser32, MessageBoxA);
    if (MessageBoxA == nullptr)
        goto cleanup;

    // Display a message box
    MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK);

    // Cleanup
cleanup:
    return;
}

يبدو الأمر سهلًا بما فيه الكفاية، أليس كذلك؟

عند كتابة PIC بلغة C/C++ باستخدام إطار SILVERPICK، عليك أن تراعي القواعد التالية:

  1. تعامل مع دالة payload كما تتعامل مع دالة main في البرنامج التقليدي، أي باعتبارها نقطة الدخول (الشبه فعلية).
  2. يجب تعريف جميع الحروف النصية على أنها سلاسل نصية على المكدس.
  3. لا يجوز استخدام المتغيرات العامة في أي مكان في الكود.
  4. لا يجوز استخدام دوال Windows API أو Native API إلا عبر الربط الديناميكي في زمن التشغيل بعد التأكد من أن النموذج الأولي للدالة متاح في ملف الرأس المقابل.

التحسينات المستقبلية

يحتوي هذا القسم على قائمة غير شاملة بالتحسينات المخطط لها والتي سيتم دمجها في مشروع مستقبلي.

  • التحول إلى سلسلة أدوات Clang/LLVM.
  • التحول إلى نظام بناء مختلف.
  • إخفاء النصوص في زمن الترجمة يعمل مع السلاسل النصية على المكدس ويكون مقاومًا لـ FLOSS.
  • تجزئة النصوص في زمن الترجمة باستخدام دالة تجزئة مخصصة غير مشفرة ومزروعة البذرة (seeded).
  • طريقة بديلة لاسترداد عنوان قاعدة مقطع GS.
  • القدرة على تجاوز تخفيف استغلال تصفية عناوين التصدير (EAF).

المراجع

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

  1. Writing Shellcode with a C Compiler بقلم Nick Harbour (2010)

  2. Shellcode with a C-compiler بقلم Didier Stevens (2010)

  3. Writing Optimized Windows Shellcode in C بقلم Matt Graeber (2013)

  4. Shellcode the better way, or how to just use your compiler بقلم Justin Fisher (2016)

  5. ShellcodeStdio بقلم Jack Ullrich (2016)

  6. Shellcode: A Windows PIC using RSA-2048 key exchange, AES-256, SHA-3 بقلم Odzhan (2016)

  7. Writing Optimized Windows Shellcode بقلم Dimitri Fourny (2017)

  8. Writing and Compiling Shellcode in C بقلم Aleksandra Doniec و Mantvydas Baranauskas (2021)

  9. Creating Shellcode from any Code Using Visual Studio and C++ بقلم Hamid Memar (2021)

  10. Writing Optimized Windows Shellcode in C بقلم Philip Woldhek (2021)

  11. From C, with inline assembly, to shellcode بقلم Steve Salinas (2023)

  12. How To Craft Your Own Windows x86/64 Shellcode with Visual Studio بقلم Yazid Benjamaa (2023)

  13. Modern implant design: position independent malware development بقلم Paul Ungur (2024)

ملاحظات وتصحيحات

VirusTotal

ما أثار دهشتي هو تصنيف VirTool:Win64/Silepesz.A من قبل Microsoft Defender.

هذه هي البايتات التي تغطيها التوقيعات:

root@kitploit:~
[+] Target file size: 2560 bytes
[+] Analyzing...
[!] Identified end of bad bytes at offset 0x4CB
000003CB   44 24 2F 32 C6 44 24 30  2E C6 44 24 31 64 C6 44   D$/2�D$0.�D$1d�D
000003DB   24 32 6C C6 44 24 33 6C  C6 44 24 35 61 C6 44 24   $2l�D$3l�D$5a�D$
000003EB   36 6E C6 44 24 37 20 C6  44 24 38 65 C6 44 24 39   6n�D$7 �D$8e�D$9
000003FB   78 C6 44 24 3A 74 C6 44  24 3B 72 C6 44 24 3C 61   x�D$:t�D$;r�D$<a
0000040B   C6 44 24 3D 20 C6 44 24  3E 6C C6 44 24 3F 6F C6   �D$= �D$>l�D$?o�
0000041B   44 24 40 6E C6 44 24 41  67 C6 44 24 42 20 C6 44   D$@n�D$Ag�D$B �D
0000042B   24 43 68 C6 44 24 44 65  C6 44 24 45 6C C6 44 24   $Ch�D$De�D$El�D$
0000043B   46 6C C6 44 24 47 6F C6  44 24 48 20 C6 44 24 29   Fl�D$Go�D$H �D$)
0000044B   00 C6 44 24 49 77 C6 44  24 4A 6F C6 44 24 4B 72   .�D$Iw�D$Jo�D$Kr
0000045B   C6 44 24 4C 6C C6 44 24  4D 64 C6 44 24 4E 21 C6   �D$Ll�D$Md�D$N!�
0000046B   44 24 25 44 C6 44 24 26  65 C6 44 24 27 6D C6 44   D$%D�D$&e�D$'m�D
0000047B   24 28 6F E8 55 00 00 00  48 85 C0 74 4B 48 BA 58   $(o�U...H.AtKH�X
0000048B   D0 CC C6 F8 E7 BF 0A 48  89 C1 E8 86 FD FF FF 48   DI�o��.H.A�.y��H
0000049B   85 C0 74 34 48 8D 4C 24  2A FF D0 48 85 C0 74 28   .At4H.L$*�DH.At(
000004AB   48 BA D9 92 FB 55 9A AC  70 E0 48 89 C1 E8 63 FD   H�U.�U.�p�H.A�cy
000004BB   FF FF 48 85 C0 74 11 48  8D 54 24 35 45 31 C9 4C   ��H.At.H.T$5E1�L

يُترجم هذا مرة أخرى إلى الكود المصدري التالي:

root@kitploit:~
pKernel32 = GET_USER_MODULE_BASE("kernel32.dll");
if (pKernel32 == nullptr)
    goto cleanup;

RESOLVE_FUNCTION_POINTER(pKernel32, LoadLibraryA); // mov rdx, 0x0ABFE7F8C6CCD058 (FNV-1a hash of "LoadLibraryA" with modified offset basis)
if (LoadLibraryA == nullptr)
    goto cleanup;

hUser32 = LoadLibraryA(sstrUser32.data());
if (hUser32 == nullptr)
    goto cleanup;

RESOLVE_FUNCTION_POINTER(hUser32, MessageBoxA); // mov rdx, 0xE070AC9A55FB92D9 (FNV-1a hash of "MessageBoxA" with modified offset basis)
if (MessageBoxA == nullptr)
    goto cleanup;

MessageBoxA(nullptr, sstrText.data(), sstrCaption.data(), MB_OK); // arg setup only

غني عن القول، إن هذا اكتشاف هش للغاية، ولن يكتشف سوى نموذج الكود المحدد الموضح في المثال. ومع ذلك، فإنه يسلط الضوء على أهمية استخدام تجزئات API متعددة الأشكال (polymorphic).

تنزيل الأداة
  • From C to shellcode (simple way) بقلم Print3M (2024)

  • relocatable بقلم Tijme Gommers (2025)

  • PIC Development Crash Course بقلم Raphael Mudge (2025)

  • scfw بقلم Petr Beneš (2026)