Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
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
UnhookMe — Resolver e unhooker dinamico delle API di Windows che rileva e ripristina le funzioni hookate (IAT, EAT, patch inline) per invocare chiamate di sistema non monitorate da malware del Red Team. | Kitploit
Strumenti/GitHubGitHub/mgeeky/unhookme
Strumenti DifensiviRed TeamingSviluppo Payload
GitHubmgeeky/unhookme

UnhookMe

Resolver e unhooker dinamico delle API di Windows che rileva e ripristina le funzioni hookate (IAT, EAT, patch inline) per invocare chiamate di sistema non monitorate da malware del Red Team.

Vedi Repository
348484 anni 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

UnhookMe - Risolutore di importazioni con unhooking dinamico

Nell'era degli AV e EDR intrusivi che introducono hot-patch nei processi in esecuzione per i loro requisiti di visione migliorata, gli avversari moderni devono avere uno strumento robusto per scivolare attraverso queste sentinelle. L'implementazione proposta di un risolutore di importazioni dinamico in grado di eseguire l'unhooking delle funzioni utilizzate al volo è un ulteriore passo verso il rafforzamento degli sforzi di resilienza degli avversari.

La soluzione che propongo qui è di passare dall'uso di importazioni WinAPI risolte dal linker, che rimangono visibili negli header PE dell'eseguibile compilato (nello specifico nella tabella degli indirizzi di importazione), a favore di un approccio completamente dinamico che insiste sulla risoluzione delle importazioni solo in modo dinamico. Tale risolutore dinamico può essere dotato di logica di unhooking che avviene in background, senza alcun tipo di guida da parte dell'operatore.


Esempio di utilizzo più semplice

Ecco come puoi assicurarti di chiamare MessageBoxW senza hook e non monitorato:

root@kitploit:~
    RESOLVE(user32, MessageBoxW);
    _MessageBoxW(0, L"Look Ma! I'm unhooked!", L"Third - Unhooked", 0);

Tutta la magia avviene all'interno della macrodefinizione RESOLVE, che costruisce un oggetto ImportResolver<T> di nome _MessageBoxW.


Dimostrazione

Unhookme showcase animation

Ecco come funziona l'esempio di UnhookMe:

  1. Ci presenta la prima MessageBoxW che non è soggetta ad hook
  2. Poi hookiamo noi stessi il prologo di MessageBoxW per farlo sempre restituire 0 senza visualizzare il suo messaggio
  3. Finalmente, risolviamo MessageBoxW dinamicamente usando il risolutore UnhookingImportResolver, che rileverà le patch applicate al prologo e ripristinerà i byte originali, effettivamente eseguendo l'unhooking della funzionalità di MessageBoxW.

Nel frattempo, mentre compaiono le finestre di messaggio, queste sono le righe di log stampate sullo stdout della console:

root@kitploit:~
[~] Resolved symbol kernel32.dll!CreateFileA
[~] Resolved symbol kernel32.dll!ReadProcessMemory
[~] Resolved symbol kernel32.dll!MapViewOfFile
[~] Resolved symbol kernel32.dll!VirtualProtectEx
[#] Found trampoline hook in symbol: MessageBoxW . Restored original bytes from file.
[~] Resolved symbol user32.dll!MessageBoxW

Come usarlo?

Ci sono in totale 5 file di codice sorgente/header C++ che la tua soluzione deve includere. Tuttavia, il tuo file principale deve includere solo due header richiesti, come dettagliato di seguito.

  • resolver.h - header contenente la maggior parte dell'implementazione di UnhookingImportResolver e utili macrodefinizioni
  • resolver.cpp - codice sorgente con opzioni globali definite
  • usings.h - un grande e cattivo file header contenente decine di definizioni di tipo using per WinAPI comuni
  • PE.cpp - file di codice sorgente del parser PE personalizzato
  • PE.h - file header del parser PE personalizzato

Header richiesti

Il tuo programma richiederà solo due header da includere:

root@kitploit:~
#include "usings.h"
#include "resolver.h"

Opzioni globali

Ci sono un paio di opzioni globali che possono essere modificate influenzando il modo in cui il risolutore lavora o riporta la sua attività. Sono definite all'inizio del file resolver.cpp:

Opzioni globali del risolutore:

  • globalQuietOption - impostalo su true se non vuoi alcun tipo di output
  • globalVerboseOption - impostalo su true se vuoi un output dettagliato
  • globalAntiSplicingOption - esegue l'unhooking delle funzioni risolte se sono hookate.
  • globalLogFilePath - dove reindirizzare le righe di log di output. Se vuoto, sceglie stdout.
root@kitploit:~
bool globalQuietOption = false;
bool globalVerboseOption = true;
bool globalAntiSplicingOption = true;

wchar_t globalLogFilePath[MAX_PATH] = L"";

Specifica del tipo di API personalizzato

Per utilizzare il risolutore, un tipo di puntatore a funzione deve essere prima dichiarato con un'istruzione using di forma rigorosa:

root@kitploit:~
    using fn_FunctionName = ReturnType WINAPI (
        ParamType1 paramName1,
        ...,
        ParamTypeN paramNameN,
    );

Questo repository viene fornito con il file header usings.h contenente tipi using predefiniti per decine di API Windows popolari.

Il FunctionName corrisponderà alla WinAPI che vogliamo far risolvere a ImportResolver e quel puntatore a funzione deve essere marcato come avente convenzione di chiamata WINAPI (__stdcall su x86 e __fastcall su x64). Il ReturnType deve precedere il modificatore di tipo WINAPI.

Risoluzione e uso delle funzioni

Avendo definito il tipo di puntatore a funzione come specificato sopra, saremo in grado di usarlo nel seguente modo:

root@kitploit:~
    RESOLVE(libraryName, FunctionName);
    ReturnType output = _FunctionName(param1, ..., paramN);

La macro RESOLVE si occupa di istanziare l'oggetto template ImportResolver e di adattare il nome della libreria specificata.

Il risolutore introduce diverse altre macrodefinizioni che offrono un facile utilizzo dell'invocazione del costruttore in varie circostanze:

root@kitploit:~
#define RESOLVE(mod, func)                    RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, ::globalAntiSplicingOption)
#define RESOLVE_NO_UNHOOK(mod, func)          RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, false)

#define RESOLVE_VERBOSE_UNHOOK(mod, func)     RESOLVE_PARAMETERIZED(mod, func, true, true)
#define RESOLVE_VERBOSE_NOUNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, true, false)
#define RESOLVE_NOVERBOSE_UNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, false, true)
#define RESOLVE_NOVERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, false)

Costruttore del risolutore:

root@kitploit:~
    template<typename Ret, typename ...Args>
    ImportResolver<Ret WINAPI(Args...)>(
            std::string dllName,
            std::string funcName,
            bool _verbose = false,
            bool _unhook = false,
            bool *_wasItHooked = nullptr
        )

Come funziona?

Il risolutore sottostante sfrutta un parser personalizzato di header PE, che elabora ogni modulo DLL referenziato per mappare le loro esportazioni e verificare l'integrità degli header PE del modulo così come l'integrità dei byte dello stub della funzione referenziata.

L'idea è la seguente:

  1. Prima di tutto, emettiamo LoadLibrary per caricare la libreria referenziata dall'utente (quella specificata come primo parametro per la macro RESOLVE) se non può essere raggiunta tramite GetModuleHandle.

  2. Poi elaboriamo gli header PE della libreria caricata/referenziata, mappiamo le sue esportazioni, recuperiamo l'array degli indirizzi di esportazione e calcoliamo anche noi stessi questi indirizzi per una verifica incrociata.

  3. Se l'indirizzo di una routine definita nella tabella degli indirizzi di esportazione (EAT) della DLL non corrisponde a ciò che ci aspetteremmo, l'esportazione è considerata hookata via EAT. Lo stesso vale se la nostra voce della tabella degli indirizzi di importazione (IAT) dell'eseguibile per quella funzione è stata alterata e non punta più al punto corretto nella sezione codice della DLL - allora la funzione è considerata hookata via IAT.

  4. Supponendo che non siano stati trovati hook finora, recuperiamo i primi N byte del prologo della funzione e li confrontiamo con ciò che è nel file DLL memorizzato su disco. Se c'è una discrepanza tra i byte recuperati dalla memoria e quelli dal file - consideriamo che la funzione sia stata patchata inline (hot-patched).

  5. Se la funzione è considerata hookata - restituiamo l'indirizzo dell'esportazione originale (quello che abbiamo calcolato noi stessi) e/o eseguiamo l'unhooking della voce. Se c'erano byte di patch in posizione, li ripristineremo.

  6. Infine, per ottimizzare l'impatto sulle prestazioni del risolutore - mettiamo in cache tutte le basi immagine dei moduli caricati e gli indirizzi delle funzioni risolte e li restituiamo da una cache (che è una std::map) durante le chiamate successive.

Tra i problemi affrontati da un tale risolutore con unhooking dinamico ci sono le questioni relative all'attraversamento delle API inoltrate (una DLL può contenere un thunk di esportazione che dice che questa funzione non è implementata in questo modulo, ma lo è in un altro) - sebbene questa implementazione ne abbia supporto, a volte rompe la sua logica di attraversamento.


☕ Show Support ☕

Questo e altri progetti sono il risultato di notti insonni e molto duro lavoro. Se ti piace quello che faccio e apprezzi che io restituisca sempre alla comunità, Considera l'idea di offrirmi un caffè (o meglio una birra) solo per dire grazie! 💪


Author

root@kitploit:~
   Mariusz Banach / mgeeky, 21
   <mb [at] binary-offensive.com>
   (https://github.com/mgeeky)
Scarica lo strumento