Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
LdrShuffle — Tecnica di esecuzione/iniezione di codice utilizzando la manipolazione della struttura del modulo DLL PEB | Kitploit
Strumenti/GitHubGitHub/rwxstoned/ldrshuffle
Analisi del CodiceExploitMovimento LateraleShellcodePost-ExploitPenetration TestingRed TeamingSviluppo PayloadBinary Exploitation
GitHubrwxstoned/ldrshuffle

LdrShuffle

Tecnica di esecuzione/iniezione di codice utilizzando la manipolazione della struttura del modulo DLL PEB

28944101 anno faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
Vedi Repository

LdrShuffle

Esecuzione furtiva del codice tramite modifica del EntryPoint dei moduli caricati in fase di runtime.

Riepilogo

I processi Windows hanno vari moduli caricati in fase di runtime. Ognuno di questi moduli ha una funzione DllMain() definita, che verrà invocata alla creazione/distruzione del processo o del thread (quattro scenari possibili).

Per chiamare correttamente queste funzioni durante la vita del processo, le funzioni del Loader di Windows (ntdll!Ldrp*) si riferiscono a un elenco di voci contenenti parametri chiave (incluso il campo EntryPoint) per ciascun modulo.

Sovrascrivendo questo EntryPoint per una DLL, ci assicuriamo che l'esecuzione del codice venga reindirizzata verso un punto da noi scelto.

Casi d'Uso

Questo può essere utilizzato sia come primitiva per l'esecuzione del codice, sia per il proxying delle API, ad esempio per eseguire determinate API con un callstack non sospetto, poiché verranno invocate da funzioni Windows legittime.

Può anche essere utilizzato per innescare l'esecuzione in un processo remoto, a patto che l'attaccante abbia la capacità di leggere e scrivere memoria su quel processo di destinazione. Analogamente alla Threadless Injection, ciò fornisce la capacità di eseguire codice in un processo senza invocare API classiche legate all'esecuzione (CreateRemoteThread, QueueUserAPC).

Sfide

Il caricamento/scaricamento dei moduli all'interno di un processo Windows è un argomento complesso che presenta molte sfide, potenziali instabilità, race conditions e crash. Un noto ostacolo relativo all'esecuzione di codice all'interno di una funzione DllMain(), ad esempio, risiede nel fatto che è attivo un Loader Lock e che stiamo eseguendo in un thread che non è stato completamente configurato, o che è in fase di terminazione.

Pertanto, ho cercato di documentare adeguatamente cosa è possibile e cosa no. Ad esempio, mentre la maggior parte delle chiamate API standard possono essere eseguite, l'esecuzione di un beacon completo richiede determinate condizioni per essere in un processo separato, al fine di evitare deadlock causati dalle funzioni utilizzate in wininet.dll o winhttp.dll.

Implementazione

Ripasso sul Caricamento delle DLL in Windows

Ogni processo mantiene una lista di strutture _LDR_DATA_TABLE_ENTRY in fase di runtime. Queste strutture contengono molti dettagli rilevanti per la DLL, come il suo EntryPoint (che sovrascriveremo), il nome, alcuni hash, timestamp, vari flag, ecc. Alcune di queste strutture sono documentate, altre no.

Possono essere visualizzate tramite questo comando 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'indirizzo della struttura può essere ottenuto percorrendo una struttura doppiamente linkata referenziata nel PEB del processo in una struttura PEB_LDR_DATA.

dt nt!_PEB_LDR_DATA 0xb4b4c3c3

Notare il flag DontCallForThreads. Come suggerisce il nome, se questo flag è impostato, il Sistema Operativo NON chiamerà il DllMain() di quel modulo per eventi di thread (cioè DLL_THREAD_ATTACH o DLL_THREAD_DETACH).

Quando si crea una DLL, è necessario seguire il seguente modello per funzionare in sinergia con le funzioni del Loader del Sistema Operativo:

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle al modulo DLL
    DWORD fdwReason,     // motivo della chiamata
    LPVOID lpvReserved )  // riservato
{
    // Esegue azioni in base al motivo della chiamata.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Inizializza una volta per ogni nuovo processo.
         // Restituisci FALSE per far fallire il caricamento della DLL.
            break;

        case DLL_THREAD_ATTACH:
         // Esegue inizializzazione specifica del thread.
            break;

        case DLL_THREAD_DETACH:
         // Esegue pulizia specifica del thread.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // non eseguire la pulizia in caso di terminazione del processo
            }
            
         // Esegue eventuale pulizia necessaria.
            break;
    }
    return TRUE;  // DLL_PROCESS_ATTACH riuscita.
}

Dettagli Tecnici sull'Implementazione

Impostazione di una Chiamata API

Come descritto sopra, la tecnica sovrascrive temporaneamente il EntryPoint di una DLL per reindirizzare l'esecuzione. Poiché non abbiamo alcun controllo su altro oltre al reindirizzamento dell'esecuzione, è necessario predisporre alcuni accorgimenti per gestire ciò che vogliamo eseguire, con quali argomenti e come recuperare il valore di ritorno.

Ciò viene realizzato definendo una struttura DATA_T sull'heap, in modo tale che rimanga accessibile durante i vari passaggi.

Questa struttura è definita come segue:

typedef struct _DATA_T {
    // Manipolazione delle strutture LDR
    ULONG_PTR   runner;             // punto di ingresso malevolo da eseguire
    ULONG_PTR   bakOriginalBase;    // backup di OriginalBase sovrascritto
    ULONG_PTR   bakEntryPoint;      // backup di EntryPoint sovrascritto
    HANDLE      event;              // evento che segnala che il Runner ha eseguito
    // chiamata di funzione
    ULONG_PTR   ret;                // valore di ritorno
    DWORD       createThread;       // esegue questa chiamata API in un nuovo thread (richiesto per wininet/winhttp)
    ULONG_PTR   function;           // API Windows da chiamare
    DWORD       dwArgs;             // numero di argomenti
    ULONG_PTR   args[MAX_ARGS];     // array di argomenti
} DATA_T, * PDATA_T;

Per impostare un'esecuzione API, è necessario preparare questi campi. Il valore ret è quello che raccoglierà il valore di ritorno dopo l'esecuzione. L'event viene utilizzato per la sincronizzazione, per segnalare che l'esecuzione è stata completata. Tutti gli altri campi sono input che definiscono quale API chiamare (function), con quali argomenti (dwArgs e args[]), l'indirizzo della funzione Runner() dove viene reindirizzata l'esecuzione, e i backup delle voci DLL originali sovrascritte (bakOriginalBase e bakEntryPoint).

Il campo createThread deve essere impostato a 1 per quelle funzioni API complesse che non funzioneranno bene in un contesto DllMain() (questo include molte librerie wininet e winhttp).

Ecco un esempio di impostazione di una chiamata a MessageBoxA() come visibile nel PoC:

Scarica lo strumento