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

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

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

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

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

Категории

Все категории
Loading categories
static_asm — Заголовочный ассемблер времени компиляции на C++2x для кодирования инструкций x86/x86-64 | Kitploit
Инструменты/GitHubGitHub/mahmoudimus/static_asm
Генерация полезной нагрузкиАнализ КодаЭксплуатацияОбратная инженерияШелл-кодАнализ Бинарных ФайловОбучение и ОбразованиеГенерация ШеллкодаУчебные Маршруты и Курсы
GitHubmahmoudimus/static_asm

static_asm

Заголовочный ассемблер времени компиляции на C++2x для кодирования инструкций x86/x86-64

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

Популярное

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

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

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

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

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

static_asm

CI Лицензия

Библиотека только с заголовками для C++20, предназначенная для кодирования инструкций x86/x86-64 на этапе компиляции.

Этот проект распространяется под двойной лицензией: Boost Software License 1.0 и MIT License. Вы можете выбрать любую из них.

Назначение

  • Изучение кодирования ассемблера x86 практичным и увлекательным способом
  • Генерация инструкций ассемблера на этапе компиляции с полной безопасностью типов
  • Создание шаблонов shellcode и JIT-кода без накладных расходов во время выполнения

Быстрый старт

root@kitploit:~
#include "static_asm.hpp"

using namespace static_asm::x86::registers;
using namespace static_asm::x86::instructions;

// Сборка машинного кода на этапе компиляции
constexpr auto code = core::assemble(
    mov(rax, 0x12345678),    // mov rax, imm32
    add(rax, rcx),           // add rax, rcx
    xor_(r8, r8),            // xor r8, r8
    call(rax),               // call rax
    ret()                    // ret
);
// code is std::array<uint8_t, N> - полностью constexpr!

Примеры по категориям

Операции ALU

root@kitploit:~
// Регистр-регистр
add(rax, rbx);           // 48 01 D8
sub(ecx, edx);           // 29 D1
and_(r8, r9);            // 4D 21 C8
or_(rsi, rdi);           // 48 09 FE
xor_(eax, eax);          // 31 C0 (распространённый способ обнуления регистра)
cmp(rax, rcx);           // 48 39 C8

// Регистр с непосредственным значением
add(rax, 0x10);          // 48 83 C0 10 (знаковое расширение imm8)
add(rax, 0x10000);       // 48 05 00 00 01 00 (imm32)
sub(ecx, 100);           // 83 E9 64
and_(rdx, 0xFF);         // 48 83 E2 FF

// С операндами в памяти
add(eax, dword_ptr(rbx));              // 03 03
add(rax, qword_ptr(rcx + 0x10));       // 48 03 41 10
sub(dword_ptr(rsp + 0x20), eax);       // 29 44 24 20

Перемещение данных

root@kitploit:~
// Регистр-регистр
mov(rax, rbx);           // 48 89 D8
mov(eax, ecx);           // 89 C8
mov(r8, r9);             // 4D 89 C8

// Непосредственное значение в регистр
mov(rax, 0x12345678);    // 48 C7 C0 78 56 34 12
mov(eax, 0xDEADBEEF);    // B8 EF BE AD DE

// Операции с памятью
mov(rax, qword_ptr(rbx));              // 48 8B 03
mov(eax, dword_ptr(rcx + 0x10));       // 8B 41 10
mov(qword_ptr(rsp + 0x8), rax);        // 48 89 44 24 08
mov(dword_ptr(rbp - 0x20), 0x100);     // C7 45 E0 00 01 00 00

// Расширение нулём / знаком
movzx(eax, bl);          // 0F B6 C3 (расширение байта нулём до двойного слова)
movzx(rax, bx);          // 48 0F B7 C3 (расширение слова нулём до учётверённого слова)
movsx(eax, cl);          // 0F BE C1 (расширение байта знаком до двойного слова)
movsx(rax, dx);          // 48 0F BF C2 (расширение слова знаком до учётверённого слова)
movsxd(rax, ecx);        // 48 63 C1 (расширение двойного слова знаком до учётверённого слова)

// Загрузка эффективного адреса
lea(rax, qword_ptr(rbx + rcx * s4));           // 48 8D 04 8B
lea(rax, qword_ptr(rbx + rcx * s8 + 0x10));    // 48 8D 44 CB 10

// Обмен
xchg(rax, rbx);          // 48 87 D8

SIB-адресация (Scale-Index-Base)

root@kitploit:~
// [база + индекс*масштаб]
mov(eax, dword_ptr(rbx + rcx * s1));   // 8B 04 0B
mov(eax, dword_ptr(rbx + rcx * s2));   // 8B 04 4B
mov(eax, dword_ptr(rbx + rcx * s4));   // 8B 04 8B
mov(eax, dword_ptr(rbx + rcx * s8));   // 8B 04 CB

// [база + индекс*масштаб + смещение]
mov(rax, qword_ptr(rbx + rcx * s4 + 0x10));     // 48 8B 44 8B 10
mov(rax, qword_ptr(r12 + r13 * s8 + 0x1000));   // 4B 8B 84 EC 00 10 00 00

// Запись по SIB-адресу
mov(dword_ptr(rax + rdx * s4), ecx);            // 89 0C 90
mov(qword_ptr(rbx + rsi * s8 + 0x20), rax);     // 48 89 44 F3 20

// LEA с SIB (полезно для вычисления адресов)
lea(rax, qword_ptr(rbx + rcx * s4));            // 48 8D 04 8B
lea(rax, qword_ptr(rdi + rsi * s8 + 0x100));    // 48 8D 84 F7 00 01 00 00

Сдвиги и вращения

root@kitploit:~
// Сдвиг на 1
shl(eax, 1);             // D1 E0
shr(rax, 1);             // 48 D1 E8
sar(ecx, 1);             // D1 F9

// Сдвиг на непосредственное значение
shl(eax, 4);             // C1 E0 04
shr(rax, 8);             // 48 C1 E8 08
sar(rdx, 16);            // 48 C1 FA 10

// Сдвиг на регистр CL
shl(eax, cl);            // D3 E0
shr(rax, cl);            // 48 D3 E8

// Вращение
rol(eax, 1);             // D1 C0
ror(rax, 8);             // 48 C1 C8 08
rcl(ecx, cl);            // D3 D1
rcr(rdx, 1);             // 48 D1 DA

Умножение и деление

root@kitploit:~
// Однооперандные (результат в rdx:rax)
mul(rbx);                // 48 F7 E3 (беззнаковое: rdx:rax = rax * rbx)
imul(rcx);               // 48 F7 E9 (знаковое: rdx:rax = rax * rcx)
div(rbx);                // 48 F7 F3 (беззнаковое: rax = rdx:rax / rbx, rdx = остаток)
idiv(rcx);               // 48 F7 F9 (знаковое деление)

// Двухоперандный IMUL (результат = результат * источник)
imul(rax, rbx);          // 48 0F AF C3
imul(ecx, edx);          // 0F AF CA

// Трёхоперандный IMUL (результат = источник * непосредственное)
imul(rax, rbx, 10);      // 48 6B C3 0A
imul(ecx, edx, 1000);    // 69 CA E8 03 00 00

Управление потоком

root@kitploit:~
// Безусловные переходы
jmp(0x10);               // EB 10 (короткий, 8-битное смещение)
jmp(0x1000);             // E9 00 10 00 00 (близкий, 32-битное смещение)
jmp(rax);                // FF E0 (косвенный)
jmp(here);               // EB FE (jmp $, бесконечный цикл)

// Условные переходы (8-битное смещение)
jz(0x10);                // 74 10
jnz(0x20);               // 75 20
jb(0x08);                // 72 08 (ниже/перенос)
jae(0x08);               // 73 08 (выше или равно/нет переноса)
jl(0x10);                // 7C 10 (меньше, знаковое)
jge(0x10);               // 7D 10 (больше или равно, знаковое)

// Условные переходы (32-битное смещение для длинных ветвей)
jz_near(0x10000);        // 0F 84 00 00 01 00
jnz_near(0x20000);       // 0F 85 00 00 02 00

// Вызов и возврат
call(rax);               // FF D0 (косвенный вызов)
call(0x100);             // E8 00 01 00 00 (относительный вызов)
ret();                   // C3
ret(0x10);               // C2 10 00 (возврат и извлечение 16 байт)

Условные пересылки

root@kitploit:~
// Пересылка, если условие истинно (без штрафа за ветвление!)
cmovz(rax, rbx);         // 48 0F 44 C3 (переслать, если ноль)
cmovnz(eax, ecx);        // 0F 45 C1 (переслать, если не ноль)
cmovl(rax, rdx);         // 48 0F 4C C2 (переслать, если меньше, знаковое)
cmovge(ecx, esi);        // 0F 4D CE (переслать, если больше или равно, знаковое)
cmovb(rax, rbx);         // 48 0F 42 C3 (переслать, если ниже, беззнаковое)
cmovae(edx, edi);        // 0F 43 D7 (переслать, если выше или равно, беззнаковое)

// С источником в памяти
cmovz(rax, qword_ptr(rbx));           // 48 0F 44 03
cmovnz(eax, dword_ptr(rcx + 0x10));   // 0F 45 41 10

Битовые операции

root@kitploit:~
// Тест бита
bt(eax, 5);              // 0F BA E0 05
bt(rax, rbx);            // 48 0F A3 D8

// Тест бита и установка/сброс/дополнение
bts(eax, 10);            // 0F BA E8 0A (тест и установка)
btr(rax, rcx);           // 48 0F B3 C8 (тест и сброс)
btc(edx, 3);             // 0F BA FA 03 (тест и дополнение)

// Сканирование бита
bsf(eax, ecx);           // 0F BC C1 (сканирование вперёд для первого 1)
bsr(rax, rbx);           // 48 0F BD C3 (сканирование назад для первого 1)

// Подсчёт единиц и ведущие/конечные нули
popcnt(eax, ecx);        // F3 0F B8 C1
lzcnt(rax, rbx);         // F3 48 0F BD C3
tzcnt(eax, edx);         // F3 0F BC C2

// Перестановка байт
bswap(eax);              // 0F C8 (обратный порядок байт)
bswap(rax);              // 48 0F C8

Строковые операции

root@kitploit:~
// Базовые строковые операции (работают с [rsi] и/или [rdi])
movsb();                 // A4 (переместить байт [rsi] -> [rdi])
movsw();                 // 66 A5
movsd();                 // A5
movsq();                 // 48 A5

cmpsb();                 // A6 (сравнить [rsi] с [rdi])
stosb();                 // AA (записать al -> [rdi])
lodsb();                 // AC (загрузить [rsi] -> al)
scasb();                 // AE (сравнить al с [rdi])

// С префиксом REP (повторить rcx раз)
rep_movsb();             // F3 A4 (memcpy)
rep_movsq();             // F3 48 A5 (быстрый memcpy, по 8 байт за раз)
rep_stosb();             // F3 AA (memset)
rep_stosq();             // F3 48 AB

// С префиксами REPE/REPNE (повторять, пока равно/не равно)
repe_cmpsb();            // F3 A6 (сравнение строк до несовпадения)
repne_scasb();           // F2 AE (поиск байта в строке)

Стековые операции

root@kitploit:~
push(rax);               // 50
push(rbx);               // 53
push(r8);                // 41 50
push(0x10);              // 6A 10 (поместить imm8)
push(0x1000);            // 68 00 10 00 00 (поместить imm32)

pop(rax);                // 58
pop(rbx);                // 5B
pop(r15);                // 41 5F

Системные инструкции

root@kitploit:~
// Системные вызовы
syscall_();              // 0F 05 (64-битный syscall)
sysenter();              // 0F 34
sysexit();               // 0F 35

// Прерывания
int3();                  // CC (точка останова)
int_(0x80);              // CD 80 (Linux 32-битный syscall)
int_(0x21);              // CD 21 (прерывание DOS)

// Информация о CPU
cpuid();                 // 0F A2
rdtsc();                 // 0F 31
rdtscp();                // 0F 01 F9

// Привилегированные
cli();                   // FA (запрет прерываний)
sti();                   // FB (разрешение прерываний)
hlt();                   // F4 (останов)

// Возврат из прерывания
iret();                  // CF (16-битный)
iretd();                 // CF (32-битный)
iretq();                 // 48 CF (64-битный)

Сборка нескольких инструкций

Используйте core::assemble() для объединения массивов байт инструкций:

root@kitploit:~
constexpr auto prologue = core::assemble(
    push(rbp),
    mov(rbp, rsp),
    sub(rsp, 0x20)
);

constexpr auto epilogue = core::assemble(
    add(rsp, 0x20),
    pop(rbp),
    ret()
);

// Объединение
constexpr auto full_function = core::assemble(prologue, epilogue);

Установка

Вариант 1: CMake FetchContent (рекомендуется)

Добавьте в ваш CMakeLists.txt:

root@kitploit:~
include(FetchContent)
FetchContent_Declare(
    static_asm
    GIT_REPOSITORY https://github.com/mahmoudimus/static_asm.git
    GIT_TAG v1.0.0  # или конкретный коммит
)
FetchContent_MakeAvailable(static_asm)

target_link_libraries(your_target PRIVATE static_asm::static_asm)

Вариант 2: CMake add_subdirectory

Клонируйте или добавьте как git-подмодуль:

root@kitploit:~
git submodule add https://github.com/mahmoudimus/static_asm.git external/static_asm

Затем в вашем CMakeLists.txt:

root@kitploit:~
add_subdirectory(external/static_asm)
target_link_libraries(your_target PRIVATE static_asm::static_asm)

При включении через add_subdirectory или FetchContent в проект добавляется только цель интерфейсной библиотеки static_asm::static_asm. Тесты и примеры не собираются, если явно не включены с помощью -DSTATIC_ASM_BUILD_TESTS=ON.

Вариант 3: Одиночный заголовок

Скачайте static_asm.hpp со страницы релизов и включите его напрямую:

root@kitploit:~
#include "static_asm.hpp"

Вариант 4: Установка в систему

root@kitploit:~
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --install build --prefix /usr/local

Затем используйте find_package:

root@kitploit:~
find_package(static_asm REQUIRED)
target_link_libraries(your_target PRIVATE static_asm::static_asm)

Сборка

root@kitploit:~
# Конфигурация
cmake -B build -DCMAKE_BUILD_TYPE=Release

# Сборка
cmake --build build

# Запуск тестов
ctest --test-dir build --output-on-failure

# Сборка с примерами (только Clang, использует встроенный ассемблер)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSTATIC_ASM_BUILD_EXAMPLES=ON

Поддерживаемые платформы:

  • Linux (GCC 11+, Clang 14+)
  • macOS (Apple Clang, Clang)
  • Windows (MSVC 2022+)

Примечание: функция core::emit() со встроенным ассемблером требует Clang с оптимизацией -O2.

Поддерживаемые инструкции

КатегорияИнструкции
ALUADD, ADC, SUB, SBB, AND, OR, XOR, CMP, TEST
УнарныеINC, DEC, NEG, NOT
Умножение/ДелениеMUL, IMUL (формы с 1/2/3 операндами), DIV, IDIV
Перемещение данныхMOV, MOVABS, MOVZX, MOVSX, MOVSXD, LEA, XCHG, PUSH, POP
Сдвиг/ВращениеSHL, SHR, SAL, SAR, ROL, ROR, RCL, RCR
Управление потокомJMP, CALL, RET, RETF
Условные переходыJZ/JE, JNZ/JNE, JB/JC, JNB/JNC, JBE/JNA, JNBE/JA, JL, JNL, JLE, JNLE, JO, JNO, JS, JNS, JP, JNP (8-битные и 32-битные смещения)
Условные пересылкиCMOVA, CMOVAE, CMOVB, CMOVBE, CMOVE, CMOVG, CMOVGE, CMOVL, CMOVLE, CMOVNE, CMOVNO, CMOVNP, CMOVNS, CMOVO, CMOVP, CMOVS
Битовые операцииBT, BTC, BTR, BTS
Сканирование/Подсчёт битBSF, BSR, POPCNT, LZCNT, TZCNT, BSWAP
Строковые операцииMOVSB/W/D/Q, CMPSB/W/D/Q, LODSB/W/D/Q, STOSB/W/D/Q, SCASB/W/D/Q (с префиксами REP/REPE/REPNE)
СистемныеSYSCALL, SYSENTER, SYSEXIT, INT, INT3, IRET/D/Q, CLI, STI, HLT, CPUID, RDTSC, RDTSCP
РазноеNOP, UD2

Поддержка операндов:

  • Все 8-/16-/32-/64-битные регистры общего назначения (AL-R15)
  • Расширенные регистры (R8-R15, R8D-R15D, R8W-R15W, R8B-R15B)
  • Непосредственные значения (8/16/32/64 бита)
  • Операнды в памяти с базовым регистром и смещением
  • SIB-адресация: [база + индекс*масштаб + смещение] с коэффициентами масштаба 1, 2, 4, 8

Примечание: Расширения SIMD/AVX пока не поддерживаются.

Разработка

Настройка окружения

В этом проекте используется uv для инструментов Python (генерация кода, объединение в один заголовок).

root@kitploit:~
# Установка uv (если ещё не установлен)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Проверка установки
uv --version

# Все Python-скрипты можно запускать напрямую через uv (зависимости управляются автоматически)
uv run scripts/gen_from_x86ref.py --help
uv run scripts/amalgamate.sh

Необходимые инструменты:

ИнструментНазначениеУстановка
uvМенеджер пакетов/проектов Pythoncurl -LsSf https://astral.sh/uv/install.sh | sh
quomОбъединение в один заголовокuv tool install quom
CMake 3.19+Система сборкиcmake.org
Компилятор C++20GCC 11+, Clang 14+, MSVC 2022+-

Дополнительные инструменты для разработки:

ИнструментНазначениеУстановка
clang-formatФорматирование кодаЧерез LLVM или системный пакет
clang-tidyСтатический анализЧерез LLVM или системный пакет

Добавление новых инструкций

Вариант 1: Использование генератора кода

Проект включает генератор, который разбирает базу данных XML x86reference:

root@kitploit:~
# Показать сводку базы инструкций
uv run scripts/gen_from_x86ref.py

# Показать подробности для конкретной инструкции
uv run scripts/gen_from_x86ref.py -i lea
uv run scripts/gen_from_x86ref.py -i imul

# Сгенерировать файлы базы инструкций
uv run scripts/gen_from_x86ref.py --generate-db

# Сгенерировать исчерпывающий тестовый файл
uv run scripts/gen_from_x86ref.py --generate-tests

Вариант 2: Ручное добавление

  1. Добавьте в файл базы инструкций (массивы instdb, prefix_db, prefix_0fdb)
  2. Реализуйте кодировщик в encoder.hpp или расширьте существующий
  3. Добавьте тесты

Методы создания библиотеки в одном заголовке

Никакой автоматический инструмент не может надёжно преобразовать произвольную многофайловую библиотеку C++ в чистую версию только с заголовками без некоторой ручной подготовки. Следующие методы помогут убедиться, что ваша библиотека может быть успешно объединена в один файл заголовка, оставаясь при этом корректной, поддерживаемой и соответствующей стандартам.

Примечание: Этот проект использует quom для объединения и clang-tidy с проверкой google-build-using-namespace для автоматического соблюдения этих правил. quom также частично обрабатывает метод #4 (маркеры встраивания) через свои возможности обработки.

1. Избегайте using namespace в исходных файлах

using namespace на уровне файла в файлах .cpp опасен, если эти файлы позже включаются в заголовок — это загрязняет глобальное пространство имён для каждой единицы трансляции, которая включает ваш заголовок.

Рекомендуемые альтернативные шаблоны:

root@kitploit:~
// Предпочтительно: обернуть реализацию в пространство имён
namespace MyLib {
    Foo::Foo() {
        // ...
    }
}

или

root@kitploit:~
// Явная квалификация (более многословно, но очень понятно)
MyLib::Foo::Foo() {
    // ...
}

2. Размещайте внутренние/частные API во вложенном пространстве имён

Публичные API должны находиться в основном пространстве имён. Всё, что не предназначено для конечных пользователей, должно быть скрыто во вложенном пространстве имён, таком как detail или impl.

Распространённые соглашения:

root@kitploit:~
namespace MyLib {
    namespace detail {           // очень широко используется
        // внутренние классы, функции и т.д.
    }
}

или

root@kitploit:~
namespace MyLib::impl {          // короче, также распространено
    // детали внутренней реализации
}

C++17 и новее поддерживают встроенные вложенные определения пространств имён, которые чище:

root@kitploit:~
namespace MyLib::detail {
    class InternalHelper { /* ... */ };
}

3. Преобразуйте статические данные на уровне файла в члены класса static inline

static-переменные на уровне файла, определённые в файлах .cpp, становятся проблематичными в мире только заголовков (множественные определения, нарушения ODR).

Современное (C++17+) решение:

root@kitploit:~
// До (в .cpp)
namespace MyLib {
    static int s_counter = 0;

    int next_id() {
        return ++s_counter;
    }
}
root@kitploit:~
// После (безопасно для заголовка)
namespace MyLib::detail {
    struct Globals {
        static inline int counter = 0;
    };
}

inline int MyLib::next_id() {
    return ++detail::Globals::counter;
}

Переменная static inline гарантированно имеет единственное определение, даже если включается несколько раз.

4. Помечайте функции, определённые вне тел классов, как inline

Любая функция, функция-член, конструктор или деструктор, чьё тело появляется в заголовке (но не внутри определения класса) должна быть помечена как inline, чтобы избежать нарушений Правила Одного Определения (ODR).

Поскольку многие скрипты объединения являются чисто текстовыми и не разбирают семантику C++, распространённым соглашением является использование макроса-заполнителя (например, inline_t) во время разработки:

root@kitploit:~
// MyLib.h (или общий заголовок)
#define inline_t   /* пусто при обычной сборке */

// MyLib.cpp (во время разработки)
namespace MyLib::detail {
    inline_t void Helper::do_work() {
        // реализация
    }
}

Во время объединения инструмент заменяет inline_t на inline:

root@kitploit:~
// После объединения / преобразования
inline void MyLib::detail::Helper::do_work() {
    // ...
}

Вы можете выбрать любое имя макроса по своему усмотрению (например, MYLIB_INLINE, INLINE_IMP и т.д.) и настроить соответствующий скрипт объединения.

Примечание: Инструменты, такие как quom, могут частично автоматизировать этот процесс, понимая семантику включения C++ и правильно обрабатывая определения функций во время объединения, что во многих случаях снижает необходимость в ручных маркерах inline.

Резюме — Четыре ключевых правила

  1. Никогда не пишите using namespace … на уровне пространства имён/файла в файлах реализации.
  2. Помещайте все внутренние/непубличные символы во вложенное пространство имён (detail / impl).
  3. Заменяйте статические данные на уровне файла на static inline члены структуры/класса.
  4. Помечайте тела функций, определённые вне строк, макросом-маркером inline (заменяемым во время объединения).

Следование этим четырём практикам делает переход к распространению в виде одного заголовка гораздо более плавным и менее подверженным ошибкам — даже при использовании чисто текстовых инструментов объединения.

Благодарности

  • Godbolt за прототипирование
  • Руководство Intel x86-64 manual
  • Defuse.ca за проверку результатов
  • Geek ABC reference
  • mazegen/x86reference за базу данных инструкций XML

Признательность

Этот проект основан на cx_assembler от Midi12. Оригинальная библиотека предоставила основу для кодирования ассемблера x86 на этапе компиляции на C++.

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