Rilevare in usermode la presenza di codice iniettato nel proprio processo non è semplice in quanto esistono diversi modi per iniettare codice ma non esiste un unica soluzione che li rilevi tutti. Ogni metodo di iniezione richiede la sua tecnica di rilevazione che, per quanto ingegnosa, resta circoscritta al singolo caso e spesso aggirabile usando semplicemente un altro metodo di iniezione. Quindi, a meno che uno non investa gran parte del suo tempo ad implementare ed aggiornare la parte del suo programma che riguarda il rilevamento di codice iniettato anziché a sviluppare le feature del programma stesso, resta abbastanza impraticabile come contromisura.
Ci si potrebbe chiedere, a questo punto, che senso abbia scrivere qualcosa su questo argomento. In realtà questo articolo è il pretesto per introdurre un paio di argomenti davvero interessanti: API Hooking e la funzione NtQuerySystemInformation. Inoltre, si è preso in esame il caso forse più utile, e cioè la rilevazione di iniezione che sfrutta l'esecuzione di un thread remoto.
La tecnica presentata in questo articolo si basa sul processo di avvio dei thread: ogni volta che si crea un thread con CreateThread, CreateRemoteThread o funzioni simili, prima di eseguire il codice della funzione passata come argomento viene chiamata la funzione LdrInitializeThunk in NTDLL.DLL (per approfondire l'argomento si veda [2]). Eseguendo l'hook di questa funzione si possono intercettare alcune informazioni interessanti ai fini della rilevazione di codice iniettato tramite creazione di un thread remoto.
Visualizzazione post con etichetta User Mode. Mostra tutti i post
Visualizzazione post con etichetta User Mode. Mostra tutti i post
giovedì 25 aprile 2019
giovedì 18 aprile 2019
Process Hollowing (Dynamic Forking)
Per Process Hollowing, o Dynamic Forking, si intende quella tecnica in cui viene creato un processo che alla fine esegue l'immagine di un eseguibile diversa rispetto a quella associata quando è creato il processo. Per eseguire tale tecnica i passaggi fondamentali sono tre:
Un'applicazione, che chiameremo iniettore o hijacker (dipende dal compito che tale applicazione sta svolgendo), crea un processo target indicando un eseguibile di suo gradimento e sospendendo il thread principale.
Un'applicazione, che chiameremo iniettore o hijacker (dipende dal compito che tale applicazione sta svolgendo), crea un processo target indicando un eseguibile di suo gradimento e sospendendo il thread principale.
giovedì 11 aprile 2019
DLL injection tramite ShellCode
In un altro articolo (si veda [1]) si è visto come è possibile iniettare una DLL in un processo remoto semplicemente caricando DLL e parte del loader (quella che si occupa di fixare la DLL in memoria) in tale processo e creando un nuovo thread, sempre nello stesso, che eseguisse il codice del loader/fixer . Un approccio leggermente diverso (affrontato in questo articolo) ma che porta allo stesso risultato è quello di caricare del codice assembly in forma di array di byte, noto come shellcode, da eseguire nel target. Per farlo, uno dei tanti modi disponibili è quello di sospendere il thread principale del target e forzarlo a riprendere dall'inizio dello shellcode, che quindi provvederà a caricare la DLL chiamando semplicemente LoadLibrary. L'immagine sotto descrive tale tecnica in modo semplificato.
giovedì 4 aprile 2019
Mappare DLL manualmente in un altro processo
In un precedente articolo (si veda [1]) si è visto come un processo possa caricare manualmente una DLL nascondendo la sua presenza tra i moduli di dipendenza. Più interessante sarebbe vedere come si possa caricare una DLL manualmente in un altro processo. Lo schema è praticamente lo stesso, l'unica differenza è che il processo iniettore può arrivare solo fino ad un certo punto: prima carica nel proprio spazio virtuale l'immagine su disco della DLL e successivamente scrive l'immagine in memoria della DLL in uno spazio allocato nel processo target. A questo punto non può fare nient'altro. A fixare IAT (import address table) e rilocazioni ci deve pensare il processo target. A questo scopo l'iniettore può caricare il codice del loader/fixer nello spazio virtuale del processo target e creare un thread remoto che eseguirà tale codice. L'immagine sotto descrive tale tecnica in modo semplificato.
giovedì 28 marzo 2019
Caricare DLL in memoria manualmente
Se si vuole tenere nascosto il caricamento di una DLL ma allo stesso tempo sfruttarne le funzioni esportate una soluzione potrebbe essere quella di caricare manualmente la DLL nello spazio di indirizzamento del processo. Questo richiede una conoscenza, almeno basilare, del formato PE (Portable Executable) che descrive file DLL, EXE, OBJ, SYS, ecc.
In questo articolo non si descriverà in dettaglio il formato PE nel suo complesso (si veda [2] per questo) ma si prenderà in considerazione e si spiegherà in dettaglio quella parte del formato che serve allo scopo di questo articolo. Prima di cominciare la lettura l'ideale sarebbe avere almeno una conoscenza di base sul formato PE. In futuro spero di scrivere un breve articolo introduttivo sull'argomento ma, per il momento, consiglio di leggere [1] (paragrafo 9.2 e sezione relativa a export table di paragrafo 10.1) dove in una decina di pagine si riesce a descrivere tutta la parte rilevante del formato; sicuramente una delle migliori introduzioni all'argomento. Inoltre consiglio di leggere il codice di questo articolo partendo dall'entry point (wmain), seguendo il flusso del codice, istruzione dopo istruzione. Si è scelto, infatti, di inserire tutte le informazioni nei commenti al codice (quasi ogni istruzioni è commentata). Si è optato per questo stile in quanto è un articolo rivolto ai programmatori/reverser più che ai curiosi del formato PE.
Una rappresentazione semplificata del formato PE è illustrata nell'immagine sotto, dove le frecce indicano campi di una componente del formato che contengono RVA ad un'altra componente.
In questo articolo non si descriverà in dettaglio il formato PE nel suo complesso (si veda [2] per questo) ma si prenderà in considerazione e si spiegherà in dettaglio quella parte del formato che serve allo scopo di questo articolo. Prima di cominciare la lettura l'ideale sarebbe avere almeno una conoscenza di base sul formato PE. In futuro spero di scrivere un breve articolo introduttivo sull'argomento ma, per il momento, consiglio di leggere [1] (paragrafo 9.2 e sezione relativa a export table di paragrafo 10.1) dove in una decina di pagine si riesce a descrivere tutta la parte rilevante del formato; sicuramente una delle migliori introduzioni all'argomento. Inoltre consiglio di leggere il codice di questo articolo partendo dall'entry point (wmain), seguendo il flusso del codice, istruzione dopo istruzione. Si è scelto, infatti, di inserire tutte le informazioni nei commenti al codice (quasi ogni istruzioni è commentata). Si è optato per questo stile in quanto è un articolo rivolto ai programmatori/reverser più che ai curiosi del formato PE.
Una rappresentazione semplificata del formato PE è illustrata nell'immagine sotto, dove le frecce indicano campi di una componente del formato che contengono RVA ad un'altra componente.
giovedì 21 marzo 2019
Remote Thread DLL Injection
Uno dei metodi classici di iniezione del codice è quello di creare un thread da remoto all'interno del processo target per fagli eseguire una chiamata a LoadLibrary che caricherà, a sua volta, la DLL con il codice da iniettare. Si presenterà, come al solito, un esempio banale ma esplicativo. Si consideri un semplice target con il seguente sorgente
#include <Windows.h> #include <iostream> int wmain(int argc, wchar_t * argv[]) { std::cout << "Target running!" << std::endl; std::cout << "Press any key to exit!" << std::endl; std::getchar(); return 0; }
giovedì 14 marzo 2019
Caricamento ritardato di una DLL da risorsa
La tecnica di incorporare una DLL nelle risorse e di caricarla a runtime è stata usata spesso in passato da malware e virus per nascondere i moduli, contenenti codice malevolo, necessari all'esecuzione dello stesso. Il procedimento per implementare tale tecnica è relativamente semplice e richiede poco codice da scrivere manualmente. A tale scopo, in questo articolo, si presenterà un esempio banale ma esplicativo.
venerdì 8 marzo 2019
APC DLL Injection
Un metodo semplice per iniettare codice (di una DLL) all'interno di un altro processo è quello di usare le APC (Asynchronous Procedure Call) che sono nient'altro che funzioni accodate ad un thread di un processo ed eseguite nel contesto di tale thread. Si possono creare ed accodare APC da codice eseguito in user mode o kernel mode ed il thread a cui è stato accodato l'APC eseguirà la relativa funzione non appena verrà individuato (nella fase di pianificazione, scheduling) come il prossimo ad essere selezionato per l'esecuzione.
Se l'APC è stata accodata da user mode, però, è necessario anche che il thread a cui si accoda l'APC sia nello stato allertabile (alertable). Un thread entra in tale stato se chiama una tra le funzioni SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, o WaitForSingleObjectEx. Naturalmente l'APC deve essere accodata prima che l'attesa, dovuta alla chiamata di una delle funzioni appena citate, venga soddisfatta. In caso contrario la funzione relativa non sarà chiamata alla prossima pianificazione del thread ma l'APC verrà comunque accodata e la funzione eseguita non appena il thread entrerà nuovamente nello stato allertabile.
Alla luce di quanto detto, un vantaggio dell'iniezione tramite APC è che non richiede la creazione e l'esecuzione di un thread remoto nel processo target. Uno svantaggio è che il processo deve avere almeno un thread che entra nello stato allertabile.
Se l'APC è stata accodata da user mode, però, è necessario anche che il thread a cui si accoda l'APC sia nello stato allertabile (alertable). Un thread entra in tale stato se chiama una tra le funzioni SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, o WaitForSingleObjectEx. Naturalmente l'APC deve essere accodata prima che l'attesa, dovuta alla chiamata di una delle funzioni appena citate, venga soddisfatta. In caso contrario la funzione relativa non sarà chiamata alla prossima pianificazione del thread ma l'APC verrà comunque accodata e la funzione eseguita non appena il thread entrerà nuovamente nello stato allertabile.
Alla luce di quanto detto, un vantaggio dell'iniezione tramite APC è che non richiede la creazione e l'esecuzione di un thread remoto nel processo target. Uno svantaggio è che il processo deve avere almeno un thread che entra nello stato allertabile.
Iscriviti a:
Post (Atom)




