GUIDA ALL'ASSEMBLY by
|
Non
so neanch'io perchè mi son messo a fare sta guida, di solito non ne
sono il tipo. Però sono un amante delle sfide, e siccome mi piace
testare le mie abilità in tutto ho deciso di provare pure questo campo.
Inoltre, trovo questo modo molto utile per saggiare le mie conoscenze. |
-
[CAPITOLO I - Introduzione all'Assembly] - Breve introduzione all'Assembly
per chi non ne sa nulla |
Questa
è la lista dei programmi che verranno usati nel corso della guida. Si
possono trovare tutti nella rete.
|
Ok,
finalmente si parte... cominciamo col dire che qualsiasi sistema
informatico, qualunque sia la sua potenza o costo, capisce solo una
cosa: il linguaggio binario. La combinazioni di 0 e 1 (che corrispondono
ad un livello basso e ad un livello alto di corrente) assumono per il
processore un significato che lo fanno eseguire determinate operazioni.. 11011000
01010111 00001011 10101100 etc...
|
Uff,
questo capitolo non lo volevo neanche fare (dato che viene spiegato in
praticamente ogni guida per traduzioni), ma dato che me l'hanno chiesto
vedrò di dire qualcosina.
A
questo punto prendiamo tutti i resti ottenuti (1000011101) e invertiamo
tutto... otteniamo 1011100001. |
Beh,
è difficile cercare di spiegare in modo semplice e chiaro per tutti l'assembly.
Quello che questo capitolo cerca di fare è velocizzare il vostro
apprendimento delle istruzioni più usate in modo di poter, quanto
prima, permettervi di immergervi in un listato assembly. -
istruzioni di Input/Output
Notate
che ho omesso le operazioni di moltiplicazione e di divisione
(quest'ultima in alcuni processori non esiste neanche, ma è ricavata :)
). Il motivo è che le reputo più difficili da capire per un Newbie,
rispetto a queste. Comunque, se volete, potete sempre andare a
consultarle nel capitolo sulle istruzioni del x86. Sono rispettivamente MUL/IMUL
e DIV/IDIV (senza e con segno).
Istruzioni di modifica del flusso
oppure
con
Chiamata
a procedura (+ istruzioni per la manipolazione dello stack): Ora
dovrebbe essere più chiaro, vero? Qui di seguito invece trovate le
varie istruzioni di rotazione e di shift.
|
--[ x86 ]--
So
già che vi sarete chiesti "ma cosa sono questi registri?".
Ebbene essi non sono altro che locazioni interne al processore, nelle
quali memorizza variabili in modo istantaneo (senza dialogare col bus,
la ram, la cache etc...).
|
Cosa
sono gli interrupt? Non sono altro che eccezioni, che potete invocare
direttamente voi o che sono generate via hardware (ad esempio... ad ogni
pressione di un tasto viene generata un'eccezione). |
Nel
x86 potete usare vari modelli di memoria. Eccoveli elencati uno per uno. |
|
Modalità
reale: |
Uhm...
questo capitolo sono stato indeciso fino alla fine se trattarlo o no...
Il fatto è che mi sembra doveroso farci un accenno, ma io non ve ne
parlerò approfonditamente. Il motivo è che questo argomento ha
talmente tanti prerequisiti che mi ci vorrebbe una guida solo per
introdurlo (e anche perchè ammetto di non averci capito tutto neppure
io). L'obiettivo di questo capitolo è mettervi in guardia contro
particolari trucchi, indicarvi come procedere nella traduzione degli
eseguibili e ampliare un pochettino le vostre conoscenze (però vi
avverto... è roba un po' complicata. Se non vi sentite pronti di
imparare un mucchio di roba e di capirla, vi consiglio di rimandare).
IMAGE_FILE_HEADER:
|
--[ Z80 ]--
Nello
Z80 sono disponibili i seguenti registri:
S:
è il flag di segno. Se il risultato di un'operazione è negativo,
questo flag viene settato a 1.
|
Lo
Z80 dispone di due tipi di interrupt: mascherabili e non mascherabili.
Quando si verifica un interrupt non mascherabile (NMI), viene effettuata
una call all'indirizzo 0x66 e vengono disattivati gli interrupt
mascherabili (INT). La routine dovrebbe terminare con una RETN o
semplicemente con una RET. |
Sono
stato un po' a pensare a quale tipo di lista di opcode passarvi. Il
motivo è che ognuno è utile per vedere meglio un certo aspetto delle
istruzioni. Alla fine ho scelto un tipo di lista molto chiaro (ideale
per capire le istruzioni), senza tanti particolari tecnici (anche se a
volte sarebbe utile saperli).
|
Come
ho spiegato all'inizio, il GameBoy si basa su un processore che è molto
simile allo Z80, ma non identico. Ci sono alcune istruzioni assembly che
sono state aggiunte, altre che sono state modificate e altre ancora che
sono state rimosse. Queste
istruzioni invece sono state rimosse: Queste
invece hanno degli opcode differenti:
|
Bene
bene. Questo è uno dei "capitoli extra" di questa guida. Non
c'entra tanto con l'assembly, ma visto che lo so ci tengo a spiegarvelo
anche a voi. Cominciamo subito. Vi dico subito che molte di queste
informazioni sono ricavabili tramite il programma HebeGB, ma vi siete
mai chiesti come ricavi molte di queste? Io sì!
|
Uno
dei requisiti più ostici per chi vuole imparare l'assembly è una
capacità di astrazione (ben superiore a quella richiesta per linguaggi
di altro livello) non da poco. Il reverser (colui che fa
reversing-engineering, ossia quello che dovreste fare voi) deve fare una
conversione da linguaggio assembly a linguaggio naturale, e può non
essere facile (specie nel caso di algoritmi complessi) visto che a
differenza degli altri linguaggi in asm un'istruzione può avere
infiniti utilizzi (insomma, può servire per fare le cose più
disparate). .text:004031AD
; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ .text:004031AD
.text:004031AD
; Attributes: bp-based frame .text:004031AD
.text:004031AD
sub_4031AD
proc near
; CODE XREF: sub_4021F0+Fp .text:004031AD
; sub_40266E+BCp ... .text:004031AD
.text:004031AD
var_4
= dword ptr -4 .text:004031AD
arg_0
= dword ptr 8 .text:004031AD
.text:004031AD
push ebp .text:004031AE
mov
ebp, esp .text:004031B0
sub esp, 4 .text:004031B3
push ebx .text:004031B4
push esi .text:004031B5
push edi .text:004031B6
mov eax, [ebp+arg_0]
; Eax = questo è il seriale inserito,
prendete per buona la cosa e non chiedetemi come faccio a dirlo... vi
ricordo che ho omesso del codice precedente. .text:004031B9 xor ecx, ecx ; Xor di un registro con lo stesso --> Registro = 0 (a volte è fatto per inizializzazione) .text:004031BB
mov cl, [eax+7]
; Se il seriale è più lungo di 7 caratteri esci. .text:004031BE
test ecx,
ecx ; Se ecx = 0 allora si continua, altrimenti
"Seriale non corretto" Prende
la 7+1 lettera (la prima lettera sarebbe [eax] ), e vede se il suo
byte è 0x00. Quando c'è un test X,X vuol dire che si confronta con 0
e se è così allora si mette il flag Z a 1, altrimenti lo si mette a
0. Quindi con queste 2 operazioni il programma guarda se il seriale
inserito è lungo 7 caratteri, visto che il byte di fine stringa è
0x00 .text:004031C6
xor eax, eax .text:004031C8
jmp loc_403312 .text:004031CD
;
--------------------------------------------------------------------------- .text:004031CD
.text:004031CD
loc_4031CD:
; CODE XREF: sub_4031AD+13j .text:004031CD
mov [ebp+var_4], 0
; I = 0 .text:004031D4
jmp loc_4031DC .text:004031D9
;
--------------------------------------------------------------------------- .text:004031D9
.text:004031D9
loc_4031D9:
; CODE XREF: sub_4031AD+5Fj .text:004031D9
inc [ebp+var_4]
; I++ .text:004031DC
.text:004031DC
loc_4031DC:
; CODE XREF: sub_4031AD+27j .text:004031DC
cmp [ebp+var_4], 7 .text:004031E0
jge loc_403211
; Se sì salta, altrimenti continua .text:004031E6 mov eax, [ebp+var_4] ; eax = I .text:004031E9
mov ecx,
[ebp+arg_0] ;
ecx = seriale .text:004031EC
mov al,
[eax+ecx]
;
al = seriale[I] .text:004031EF
push eax .text:004031F0
call sub_403317 .text:004031F5
add esp, 4 .text:004031F8
xor ecx, ecx .text:004031FA
mov cl,
al
;
cl = Lettera modificata .text:004031FC
cmp ecx,
24h ; Se è una lettera o un numero o “[“
continua, (il perchè ho commentato così lo vedrete dopo) .text:004031FF
jle loc_40320C ; altrimenti
"seriale non valido" .text:00403205
xor eax, eax .text:00403207
jmp loc_403312 .text:0040320C
;
--------------------------------------------------------------------------- .text:0040320C
.text:0040320C
loc_40320C:
; CODE XREF: sub_4031AD+52j .text:0040320C
jmp loc_4031D9 .text:0040320C
;
--------------------------------------------------------------------------- .text:00403312
loc_403312:
; CODE XREF: sub_4031AD+1Bj .text:00403312
; sub_4031AD+5Aj ... .text:00403312
pop edi .text:00403313
pop esi .text:00403314
pop ebx .text:00403315
leave .text:00403316
retn .text:00403316
sub_4031AD
endp .text:00403317
; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E
¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ .text:00403317
.text:00403317
; Attributes: bp-based frame .text:00403317
.text:00403317
sub_403317
proc near
; CODE XREF: sub_4031AD+43p .text:00403317
; sub_4031AD+6Bp ... .text:00403317
.text:00403317
arg_0
= byte ptr 8 .text:00403317
.text:00403317
push ebp .text:00403318
mov ebp, esp .text:0040331A
push ebx .text:0040331B
push esi .text:0040331C
push
edi .text:0040331D
xor eax, eax .text:00403322
cmp eax, 61h
; A .text:00403325
jl loc_403336 ; | .text:0040332B
xor eax,
eax ; |
Se è minuscola converte in maiuscolo, .text:0040332D
mov al,
[ebp+arg_0] ;
| altrimenti continua .text:00403330
sub eax, 20h
; | .text:00403333
mov [ebp+arg_0],
al ; V .text:00403336
.text:00403336
loc_403336:
; CODE XREF: sub_403317+Ej .text:00403336
xor eax, eax
; A .text:00403338
mov al, [ebp+arg_0] ; | .text:0040333B cmp
eax, 41h
;
| Se è >= “A” (leggasi "se è una lettera"), sottrai 7 al codice .text:0040333E
jl
loc_40334F ;
| della lettera. .text:00403344
xor eax,
eax ; |
altrimenti continua .text:00403346
mov al, [ebp+arg_0] ; | .text:00403349
sub eax, 7
; | .text:0040334C
mov [ebp+arg_0], al ; V .text:0040334F
.text:0040334F
loc_40334F:
; CODE XREF: sub_403317+27j .text:0040334F
xor eax, eax
.text:00403351
mov al, [ebp+arg_0] .text:00403354
sub eax,
30h ; Sottrai comunque 30h al codice della lettera. .text:00403357
jmp $+5 .text:0040335C
pop edi .text:0040335D
pop esi .text:0040335E
pop ebx .text:0040335F
leave .text:00403360
retn
;
eax contiene il codice della lettera .text:00403360
sub_403317
endp
; modificata. .text:00403211
;
--------------------------------------------------------------------------- .text:00403211
TABELLA DELLE LETTERE
MODIFICATE:
Dal di qua si capisce che: : = A ; = B < = C = = D > = E ? = F @ = G
.text:00403211
mov eax, [ebp+arg_0] ; eax = seriale .text:00403214
mov al,
[eax+5] ; al = 6° lettera del seriale .text:00403217
push eax
.text:00403218
call sub_403317 .text:0040321D
add esp, 4 .text:00403220
xor ebx,
ebx .text:00403222
mov bl,
al
; bl = 6° lettera
modificata .text:00403224
mov eax, [ebp+arg_0] .text:00403227
mov al,
[eax+2] ; al = 3° lettera del seriale .text:0040322A
push eax .text:0040322B
call sub_403317 .text:00403230
add esp, 4 .text:00403233
xor ecx, ecx .text:00403235
mov cl,
al
;
cl = 3° lettera modificata .text:00403237
mov
esi, 24h .text:0040323C
lea eax, [ecx+ebx*2+1Ch] .text:00403240
cdq .text:00403241
idiv esi
;
Guardatevi come funzionano queste istruzioni nei capitoli precedenti. .text:00403243
mov ebx, edx
; ebx = eax mod 24h [6|3] .text:00403245
mov eax, [ebp+arg_0] .text:00403248
mov al,
[eax]
;
al = 1° lettera del seriale .text:0040324A
push eax .text:0040324B
call sub_403317 .text:00403250
add esp, 4 .text:00403253
xor ecx, ecx .text:00403255
mov cl,
al
;
cl = 1° lettera modificata .text:00403257
cmp ebx,
ecx ; se ebx (mod[6|3]) non è = alla prima .text:00403259 jnz loc_40330B ; lettera modificata, vai a "Seriale non valido" .text:0040325F
mov eax, [ebp+arg_0] .text:00403262
mov al,
[eax+4] ; al = 5° lettera del seriale .text:00403265
push eax .text:00403266
call
sub_403317 .text:0040326B
add esp, 4 .text:0040326E
xor ebx, ebx .text:00403270
mov bl,
al
;
bl = 5° lettera modificata .text:00403272
mov eax, [ebp+arg_0] .text:00403275
mov al,
[eax+1] ; al = 2° lettera del seriale .text:00403278
push eax .text:00403279
call sub_403317 .text:0040327E
add esp,
4 .text:00403281
xor ecx,
ecx .text:00403283
mov cl,
al
;
cl = 2° lettera modificata .text:00403285
mov esi, 24h .text:0040328A
lea eax, [ecx+ebx*2+1Ch] .text:0040328E
cdq .text:0040328F
idiv
esi .text:00403291
mov ebx, edx
; ebx = eax mod 24h [5|2] .text:00403293
mov eax, [ebp+arg_0] .text:00403296
mov al,
[eax+6] ; al = 7° lettera del seriale .text:00403299
push
eax .text:0040329A
call sub_403317 .text:0040329F
add esp, 4 .text:004032A2
xor ecx, ecx .text:004032A4
mov cl,
al
;
cl = 7° lettera modificata .text:004032A6 cmp
ebx, ecx
; se ebx (mod[5|2]) non
è = alla settima .text:004032A8 jnz loc_40330B ; lettera modificata, vai a "Seriale non valido" .text:004032AE
mov eax, [ebp+arg_0] .text:004032B1
mov al,
[eax+6] ; al = 7° lettera del seriale .text:004032B4
push eax .text:004032B5
call sub_403317 .text:004032BA
add esp, 4 .text:004032BD
xor ebx, ebx .text:004032BF mov
bl, al
; bl = 7° lettera
modificata .text:004032C1
mov eax, [ebp+arg_0] .text:004032C4
mov al,
[eax]
;
al = 1° lettera del seriale .text:004032C6
push eax .text:004032C7
call
sub_403317 .text:004032CC
add esp, 4 .text:004032CF
xor ecx, ecx .text:004032D1
mov cl,
al
;
cl = 1° lettera modificata .text:004032D3
mov esi, 24h .text:004032D8
lea
eax, [ecx+ebx*2+1Ch] .text:004032DC
cdq .text:004032DD
idiv esi .text:004032DF
mov ebx, edx
; ebx = eax mod 24h [7|1] .text:004032E1
mov eax, [ebp+arg_0] .text:004032E4
mov al,
[eax+3] ; al = 4° lettera del seriale .text:004032E7
push eax .text:004032E8
call sub_403317 .text:004032ED
add esp, 4 .text:004032F0
xor ecx,
ecx .text:004032F2
mov cl,
al
;
cl = 4° lettera modificata .text:004032F4
cmp ebx,
ecx ; se ebx (mod[5|2] non è = alla quarta .text:004032F6 jnz loc_40330B ; lettera modificata, vai a "Seriale non valido" .text:004032FC
mov eax,
1 ; altrimenti setta il flag a 1: .text:00403301
jmp loc_403312 ; seriale corretto. .text:00403306
;
--------------------------------------------------------------------------- .text:00403306
jmp loc_403312 .text:0040330B
;
--------------------------------------------------------------------------- .text:0040330B
.text:0040330B
loc_40330B:
; CODE XREF: sub_4031AD+ACj .text:0040330B
;
sub_4031AD+FBj ... .text:0040330B
xor eax, eax .text:0040330D
jmp $+5 .text:00403312
.text:00403312
loc_403312:
; CODE XREF: sub_4031AD+1Bj .text:00403312
; sub_4031AD+5Aj ... .text:00403312
pop edi .text:00403313
pop esi .text:00403314
pop ebx .text:00403315
leave .text:00403316
retn .text:00403316
sub_4031AD
endp Bene,
abbiamo reversato un controllo di un seriale. Vi pare ancora così
difficile? Sì? Beh, il trucco sta nell'esercitarsi con buona costanza.
Dovete cercare di far vostri i concetti spiegati e vedere codice di alto
livello su quello asm (azz... sembra quasi un discorso alla Matrix. Io
ci vedo brunette... :D) |
Finalmente
siamo arrivati ad un bel esempio pratico in cui l'assembly è
fondamentale per tradurre una certa cosa.
.data:00447740 word_447740 dw 4E45h
; DATA XREF: sub_423860+DEr .text:004216EE loc_4216EE: ; CODE XREF: .text:004216B5j ;
Questo è il case che ci interessa... quello che va a prendere
"HELP" |
Siamo
giunti finalmente al capitolo che un po' tutti aspettavano... le
compressioni e/o criptazioni. |
|
Bene...
eccomi arrivato alle conclusioni. Che dire... è stata proprio una
faticaccia realizzare questa guida e penso che non finirò mai di
completarla, tante sono le cose da dire e tante sono le cose nuove da
scoprire. Beh... comunque spero che abbiate trovato la guida perlomeno
interessante e che magari abbiate imparato qualcosa da essa. |