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 :)))
:PP

Chi tardi arriva male alloggia.

Andando piano arriva tardi. 

Home Page: http://bigspider.cjb.net
E-mail: spider_xx87@hotmail.com

Canali IRC frequentati: #crack-it e #asm su irc.azzurra.org
Nickname: ^Spider^
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:

  1. Nag Screen iniziale per almeno 20 secondi (argh!)
  2. Possibilità limitata di scelta della mossa (ossia alcune mosse vengono scelte casualmente dal programma, facendovi praticamente perdere anche una partita già vinta :-) )
  3. Possibilità di salvare la partita in corso ma non di aprire una partita precedentemente salvata.
  4. Comandi "Annulla mossa" e "Ripristina mossa" non disponibili.

Cracking di Dama Deluxe 3.1
Written by Spider

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 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


Potete scaricare la versione dimostrativa da http://volftp.mondadori.com/italiani/galletta/

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
[. . .]

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:

1. OnCloseQuery = FormCloseQuery
2. 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:

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.


<!-- DIV 728x90 IAM --> <div id="ad72890bottom" align="center"></div> <!-- START Digilander F --> <SCRIPT LANGUAGE="Javascript"> <!-- if ( typeof(bsl1_boot) != 'undefined' ) { setTimeout("bsl1_boot()",100); } var rs_DLR=1; var rs_DLRERR=0; //--> </SCRIPT> <SCRIPT LANGUAGE="Javascript" SRC="http://digilander.libero.it/_ad/digi_ad_13.js"> </SCRIPT> <!-- Begin Nielsen DCR SDK --> <script> if(window.location === window.parent.location){ // Static Queue Snippet ! function(t, n) { t[n] = t[n] || { nlsQ: function(e, o, c, r, s, i) { return s = t.document, r = s.createElement("script"), r.async = 1, r.src = ("http:" === t.location.protocol ? "http:" : "https:") + "//cdn-gl.imrworldwide.com/conf/" + e + ".js#name=" + o + "&ns=" + n, i = s.getElementsByTagName("script")[0], i.parentNode.insertBefore(r, i), t[n][o] = t[n][o] || { g: c || {}, ggPM: function(e, c, r, s, i) { (t[n][o].q = t[n][o].q || []).push([e, c, r, s, i]) } }, t[n][o]}}} (window, "NOLBUNDLE"); // SDK Initialization var nSdkInstance = NOLBUNDLE.nlsQ("P1504C48C-9D0B-4ADE-B7CD-04AF56A52362", "nlsnInstance"); // Content Metadata var nielsenMetadata = { type: 'static', assetid: ( location.hostname + location.pathname + location.search ).replace( /([^\w]|_)+/g, '-' ).replace( /^-+|-+$/g, '' ) || 'homepage', section: 'LiberoCommunity_BRW' }; // Event 'staticstart' Call nSdkInstance.ggPM("staticstart", nielsenMetadata); } </script> <!-- End Nielsen DCR SDK --> <!-- Libero COMSCORE start - Version 1.53 --> <script type="text/javascript"> if ( rs_DLRERR == 1 ) { var libero_comscore_error = 404; } </script> <script type="text/javascript"> document.write(unescape("%3Cscript src='" + (document.location.protocol == "https:" ? "https://sb" : "http://b") + ".scorecardresearch.com/beacon.js'%3E%3C/script%3E")); </script> <script type="text/javascript"> if (rs_DLR) { document.write(unescape("%3Cscript id='libero_tracking_js_site' src='http://digistatic.libero.it/js/comscore_8_3_04/comscore_digilander.libero.it.js'%3E%3C/script%3E")); document.write(unescape("%3Cscript id='libero_tracking_js_site' src='http://digistatic.libero.it/js/comscore_8_3_04/comscore_engine.js'%3E%3C/script%3E")); } </script> <noscript> <img src="http://b.scorecardresearch.com/p?c1=2&amp;c2=13259779&amp;cj=1&amp;name=libero.others&amp;ns_site=libero" /> </noscript> <!-- Libero COMSCORE end --> <!-- IOL Analytics --> <script src="//i.plug.it/iplug/js/lib/iol/analytics/data/digilander-libero-it/tracking_digilander-libero-it.min.js"></script> <script src="//i.plug.it/iplug/js/lib/iol/analytics/engine/IOL.Analytics.Tracking.min.js"></script> <script type="text/javascript"> var iat = new IOL.Analytics.Tracking.Engine(); iat.send(); </script> <noscript><img src="//italiaonline01.wt-eu02.net/215973748390194/wt.pl?p=315,libero.web.share.digiland.siti.digilander&amp;cg1=libero&amp;cg2=web&amp;cg3=share&amp;cg4=digiland&amp;cg5=siti&amp;cg6=digilander&amp;cg7=libero.web.share.digiland.siti.digilander" height="1" width="1" alt=""></noscript> <!-- /IOL Analytics --> <!-- BEGIN Global site tag (gtag.js) - Google Analytics 4 --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-9K5Y6YYGV4"></script> <script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-9K5Y6YYGV4'); </script> <!-- END Global site tag (gtag.js) - Google Analytics 4 --> <div id="adinterstitial"></div> </body> </html>

Home