A:link { TEXT-DECORATION: underline } A:visited { TEXT-DECORATION: underline } A:active { COLOR: red; TEXT-DECORATION: underline } A:hover { COLOR: #8080ff; TEXT-DECORATION: underline }
Dama Deluxe 3.1 Cracking |
||
Data |
by Spider |
|
agosto 2002 |
UIC's Home Page |
Published by Quequero |
Chi va piano va sano e va lontano. |
Qualche mio eventuale commento
sul tutorial :))) |
Chi tardi arriva male alloggia. |
Andando piano arriva tardi. |
|
Chi va piano male alloggia. :) |
Difficoltà |
( )NewBies (X)Intermedio ( )Avanzato ( )Master |
Oggi vi invito a pranzo... crackeremo la versione dimostrativa di un interessante programmino chiamato Dama Deluxe 3.1. E' un gioco di dama italiana molto ben fatto, ma che nella demo presenta diverse limitazioni:
Aperitivo |
Bene bene! Il programmatore ci lancia una sfida
e noi la raccogliamo: dopo aver eliminato NagScreen e limitazione sulla scelta
delle mosse, aggiungeremo il codice per "Apri partita" :)
Tools usati |
- SoftICE 4.05
- IDA Pro 4.15 o superiore
- Resource Hacker 3.2.6.51
- Hex Workshop 3.11
- PEditor 1.7
- IceDump 6.23
Si presuppone nel lettore una conoscenza base
dei comandi di IDA; in caso contrario, si rimanda al tutorial di Quequero.
URL o FTP del programma |
Essay |
Primo piatto - Eliminiamo la Nag Screen
Come già detto in precedenza, all'avvio il programma mostra una Nag Screen che tiene per ben 20 secondi prima di abilitare il pulsante OK per avviare il programma. Dopo qualche tentativo (non riuscito) di breaking, decidiamo di optare per un'altra strada. Riteniamo subito improbabile che il programmatore abbia usato CreateWindowExA per ogni finestra e controllo che crea, perciò diamo un'occhiata alle risorse del proggie (a tal scopo io ho usato Resource Hacker 3.2). Osserviamo che non ci sono risorse di tipo Dialog, ma, accanto ai classici tipi di risorse, troviamo uno sconosciuto tipo di risorse chiamato RCData. Esso è composto dalle seguenti risorse:
DVCAL
TFRMAUTORE
TFRMGRAFICO
TFRMMAIN
TFRMOPZIONI
TFRMPDNHEADER
TFRMPDNMAN
TFRMPREF
TFRMREGIS
TFRMSHARE
Notate niente di strano? Si, direte voi... a parte la prima, sono tutti nomi che ricordano proprio le finestre del programma:
TFRMAUTORE
TFRMGRAFICO
TFRMMAIN
TFRMOPZIONI
TFRMPDNHEADER
TFRMPDNMAN[AGER]
TFRMPREF[ERENZE]
TFRMREGIS[TRAZIONE]
TFRMSHARE[WARE]
In particolare ci interessa l'ultima resource...
Basta dare un'occhiata allo script mostrato da ResHacker per capire che si
tratta della nostra bella (si fa per dire...) NagScreen... :)
Lo script è un po' grandicello, per cui ne riporto solo uno spezzone:
[. . .]
Font.Style = []
OldCreateOrder = True
Position = poScreenCenter
OnCloseQuery = FormCloseQuery
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
[. . .]
1. Quando si tenta di chiudere
la finestra chiama la funzione FormCloseQuery;
2. Quando la finestra viene visualizzata chiama la funzione FormShow;
Perfetto, direte voi... sappiamo che ci sono le funzioni FormShow e FormCloseQuery, ma a cosa ci serve tutto ciò se non sappiamo dove si trovano tali funzioni? Niente di + semplice... come non lo sappiamo noi non lo saprebbe neppure il programma stesso, tranne che non mettesse in memoria da qualche parte l'indirizzo di tali funzioni. Apriamo Dama.exe dall'HexWorkshop e cerchiamo la stringa FormShow; com'era prevedibile troviamo la stringa diverse volte all'interno del file: FormShow è un nome molto generico, evidentemente lo stesso evento viene gestito anche nelle altre finestre. Però noi sappiamo un'altra cosa: nella finestra TFRMSHARE gli unici eventi gestiti sono OnShow e OnCloseQuery, per cui in memoria dovremmo aspettarci di trovare adiacenti o comunque molto vicini i nomi delle due funzioni FormCloseQuery e FormShow... e difatti li troviamo all'offset 000C18BE:
000C1868
6769 73FC 0200
0002 0008 4564
7454 696D 6572
0003 gis.......EdtTimer..
000C187C 0000 0300 0A54
696D 6572 5368
6172 6504 0014
0010 .....TimerShare.....
000C1890 BE40 000D 4274
6E52 6567 6973
436C 6963 6B16
0020 .@..BtnRegisClick..
000C18A4 BE40 000F 5469
6D65 7253 6861
7265 5469 6D65
7215 .@..TimerShareTimer.
000C18B8 00C8 BE40 000E
466F 726D 436C
6F73 6551 7565
7279 ...@..FormCloseQuery
000C18CC 0F00 F4BE
4000
0846
6F72 6D53 686F
7709 5446 726D
....@..FormShow.TFrm
000C18E0 5368
6172 6504 0004
0F4A 00C4 3644
00EC 3E44 00E0
Share....J..6D..>D..
000C18F4 7147 0090 0000
0000 DCFF FFFF
0000 0000 0000
0000 qG..................
Notiamo, immediatamente prima della stringa "FormShow", la presenza di una dword (0040BEF4) il cui significato è più che evidente: è un indirizzo, e guarda caso è proprio l'indirizzo dell'evento FormShow, cosa di cui possiamo facilmente accertarci mettendo un breakpoint a quell'indirizzo e avviando il programma.
Il primo tentativo che ci viene in mente per eliminare la nagscreen è quello di eliminare la risorsa TFRMSHARE. Facciamo una copia di backup di dama.exe, e da Resource Hacker eliminiamo la dialog relativa alla nagscreen, e a parte un crash non otteniamo nulla. Decidiamo di cambiare strada...
E' ovvio che ad "ordinare" la visualizzazione della NagScreen sia codice appartenente alla finestra principale del programma, dato che quando la Nag compare sullo schermo la finestra principale è già visualizzata sullo schermo. Osservando lo script relativo alla dialog TFRMMAIN, notiamo le seguenti 5 righe:
OnActivate = FormActivate
OnCloseQuery = FormCloseQuery
OnKeyDown = FormKeyDown
OnPaint = FormPaint
OnResize = FormResize
Tra questi eventi l'unico in
cui può sembrar plausibile che si trovi il codice che richiama la nagscreen è
chiaramente l'evento OnActivate. Cercando nell'HexWorkshop la stringa
FormActivate scopriamo che questa funzione si trova all'indirizzo 004020A8.
Apriamo IDA e disassembliamo il file. All'indirizzo 004020A8 abbiamo la seguente
procedura (che non riporto per intero a causa delle dimensioni) che da IDA ho
rinominato frmMain_FormActivate:
.text:004020A8 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
.text:004020A8
.text:004020A8 ; Attributes: bp-based frame
.text:004020A8
.text:004020A8 frmMain_FormActivate proc near ; DATA XREF: .data:004C0B96o
.text:004020A8
.text:004020A8 var_2C = dword ptr -2Ch
.text:004020A8 var_1C = word ptr -1Ch
.text:004020A8 var_10 = dword ptr -10h
.text:004020A8 var_8 = dword ptr -8
.text:004020A8 var_4 = dword ptr -4
.text:004020A8
.text:004020A8 push ebp
.text:004020A9 mov ebp, esp
.text:004020AB add esp, 0FFFFFFD4h
.text:004020AE mov eax, offset unk_4BDF00
.text:004020B3 call sub_4AFF88
.text:004020B8 call sub_405AC0
.text:004020BD test al, al
.text:004020BF jnz short loc_4020E6
.text:004020C1 mov eax, off_4CD824
.text:004020C6 push 10h
.text:004020C8 mov ecx, offset aProgrammaIncom ; "Programma incompleto"
.text:004020CD mov edx, offset aMancanoAlcuniF ; "Mancano alcuni files: programma termina"...
.text:004020D2 mov eax, [eax]
.text:004020D4 call sub_4BA9CC
.text:004020D9 mov edx, off_4CD824
.text:004020DF mov eax, [edx]
.text:004020E1 call sub_474E18
.text:004020E6
.text:004020E6 loc_4020E6: ; CODE XREF: frmMain_FormActivate+17j
.text:004020E6 mov eax, dword_4DE764
.text:004020EB mov edx, [eax]
.text:004020ED call dword ptr [edx+0D8h]
.text:004020F3 mov cl, byte_4CDCB6
.text:004020F9 test cl, cl
.text:004020FB jz short loc_40210E
.text:004020FD push 40005h ; fdwSound
.text:00402102 push 0 ; hmod
.text:00402104 push offset aIntro ; pszSound
.text:00402109 call PlaySoundA
Poiché il codice è piuttosto
corposo e ci sono diverse call, prendiamo la saggia decisione di passare al
softice :-)
Mettiamo un bpx all'indirizzo 004020A8 e avviamo il programma. Softice brekka
immediatamente dopo la comparsa su schermo della finestra principale. Iniziamo a
steppare con F10 (NON con F8 altrimenti ci perderemmo all''interno delle varie
call) e vediamo che steppando la call all'indirizzo 4020ED softice sparisce e
ripoppa solo dopo la chiusura della nagscreen. Bene, noppiamo la call e la prima
parte del lavoro è terminata :-). Avviamo il programma e finalmente non dovremo
+ aspettare per 20 secondi prima di poter giocare a dama :P
Secondo piatto - La limitazione sulla scelta delle mosse
Abbiamo eliminato la NagScreen, adesso possiamo farci una bella partitina a dama... Clicchiamo su "Nuova partita", e giochiamo... stiamo vincendo, abbiamo già 2 pedine di vantaggio... Ma ecco saltar fuori una MessageBox che ci dice:
"Versione SHAREWARE: questa mossa la scelgo io!"
Risultato? Abbiamo perso :)
Non è molto bello giocare così, perciò rimettiamoci al lavoro.
Entriamo in softice e mettiamo un breakpoint su MessageBoxA. Iniziamo una partita e aspettiamo che softice becchi il breakpoint... e difatti dopo un po' di mosse compare il nostro amato debugger :) Premiamo F12, clicchiamo su OK nella MessageBox e, non appena SoftICE ripoppa sullo schermo, ancora altre due volte F12, fino ad arrivare in questo punto di codice:
00406C5C lea ecx, [ebp-008C]
00406C62 lea edx, [ebp-74]
00406C65 push 10
00406C67 mov eax, [eax]
00406C69 call 004BA9CC
00406C6E mov ebx, [004CDD30] <--- noi
siamo qui
00406C74 test ebx, ebx
00406C76 jz 00406C82
00406C78 call 004B35A4
Se guardiamo cosa c'è agli indirizzi
puntati da ebp-008C
e ebp-74
troviamo le stringhe "Limitazione shareware" e "Versione
SHAREWARE: questa mossa la scelgo io!", rispettivamente il titolo e il
contenuto della MessageBox. Intuiamo subito che all'indirizzo 004BA9CC abbiamo
una funzione ponte per l'API MessageBoxA.
Mano ad IDA... Risaliamo un po' dal disassemblato il codice appena visto e
troviamo ciò che segue:
.text:00406BAC mov ecx, dword_4BC914
.text:00406BB2 test ecx, ecx
.text:00406BB4 jg loc_406CAB
.text:00406BBA mov al, byte_4CDC84
.text:00406BBF test al, al
.text:00406BC1 jz loc_406CAB
.text:00406BC7 mov dl, byte_4CDCAB
.text:00406BCD test dl, dl
.text:00406BCF jnz loc_406CAB
Come potete vedere, ci sono diversi jump
condizionali che puntano ad un indirizzo ben oltre il codice che visualizza la
MessageBox. E' evidente che forzando il jg
all'indirizzo 00406BB4 il problema sia risolto... ma per vederci più chiaro
mettiamo un bpx all'indirizzo del jg.
Osserviamo che Softice breaka ad ogni mossa effettuata, e ad ogni break il
valore di ecx diminuisce di 2. Inoltre il jump condizionale salta sempre, tranne
quando il valore di ecx raggiunge 0. E' quindi chiaro che il programma utilizzi
un contatore di mosse e decida che sia il momento di rompere con la sua
MessageBox ogni qualvolta tale contatore giunga a 0 :-). Trasformiamo il jg in
jmp e finalmente possiamo farci una partita seriamente e senza interruzioni :)
Dolce - Abilitiamo il comando "Apri Partita"
Con le modifiche effettuate abbiamo già una versione perfettamente giocabile del programma. Ma il programmatore ci ha lanciato una sfida... Secondo lui crackare completamente il programma è impossibile perché il codice manca... Io dico che si sbaglia... Come ha scritto lui quel codice, possiamo farlo anche noi! :)
Osserviamo bene la situazione: è meglio avere le idee chiare, ci eviterà di perdere tempo inutilmente. Iniziamo una partita e facciamo qualche mossa. A questo punto clicchiamo sul menu "Partita" alla voce "Salva partita". Vediamo che il programma funziona regolarmente, e al percorso indicato troveremo il file che abbiamo salvato. Diverso è ciò che accade appena clicchiamo su "Apri partita": il programma ci lascia scegliere un file, ma non appena premiamo il pulsante OK ci lancia una bella MessageBox che ci avvisa che il comando APRI non è disponibile nella versione shareware.
I più attenti avranno già notato quello che sarà il nostro punto d'attacco. Il programma ci consente di salvare, e questa è una debolezza: studiando il codice di salvataggio potremo molto più facilmente risalire al codice usato per aprire una partita salvata, o comunque ad un codice funzionante.
Per trovare il codice di salvataggio ricorriamo al
solito metodo; osservando le risorse del file notiamo infatti che la funzione che viene
richiamata quando si clicca su Salva Partita prende il nome di
mnuSalvaPartitaClick (di seguito uno spezzone dello script di Resource Hacker
relativo alla risorsa TFRMMAIN):
object mnuSalvaPartita: TMenuItem
Bitmap.Data = {
36030000424D3603000000000000360000002800000010000000100000000100
[. . .]
BFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBFBF}
Caption = '&Salva partita...'
HelpContext = 104
ShortCut = 115
OnClick = mnuSalvaPartitaClick
end
Dall'Hex Workshop cerchiamo la stringa mnuSalvaPartitaClick, che troviamo all'offset C025C; poco prima troviamo la dword 00402258 che è proprio l'indirizzo della funzione cercata (e che da IDA rinomineremo in mnuSalvaPartitaClick per rendere più chiaro il codice):
.text:00402258 .text:00402258 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ .text:00402258 .text:00402258 ; Attributes: bp-based frame .text:00402258 .text:00402258 mnuSalvaPartitaClick proc near ; DATA XREF: .data:004C0C57o .text:00402258 .text:00402258 var_28 = dword ptr -28h .text:00402258 var_18 = word ptr -18h .text:00402258 var_C = dword ptr -0Ch .text:00402258 var_4 = dword ptr -4 .text:00402258 .text:00402258 push ebp .text:00402259 mov ebp, esp .text:0040225B add esp, 0FFFFFFD8h .text:0040225E push ebx .text:0040225F mov ebx, eax .text:00402261 mov eax, offset unk_4BDF78 .text:00402266 call sub_4AFF88 .text:0040226B mov eax, [ebx+35Ch] .text:00402271 mov edx, [eax] .text:00402273 call dword ptr [edx+3Ch] ;Visualizza la dialog "Salva partita" .text:00402276 test al, al .text:00402278 jz short loc_4022B2 ;salta se nessun file è stato selezionato .text:0040227A mov [ebp+var_18], 8 .text:00402280 xor ecx, ecx .text:00402282 mov [ebp+var_4], ecx .text:00402285 lea edx, [ebp+var_4] .text:00402288 inc [ebp+var_C] .text:0040228B mov eax, [ebx+35Ch] .text:00402291 call sub_47A8A4 .text:00402296 lea ecx, [ebp+var_4] .text:00402299 mov eax, [ecx] .text:0040229B push eax .text:0040229C call sub_40A1DC .text:004022A1 pop ecx .text:004022A2 dec [ebp+var_C] .text:004022A5 lea eax, [ebp+var_4] .text:004022A8 mov edx, 2 .text:004022AD call sub_4BAC04 .text:004022B2 .text:004022B2 loc_4022B2: ; CODE XREF: mnuSalvaPartitaClick+20j .text:004022B2 mov ecx, [ebp+var_28] .text:004022B5 mov large fs:0, ecx .text:004022BC pop ebx .text:004022BD mov esp, ebp .text:004022BF pop ebp .text:004022C0 retn .text:004022C0 mnuSalvaPartitaClick endp
Vediamo che il codice è relativamente poco: impossibile che qui dentro ci sia il codice che si occupa di salvare la partita corrente. E' dunque evidente che questa funzione demandi il lavoro di salvataggio ad una delle subroutines che richiama. Per individuare quale potremmo provare qualche breakpoint, oppure se siete pigri e non vi va di entrare nel softice ;-) basta ragionare: tale procedura deve essere piuttosto corposa e deve presumibilmente essere richiamata una sola volta (ossia non deve avere altre xref oltre a quella della procedura mnuSalvaPartitaClick. Dopo una breve ricerca concludiamo che l'unica subroutine che corrisponda a questi requisiti è la sub_40A1DC. Entriamo in essa, e troviamo conferma di ciò un po' più giù nel codice:
.text:0040A298 mov ecx, offset aSalvaPartita ; "Salva partita" .text:0040A29D mov edx, offset aSostituireIlFi ; "Sostituire il file già esistente?" .text:0040A2A2 mov eax, [eax] .text:0040A2A4 call sub_4BA9CC ;sub_4BA9CC è una funzione ponte per MessageBoxA
Abbiamo quindi trovato la funzione di
salvataggio (che da IDA rinominiamo SalvaPartita);
ora non ci resta che... studiarla :-) Ma niente paura, non dobbiamo
studiare TUTTO il codice, potremo direttamente saltare alla parte che si occupa
di scrivere su file.
Per far ciò, chiediamo aiuto al nostro bravo softice che mai ci abbandona...
Potremmo mettere un breakpoint su CreateFileA per vedere dove il file viene
aperto, oppure possiamo saltare direttamente al punto in cui il programma scrive
su file. Iniziamo una partita, facciamo un
paio di mosse, clicchiamo su Salva Partita, diamo il nome al file, e prima di
premere ok entriamo in SoftICE e mettiamo un breakpoint su WriteFile. SoftICE
breakka, premiamo F12 e notiamo di trovarci in una piccola procedura che inizia
all'indirizzo 004A5E28 e che altro non è se non una funzione ponte per
richiamare WriteFile (e che pertanto rinomineremo da IDA _WriteFile).
Premiamo F12 per tornare al chiamante e ci troveremo all'interno di una
procedura che inizia all'indirizzo 0040A574 e che riporto di seguito:
.text:0040A574
sub_40A574 proc near
; CODE XREF: SalvaPartita+10Ap
.text:0040A574
.text:0040A574 arg_0
= dword ptr 8
.text:0040A574
.text:0040A574
push ebp
.text:0040A575
mov ebp, esp
.text:0040A577
push ebx
.text:0040A578
push esi
.text:0040A579
mov esi, [ebp+arg_0]
.text:0040A57C
cmp esi, 0FFFFFFFFh
.text:0040A57F
jz short loc_40A5C0
.text:0040A581
mov edx, offset aDamaDel31 ; "Dama Del 31"
.text:0040A586
mov ecx, 0Bh
.text:0040A58B
mov eax, esi
.text:0040A58D
call _WriteFile
.text:0040A592
test eax, eax
.text:0040A594
jle short loc_40A5C0
.text:0040A596
mov edx, offset byte_4CDC84
.text:0040A59B
mov ecx, 98h
.text:0040A5A0
mov eax, esi
.text:0040A5A2
call _WriteFile
.text:0040A5A7
test eax, eax
.text:0040A5A9
jle short loc_40A5C0
.text:0040A5AB
mov edx, offset word_4CDD1C
.text:0040A5B0
mov ecx, 85Ch
.text:0040A5B5
mov eax, esi
.text:0040A5B7
call _WriteFile
.text:0040A5BC
test eax, eax
.text:0040A5BE
jg short loc_40A5C4
.text:0040A5C0
.text:0040A5C0
loc_40A5C0:
; CODE XREF: sub_40A574+Bj
.text:0040A5C0
; sub_40A574+20j ...
.text:0040A5C0
xor ebx, ebx
.text:0040A5C2
jmp short loc_40A5C9
.text:0040A5C4 ;
---------------------------------------------------------------------------
.text:0040A5C4
.text:0040A5C4
loc_40A5C4:
; CODE XREF: sub_40A574+4Aj
.text:0040A5C4
mov ebx, 1
.text:0040A5C9
.text:0040A5C9
loc_40A5C9:
; CODE XREF: sub_40A574+4Ej
.text:0040A5C9
mov eax, dword_4CDD04
.text:0040A5CE
test eax, eax
.text:0040A5D0
jz short loc_40A602
.text:0040A5D2
test bl, bl
.text:0040A5D4
jz short loc_40A5F7
.text:0040A5D6
mov ecx, dword_4CDD04
.text:0040A5DC
mov eax, esi
.text:0040A5DE
mov edx, ecx
.text:0040A5E0
lea ecx, [edx+ecx*4]
.text:0040A5E3
lea ecx, [edx+ecx*2]
.text:0040A5E6
mov edx, offset unk_4CE578
.text:0040A5EB
shl ecx, 2
.text:0040A5EE
call _WriteFile
.text:0040A5F3
test eax, eax
.text:0040A5F5
jg short loc_40A5FB
.text:0040A5F7
.text:0040A5F7
loc_40A5F7:
; CODE XREF: sub_40A574+60j
.text:0040A5F7
xor ecx, ecx
.text:0040A5F9
jmp short loc_40A600
.text:0040A5FB ;
---------------------------------------------------------------------------
.text:0040A5FB
.text:0040A5FB
loc_40A5FB:
; CODE XREF: sub_40A574+81j
.text:0040A5FB
mov ecx, 1
.text:0040A600
.text:0040A600
loc_40A600:
; CODE XREF: sub_40A574+85j
.text:0040A600
mov ebx, ecx
.text:0040A602
.text:0040A602
loc_40A602:
; CODE XREF: sub_40A574+5Cj
.text:0040A602
mov eax, esi
.text:0040A604
call sub_4A5E60
.text:0040A609
mov eax, ebx
.text:0040A60B
pop esi
.text:0040A60C
pop ebx
.text:0040A60D
pop ebp
.text:0040A60E
retn
.text:0040A60E sub_40A574 endp
La funzione _WriteFile del programma richiede i seguenti
parametri, passati attraverso i registri:
eax = hFile del file su cui si scrive
ecx = numero di bytes da scrivere
edx = indirizzo dei bytes da scrivere
Ci sono 4 chiamate a WriteFile: le prime 3 hanno indirizzi (messi in edx) costanti e lunghezze (in ecx) costanti, l'ultimo ha invece un indirizzo costante ma una lunghezza variabile: poiché la scacchiera ha sempre le stesse dimensioni, è ovvio che l'ultimo WriteFile scriva la lista delle mosse effettuate fina a quel momento, dato che questo è l'unico parametro variabile. La call sub_4A5E60 si occupa infine di chiudere il file con CloseHandle.
Adesso che conosciamo il codice di salvataggio
possiamo cominciare a progettare il codice che dovremo aggiungere: dovremo
aprire il file con CreateFileA e leggerne il contenuto con delle chiamate a
ReadFile.
Inoltre, poiché il quarto WriteFile non è necessario per i nostri scopi (lo
sarebbe se volessimo abilitare anche i comandi "Annulla mossa" e
"Ripristina mossa"), ci limiteremo a considerare solo i primi 3.
La seguente tabella riassume quello che dovremo fare con i 3 ReadFile:
Indirizzo: 004BDE62h (indirizzo della stringa
"Dama Del 31")
Lunghezza: 0Bh bytes
Indirizzo: 004CDC84h
Lunghezza: 98h bytes
Indirizzo: 004CDD1Ch
Lunghezza: 85Ch bytes
Poiché non ho voglia di cercare una cava :P, aggiungeremo comodamente una sezione tramite il PEditor. Apriamo il PEditor, carichiamo il file, clicchiamo su "sections" e avremo la lista delle sezioni. Click con il pulsante destro del mouse e poi sulla voce "add a section". Diamo un nome alla sezione ("Spider" :)), settiamo le characteristics a E0000020 (per avere possibilità di lettura, scrittura ed esecuzione), e modifichiamo Virtual Size e Raw Size portandoli a 00001000. Ricordiamoci il Virtual Offset (004D8000), che convertito in Virtual Address (tramite il FLC) corrisponde ad 8D8000, e il Raw Offset (2AE800): ci serviranno in seguito. A questo punto abbiamo il file pronto per l'aggiunta del codice necessario al raggiungimento dei nostri obiettivi :)
Adesso dobbiamo cercare la porzione di programma da modificare per aggiungere il nostro codice. Come al solito, cerchiamo dall'Hex Workshop la stringa "mnuApriPartitaClick", che troviamo all'offset C0424; poco prima troviamo la dword 004021EC che è proprio l'indirizzo di tale funzione (che provvediamo a rinominare da IDA):
.text:004021EC mnuApriPartitaClick proc
near ; DATA XREF:
.data:004C0C3Do
.text:004021EC
.text:004021EC var_28 =
dword ptr -28h
.text:004021EC var_18 =
word ptr -18h
.text:004021EC var_C
= dword ptr -0Ch
.text:004021EC var_4
= dword ptr -4
.text:004021EC
.text:004021EC
push ebp
.text:004021ED
mov ebp, esp
.text:004021EF
add esp, 0FFFFFFD8h
.text:004021F2
push ebx
.text:004021F3
mov ebx, eax
.text:004021F5
mov eax, offset unk_4BDF54
.text:004021FA
call sub_4AFF88
.text:004021FF
mov eax, [ebx+358h]
.text:00402205
mov edx, [eax]
.text:00402207
call dword ptr [edx+3Ch] ; Visualizza la dialog
"Apri"
.text:0040220A
test al, al
.text:0040220C
jz short loc_402246 ; salta se nessun file
selezionato
.text:0040220E
mov [ebp+var_18], 8
.text:00402214
xor ecx, ecx
.text:00402216
mov [ebp+var_4], ecx
.text:00402219
lea edx, [ebp+var_4]
.text:0040221C
inc [ebp+var_C]
.text:0040221F
mov eax, [ebx+358h]
.text:00402225
call sub_47A8A4
.text:0040222A
lea ecx, [ebp+var_4]
.text:0040222D
mov eax, [ecx]
.text:0040222F
push eax
.text:00402230
call sub_40A048
.text:00402235
pop ecx
.text:00402236
dec [ebp+var_C]
.text:00402239
lea eax, [ebp+var_4]
.text:0040223C
mov edx, 2
.text:00402241
call sub_4BAC04
.text:00402246
.text:00402246
loc_402246:
; CODE XREF: mnuApriPartitaClick+20j
.text:00402246
mov ecx, [ebp+var_28]
.text:00402249
mov large fs:0, ecx
.text:00402250
pop ebx
.text:00402251
mov esp, ebp
.text:00402253
pop ebp
.text:00402254
retn
.text:00402254 mnuApriPartitaClick endp
Il codice è molto simile a quello della
funzione mnuSalvaPartitaClick,
e anche qui dopo una breve ricerca concludiamo immediatamente che la subroutine
che si occupa (o che dovrebbe occuparsi, dato che nella versione demo non lo fa
:) ) di aprire sia la sub_40A048.
Ecco il codice (che non riporto per intero):
.text:0040A048
push ebp
.text:0040A049
mov ebp, esp
.text:0040A04B
add esp, 0FFFFFFCCh
.text:0040A04E
push ebx
.text:0040A04F
push esi
.text:0040A050
mov eax, offset unk_4BFCB0
.text:0040A055
call sub_4AFF88
.text:0040A05A
mov [ebp+var_18], 1
.text:0040A061
lea edx, [ebp+arg_0]
.text:0040A064
lea eax, [ebp+arg_0]
.text:0040A067
call sub_4BAADC
.text:0040A06C
inc [ebp+var_18]
.text:0040A06F
xor edx, edx
.text:0040A071
mov [ebp+var_24], 8
.text:0040A077
mov eax, dword ptr [ebp+arg_0]
.text:0040A07A
call _CreateFile ;_CreateFile è
solo una funzione ponte
;per l'API CreateFileA. Ritorna in eax
;l'hFile del file aperto.
.text:0040A07F
mov [ebp+var_24], 14h
.text:0040A085
mov esi, eax
.text:0040A087
xor eax, eax
.text:0040A089
mov [ebp+var_8], eax
.text:0040A08C
xor ebx, ebx
.text:0040A08E
inc [ebp+var_18]
.text:0040A091
lea edx, [ebp+var_8]
.text:0040A094
mov eax, dword ptr [ebp+arg_0]
.text:0040A097
call sub_4A8CA4
.text:0040A09C
lea eax, [ebp+var_8]
.text:0040A09F
mov eax, [eax]
.text:0040A0A1
xor edx, edx
.text:0040A0A3
mov [ebp+var_4], edx
.text:0040A0A6
lea edx, [ebp+var_4]
.text:0040A0A9
inc [ebp+var_18]
.text:0040A0AC
call sub_4A6008
.text:0040A0B1
dec [ebp+var_18]
.text:0040A0B4
lea eax, [ebp+var_8]
.text:0040A0B7
mov edx, 2
.text:0040A0BC
call sub_4BAC04
.text:0040A0C1
mov [ebp+var_24], 8
.text:0040A0C7
mov [ebp+var_24], 20h
.text:0040A0CD
mov edx, offset a_dlx_0 ; ".dlx"
.text:0040A0D2
lea eax, [ebp+var_C]
.text:0040A0D5
call sub_4BAAA4
.text:0040A0DA
inc [ebp+var_18]
.text:0040A0DD
lea edx, [ebp+var_C]
.text:0040A0E0
lea eax, [ebp+var_4]
.text:0040A0E3
call sub_4BACE8
.text:0040A0E8
push eax
.text:0040A0E9
dec [ebp+var_18]
.text:0040A0EC
lea eax, [ebp+var_C]
.text:0040A0EF
mov edx, 2
.text:0040A0F4
call sub_4BAC04
.text:0040A0F9
pop ecx
.text:0040A0FA
test cl, cl
.text:0040A0FC
jz short loc_40A109
.text:0040A0FE
push esi ;qui riprende l'hFile
.text:0040A0FF
call sub_40917C
.text:0040A104
pop ecx
.text:0040A105
mov ebx, eax
.text:0040A107
jmp short loc_40A156
Il programma richiama CreateFileA per
aprire il file selezionato, e un po' di righe più giù passa il return value ad
una subroutine...ciò dovrebbe insospettirci... Entriamo nella sub_40917C:
.text:0040917C sub_40917C proc near ; CODE XREF: sub_40A048+B7p .text:0040917C .text:0040917C arg_0 = dword ptr 8 .text:0040917C .text:0040917C push ebp .text:0040917D mov eax, off_4CD824 .text:00409182 mov ebp, esp .text:00409184 push 10h .text:00409186 mov eax, [eax] .text:00409188 mov ecx, offset aApriPartita ; "Apri partita" .text:0040918D mov edx, offset aComandoApriNon ; "Comando APRI non disponibile in version"... .text:00409192 call sub_4BA9CC .text:00409197 mov eax, [ebp+arg_0] .text:0040919A call sub_4A5E60 ; chiamata a CloseHandle .text:0040919F mov al, 1 .text:004091A1 pop ebp .text:004091A2 retn .text:004091A2 sub_40917C endp ; sp = -4
Bingo! Questa è la funzione che nella versione
registrata si sarebbe dovuta occupare di aprire il file... e che tra poco lo
farà anche nella versione dimostrativa :P
La chiamata alla sub_4BA9CC
serve per visualizzare la MessageBox di errore; noppiamo il push
10h per evitare di
ritrovarci con lo stack rovinato e trasformiamo la call
004BA9CC in call
008D8000 (che punta all'inizio della
sezione da noi aggiunta, dove inseriremo il nostro
codice).
Adesso ci resta solo da scrivere il codice
necessario nella nostra sezione. Per far ciò potremmo scrivere e assemblare il
codice a parte e poi applicare le dovute correzioni, oppure potremmo scrivere
direttamente in memoria (tramite il softice) il codice che ci serve e poi
dumparcelo su disco e aggiungerlo nell'exe tramite un HexEditor. Io prediligo
sempre la seconda opzione. L'unica cosa che ci serve sapere è l'indirizzo
del thunk all'API ReadFile, che possiamo
facilmente sapere da IDA (basta premere G e digitare "ReadFile"); tale
thunk si trova all'indirizzo 004BB736.
Carichiamo l'IceDump, avviamo il file dama.exe tramite il symbol loader del
softice, e settiamo un "bpx 008D8000". A questo punto lasciamo runnare,
clicchiamo su Apri File e selezioniamo un file precedentemente salvato. SoftICE
breaka immediatamente, togliamo il breakpoint e assembliamo il seguente codice
(ricordatevi che in esi abbiamo l'hFile del file aperto!):
push 0 ;lpOverlapped push 8D8600 ;lpNumberOfBytesRead push 0B ;nNumberOfBytesToRead push 4BDE62 ;lpBuffer push esi ;hFile call 4BB736 ;call ReadFile push 0 ;lpOverlapped push 8D8600 ;lpNumberOfBytesRead push 98 ;nNumberOfBytesToRead push 4CDC84 ;lpBuffer push esi ;hFile call 4BB736 ;call ReadFile push 0 ;lpOverlapped push 8D8600 ;lpNumberOfBytesRead push 85C ;nNumberOfBytesToRead push 4CDD1C ;lpBuffer push esi ;hFile call 4BB736 ;call ReadFile ret
Fate attenzione durante la scrittura del codice e controllate che le opcodes generate dal softice siano esatte: mi è capitato più di una volta che il softice assembli in maniera errata; in questi casi scrivete l'opcode manualmente.
Una volta scritto il codice, lo dumpiamo con "/dump 8D8000 1000 c:\dump.bin" e premiamo F5... se non funziona o il programma va in crash rifate tutto da capo perché avete sbagliato qualcosa :), altrimenti aggiungete il file dumpato alla fine di dama.exe e avrete finalmente un "Apri Partita funzionante" :))
Spider
Note finali |
Saluti ad AndreaGeddon che ha scritto il tutorial su Tarantula :)
Disclaimer |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.