Il documento si propone di analizzare le tecniche di base per effettuare il reverse engineering. Verranno inoltre prese in esame le caratteristiche principali di alcuni dei tool utilizzati nel reverse engineering .
1. LUIGI CAPUZZELLO
Reverse Engineering
Tecniche di base per il reverse engineering
Versione: 1.0
Luigi Capuzzello
05/12/2013
http://www.linkedin.com/pub/luigi-capuzzello/7/561/12a
http://www.slideshare.net/luigicapuzzello
@FisherKasparov
luigi.capuzzello
Il documento si propone di analizzare le tecniche di base per effettuare il reverse engineering. Verranno
inoltre prese in esame le caratteristiche principali di alcuni dei tool utilizzati nel reverse engineering .
2. Definizioni Generali.2
Sommario
Definizioni Generali.............................................................................................................................3
Memoria...........................................................................................................................................3
Registri.............................................................................................................................................4
Principali Direttive Assembly..............................................................................................................6
Dati: estrarre informazioni...............................................................................................................6
Istruzioni di SALTO.........................................................................................................................6
Metodi di Indirizzamento.................................................................................................................6
LEA, LDS, LES................................................................................................................................7
LOOPE, LOOPZ, LOOPNE, LOOPNZ...........................................................................................7
MOV.................................................................................................................................................7
PUSH, POP, PUSHF, POPF, PUSHA, POPA, PUSHAD, POPAD................................................7
PTR...................................................................................................................................................7
Stringa - Direttiva STOS, STOSB, STOSW....................................................................................7
Stringa - Direttiva CMPS, CMPSB, CMPSW..................................................................................8
Stringa - Direttiva LODSB, LODSW, LODSD (ESI --> EAX).......................................................8
Stringa - Direttiva MOVS, MOVSB, MOVSW, MOVSD (ESI --> EDI).......................................8
Stringa - Direttiva REP, REPE, REPZ, REPNE, REPNZ (CX).......................................................9
Stringa - Direttiva SCAS, SCASB, SCASW....................................................................................9
Gestione dei messaggi in windows....................................................................................................11
OllyDbg e i messeggi.....................................................................................................................13
Reverse Engineering: ricerca di un seriale.........................................................................................14
WindAsm........................................................................................................................................15
Hiew...............................................................................................................................................16
Patch mediante file c++..................................................................................................................16
OllyDbg..........................................................................................................................................17
P32Dasm........................................................................................................................................18
PEditor............................................................................................................................................18
Regmon...........................................................................................................................................18
Filemon...........................................................................................................................................18
Programmi Criptati.............................................................................................................................19
VISUAL BASIC: API SU CUI SETTARE UN BREAKPOINT......................................................20
Usare SmartCheck..........................................................................................................................21
Configurare SmartCheck............................................................................................................21
Capire le API in SmartCheck.....................................................................................................22
Consigli per usare SmartCheck..................................................................................................23
Luigi Capuzzello
3. Definizioni Generali.3
Definizioni Generali.
Memoria.
In generale facendo riferimento a questa figura potremo dare le seguenti definizioni:
Supponiamo di avere un processo caricato in memoria, il processo contiene una sezione (RData) e all’interno della
sezione ho un’istruzione. Allora definiamo:
•
Raw Offset: l’indirizzo che aveva quella sezione quando era su disco;
•
Virtual Offset: l’indirizzo della sezione a partire dalla base della RAM;
•
Image Base (Base Address): indirizzo in RAM in cui viene caricato il mio processo. Generalmente:
o
.exe hanno Base Address = 0x00400000;
o
.dll hanno Base Address = 0x10000000;
•
RVA: indirizzo dell’istruzione a partire dall’image base;
•
Virtual Address: indirizzo della istruzione in RAM: VA = RVA + Base Address;
•
Virtual Size: dimensione in RAM della mia sezione;
•
Raw Size: dimensione su disco della mia sezione;
Un indirizzo di memoria potrà essere sempre indicato come segment:offset
•
segment sarà sempre un valore a 16 bit
•
offset sarà un valore a 16 o 32 bit a seconda del tipo di processore. Nel caso di processore a 16 bit l’offset
deve poter essere di 16 (e non di 0) per indicare un segmento di 64Kb.
La RAM è poi divisa in pagine di memoria della grandezza di 4.096 byte (10 bit)
I segmenti o paragrafi sono sempre lunghi 65.536 byte (16 bit)
Luigi Capuzzello
4. Definizioni Generali.4
Registri.
Esistono fondamentalmente otto registri. La lettera ‘E’ indica che i registri sono a 32 bit. Il legame tra con i registri a
16 bit è il seguente:
AL:
AX:
EAX:
1 byte ---> FF
2 byte ---> FF FF
4 byte ---> FF FF FF FF
Il significato di ogni registro è il seguente:
Registro
Nome
Uso
EAX
Accumulator
Manipolazione degli operandi e dei risultati delle operazioni logico-aritmetiche, nonché,
in genere, del valore restituito dalle funzioni al codice chiamante.
EBX
Base Addressing
Puntatore ai dati contenuti nel segmento DS (ad esempio, viene usato per contenere
l'indirizzo del primo byte di un array o di un tipo di dato complesso, come una
struttura o un record).
ECX
Counter
Usato come contatore nei cicli, nelle istruzioni di shifting e rotazione e nelle operazioni
di manipolazione di stringhe.
EDX
Data
Puntatore nelle operazioni di I/O nonché come registro ausiliario in alcune istruzioni
aritmetiche (moltiplicazione e divisione).
ESI
Source Index
Usato nelle operazioni di manipolazione di stringhe, come puntatore ai dati di
partenza. Viene usato anche per contenere l'offset di dati contenuti nel segmento DS
(rispetto all'indirizzo di base contenuto in EBX).
EDI
Destination Index
Usato nelle operazioni di manipolazione di stringhe, come puntatore ai dati di
destinazione. Viene usato anche per contenere il puntatore ai dati (o all'indirizzo) di
destinazione contenuti nel segmento ES o l'offset di dati contenuti nel segmento DS
(rispetto all'indirizzo di base contenuto in EBX).
EBP
Base Pointer
Puntatore ai dati sullo stack (ad esempio, i parametri e le variabili locali di procedure e
Luigi Capuzzello
5. Definizioni Generali.5
funzioni). Viene usato anche per contenere l'indirizzo dei dati contenuti nel segmento
SS.
ESP
Stack Pointer
Esistono
•
•
•
•
•
Puntatore all'ultimo elemento posto sullo stack. Viene anche usato per contenere
l'indirizzo di offset di dati contenuti nel segmento SS (rispetto all'indirizzo di base
contenuto in EBP).
poi cinque segmenti usati dal processore per elaborare un processo:
CS: Code Segment; è il segmento usato di default per le istruzioni;
SS: Stack Segment; è il segmento usato di default per lo stack;
DS: Data Segment; è il segmento usato di default per gli accessi alla memoria in RW;
ES: Data Segment;
è il segmento usato di default per gli operandi di dest. e per la manipolazione di
stringhe
Per modificare i segmenti di default occorre utilizzare l’operatore ‘:’
Esempio:
mov es:[eax],edx
Luigi Capuzzello
6. Definizioni Generali.6
Principali Direttive Assembly.
Dati: estrarre informazioni.
•
•
•
•
•
TYPE: dimensione in bytes di ogni singolo elemento del dato
LENGTH: numero di elementi che compongono il dato
SIZE: fornisce il numero di bytes totali che formano il dato
SEG: Fornisce il segmento in cui è inserito il dato
OFFSET: Fornisce l'indirizzo del primo bytes del dato relativo al suo segmento
Ad esempio:
Dati
SEGMENT
Prova DW 232 DUP (5)
ENDS
Dati
...
MOV
MOV
MOV
MOV
MOV
AX,TYPE Prova
CX,LENGTH Prova
DX,SIZE Prova
BX,SEG Prova
SI,OFFSET Prova
;
;
;
;
;
AX = 2
BX = 232
DX = 232 * 2
BX = Dati
SI = 0 (in questo caso Prova è il primo elemento)
Istruzioni di SALTO
Le piu' comuni sono:
•
je (jump if egual) salta se si e' verificata una condizione di eguaglianza in un confronto
•
jne (jump if not egual) salta se non si e' verificata una condizione di eguaglianza
•
jz (jump if zero) salta se il se il bit del registro di flag che rappresenta lo zero e' settato
•
jnz (jump if not zero) salta se il se il bit che rappresenta lo zero non e' settato
•
ja (jump if above) salta se e' maggiore
•
jb (jump if below) salta se e' minore
•
jae/jbe (jump if above or equal/jump if below or equal) come sopra ma salta anche se uguale
•
jg (jump if great) come ja ma tiene conto del segno
•
jl (jump if less) come jb ma tiene conto del segno
•
jge/jle (jump if great or equal/jump if less or equal)
I loro opcode sono
Mnemonico
Opcode salto corto
Opcode salto lungo
je
74
OF 84
jz
74
OF 84
jne
75
OF 85
jnz
75
OF 85
ja
77
OF 87
jb
72
OF 82
jae
73
OF 83
jbe
76
OF 86
jg
7F
OF 8F
jl
7C
OF 8C
jge
7D
OF 8D
jle
7E
OF 8E
Come potate notare praticamente le istruzioni je/jz jne/jnz sono esattamente uguali per la cpu.
Metodi di Indirizzamento
Esistono diversi metodi di indirizzamento delle variabili:
1. Indirizzamento immediato
mov ax, 1643
; 1643 è il dato numerico da mettere in AX
2.
Indirizzamento assoluto
mov ax,[7563] ; 7563 è l'indirizzo del dato da mettere in AX
Luigi Capuzzello
7. Definizioni Generali.7
3.
Indirizzamento con registro
mov ax, [si]
;metto in ax il dato puntato da si (che si trova all'indirizzo si).Si può anche scrivere:
mov ax, [si+45] ; avete capito cosa fa vero?
MOV AX,[BX+SI+5]
MOV AX,[BX][SI]5 ;è come scrivere quello che c’è sopra
4.
Indirizzamento indiretto
mov ax, [[1450]];in pratica in 1450 c'è l'indirizzo del dato da mettere in AX.
LEA, LDS, LES
L’operatore LEA carica un OFFSET (punatatore di tipo NEAR appartenente cioè allo stesso segmento) all’interno del
registro specificato.
LEA dx , stringa
MOV dx , OFFSET stringa
;carica in dx l’OFFSET di stringa
;è la stessa operazione
LEA EAX, [EBP+dst]
;mette in EAX il puntatore all’indirizzo EBX+dst
LOOPE, LOOPZ, LOOPNE, LOOPNZ
Sono le istruzioni per compiere cicli. Si basano sul valore del registro CX. In particolare:
Il valore del registro CX viene decrementato ogni volta che si arriva ad uno di questi LOOPxx
•
•
•
•
LOOPE
LOOPNE
LOOPZ
LOOPNZ
gira
gira
gira
gira
mentre
mentre
mentre
mentre
è uguale
non è uguale
è zero
non è zero
MOV
Per prelevare un byte all’indirizzo puntato dal registro EAX faccio
MOV AL, [EAX]
PUSH, POP, PUSHF, POPF, PUSHA, POPA, PUSHAD, POPAD
•
•
•
PUSH, POP:
PUSHF, POPF:
PUSHA, POPA:
consente di inserire e prelevare valori dallo stack
conente di salvare nello stack i valori dei flag
consente di salvare nello stack i valori di tutti i registri (AX,BX,CX,DX,SP,BP)
PTR
Consente di accedere ad una variabile cambiando il tipo con cui è stata definita. Il tipo della nuova etichetta potrà
essere BYTE, WORD, DWORD, QWORD, TBYTE, NEAR, FAR.
RamVideo DW 0B800H
MOV AL, BYTE PTR CS:[RamVideo] AL <---- 00H
MOV AL, BYTE PTR CS:[RamVideo +1]
AL <---- B8H
Stringa - Direttiva STOS, STOSB, STOSW
Usata per caricare una stringa con determinati valori
.MODEL small
.DATA
stringa DB
100 DUP(?)
.CODE
...
...
cld
; direzione dx-->sx
mov ax, 'aa'
; valore con cui riempire
mov cx, 50
; numero di iterazioni
mov di, OFFSET stringa ; puntatore alla stringa
Luigi Capuzzello
8. Definizioni Generali.8
rep stosw
; riempio con word
Stringa - Direttiva CMPS, CMPSB, CMPSW
Consente di confrontare due stringhe
.MODEL Large
.DATA
string1 DB "Ken il guerriero"
.FARDATA
; uso un altro segmento dati di tipo FAR
string2 DB "Ken il pippero"
lung EQU $-string2
.CODE
mov ax, @data
; carico i due segmenti in DS e ES
mov ds, ax
mov ax@fardata
mov es, ax
...
...
cld
mov cx, lung
mov si, OFFSET string1
mov di, OFFSET string2
repe cmps
; confronto
jnz sono_uguali
; se ZF = 0 sono uguali, altrimenti...
dec di
; posiziono i puntatori sul carattere diverso
dec si
...
sono_uguali:
Stringa - Direttiva LODSB, LODSW, LODSD (ESI --> EAX)
Usato per caricare il valore di una stringa in un registro
LODSB : carica un BYTE da
DS:(E)SI ----> AL
LODSW : carica una WORD da
DS:(E)SI ----> AX
LODSD : carica una DOUBLEWORD da
DS:(E)SI ----> EAX
.DATA
numeri DB 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
.CODE
...
cld
mov cx, 10
; numero di iterazioni
mov si, OFFSET numeri
mov ah, 2
prendi:
lodsb
add al,'0'
; converte in ASCII
mov dl, al
int 21h
; lo visualizza
loop prendi
; torna a cercare
Stringa - Direttiva MOVS, MOVSB, MOVSW, MOVSD (ESI -->
EDI)
Spostano byte da una stringa ad un’altra:
MOVSB: sposta un BYTE da
MOVSW: sposta una WORD da
MOVSD: sposta una DWORD
DS:[(E)SI] ---> ES:[(E)DI]
DS:[(E)SI] ---> ES:[(E)DI]
DS:[(E)SI] ---> ES:[(E)DI]
Dunque l’istruzione MOVSB esegue in colpo solo quattro istruzioni:
MOV AL, DS:[SI]
Luigi Capuzzello
9. Definizioni Generali.9
MOV ES:[DI], AL
INC SI
INC DI
.MODEL small
.DATA
source DB 10 DUP ('012345679')
detin DB 10 DUP (?)
.CODE
mov ax, @data
; è un altro modo per carica il segmento
mov ds, ax
; lo metto in DS
mov es, ax
; e anche in ES
...
...
...
cld
mov cx, 10
; imposto il numero di iterazioni
mov si, OFFSET source
; Carico l'ind. del sorgente
mov di, OFFSET destin
; Carico l'ind. della desinazione
rep movsb
; sposto 10 byte
Stringa - Direttiva REP, REPE, REPZ, REPNE, REPNZ (CX)
Consentono di effettuare loop nelle seguenti circostanze
REP:
ripete fino a quando CX=0
REPE: ripete fino a quando CX è uguale
REPZ: ripete fino a quando CX=0
REPNE: ripete fino a quando CX è diverso
REPNZ: ripete fino a quando CX <> 0
Esempio 1.
Posso utilizzare questo costrutto per fare il confronto tra stringhe
004012C8 . MOV
ESI,CrackMe5.00403273
; ESI = User
004012CD . MOV
EDI,CrackMe5.0040327F
; EDI = Seriale
004012D2 . MOV
ECX,0C
004012D7 . REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
004012D9 . JE
SHORT CrackMe5.00401300
Esempio 2.
Codice SMC automodificante
00401363 .
MOV
ESI,Tre.004015C3
00401368 .
MOV
EDI,Tre.00401374
0040136D .
MOV
ECX,49
00401372 .
REP
MOVS BYTE PTR ES:[EDI],BYTE PTR
Stringa - Direttiva SCAS, SCASB, SCASW
Usata per cercare un dato valore all’interno di una stringa.
.DATA
stringa DB "Ken il guerriero"
lung EQU $-stringa
; lunghezza della stringa
pstringa DD string
; puntatore alla stringa
.CODE
...
...
cld
; direzione dx --> sx
mov cx, lung
les di, pstringa
mov al, 'r'
; carattere da cercare
repne scasb
; cerco
jnz non_trovato
; se non lo trovo salto
...
...
; ES:DI punta al carattere che cercavo
...
; in questo caso la prima r
...
Luigi Capuzzello
11. Definizioni Generali.11
Gestione dei messaggi in windows
Ogni applicazione ha un coda di messaggi: Message Queue.
I messaggi vengono inseriti nella coda dal sistema operativa che intercetta i click del mouse, la tastiera ecc. Ecc.
I messaggi vengono prelevati dall’applicazione utilizzando un ciclo infinito all’interno della winMain.
Il ciclo prevede tre operazioni:
•
PeekMEssage / GetMessage: prende il messaggio dalla coda dei messaggi in coda su quella finestra
•
TranslateMessage: traduce il messaggio in modo che diventi un messaggio di caratteri
•
DispatchMessage: invia il messaggio winProc delle finestra corretta. Per farlo usa l’handler della finestra che
è contenuto all’interno del messaggio.
Una volta che il messaggio arriva alla finestra corretta (con il dispatchMessage) viene poi gestito dalla funzione
winProc (DialogProc) che gestisce gli eventi di quella finestra.
Posso creare un winProc per ogni finestra oppure un solo
winProc può gestire gli eventi di più finestre.
Per associare ad una finsetra il suo winProc/dialogProc si usano funzioni come:
DialogBoxParam, DialogBoxIndirectParam, CreateDialogParam, CreateDialogIndirectParam
o
Codice SMC automodificante: se incontro istruzioni che modificano il codice a seguire come
00401363 .
MOV
ESI,Tre.004015C3
00401368 .
MOV
EDI,Tre.00401374
0040136D .
MOV
ECX,49
00401372 .
REP
MOVS BYTE PTR ES:[EDI],BYTE PTR
Luigi Capuzzello
12. Definizioni Generali.12
Che prende il codice dalla sorgente 4015C3 e lo copia in 401374 (che la mia prossima istruzione) allora per non
incartare Olly devo:
Arrivato sulla istruzione REP (che è una istruzione multipla) usare il tasto F7 in modo da fargli modificare
il codice byte per byte;
Il codice modificato non sempre è chiaro (compaiono dei punti di domanda) allora devo selezionare il
codice modificato --> tasto destro --> Analisys --> Remove Analysys from selection
Procedere poi sul codice modificato con F7 o F8
Luigi Capuzzello
13. Definizioni Generali.13
OllyDbg e i messeggi.
o
Posso settare un messaggio sulla queue principale e intercettare un messaggio con un bp condizionale.
Es. per break su LBUTTONUP
bpx TranslateMessage && [EDX+4]==202
Non so come arrivare dalla TranslateMessage alla winProc.
o
Per settare dei break sui messaggi inviati alle finestre (WM_CLOSE) è necessario che OllyDbg sia stato attivato (su
un breakpoint o mentre si debugga passo-passo) sul programma da analizzare.
occorre aprire la finestra ‘Windows’ che mostra tutte le windows del progetto che stiamo analizzando;
tasto destro sulla finestra che vogliamo analizzare;
selezionare la voce ‘Message BreakPoint on WinProc’;
seleziono il messaggio che voglio intercettare (generalmente conviene intercettare i messaggi 201WM_LBUTTONDOWN, il 202-WM_LBUTTONUP o 111-WM_COMMAND) e la finestra su cui intercettarlo;
automaticamente viene generato il breakpoint condizionale.
Il problema è che non si riesce a risalire al programma originale partendo dal breakpoint.
o
La stessa cosa posso farla debuggando funzioni come (DialogBoxParam, DialogBoxIndirectParam,
CreateDialogParam, CreateDialogIndirectParam):
DialogBoxParam: crea un Dialog modale e associa a questa form la funzione dei gestione dei messaggi
INT_PTR DialogBoxParam(
HINSTANCE hInstance,
LPCTSTR lpTemplateName,
HWND hWndParent,
DLGPROC lpDialogFunc,
LPARAM dwInitParam
);
DialogProc: funzione di gestione dei messaggi.
INT_PTR CALLBACK DialogProc(
HWND hwndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);
Quindi se voglio arrivare alla DialogBox devo:
•
bpx DialogBoxParamA
•
guardo l’indirizzo del primo parametro che è l’indirizzo della DialoProc
•
debaggo la DialogProc che è una funzione del tipo
BOOL CALLBACK unaDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
......
switch (uMsg)
{
case WM_CLOSE: //messaggio generato premendo il tasto X
CHIUDI_DIALOG();
break;
case WM_INITDIALOG:
//messaggio che si genera subito prima che venga
//visualizzato a schermo il dialog
QUALCOSA();
break;
case WM_COMMAND:
//messaggio che si genera ad esempio quando
//clicchiamo sul tasto OK posizionato sul form alla
//destra del textedit
IDENTIFICA_CONTROLLO_CHIAMANTE();
ESEGUI_OPERAZIONE();
break;
default:
//se uMsg non è nessuno dei precedenti messaggi
NON_FARE_NIENTE();
//inutile aggiungere dettagli di programmazione
break;
Luigi Capuzzello
14. Definizioni Generali.14
}
}
La descrizione dei messaggi che possono arrivare è nel file winuser.h. i più importanti sono:
•
WM_INITDIALOG:
0x0110
•
WM_COMMAND
0x0111
•
•
•
0x0200
0x0200
•
•
•
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
0x0201
0x0202
0x0203
•
•
•
WM_RBUTTONDOWN
WM_RBUTTONUP
WM_RBUTTONDBLCLK
0x0204
0x0205
0x0206
•
•
•
WM_MBUTTONDOWN
WM_MBUTTONUP
WM_MBUTTONDBLCLK
0x0207
•
•
WM_MOUSEFIRST
WM_MOUSEMOVE
WM_CLOSE
0x0010
0x0208
0x0209
Posso anche mettere dei BK condizionali con SHIFT+F2 per prelevare solamente i messaggi che mi
interessano (es. SHIFT+F2: EAX == 111h)
Reverse Engineering: ricerca di un seriale
…
Luigi Capuzzello