Manual Unpacking |
|
|
Giugno 2000 |
by "phobos" |
|
|
Published by Quequero |
|
"Ti fai le seghe mentali... |
Bravo Phob, oramai alla UIC solo tutorial D.O.C. :) sei sulla buona strada per guadagnare il titolo di "Bonzo", ti manca ancora un po', ma tra poco potrai fregiarti del mitico aggettivo che una sola persona al mondo può sfoggiare.....Ovvero: IO, che sono l'unico e inimitabile QueBonzo, spero che un giorno anche tu potrai sfoggiare il tuo PhoBonzo :))))) tanti Bonzauguriiii |
...ed affoghi nello sperma psicologico che produci!" K4ti4 (non era rivolta a me! ;-)) |
UIC's form |
|
UIC's form |
Difficoltà |
(X)NewBies ( )Intermedio ( )Avanzato ( )Master |
Adesso mi impegno... ;-)
Il mio 'target' per dirlo all'americana e' lo spiegarvi in modo quanto piu' generale
possibile la tecnica del Manual
Unpacking...
Introduzione |
Tools usati |
URL o FTP del programma |
Notizie sul programma |
Essay |
Vediamo come intervenire...
Innanzitutto, una volta stabilito che il programma in questione e' packato (ed a questo
scopo potete utilizzare il GetType) per
effettuare il dumping abbiamo bisogno di alcune informazioni, la prima da 'prelevare' e'
l'entry point del programma (in
parole povere la prima riga di istruzioni che viene eseguita dal programma) per reperire
questa informazione utilizzeremo il
ProcDump: andiamo nella sezione che tratta il PE editor,
selezioniamo il programma in esame, e nella prima casella della
box che ci appare, abbiamo il nostro Entry Point in formato RVA
(Real Virtual Address) segnamoci il numero, ed a questo
punto, ci serve convertirlo nell'offset del file.
A questo scopo, o lo fate 'a mano' (e se siete in grado di farlo, potete risparmiarvi il
resto del tute!!!), oppure, vi affidate
all'utility preposta (Virtual Address to File Offset), aprite l'utility,
e nella casella 'Memory Address' inseriamo l'indirizzo
fornitoci dal Pdump, selezioniamo la check box relativa ad RVA, apriamo
il programma cliccando su 'file' ed andandolo a
selezionare (se a questo punto vi viene comunicato un errore dal RVA to File Offset, e'
perche' il programma in esame e' gia
aperto, quindi per risolvere, sara' sufficiente chiuderlo e rieseguire la procedura
nell'utility) e clicchiamo su 'Do It'...
otteniamo l'offset, in formato esadecimale e decimale, a noi interessa quello
esadecimale... segnatevelo e chiudete tutti i
programmi aperti.
La seconda fase consiste nell'aprire l'utility ADump (e lasciarla aperta per tutte
le rimanenti fasi). L'ADump e' una utility che 'gira' sotto consolle DOS, quindi, una
volta avviata, la potrete utilizzare dalla finestra MS-DOS di Windows.
All'interno della consolle del programma digitate il comando r seguito da invio, vi
apparira' una lista del tipo
:Internal Variables:
---------------------
STARTOFFS: 0x83AA1000 <--(1)
ENDOFFS: 0x83B95240 <--(1bis)
LIMIT: 0xF4240 <1000000> <--(2)
CUROFFS: 0x83AA1000
MAPFN: C:\WINDOWS\TEMP\Adump.log <--(3)
MAPFSIZE: 0xF4240 <--(4)
ANFILTER: A..Z,a..z,0..9
Dove, STARTOFFS indica l'indirizzo di partenza dello spazio di ambiente
mappato per il programma ADump (1), LIMIT
indica la dimensione in byte (formato esedecimale e decimale, tra parentesi) (2), MAPFN e' il nome del file che sara' usato
per salvare il dump della memoria (3), e MAPFSIZE
e' la dimensione del file suddetto (4).
Chiaramente, i valori esadecimali in (1) ed (1bis) potranno differire da quelli indicati (dipende da cosa avete
attivo in
memoria) e il percorso del file Adump.log (3)
sara' quello dove avrete installato il programma. Ma cio' e' irrilevante,
Segnatevi il valore di STARTOFFS e andiamo avanti.
Ora dobbiamo intercettare l'entry point del programma con il SoftICE (e per questa
simpatica procedura thanx to Andreuzzo
& Quequero): come gia' detto prima, l'entry point e' la prima istruzione che viene
eseguita dal programma durante la sua
esecuzione, questa non coincide mai con la prima istruzione del file disassemblato
(infatti, se avete notato, nel windasm, tra
i menu' e' presente una voce che permette di raggiungere l'entry point del programma). A
questo scopo abbiamo bisogno
dell'editor esadecimale: apriamolo, portiamoci sul byte indicato dall'offset fornitoci
dall'utility di Iczelion e sostituiamo il
byte (avendo cura di segarci quello originale) con il codice esadecimale CC
che e' l'equivalente dell'istruzione INT 03.
Salviamo il programma, avendo la cura di farne una copia di backup (sempre... se qualcosa
va storto dobbiamo poter
tornare indietro) attiviamo il SoftICE e settiamo un break point sull'int 03 (BPINT
3 <invio>) usciamo dall'ice e avviamo il
programma che abbiamo modificato, a questo punto l'ice dovrebbe intervenire e femarsi
sull'istruzione immediatamente
successiva a quella da noi cambiata in INT 03 che altro non e' che il nostro Entry Point.
Il passo successivo consiste nel
disabilitare il break settato, segnarsi l'indirizzo della riga di codice che contiene
l'istruzione int 03 (il nostro entry point, e a
questo punto, potete anche mettere un break su questa locazione di codice, oppure
aspettare tanto dovremo farlo dopo) uscire dall'ice e ripristinare il programma, togliendo
il codice CC dall'offset dell'entry point e rimettendo quello originale.
Fatto? Ok, andiamo avanti! ;-)
Il passo successivo consiste nell'eseguire lo stepping all'interno del SoftICE, partendo
dall'entry point del programma, fino a
raggiungere la sezione denominata '.code', la individuate facilmente
perche' e' referenziata all'interno dell'ice sulla riga della
finestra del codice. (per intenderci, apparira' una stringa del tipo NOME_PROGRAMMA
.CODE + XXXX) Quando saremo arrivati nella sezione code del nostro programma,
dobbiamo andare a vedere quali sono le pagine di memoria che esso sta utilizzando (in
gergo tecnico: che sono state mappate).
Per fare cio', dobbiamo digitare nel SoftICE il comando MAP32 NOME_PROGRAMMA
<invio> dove al posto di
NOME_PROGRAMMA dobbiamo inserire il nome che identifica il nostro
programma in memoria (se non lo conoscete,
utilizzate il comando TASK, vi apparira' un elenco con tutti i processi
attivi nel sistema in quell'istante, compreso il nome
del programma in esame).
Fatto cio', vi apparira' una lista del tipo:
Owner Obj Name
Obj# Address Size
Type
NOME_PROG .CODE 1 0XXX:00XXXXXX
00XXXXXX CODE RO
NOME_PROG .DATA 2 0XXX:00XXXXXX
000XXXXX IDATA RW
NOME_PROG .BSS 3 0XXX:00XXXXXX
00XXXXXX XXXX RW
NOME_PROG .idata 4 0XXX:00XXXXXX
0000XXXX IDATA RW
NOME_PROG .tls 5 0XXX:00XXXXXX
0000XXXX XXXX XX
NOME_PROG .rdata 6 0XXX:00XXXXXX
0000XXXX IDATA XX
NOME_PROG .reloc 7 0XXX:00XXXXXX
000XXXXX XXXX XX
NOME_PROG .rsrc 8 0XXX:00XXXXXX
000XXXXX XXXX XX
NOME_PROG .rsrc 9 0XXX:00KKKKKK 0000XXXX XXXX XX <--ultima sezione
Dove: Owner, indica il programma in esame; Obj Name, e'
il nome della sezione; Obj#, indica il numero sequenziale della
sezione; Address, e' costituito dagli indirizzi in memoria da cui partono
le varie sezioni (<- IMPORTANTE); Size,
indica la dimensione della sezione (<-
IMPORTANTE); Type, indica la tipologia della sezione e la due
lettere che seguono la
tipologia, sono i 'flags' della sezione (RW: Readable/Writable;
RO: Read Only; e cosi' via).
Una nota: tutti i dati contenuti nella tabella risultante dal comando Map32 sono in
formato esadecimale.
A questo punto, ci serve conoscere le dimensioni del file, e a questo proposito, ci
possiamo riallacciare al discorso che
facevamo all'inizio, circa la 'sequenzialita'' delle varie sezioni che costituiscono un
programma, da quando vengono lette
dalla memoria di massa a quando questo risiede in memoria. Praticamente, dobbiamo
utilizzare la formula:
(*) ( RVA Ultima Sezione - Image Base )
+ PE Size
Praticamente, RVA Ultima sezione e' quel numero evidenziato in rosso
nell'esempio (KKKKKK), Image Base e' sempre
uguale a 00400000h (cio' e' vero solo per gli eseguibili, per altri files, tipo le dll
cambia, se volete sapere perche' vi do' due
parole chave: rilocazione e collisioni ;-)) PE Size, e' solitamente
uguale al valore 1000h.
Si'... Solitamente... poiche' non eseste una formula per conoscere al PE Size, e, almeno secondo quanto ne so io, nemmeno un tool che lo faccia, il metodo che si puo' utilizzare a questo proposito e' il seguente: si apre l'eseguibile con un editor esadecimale, si controlla la prima parte del file (quella dove sono presenti tutti i bytes 00 e le voci relativa alle varie sezioni: .code, .idata., .reloc, ecc.), ci si porta alla fine di tutti i byte con valore 00, subito dopo le suddette stringhe, e immediatamente prima dell'inizio dela sequenza di caratteri 'illegibili', all'offset corrispondente all'ultimo byte pari a 00, corrisponde la PE Size. Facciamo un esempio 'grafico'
00000000h 4D 5A 50 00 02 00 00 00 04 00 0F 00 FF FF 00 00 MZP.........˙˙.. <-Prima riga dell'exe
00000010h B8 00 00 00 00 00 00 00 40 00 1A 00 00 00 00 00 ¸.......@......
Omissis...
000003E0h 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000003F0h 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ <-Ultimo byte PE
00000400h 00 A3 00 20 40 00 8B 58 3C 03 D8 0F B7 43 14 0F .£. @.X<.Ĝ.·C..
Nel nostro caso, il byte evidenziato in rosso, e' l'ultimo del PE, (mi
sono risparmiato tutti i precedenti, ma non potete sbaglare, ce ne sono all'incirca 256,
tutti uguali a 00h), l'offset corrispondente al byte in questione corrisponde alla PE
Size, nel nostro caso: 3FFh.
E' possibile anche fare un'altra procedura: semplicemente sommare tutti i valori
esadecimali che troviamo sotto la voce
Size, stesso dentro il SoftICE (il comando e' ? seguito dalla somma) e otteniamo piu' o
meno lo stesso risultato.
Una piccola nota, le dimensioni che otteniamo con queste due procedure, differiscono,
oltre che tra loro stesse, anche da
quelle effettive del programma, di qualche kbyte, quindi non vi preoccupate...
Fatta questa procedura, lo 'step' successivo consiste ne calcolare il numero di pagine di
memoria che il loader ha allocato
per il programma (la memoria del computer e' suddivisa, per costruzione, in pagine, ognuna
delle quali ha una dimenzione
di 65535 byte cioe' di 64 kbyte, se vi interessa l'argomento, vi rimando allo studio delle
tecniche e dei metodi di
indirizzamento).
Quindi dobbiamo eseguire una semplice divisione: VALORE_OTTENUTO_DA_(*) / FFF
Dove, VALORE_OTTENUTO_DA_(*), e' la
dimensione del file ottenuta con una delle due procedure viste sopra (mi
raccomando: in esadecimale!!!), e FFF non e' altro che il valore 65535
in esadecimale.
A questo punto, abbiamo ottenuto un numero (sempre esadecimale) che indica la quantita' di
pagine della memoria che sono
state mappate per il nostro programma.
Ora dobbiamo vedere quali pagine non sono 'fisicamente' presenti: infatti, la mappatura,
e' un processo che 'riserva' una
serie di pagine per l'uso da parte del programma, ma non e' detto che tutte queste siano
effettivamente 'riempite' dei dati o
del codice del programma stesso. Questa procedura, ci permettera' di caricare tutto il
programma in memoria, onde evitare
che nella fase successiva (quella del 'furto' vero e proprio del programma) ci ritroviamo
con 'pezzi' di codice mancanti.
Utilizzeremo a questo scopo il comando:
page 0400000 L XXX
Dove, 0400000 e' il nostro image base (vedi sopra), mentre al posto delle XXX
dobbiamo inserire il risultato della
divisione tra dimensione del file ed FFF, che abbiamo fatto pocanzi. Il SoftICE (non vi ho
detto che tutti questi comandi
vanno inseriti nell'ice?? ;-)) ci fornira' una lista di indirizzi del tipo
Linear Physical
Attributes Type
00400000 00XXXXXX X X X X X XX XXXXXXXX
00401000 NP 00XXXXX
00402000 00XXXXXX X X X X X XX XXXXXXXX
Chiaramente la lista sara' un bel po' piu' lunga!
Le pagine precedute da NP sono quelle Non Presenti le altre sono quelle presenti. Scorrete
tutta la lista e segnatevi
l'indirizzo di tutte le pagine non presenti.
Quindi, ora bisogna caricare in memoria le pagine che non sono presenti, per farlo
utilizzaremo il comando PAGEIN seguito
dagli indirizzi delle pagine non presenti (la procedura va effettuata per ogni pagina non
presente, lo so... e' una rottura di
palle... ma il reversing ha bisogno di molta pazienza... quindi coraggio!)
Finito questo 'snervante' compito (hehehehehe se sapeste quante altre volte dovremo
farlo!!! hihihihihi!), abbiamo in
memoria tutto il nostro eseguibile, pronto per essere 'sniffato' ;-)
Adesso intraprendiamo una fase delicata: dobbiamo trasferire il programma, dall'area in
cui risiede in memoria, all'area di
memoria allocata per l'ADump. Pero' qui' bisogna fare una precisazione: l'ADump 'mappa' in
memoria un'area di 'soli' un
milione di byte (hehehehehehe ce ne fossero stati 5/6 di milioni di byte sul mio 'defunto'
386!! ;-)) quindi, se la dimensione
risultante del nostro eseguibile e' superiore a questo milione, dobbiamo deguire questa
procedura:
Digitiamo in SoftICE il comando
m 00400000 L F4240 83AA1000
Avendo l'accortezza di sostituire all'indirizzo che io ho evidenziato in rosso il
valore di STARTOFFS che abbiamo segnato
prima. Il valore 00400000 e' la nostra image base, F4240 e' la lunghezza del blocco di
memoria che andiamo a trasferire (in
esadecimale, coincide proprio con quel milione di byte che abbiamo detto sopra) nell'area
riservata all'ADump.
La procedura va eseguita tante volte, quanti sono i segmenti da un milione di byte
contenuti nel nostro programma 'target'.
Cerco di essere piu' chiaro: se dobbiamo 'dumpare' un programma grande 2.5 megabyte avremo
in bytes una lunghezza di
2621440 byte (2.5 * 1024 * 1024) che in esadecimale corrisponde a 280000. Se effettuiamo
una divisione intera (non sapete
cos'e'? Semplicemente quella divisione che ci facevano fare alle scuole elementari, quella
che fornisce un risultato intero ed
un resto che non e' piu' divisibile per il divisore) tra la dimensione del nostro
programma e lo spazio che abbiamo a
disposizione nell'ADump, otteniamo quante volte dobbiamo ripetere questa procedura, poi,
l'ultimo passaggio dovremo
sostituire ad F4240 il valore del resto della nostra divisione... mi sa che invece di
chiarirvi le idee ve le ho incasinate
ancora di piu'!!! :-D
Facciamo un esempio pratico, ipotizzando che il nostro programma sia di 2.5 mega e
utilizzando gli indirizzi che abbiamo
visto sopra per l'ADump:
1) Apriamo l'ice, steppiamo dall'entry point del programma alla sezione .code
2) Mappiamo le pagine presenti e quelle non presenti, e carichiamo in memoria quelle non
presenti (la parte che utilizza il
comando PAGEIN).
3) Digitiamo il comando m 00400000 L F4240 83AA1000 nel SoftICE
4) Usciamo dal softice e andiamo nell'ADump, e salviamo il dumping della memoria su un
file, con il comando: w
c:\percorso\nome_programma_1.log (al posto di 'percorso' e di 'nome_programma',
chiaramente, mettiamo il percorso in
cui vogliamo salvare il file dump e il nome dello stesso)
5) Rientriamo nel softice, risteppiamo dal program entry point alla nostra sezione .code
6) Ricarichiamo in memoria le pagine della destinate al programma che non sono presenti
7) Mappiamo la parte di memoria successiva nell'ADump con il comando: m 0040F4240
L F4240 83AA1000 (la parte
successiva 'parte' dalla fine di quella che abbiamo appena mappato, cioe': 00400000 +
F4240)
8) Usciamo dall'ice e andiamo in ADump a salvare quest'altra parte di codice: w
c:\percorso\nome_programma_2.log
9) Rientriamo in SoftICE, steppiamo dal program entry point alla sezione code e carichiamo
le pagine non presenti
10) Trasferiamo l'ultima parte del nostro programma nell'ADump: m 41E8480 L 97B80
83AA1000
Dove: 41E8480 non e' altro che l'ultimo indirizzo (quello al passaggio (7)) addizionato di
un altro milione di byte (F4240);
97B80 e' la parte rimanente di programma (ottenibile, sottraendo F4240 dalla dimensione
del file tante volte fino a che non
otteniamo che il risultato della sottrazione e' minore di F4240)
11) Usciamo dall'ice e salviamo l'ultima parte del nostro 'futuro' programma: w
c:\percorso\nome_programma_3.log
A questo punto abbiamo su disco dei files che rappresntano il dumping del programma in
memoria, per segmenti di un
milione di bytes, quindi dobbiamo fare ancora due operazioni prima di avere finito.
La prima consiste nel collegare tra loro i vari files, fino ad ottenerne uno solo, che poi
rinomineremo in
nome_programma.exe. Per fare cio' sara' sufficiente utilizzare un editor esadecimale,
aprire i files uno alla volta, copiare ed
incollare tutto il loro contenuto sequenzialmente a partire dal primo, dopo di che' 'salvare
con nome...'.
La procedura finale (e quella piu' importante) e' denominata 'riallineamento del PE'. Ed a
questo scopo utilizzeremo il
ProcDump.
Apriamo il PDump e da questo apriamo il nostro programma dalla sezione del procDump detta
'PE Editor', e controlliamo
le sezioni del file (cliccando sul pulsante 'sections') dopo di che'
dobbiamo fare click con il pulsante destro su ognuna delle
sezioni che ci appaiono in elenco e selzionare la voce di menu' 'Edit Section',
a questo punto ci apparira' una finestra con
delle dialog contenti dei numeri esadecimali (gli addres fisici e virtuali delle varie
sezioni del programma), e noi le
dobbiamo 'riallineare'... mi spiego: supponiamo di avere nella section .data i valori
VSize: 0001AF84 RVA:
000B8000
PSize: 00019A00 Offset: 000B6200
Noi le dobbiamo cambiare in
VSize: 0001AF84 RVA:
000B8000
PSize: 0001AF84 Offset: 000B8000
A questo punto, dopo aver eseguito la procedura descritta per tutte le sezioni presenti
nel programma, e, dopo aver
confermato tutte le modifiche nel PDump (cliccando su 'OK') ,il nostro
eseguibile dovrebbe essere pronto per 'girare' (una
conferma dovrebbe essere data dal fatto che viene ripristinata l'icona interna del
programma, sempre che ne sia prevista una
;-))
Ora il programma e' pronto per essere... mah'... fatene cio' che volete! ;-)
E' tutto. Spero di esservi stato di aiuto :-)
Ciao.
phobos (aka D4rKSP4rr0W)
|
I MIEI PIU' SENTITI RINGRAZIAMENTI E SALUTI VANNO A:
*Yado, per avermi fatto incuriosire su questa tecnica
* Quequero per il suo continuo impegno e anche perche' e' un Bonzo
* Kill3xx, per aver contribuito ad 'aprire il vaso di Pandora'
* AndreaGeddon perche' ha scritto un tute sul manual unpacking per cerebrolesi come me (e
che io ho generalizzato)!
* Tutti i membri o semplici partecipanti di RingZ3r0, della UIC, di #crack-it
* Tutti coloro che hanno la pazienza di ascoltare un pazzo scatenato come me! :-)
A PRESTO!!
Disclaimer |
UIC's page of reverse engineering, scegli dove andare: |
Home Anonimato Assembly CrackMe ContactMe Forum Iscrizione |
Lezioni Links Linux NewBies News Playstation |
Tools Tutorial Search UIC Faq |
UIC |