Dama Deluxe 3.1
From UIC
Dama Deluxe 3.1 Cracking
Contents |
| Infos | |
|---|---|
| Author: | Spider |
| Email: | spider_xx87 (AT) hotmail (DOT) com |
| Website: | http://bigspider.has.it/ |
| Date: | ??/08/2002 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Que: Spi come al solito sei stato preciso e chiaro nella spiegazione, bravo |
Introduzione
Oggi vi invito a pranzo... crackeremo la versione dimostrativa di un interessante programmino chiamato Dama Deluxe 3.1. È un gioco di dama italiana molto ben fatto, ma che nella demo presenta diverse limitazioni:
- Nag Screen iniziale per almeno 20 secondi (argh!)
- Possibilità limitata di scelta della mossa (ossia alcune mosse vengono scelte casualmente dal programma, facendovi praticamente perdere anche una partita già vinta :-) )
- Possibilità di salvare la partita in corso ma non di aprire una partita precedentemente salvata.
- Comandi "Annulla mossa" e "Ripristina mossa" non disponibili.
Aperitivo
Avviamo il programma e compare la bella NagScreen :-) Clicchiamo su Informazioni e ci compare l'help su una paginetta piena di cose inutili... Solo l'ultimo paragrafo è interessante:
- Esiste un crack per Dama Deluxe? ;-)
Un crack, cioè uno di quei piccoli programmi che trasformano la versione shareware in registrata, non esiste e non esisterà mai!!!
Questo non perché le tecniche di protezione sono troppo sofisticate, ma per il semplice motivo che il codice che esegue le funzioni non disponibili (es. Apri partita) non è presente nel file eseguibile, in quanto è stato rimosso dai sorgenti prima della compilazione della versione shareware. L'unico modo è diffondere la propria versione completa, ma in questo caso posso immediatamente risalire a chi l'ha distribuita, perché ogni versione registrata ha codificato all'interno il nome a cui appartiene.
Questo per scoraggiare coloro i quali vogliono usufruire del programma, ma non ritengono giusto versare un minimo contributo per le ore di lavoro (e sono state tante) impiegate per creare Dama Deluxe.
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
- 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 o un altro dumper.
Si presuppone nel lettore una conoscenza base dei comandi di IDA; in caso contrario, si rimanda al tutorial di Quequero.
Link e riferimenti
Potete scaricare la versione dimostrativa da hxxp://volftp.mondadori.com/italiani/galletta/
ADDENDUM: Il link precedente non funziona più, ma cercando in rete si dovrebbe trovare comunque.
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? Sì, 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
[. . .]
Come vedete c'è una sintassi molto chiara e leggibile e, soprattutto, ci sono TUTTI i nomi in chiaro :-)) Ciò semplificherà molto il nostro lavoro di reversing. Ciò che però salta ancora più all'occhio sono queste 2 righe:
- OnCloseQuery = FormCloseQuery
- OnShow = FormShow
Assomigliano molto ad eventi... traducendo in italiano quelle righe significano palesemente:
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:
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 <font color="#A0A000">F4BE 4000</font> 0846 6F72 6D53 686F 7709 5446 726D ..<font color="#A0A000">..@.</font>.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...
È 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
.text:004020A8 ; Attributes: bp-based frame
.text:004020A8
.text:004020A8 frmMain_FormActivate proc near ; DATA XREF: .data:004C0B96�o
.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+17�j
.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 allinterno 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:
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: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. È 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. È 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 giunge 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 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ 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:004C0C57�o
.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+20�j
.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. È 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: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
.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+B�j
..text:0040A5C0 ; sub_40A574+20�j ...
..text:0040A5C0 xor ebx, ebx
..text:0040A5C2 jmp short loc_40A5C9
..text:0040A5C4 ; ---------------------------------------------------------------------------
..text:0040A5C4
..text:0040A5C4 loc_40A5C4: ; CODE XREF: sub_40A574+4A�j
..text:0040A5C4 mov ebx, 1
..text:0040A5C9
..text:0040A5C9 loc_40A5C9: ; CODE XREF: sub_40A574+4E�j
..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+60�j
.text:0040A5F7 xor ecx, ecx
.text:0040A5F9 jmp short loc_40A600
.text:0040A5FB ; ---------------------------------------------------------------------------
.text:0040A5FB
.text:0040A5FB loc_40A5FB: ; CODE XREF: sub_40A574+81�j
.text:0040A5FB mov ecx, 1
.text:0040A600
.text:0040A600 loc_40A600: ; CODE XREF: sub_40A574+85�j
.text:0040A600 mov ebx, ecx
.text:0040A602
.text:0040A602 loc_40A602: ; CODE XREF: sub_40A574+5C�j
.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 nel codice :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
.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+20�j
.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: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
.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 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 :) (Crackme 39 nella pagina Crackme 26 - 50)
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.