Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
static_asm — Assembleur compile-time C++2x en en-tête uniquement pour l'encodage d'instructions x86/x86-64 | Kitploit
Outils/GitHubGitHub/mahmoudimus/static_asm
Génération de PayloadsAnalyse de CodeExploitationRétro-ingénierieShellcodeAnalyse de BinairesApprentissage et ÉducationGénération de ShellcodeParcours et Cours
GitHubmahmoudimus/static_asm

static_asm

Assembleur compile-time C++2x en en-tête uniquement pour l'encodage d'instructions x86/x86-64

3116il y a 7 moisVérifié par Kitploit
Voir le dépôtSite web

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

static_asm

CI License

Une bibliothèque C++20 uniquement header pour l'encodage d'instructions x86/x86-64 à la compilation.

Ce projet est sous double licence Boost Software License 1.0 et MIT License. Vous pouvez choisir l'une ou l'autre.

Objectif

  • Apprendre l'encodage assembleur x86 de manière pratique et amusante
  • Générer des instructions assembleur à la compilation avec une sécurité de type totale
  • Créer des templates de shellcode et de code JIT sans surcoût à l'exécution

Démarrage rapide

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

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

// Build machine code at compile time
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> - fully constexpr!

Exemples par catégorie

Opérations ALU

root@kitploit:~
// Register to register
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 (common idiom to zero a register)
cmp(rax, rcx);           // 48 39 C8

// Register with immediate
add(rax, 0x10);          // 48 83 C0 10 (sign-extended imm8)
add(rax, 0x10000);       // 48 05 00 00 01 00 (imm32)
sub(ecx, 100);           // 83 E9 64
and_(rdx, 0xFF);         // 48 83 E2 FF

// With memory operands
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

Mouvement de données

root@kitploit:~
// Register to register
mov(rax, rbx);           // 48 89 D8
mov(eax, ecx);           // 89 C8
mov(r8, r9);             // 4D 89 C8

// Immediate to register
mov(rax, 0x12345678);    // 48 C7 C0 78 56 34 12
mov(eax, 0xDEADBEEF);    // B8 EF BE AD DE

// Memory operations
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

// Zero/sign extension
movzx(eax, bl);          // 0F B6 C3 (zero-extend byte to dword)
movzx(rax, bx);          // 48 0F B7 C3 (zero-extend word to qword)
movsx(eax, cl);          // 0F BE C1 (sign-extend byte to dword)
movsx(rax, dx);          // 48 0F BF C2 (sign-extend word to qword)
movsxd(rax, ecx);        // 48 63 C1 (sign-extend dword to qword)

// Load effective address
lea(rax, qword_ptr(rbx + rcx * s4));           // 48 8D 04 8B
lea(rax, qword_ptr(rbx + rcx * s8 + 0x10));    // 48 8D 44 CB 10

// Exchange
xchg(rax, rbx);          // 48 87 D8

Adressage SIB (Échelle-Indice-Base)

root@kitploit:~
// [base + index*scale]
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

// [base + index*scale + displacement]
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

// Store to SIB address
mov(dword_ptr(rax + rdx * s4), ecx);            // 89 0C 90
mov(qword_ptr(rbx + rsi * s8 + 0x20), rax);     // 48 89 44 F3 20

// LEA with SIB (useful for address calculations)
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

Décalages et Rotations

root@kitploit:~
// Shift by 1
shl(eax, 1);             // D1 E0
shr(rax, 1);             // 48 D1 E8
sar(ecx, 1);             // D1 F9

// Shift by immediate
shl(eax, 4);             // C1 E0 04
shr(rax, 8);             // 48 C1 E8 08
sar(rdx, 16);            // 48 C1 FA 10

// Shift by CL register
shl(eax, cl);            // D3 E0
shr(rax, cl);            // 48 D3 E8

// Rotate
rol(eax, 1);             // D1 C0
ror(rax, 8);             // 48 C1 C8 08
rcl(ecx, cl);            // D3 D1
rcr(rdx, 1);             // 48 D1 DA

Multiplication et Division

root@kitploit:~
// Single operand (result in rdx:rax)
mul(rbx);                // 48 F7 E3 (unsigned: rdx:rax = rax * rbx)
imul(rcx);               // 48 F7 E9 (signed: rdx:rax = rax * rcx)
div(rbx);                // 48 F7 F3 (unsigned: rax = rdx:rax / rbx, rdx = remainder)
idiv(rcx);               // 48 F7 F9 (signed division)

// Two-operand IMUL (dest = dest * src)
imul(rax, rbx);          // 48 0F AF C3
imul(ecx, edx);          // 0F AF CA

// Three-operand IMUL (dest = src * imm)
imul(rax, rbx, 10);      // 48 6B C3 0A
imul(ecx, edx, 1000);    // 69 CA E8 03 00 00

Flux de contrôle

root@kitploit:~
// Unconditional jumps
jmp(0x10);               // EB 10 (short, 8-bit offset)
jmp(0x1000);             // E9 00 10 00 00 (near, 32-bit offset)
jmp(rax);                // FF E0 (indirect)
jmp(here);               // EB FE (jmp $, infinite loop)

// Conditional jumps (8-bit offset)
jz(0x10);                // 74 10
jnz(0x20);               // 75 20
jb(0x08);                // 72 08 (below/carry)
jae(0x08);               // 73 08 (above or equal/no carry)
jl(0x10);                // 7C 10 (less than, signed)
jge(0x10);               // 7D 10 (greater or equal, signed)

// Conditional jumps (32-bit offset for longer branches)
jz_near(0x10000);        // 0F 84 00 00 01 00
jnz_near(0x20000);       // 0F 85 00 00 02 00

// Call and return
call(rax);               // FF D0 (indirect call)
call(0x100);             // E8 00 01 00 00 (relative call)
ret();                   // C3
ret(0x10);               // C2 10 00 (return and pop 16 bytes)

Déplacements conditionnels

root@kitploit:~
// Move if condition is true (no branch penalty!)
cmovz(rax, rbx);         // 48 0F 44 C3 (move if zero)
cmovnz(eax, ecx);        // 0F 45 C1 (move if not zero)
cmovl(rax, rdx);         // 48 0F 4C C2 (move if less, signed)
cmovge(ecx, esi);        // 0F 4D CE (move if greater or equal, signed)
cmovb(rax, rbx);         // 48 0F 42 C3 (move if below, unsigned)
cmovae(edx, edi);        // 0F 43 D7 (move if above or equal, unsigned)

// With memory source
cmovz(rax, qword_ptr(rbx));           // 48 0F 44 03
cmovnz(eax, dword_ptr(rcx + 0x10));   // 0F 45 41 10

Opérations sur les bits

root@kitploit:~
// Bit test
bt(eax, 5);              // 0F BA E0 05
bt(rax, rbx);            // 48 0F A3 D8

// Bit test and set/reset/complement
bts(eax, 10);            // 0F BA E8 0A (test and set)
btr(rax, rcx);           // 48 0F B3 C8 (test and reset)
btc(edx, 3);             // 0F BA FA 03 (test and complement)

// Bit scan
bsf(eax, ecx);           // 0F BC C1 (scan forward for first 1)
bsr(rax, rbx);           // 48 0F BD C3 (scan reverse for first 1)

// Population count and leading/trailing zeros
popcnt(eax, ecx);        // F3 0F B8 C1
lzcnt(rax, rbx);         // F3 48 0F BD C3
tzcnt(eax, edx);         // F3 0F BC C2

// Byte swap
bswap(eax);              // 0F C8 (reverse byte order)
bswap(rax);              // 48 0F C8

Opérations sur les chaînes

root@kitploit:~
// Basic string ops (operate on [rsi] and/or [rdi])
movsb();                 // A4 (move byte [rsi] -> [rdi])
movsw();                 // 66 A5
movsd();                 // A5
movsq();                 // 48 A5

cmpsb();                 // A6 (compare [rsi] with [rdi])
stosb();                 // AA (store al -> [rdi])
lodsb();                 // AC (load [rsi] -> al)
scasb();                 // AE (compare al with [rdi])

// With REP prefix (repeat rcx times)
rep_movsb();             // F3 A4 (memcpy)
rep_movsq();             // F3 48 A5 (fast memcpy, 8 bytes at a time)
rep_stosb();             // F3 AA (memset)
rep_stosq();             // F3 48 AB

// With REPE/REPNE (repeat while equal/not equal)
repe_cmpsb();            // F3 A6 (compare strings until mismatch)
repne_scasb();           // F2 AE (scan for byte in string)

Opérations sur la pile

root@kitploit:~
push(rax);               // 50
push(rbx);               // 53
push(r8);                // 41 50
push(0x10);              // 6A 10 (push imm8)
push(0x1000);            // 68 00 10 00 00 (push imm32)

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

Instructions système

root@kitploit:~
// System calls
syscall_();              // 0F 05 (64-bit syscall)
sysenter();              // 0F 34
sysexit();               // 0F 35

// Interrupts
int3();                  // CC (breakpoint)
int_(0x80);              // CD 80 (Linux 32-bit syscall)
int_(0x21);              // CD 21 (DOS interrupt)

// CPU info
cpuid();                 // 0F A2
rdtsc();                 // 0F 31
rdtscp();                // 0F 01 F9

// Privilege
cli();                   // FA (clear interrupts)
sti();                   // FB (set interrupts)
hlt();                   // F4 (halt)

// Interrupt return
iret();                  // CF (16-bit)
iretd();                 // CF (32-bit)
iretq();                 // 48 CF (64-bit)

Assemblage de plusieurs instructions

Utilisez core::assemble() pour concaténer les tableaux d'octets d'instructions :

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()
);

// Combine them
constexpr auto full_function = core::assemble(prologue, epilogue);

Installation

Option 1 : CMake FetchContent (Recommandé)

Ajoutez à votre CMakeLists.txt :

root@kitploit:~
include(FetchContent)
FetchContent_Declare(
    static_asm
    GIT_REPOSITORY https://github.com/mahmoudimus/static_asm.git
    GIT_TAG v1.0.0  # or specific commit
)
FetchContent_MakeAvailable(static_asm)

target_link_libraries(your_target PRIVATE static_asm::static_asm)

Option 2 : add_subdirectory CMake

Clonez ou ajoutez en tant que sous-module git :

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

Puis dans votre CMakeLists.txt :

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

Lorsqu'il est inclus via add_subdirectory ou FetchContent, seule la cible de bibliothèque d'interface static_asm::static_asm est ajoutée à votre projet. Les tests et exemples ne sont pas construits sauf s'ils sont explicitement activés avec -DSTATIC_ASM_BUILD_TESTS=ON.

Option 3 : Header unique

Téléchargez static_asm.hpp depuis la page des releases et incluez-le directement :

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

Option 4 : Installation système

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

Utilisez ensuite find_package :

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

Construction

root@kitploit:~
# Configuration
cmake -B build -DCMAKE_BUILD_TYPE=Release

# Construction
cmake --build build

# Exécution des tests
ctest --test-dir build --output-on-failure

# Construction avec exemples (Clang uniquement, utilise l'assembleur inline)
cmake -B build -DCMAKE_BUILD_TYPE=Release -DSTATIC_ASM_BUILD_EXAMPLES=ON

Support de plate-forme :

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

Note : La fonctionnalité core::emit() d'assembleur inline nécessite Clang avec l'optimisation -O2.

Instructions supportées

CatégorieInstructions
ALUADD, ADC, SUB, SBB, AND, OR, XOR, CMP, TEST
UnaryINC, DEC, NEG, NOT
Multiply/DivideMUL, IMUL (formes à 1/2/3 opérandes), DIV, IDIV
Data MovementMOV, MOVABS, MOVZX, MOVSX, MOVSXD, LEA, XCHG, PUSH, POP
Shift/RotateSHL, SHR, SAL, SAR, ROL, ROR, RCL, RCR
Control FlowJMP, CALL, RET, RETF
Conditional JumpsJZ/JE, JNZ/JNE, JB/JC, JNB/JNC, JBE/JNA, JNBE/JA, JL, JNL, JLE, JNLE, JO, JNO, JS, JNS, JP, JNP (décalages 8 et 32 bits)
Conditional MovesCMOVA, CMOVAE, CMOVB, CMOVBE, CMOVE, CMOVG, CMOVGE, CMOVL, CMOVLE, CMOVNE, CMOVNO, CMOVNP, CMOVNS, CMOVO, CMOVP, CMOVS
Bit OperationsBT, BTC, BTR, BTS
Bit Scan/CountBSF, BSR, POPCNT, LZCNT, TZCNT, BSWAP
String OperationsMOVSB/W/D/Q, CMPSB/W/D/Q, LODSB/W/D/Q, STOSB/W/D/Q, SCASB/W/D/Q (avec préfixes REP/REPE/REPNE)
SystemSYSCALL, SYSENTER, SYSEXIT, INT, INT3, IRET/D/Q, CLI, STI, HLT, CPUID, RDTSC, RDTSCP
MiscNOP, UD2

Support des opérandes :

  • Tous les registres généraux 8/16/32/64 bits (AL-R15)
  • Registres étendus (R8-R15, R8D-R15D, R8W-R15W, R8B-R15B)
  • Valeurs immédiates (8/16/32/64 bits)
  • Opérandes mémoire avec registre de base et déplacement
  • Adressage SIB : [base + index*échelle + déplacement] avec facteurs d'échelle 1, 2, 4, 8

Note : Pas encore d'extensions SIMD/AVX.

Développement

Configuration de l'environnement

Ce projet utilise uv pour les outils Python (génération de code, amalgamation en header unique).

root@kitploit:~
# Installer uv (si ce n'est pas déjà fait)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Vérifier l'installation
uv --version

# Tous les scripts Python peuvent être exécutés directement avec uv (les dépendances sont gérées automatiquement)
uv run scripts/gen_from_x86ref.py --help
uv run scripts/amalgamate.sh

Outils requis :

OutilObjectifInstallation
uvGestionnaire de paquets/projets Pythoncurl -LsSf https://astral.sh/uv/install.sh | sh
quomAmalgamation en header uniqueuv tool install quom
CMake 3.19+Système de constructioncmake.org
Compilateur C++20GCC 11+, Clang 14+, MSVC 2022+-

Outils optionnels pour le développement :

OutilObjectifInstallation
clang-formatMise en forme du codeVia LLVM ou le gestionnaire de paquets système
clang-tidyAnalyse statiqueVia LLVM ou le gestionnaire de paquets système

Ajout de nouvelles instructions

Option 1 : Utiliser le générateur de code

Le projet inclut un générateur qui analyse la base de données XML x86reference :

root@kitploit:~
# Afficher le résumé de la base de données d'instructions
uv run scripts/gen_from_x86ref.py

# Afficher les détails d'une instruction spécifique
uv run scripts/gen_from_x86ref.py -i lea
uv run scripts/gen_from_x86ref.py -i imul

# Générer les fichiers de la base de données d'instructions
uv run scripts/gen_from_x86ref.py --generate-db

# Générer un fichier de test exhaustif
uv run scripts/gen_from_x86ref.py --generate-tests

Option 2 : Ajout manuel

  1. Ajouter au fichier de base de données d'instructions (tableaux instdb, prefix_db, prefix_0fdb)
  2. Coder l'encodeur dans encoder.hpp ou étendre un encodeur existant
  3. Ajouter des tests

Techniques pour créer une bibliothèque à header unique

Aucun outil automatique ne peut convertir de manière fiable une bibliothèque C++ multi-fichiers arbitraire en une version header-only propre sans une certaine préparation manuelle. Les techniques suivantes aident à garantir que votre bibliothèque peut être amalgamée avec succès en un seul fichier header tout en restant correcte, maintenable et conforme aux normes.

Note : Ce projet utilise quom pour l'amalgamation et clang-tidy avec la vérification google-build-using-namespace pour automatiser l'application de ces règles. quom gère également partiellement la technique #4 (marqueurs inline) via ses capacités de traitement.

1. Éviter using namespace dans les fichiers sources

using namespace à la portée du fichier dans les fichiers .cpp est dangereux lorsque ces fichiers sont ensuite inclus dans un header — cela pollue l'espace de noms global pour chaque unité de traduction qui inclut votre header.

Modèles recommandés à la place :

root@kitploit:~
// Préféré : envelopper l'implémentation dans un espace de noms
namespace MyLib {
    Foo::Foo() {
        // ...
    }
}

ou

root@kitploit:~
// Qualification explicite (plus verbeuse mais très claire)
MyLib::Foo::Foo() {
    // ...
}

2. Placer les API internes/privées dans un espace de noms imbriqué

Les API publiques doivent vivre dans l'espace de noms principal. Tout ce qui n'est pas destiné aux utilisateurs finaux doit être caché dans un espace de noms imbriqué tel que detail ou impl.

Conventions courantes :

root@kitploit:~
namespace MyLib {
    namespace detail {           // très utilisé
        // classes internes, fonctions, etc.
    }
}

ou

root@kitploit:~
namespace MyLib::impl {          // plus court, également courant
    // détails d'implémentation internes
}

C++17 et versions ultérieures prennent en charge les définitions d'espaces de noms imbriqués en ligne, ce qui est plus propre :

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

3. Convertir les données statiques à portée de fichier en membres de classe static inline

Les variables static à portée de fichier définies dans des fichiers .cpp deviennent problématiques dans un monde header-only (définitions multiples, violations ODR).

Solution moderne (C++17+) :

root@kitploit:~
// Avant (dans .cpp)
namespace MyLib {
    static int s_counter = 0;

    int next_id() {
        return ++s_counter;
    }
}
root@kitploit:~
// Après (sûr pour un header)
namespace MyLib::detail {
    struct Globals {
        static inline int counter = 0;
    };
}

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

La variable static inline est garantie d'avoir une seule définition même lorsqu'elle est incluse plusieurs fois.

4. Marquer les fonctions définies en dehors des corps de classe comme inline

Toute fonction, fonction membre, constructeur ou destructeur dont le corps apparaît dans le header (mais pas à l'intérieur de la définition de classe) doit être marqué inline pour éviter les violations de la règle de définition unique (ODR).

Étant donné que de nombreux scripts d'amalgamation sont purement textuels et ne parsent pas la sémantique C++, une convention courante est d'utiliser une macro d'espace réservé (par exemple inline_t) pendant le développement :

root@kitploit:~
// MyLib.h (ou header commun)
#define inline_t   /* vide pendant les constructions normales */

// MyLib.cpp (pendant le développement)
namespace MyLib::detail {
    inline_t void Helper::do_work() {
        // implémentation
    }
}

Pendant l'amalgamation, l'outil remplace inline_t par inline :

root@kitploit:~
// Après amalgamation/transformation
inline void MyLib::detail::Helper::do_work() {
    // ...
}

Vous pouvez choisir n'importe quel nom de macro que vous préférez (par exemple MYLIB_INLINE, INLINE_IMP, etc.) et configurer votre script d'amalgamation en conséquence.

Note : Des outils comme quom peuvent partiellement automatiser cela en comprenant les sémantiques d'inclusion C++ et en gérant correctement les définitions de fonctions pendant l'amalgamation, réduisant ainsi le besoin de marqueurs inline manuels dans de nombreux cas.

Résumé — Les quatre règles clés

  1. Ne jamais écrire using namespace … à la portée d'espace de noms/fichier dans les fichiers d'implémentation.
  2. Mettre tous les symboles internes/non publics dans un espace de noms imbriqué (detail / impl).
  3. Remplacer les données static à portée de fichier par des membres static inline d'une structure/classe.
  4. Marquer les corps de fonctions hors ligne avec une macro de marqueur inline (remplacée pendant l'amalgamation).

Suivre ces quatre pratiques rend la transition vers une distribution header unique beaucoup plus fluide et bien moins sujette aux erreurs — même avec des outils d'amalgamation purement textuels.

Crédits

  • Godbolt pour le prototypage
  • Manuel Intel x86-64 manuel
  • Defuse.ca pour vérifier les sorties
  • Référence Geek ABC
  • mazegen/x86reference pour la base de données XML d'instructions

Remerciements

Ce projet est basé sur cx_assembler par Midi12. La bibliothèque originale a fourni les fondations pour l'encodage d'instructions assembleur x86 à la compilation en C++.

Télécharger l’outil