Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
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é.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
LdrShuffle — Technique d'exécution/injection de code utilisant la manipulation de la structure du module PEB des DLL | Kitploit
Outils/GitHubGitHub/rwxstoned/ldrshuffle
Analyse de CodeExploitationMouvement LatéralShellcodePost-ExploitationTests d'IntrusionRed TeamingDéveloppement de Charges UtilesExploitation de Binaires
GitHubrwxstoned/ldrshuffle

LdrShuffle

Technique d'exécution/injection de code utilisant la manipulation de la structure du module PEB des DLL

2894410il y a 1 anVérifié par Kitploit

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
Voir le dépôt

LdrShuffle

Exécution furtive de code via la modification du EntryPoint des modules chargés à l'exécution.

Résumé

Les processus Windows ont divers modules chargés à l'exécution. Chacun de ces modules possède une fonction DllMain() définie, qui sera appelée lors de la création/destruction d'un processus ou d'un thread (quatre scénarios possibles).

Afin d'appeler correctement ces fonctions pendant la durée de vie du processus, les fonctions du chargeur Windows (ntdll!Ldrp*) se réfèrent à une liste d'entrées contenant des paramètres clés (dont le champ EntryPoint) pour chaque module.

En écrasant ce EntryPoint pour une DLL, nous garantissons que l'exécution du code sera redirigée vers un endroit de notre choix.

Cas d'utilisation

Cela peut être utilisé à la fois comme une primitive d'exécution de code, et pour du proxy d'API, par exemple pour exécuter certaines API avec une pile d'appels non suspecte puisqu'elles seront invoquées par des fonctions Windows légitimes.

Cela peut également être utilisé pour déclencher l'exécution dans un processus distant, à condition que l'attaquant ait la capacité de lire et d'écrire la mémoire sur ce processus cible. De manière similaire à Threadless Injection, cela offre la capacité d'exécuter du code dans un processus sans invoquer les API classiques liées à l'exécution (CreateRemoteThread, QueueUserAPC).

Défis

Le chargement/déchargement des modules au sein d'un processus Windows est un sujet complexe qui présente de nombreux défis, des risques d'instabilité, de courses critiques et de plantages. Un obstacle bien connu lié à l'exécution de code dans une fonction DllMain() par exemple, réside dans le fait qu'un verrou du chargeur (Loader Lock) est en place et que nous tournons dans un thread qui n'a pas été complètement initialisé, ou qui est en cours de terminaison.

Par conséquent, j'ai essayé de documenter correctement ce qui est possible et ce qui ne l'est pas. Par exemple, bien que la plupart des appels API habituels puissent être effectués, exécuter un beacon complet nécessite certaines contraintes pour être dans un processus séparé, afin d'éviter les interblocages causés par les fonctions utilisées dans wininet.dll ou winhttp.dll.

Implémentation

Rappel sur le chargement des DLL sous Windows

Chaque processus maintient une liste de structures _LDR_DATA_TABLE_ENTRY à l'exécution. Ces structures contiennent de nombreux détails pertinents sur la DLL, tels que son EntryPoint (que nous allons écraser), son nom, certains hachages, des horodatages, divers indicateurs, etc. Certaines de ces structures sont documentées, d'autres non.

On peut les visualiser via cette commande WinDbg :

dt nt!_LDR_DATA_TABLE_ENTRY 0xdeadbeef

0:006> dt nt!_LDR_DATA_TABLE_ENTRY 0x18d1c4838c0
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x0000018d`1c485de0 - 0x0000018d`1c4832b0 ]
   +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x0000018d`1c485df0 - 0x0000018d`1c4832c0 ]
   +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x0000018d`1c4832d0 - 0x0000018d`1c482c80 ]
   +0x030 DllBase          : 0x00007ffe`e87e0000 Void
   +0x038 EntryPoint       : 0x00007ffe`e8838d00 Void
   +0x040 SizeOfImage      : 0x2fe000
   +0x048 FullDllName      : _UNICODE_STRING "C:\WINDOWS\System32\KERNELBASE.dll"
   +0x058 BaseDllName      : _UNICODE_STRING "KERNELBASE.dll"
   +0x068 FlagGroup        : [4]  "???"
   +0x068 Flags            : 0x8a2cc
   +0x068 PackagedBinary   : 0y0
   +0x068 MarkedForRemoval : 0y0
   +0x068 ImageDll         : 0y1
(...)
   +0x0e0 MappingInfoIndexNode : _RTL_BALANCED_NODE
   +0x0f8 OriginalBase     : 0x00007ffe`e87e0000
   +0x100 LoadTime         : _LARGE_INTEGER 0x01db5f86`2fa735fc
   +0x108 BaseNameHashValue : 0x235bec4
   +0x10c LoadReason       : 0 ( LoadReasonStaticDependency )
   +0x110 ImplicitPathOptions : 0x4000
   +0x114 ReferenceCount   : 1
   +0x118 DependentLoadFlags : 0x800
   +0x11c SigningLevel     : 0 ''

L'adresse de la structure peut être obtenue en parcourant une structure doublement chaînée référencée dans le PEB du processus, dans une structure PEB_LDR_DATA.

dt nt!_PEB_LDR_DATA 0xb4b4c3c3

Notez le drapeau DontCallForThreads. Comme son nom l'indique, si ce drapeau est défini, le système d'exploitation n'appellera PAS le DllMain() de ce module pour les événements de thread (c'est-à-dire DLL_THREAD_ATTACH ou DLL_THREAD_DETACH).

Lors de la création d'une DLL, le modèle suivant doit être suivi pour fonctionner de concert avec les fonctions du chargeur OS :

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

Détails techniques sur l'implémentation

Configuration d'un appel API

Comme décrit ci-dessus, la technique écrase temporairement le EntryPoint d'une DLL afin de rediriger l'exécution. Puisque nous n'avons aucun contrôle au-delà de la redirection de l'exécution, certains arrangements doivent être faits du côté pour gérer ce que nous voulons exécuter, avec quels arguments, et comment récupérer la valeur de retour.

Cela se fait en définissant une structure DATA_T sur le tas, de manière à ce qu'elle reste accessible tout au long des différentes étapes.

Cette structure est définie comme suit :

typedef struct _DATA_T {
    // LDR structures manipulation
    ULONG_PTR   runner;             // malicious entry point to execute
    ULONG_PTR   bakOriginalBase;    // backup of overwritten OriginalBase
    ULONG_PTR   bakEntryPoint;      // backup of overwritten EntryPoint
    HANDLE      event;              // event signalling that the Runner has executed
    // function call
    ULONG_PTR   ret;                // return value
    DWORD       createThread;       // run this API call in a new thread (required for wininet/winhttp)
    ULONG_PTR   function;           // Windows API to call
    DWORD       dwArgs;             // number of args
    ULONG_PTR   args[MAX_ARGS];     // array of args
} DATA_T, * PDATA_T;

Pour configurer une exécution d'API, ces champs doivent être préparés. La valeur ret est celle qui récupérera la valeur de retour après l'exécution. L'event est utilisé pour la synchronisation, afin de signaler que l'exécution est terminée. Tous les autres champs sont des entrées définissant quelle API appeler (function), avec quels arguments (dwArgs et args[]), l'adresse de la fonction Runner() où l'exécution est redirigée, et les sauvegardes des entrées originales de la DLL écrasée (bakOriginalBase et bakEntryPoint).

Le champ createThread doit être défini à 1 pour les fonctions API complexes qui ne fonctionnent pas bien dans une configuration DllMain() (cela inclut de nombreuses bibliothèques wininet et winhttp).

Voici un exemple de configuration d'un appel à MessageBoxA() comme visible dans le PoC :

Télécharger l’outil