Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
SILVERPICK — Фреймворк для разработки шеллкода в пользовательском режиме Windows (WUMSDF) | Kitploit
Инструменты/GitHubGitHub/winterknife/silverpick
ЭксплуатацияШелл-кодRed TeamingГенерация ШеллкодаРазработка Полезной Нагрузки
GitHubwinterknife/silverpick

SILVERPICK

Фреймворк для разработки шеллкода в пользовательском режиме Windows (WUMSDF)

Репозиторий
15817192 месяцев назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
# SILVERPICK

## ВЕРСИЯ

- `v1.1`

## ОПИСАНИЕ

Проект `SILVERPICK` — это `Windows User-Mode Shellcode Development Framework (WUMSDF)`, единственная цель которого — дать разработчикам специализированных средств простой способ создавать `Position Independent Code (PIC)`-заготовки для `Windows` `x64` на `C/C++`, снижая тем самым стоимость разработки таких решений.

Он происходит от проекта [WILDBEAST](https://github.com/winterknife/WILDBEAST) и, соответственно, использует:
1. `Visual Studio Code` в качестве редактора кода
2. инструментальный набор `MinGW-w64` в качестве компилятора
3. `GNU Make` в качестве системы сборки

## УСТАНОВКА

Инструкции по настройке вы можете найти здесь: [GCC-Clang-Setup-Windows](https://gist.github.com/winterknife/0b177a75a55bad895b19aad64cffa14f)

Обратите внимание, что в этом проекте используется [MSYS2](https://www.msys2.org/).

## ВОЗМОЖНОСТИ

Написание шеллкода на языках программирования высокого уровня — не новость, и с 2010 года об этом опубликовано бесчисленное множество постов в блогах и исследовательских работ. Так что же нового в `SILVERPICK`?

Что ж, рад, что вы спросили.

У `SILVERPICK` в рукаве припрятано несколько неплохих трюков, но самое главное — это моё видение этой темы.

Итак, без лишних предисловий, представляю вам мой первый трюк.

### ТРЮК 01

С тех пор как Мэтт Грэбер популяризировал написание шеллкода на `C`, большинство людей используют его [16-байтовый стаб выравнивания стека, написанный на `Assembly`](https://github.com/mattifestation/PIC_Bindshell/blob/master/PIC_Bindshell/AdjustStack.asm).

Хотя это и не проблема, но, поскольку мы не `IKEA`, ассемблер не должен быть обязательным — и, действительно, это так.

Существует [`GCC` Function Attribute](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html), который сам сгенерирует стаб выравнивания стека.

Встречайте атрибут функции `force_align_arg_pointer` в виде удобного макроса [ALIGN_STACK](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L84), который генерирует следующий ассемблерный код:
```asm
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`, но на самом деле именно Пол Унгури возродил это чёрное искусство в [Stardust](https://github.com/Cracked5pider/Stardust).

В `Stardust` используется [скрипт компоновщика Binutils](https://sourceware.org/binutils/docs/ld/Scripts.html), чтобы управлять размещением функций и данных в нужной секции `PE` в правильном порядке. Сама эта техника основана на работе Остина Хадсона, и многие используют вариант его скриптов компоновщика.

Хотя скрипты компоновщика отлично подходят для упорядочивания секций, они не нужны, если вам нужно лишь поместить определённую функцию в начало секции кода.

Представляю вам атрибут функции `section` с особым именем секции `.init`, который сообщает компоновщику, что функция содержит код инициализации времени выполнения до `main()` и должна быть _первой_ в порядке компоновки.

Для этой цели был создан макрос [CODE_BEGIN](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L81).

### ТРЮК 03

Для своего третьего трюка представляю вам макрос [STACK_STRING](https://github.com/winterknife/silverpick/blob/master/Inc/StackString.h#L35).

В `C` вы можете создать стековую строку (строку, динамически формируемую на стеке), объявив строковый литерал как массив символов `ANSI`:
```c
char charrHelloKitty[] = { 'H', 'e', 'l', 'l', 'o', 'K', 'i', 't', 't', 'y', '\0' };
```

В `C++` вы можете создать стековую строку, просто пометив массив `char` как `constexpr`:
```cpp
constexpr char charrHelloKitty[]{ "HelloKitty" };
```

Однако обе эти техники становятся бесполезными перед лицом оптимизаций компилятора, _если_ строковые литералы достаточно велики. В отличие от нашего решения, которое работает независимо от длины строки и уровня оптимизаций, — благодаря одному остроумному трюку с шаблонным метапрограммированием на `C++` от Джана Бёлюка.

Использовать этот макрос довольно просто:
```c
STACK_STRING(sstrText, "an extra long hello world!");
STACK_STRING(sstrCaption, "Demo");

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

Это приведёт к генерации следующего ассемблерного кода:
```asm
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++`, представляю вам хеширование строк на этапе компиляции — это мой четвёртый трюк.

Хотя это не новая концепция, `SILVERPICK` предлагает некоторые улучшения по сравнению с существующими публичными реализациями.

Во-первых, мы используем 64-битный вариант популярной некриптографической хеш-функции `FNV-1a`, чтобы снизить вероятность успешной атаки на коллизии хеша.

Во-вторых, мы используем модифицированный параметр хеш-функции для защиты от поиска по предвычисленным хеш-таблицам, например [HashDB](https://github.com/OALabs/hashdb). Важно: это _не_ меняет свойства хеш-функции.

Для хеширования короткой строки во время выполнения просто используйте макрос [HASH_STRING_RUN_TIME](https://github.com/winterknife/silverpick/blob/master/Inc/HashString.h#L45).

Для хеширования короткого строкового литерала на этапе компиляции просто используйте макрос [HASH_STRING_COMPILE_TIME](https://github.com/winterknife/silverpick/blob/master/Inc/HashString.h#L42). Вычисление только на этапе компиляции гарантируется с помощью `consteval`.

### ТРЮК 05

Оказывается, с помощью строковых инструкций `x86` можно реализовать целый ряд функций `C Runtime Library (CRT)`. Поэтому, разумеется, мне пришлось реализовать их, используя сочетание встроенных функций компилятора и встроенного ассемблера.

Хотите использовать функцию `msvcrt!memset` в своём коде? Вместо этого используйте макрос [ZERO_MEMORY](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L123), который использует инструкцию `rep stosb`, генерируемую встроенной функцией компилятора.

А как насчёт функций `msvcrt!memcpy` или `msvcrt!memmove`, спросите вы? В качестве замены — макрос [COPY_MEMORY](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L126), который использует инструкцию `rep movsb`, генерируемую встроенной функцией компилятора.

А как насчёт альтернативы функции `msvcrt!memcmp`? Оказывается, встроенной функции компилятора для генерации инструкции `repe cmpsb` не существует. Поэтому мы пишем функцию [compare_memory](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L141) с использованием встроенного ассемблера.

Наконец, если вам нужна замена функции `msvcrt!memchr`, познакомьтесь с функцией [scan_memory](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h#L167), которая опять же использует встроенный ассемблер, поскольку встроенной функции компилятора для генерации инструкции `repne scasb` не существует.

И да, чуть не забыл упомянуть: вы можете написать собственную более безопасную версию функции `msvcrt!strlen`, используя процедуру `scan_memory`, например так:
```cpp
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](https://github.com/winterknife/silverpick/blob/master/Inc/Common.h) есть множество других небольших макросов, которые абстрагируют некоторые сложности, связанные с «приручением» компилятора.

Реализация функции [GetModuleHandle](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea) без зависимостей находится в [UserModuleBase.cpp](https://github.com/winterknife/silverpick/blob/master/Src/UserModuleBase.cpp). Для удобства использования создан полезный макрос [GET_USER_MODULE_BASE](https://github.com/winterknife/silverpick/blob/master/Inc/UserModuleBase.h#L36).

Аналогично, реализация функции [GetProcAddress](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress) без зависимостей находится в [PEParse.cpp](https://github.com/winterknife/silverpick/blob/master/Src/PEParse.cpp), которая затем обёрнута в удобный макрос с точным названием [GET_EXPORTED_SYMBOL_ADDRESS](https://github.com/winterknife/silverpick/blob/master/Inc/PEParse.h#L36). Кроме того, предоставлены ещё два макроса, помогающих с динамическим связыванием во время выполнения: [INITIALIZE_FUNCTION_POINTER](https://github.com/winterknife/silverpick/blob/master/Inc/PEParse.h#L39) для объявления и инициализации указателя на функцию нулём и [RESOLVE_FUNCTION_POINTER](https://github.com/winterknife/silverpick/blob/master/Inc/PEParse.h#L42) для разрешения указанного указателя на функцию.

В проект встроена [интеграция с Visual Studio Code](https://github.com/winterknife/silverpick/blob/master/.vscode), чтобы разработчики могли использовать сочетание клавиш `Ctrl+Shift+B` для простой сборки без лишних хлопот.

В проект также встроена [интеграция с GitHub Actions](https://github.com/winterknife/silverpick/blob/master/.github/workflows/build.yml) для поддержки `CI`-сборок.

Проект по праву гордится своей хорошо организованной структурой, а также тщательно прокомментированным и относительно чистым кодом.

Наконец, взгляните на [Makefile](https://github.com/winterknife/silverpick/blob/master/Makefile) проекта: в нём подобран отборный набор флагов компилятора и компоновщика, которые генерируют небольшой, безопасный и дружественный к `OPSEC` код. При этом подробное журналирование и генерируемый файл карты компоновщика обеспечат наглядность процесса сборки и помогут глубже понять инструментарий. Кроме того, каждая единица трансляции также создаёт файл дизассемблирования, глядя на который вы часто будете восклицать что-то вроде «что это компилятор сделал?» и так далее.

## ИСПОЛЬЗОВАНИЕ

Если вас заинтересовал этот фреймворк, в этом разделе описано, как им пользоваться.

Ниже приведён фрагмент из [PicMain.cpp](https://github.com/winterknife/silverpick/blob/master/Src/PicMain.cpp):
```cpp
/// @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](https://github.com/mandiant/flare-floss).
- [ ] Хеширование строк на этапе компиляции с использованием некриптографической пользовательской хеш-функции с сидом.
- [ ] Альтернативный способ получения базового адреса сегмента `GS`.
- [ ] Возможность обхода защитной меры `Export Address Filtering (EAF)`.

## ИСТОЧНИКИ

Ниже приведён список ссылок в хронологическом порядке, которые оказались для меня бесценными во время исследования и послужили источником вдохновения для этого проекта:

1. [Написание шеллкода с помощью компилятора C](https://nickharbour.wordpress.com/2010/07/01/writing-shellcode-with-a-c-compiler/) — Nick Harbour (2010)

2. [Шеллкод с помощью компилятора C](https://blog.didierstevens.com/programs/shellcode/) — Didier Stevens (2010)

3. [Написание оптимизированного шеллкода для Windows на C](https://web.archive.org/web/20201202085848/http://www.exploit-monday.com/2013/08/writing-optimized-windows-shellcode-in-c.html) — Matt Graeber (2013)

4. [Шеллкод правильным способом, или как просто использовать компилятор](https://phrack.org/issues/69/4) — Justin Fisher (2016)

5. [ShellcodeStdio](https://winternl.com/shellcodestdio/) — Jack Ullrich (2016)

6. [Шеллкод: Windows PIC с использованием обмена ключами RSA-2048, AES-256, SHA-3](https://web.archive.org/web/20240316160314/https://modexp.wordpress.com/2016/12/26/windows-pic/) — Odzhan (2016)

7. [Написание оптимизированного шеллкода для Windows](https://dimitrifourny.github.io/2017/04/28/optimized-shellcode.html) — Dimitri Fourny (2017)

8. [Написание и компиляция шеллкода на C](https://www.ired.team/offensive-security/code-injection-process-injection/writing-and-compiling-shellcode-in-c) — Aleksandra Doniec и Mantvydas Baranauskas (2021)

9. [Создание шеллкода из любого кода с помощью Visual Studio и C++](https://www.codeproject.com/articles/Creating-Shellcode-from-any-Code-Using-Visual-Stud#comments-section) — Hamid Memar (2021)

10. [Написание оптимизированного шеллкода для Windows на C](https://phasetw0.com/malware/writing-optimized-windows-shellcode-in-c/) — Philip Woldhek (2021)

11. [От C со встроенным ассемблером до шеллкода](https://steve-s.gitbook.io/0xtriboulet/archive/notice/just-malicious/from-c-with-inline-assembly-to-shellcode) — Steve Salinas (2023)

12. [Как создать собственный Windows x86/64 Shellcode с помощью Visual Studio](https://xacone.github.io/custom_shellcode.html) — Yazid Benjamaa (2023)

13. [Современный дизайн имплантов: разработка позиционно-независимого вредоносного ПО](https://5pider.net/blog/2024/01/27/modern-shellcode-implant-design) — Paul Ungur (2024)

14. [От C к шеллкоду (простой способ)](https://print3m.github.io/blog/from-c-to-shellcode) — Print3M (2024)

15. [relocatable](https://github.com/tijme/relocatable) — Tijme Gommers (2025)

16. [Ускоренный курс по разработке PIC](https://player.vimeo.com/video/1100089433) — Raphael Mudge (2025)

17. [scfw](https://github.com/vmi-rs/scfw) — Petr Beneš (2026)

## ЭРРАТА

[VirusTotal](https://www.virustotal.com/gui/file/5f7bf9375a206690bd0515db279f313a1ea2c520e627f1a341457838c6bb513e)

Что показалось мне странным, так это классификация `VirTool:Win64/Silepesz.A` антивирусом `Microsoft Defender`.

Вот байты, которые покрывает сигнатура:
```hex
[+] 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
```

Это соответствует следующему исходному коду:
```c
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`.
Скачать инструмент