
Indice
- Cos'è un packer?: Un'introduzione allo scopo dei packer e una guida su quali siano le necessità di sviluppo di un packer.
- Prerequisiti: Strumenti necessari per lavorare con questo tutorial.
- Un assaggio: Una dimostrazione di ciò che costruirai con questo tutorial.
- Impostare il progetto CMake: Un mini-tutorial su CMake per creare un sistema di compilazione adatto alle esigenze piuttosto complesse di un packer.
- Impacchettare i binari nello stub: Un tutorial su come scrivere la parte packer del duo packer/stub, oltre a un'introduzione al formato dei file eseguibili Windows.
- Gestire le risorse
- Analizzare un file PE
- Manipolare un file PE
- Simulare il loader: Un tutorial su come costruire uno stub eseguibile minimale per decomprimere e caricare un eseguibile target, oltre a un'introduzione a tecniche più avanzate di manipolazione degli eseguibili Windows.
- Leggere il nostro PE dalla memoria
- Caricare il nostro PE per l'esecuzione
- Risolvere le importazioni API
- Risolvere gli indirizzi
- Trasferire l'esecuzione
- Esercizi successivi: Alcuni esercizi per ampliare ulteriormente le tue conoscenze sullo sviluppo di packer.
Ti sembra una lettura impegnativa? Prova la versione in presentazione, che riassume questo readme. Video YouTube in arrivo!
Cos'è un packer?
Un packer è un programma che decomprime e lancia un altro programma all'interno del proprio spazio di indirizzi (o, a volte, dello spazio di indirizzi di un altro processo). È talvolta noto per essere il vettore che attacca gli ambienti di analisi, come debugger e sandbox virtuali. Viene usato principalmente per alcune cose:
- Compressione: I packer sono comunemente impiegati per comprimere il codice di un dato binario. Questo è uno dei loro pochi usi legittimi. Vedi UPX per un esempio di packer comprimente.
- Offuscamento: I packer sono anche impiegati quando si tenta di offuscare o comunque difendere un programma dal reverse engineering. Vedi il packer packman di Riot Games per un esempio di packer anti-reverse.
- Evasione: Il malware usa frequentemente una varietà di packer per eludere antivirus e persino EDR. Vedi questa analisi di un packer SmokeLoader per un esempio di packer evasivo.
In sostanza, un packer ha solo pochi passaggi fondamentali:
- Fase di compressione: Qui l'eseguibile originale viene compresso, offuscato o entrambe le cose, in un nuovo binario.
- Fase di decompressione: Qui l'eseguibile impacchettato decomprime o deoffusca il suo eseguibile originale per il caricamento.
- Fase di caricamento: Qui il packer imita una serie di passaggi simili al caricatore di eseguibili della piattaforma host. A causa della complessità dei binari eseguibili, questo è il passaggio più complicato, a seconda del livello di profondità che si desidera emulare.
- Fase di esecuzione: Qui il codice passa dallo stub eseguibile host al codice appena caricato (cioè decompresso).
Altrettanto semplice, un packer consiste solo di pochi componenti:
- Il packer: Questo componente è responsabile della preparazione del cosiddetto stub eseguibile e del fornirgli i dati necessari per decomprimere un dato binario. In definitiva, questa è la fase di compressione.
- Lo stub: Questo binario è il componente di codice responsabile di decomprimere, caricare ed eseguire il binario originale. Come puoi vedere, lo stub eseguibile fa la maggior parte del lavoro pesante per un packer.
Costruire un packer può essere complicato, poiché lo stub deve essere compilato e importato in qualche modo nell'eseguibile del packer. Su Windows, imparare il sistema di build di Visual Studio oltre la semplice compilazione può essere un compito laborioso. Fortunatamente, CMake fornisce un sistema di build semplice e cross-platform che supporta Visual Studio, ed è altamente personalizzabile!
Questo tutorial mira a insegnare quanto segue:
- Come segmentare e integrare i due componenti principali di un packer in un ambiente di build coeso e testabile.
- Come manipolare un eseguibile Windows per aggiungere dati aggiuntivi caricabili.
- Come navigare un eseguibile Windows per recuperare e caricare dati arbitrari.
- Come emulare parti del loader di Windows per caricare ed eseguire un eseguibile Windows.
Se hai già familiarità con C++ e CMake, sentiti libero di saltare direttamente alla sezione sull'impacchettamento. Altrimenti, continua a leggere!
Prerequisiti
- Conoscenza del C++: Questo tutorial è abbastanza inutile se non conosci il C++, dato che faremo un sacco di aritmetica dei puntatori.
- Visual Studio: Visual Studio contiene un compilatore C++ per Windows completo. Questo progetto è testato con Visual Studio 2019, ma le versioni più recenti dovrebbero andare bene.
- CMake: CMake è il sistema di build che usiamo per aiutare a strumentare le nostre compilazioni con il compilatore Visual Studio.
Un assaggio
Prima di tutto, dimostriamo in qualche modo che questo sistema di build funziona e crea un eseguibile correttamente impacchettato. Una volta installati CMake e Visual Studio, naviga nella directory radice di questo repository con il terminale che preferisci ed esegui quanto segue:```
$ mkdir build
$ cd build
$ cmake ../
Questo creerà i file di progetto necessari per compilare il codice del tutorial di Packer. Quindi esegui:```
$ cmake --build ./ --config Release
Questo compilerà il progetto packer in modalità Release. Quindi, puoi eseguire il seguente test:```
$ ctest -C Release ./
Se tutto va bene, test\_pack e test_unpack dovrebbero riuscire. Se vuoi vedere tu stesso i risultati del packing, dovresti vedere questo:```
$ ./packed.exe
I'm just a little guy!
Parliamo di tutte le componenti che hanno reso possibile tutto questo.
Delineare il nostro progetto CMake
Quindi sappiamo quali sono i componenti principali di un packer, ma come possiamo testare il nostro packer immediatamente come parte del ciclo di sviluppo? Dovremo aggiungere un terzo binario al nostro progetto complessivo per testare adeguatamente il processo di pack e unpack del nostro binario. Quindi, complessivamente, abbiamo bisogno di tre progetti:
- il packer
- lo stub
- l'eseguibile fittizio da impacchettare
Abbiamo anche bisogno di una libreria di compressione per assicurarci che il binario si comprima nell'eseguibile stub. zlib andrà benissimo per questo.