Oggi imparerete un nuovo modo di proteggere i programmi in VB. Vedo che gi� il
disgusto si delinea sulle vostre facce al solo nominare di questo linguaggio, ma vedremo
di rendere piacevole anche ci� che non lo �. Importante: questo � il Visual Basic in
formato p-code.
- Allora, mettevi comodi perch� non sar� molto semplice (a meno che non vi intendiate
gi� di p-code). Spero di non arrivare a scrivere un enciclopedia (scusa Que :))), ma
alcuni concetti fondamentali devo darveli altrimenti 'sto tutorial serve solo a vedere
come ho lavorato io, senza essere di aiuto a nessuno. Per questo motivo non partiremo
direttamente con l'utilizzo del disassembler di JosephCo_, ma dall'inizio inizio.
- INIZIAMO
- Facciamo partire il programma di installazione ("Setup.exe") e ci appare
subito la schermata che ci mostra due codici ("Codice programma" e "Codice
computer") e ce ne richiede un terzo "Codice Utente". Se inserite il codice
errato vi appare il messaggio con scritto "Il codice Utente inserito non �
valido". Chi � suicida, pu� tentare di fare "bpx hmemcpy". La vita non �
abbastanza lunga per permetterci di esaminare tutte le call che ci sono. Quindi dobbiamo
cambiare strategia. Controlliamo con Filemon, per vedere se c'� qualcosa di interessante.
Facciamolo partire ed escludiamo i processi che possono disturbarci (Explorer, Kernel32,
...) e lanciamo di nuovo "Setup.exe" .... Cavoli! Quanta roba. Vabb� cominciamo
a vedere le informazioni che ci sono fornite e ... aspetta un attimo. Guardate tra i nomi
dei processi: oltre a "Setup.exe" � presente un altro file
"Imsetup.exe". Tremendo sospetto! Perch�?? Guardate il nome: IMsetup = IncoMedia.
Questo file � localizzato (lo si vede dal FileMon) in "C:\WINDOWS\SYSTEM\", ed
� il vero file del programma di installazione, ovvero, il file "Setup.exe" ha
una funzione di copertura: richiama infatti "Imsetup.exe", che rappresenta il
vero obiettivo.
- Piccola parentesi: sempre con Filemon, si ottiene un'altra informazione interessante:
sempre in "C:\WINDOWS\SYSTEM\", � presente un file chiamato fxlbl50g.ocx che contiene il "Codice
programma": se rimuovete tale file, il setup ne crea un'altro con un diverso codice.
(Spunto per creare un Keygen). Il codice computer penso venga calcolato una volta sola, ma
non so per certo da dove.
- Ora che sappiano qual'� il vero obiettivo, non ci resta che concentrarci su
"Imsetup.exe". Apriamo Win32DASM (con patch per il VB) e disassembliamo
"Imsetup.exe" e... arggghhh. �String Data Reference� non � disponibile.
Orrore e disgusto! Che fare? Possiamo provare con le �Imported Function�, ma non
ricaveremmo niente di buono: solo VB e VB e VB.
- Ma ecco che interviene il nostro JosephCo_ con il suo disassembler Exdec!
- Prima di iniziare occorrono due parole: come configurarlo, come funziona e brevi cenni
sul p-code.
-
- CONFIGURARE EXDEC --> Estraete il contenuto dei due file zip che avete scaricato
nella stessa cartella: il file "betaexdec.zip" � una versione (ma va?) beta del
programma, che � in continuo aggiornamento, quindi ogni tanto visitate il sito per
ottenere l'ultima release. All'interno del file zip troverete anche crackme di esempio e
tutorial su come sproteggerli e su come funziona il p-code (leggeteveli tutti, anche se
capite tutto in questo tutorial). Nell'altro file .zip si trovano i simboli, che sono
quelli che dovrete caricare in SofIce. All'interno del file zip ne troverete 2:
Msvbvm50.nms e Msvbvm60.nms. Avrete gi� capito che se volete reversare un'applicazione in
VB5 userete Msvbvm50.nms, se in VB6 userete Msvbvm60.nms. A noi interessa Msvbvm50.nms.
Quindi fate partire Loader32 (caricatore di simboli per SoftIce) che si trova nella
cartella di SoftIce con il nome di "Loader32.exe". Cliccate sull'icona
"Apri" (la prima a sinistra) e selezionate Msvbvm50.nms; poi premete l'icona
subito dopo "Apri" per caricare i simboli. Infine chiudete Loader32.
- Ora eseguite "Exdec.exe" e selezionate "File", "Disassemble
Prog" (non selezionate l'icona con al cartella: serve per aprire il formato TXT di un
listato decompilato e salvato) e selezionate "Imsetup.exe" e ... voil�, ecco il
nostro listato. Ma come vi ho detto, il p-code � tutta un'altra cosa: quello che vedete
non � ASM, quindi frenate il vostro entusiasmo.
-
- CENNI SUL P-CODE --> Premetto che non mi soffermer� a lungo sulla spiegazione del
p-code perch� troverete molte informazioni nel file "betaexdec.zip". Detto
questo vi dico subito che il p-code � una cosa che non avete mai visto prima ed �
TOTALMENTE (ed intendo TOTALMENTE) diversa dal codice ASM. Se osservate il listato
prodotto da Exdec, noterete che gli indirizzi (indirizzi esadecimali) sono raggruppati in
procedure che vengono identificate cos�:
-
- Proc: xxxxxx
<-- xxxxxx rappresenta l'indirizzo
-
414EF0: 7f
ILdI2
<-- Esempio di codice p-code
414EF3: 1c BranchF:
414F75
<-- Esempio di codice p-code (salta a 414f75 se � falso)
414EF6: 04 FLdRfVar
local_0086 <-- Esempio di codice p-code
414EF9: 07
FMemLdRf <-- Esempio di codice p-code
414EFE: 24 NewIfNullPr 4056f4 (classSetupDef) <-- Esempio di codice p-code
- il funzionamento delle Proc � simile a quello delle call, con la differenza che tute le
call terminano con RET (che in exdec corrispondono a ExitProc), mentre le Proc possono non
avere questo parametro.
- Nell'esempio sopra riportato notate che dopo l'indirizzo esadecimale (e i due punti)
c'� una sequenza che ricorda il linguaggio ASCII: nel p-code questo rappresenta il nome
che segue nel codice! Mi spiego. "7F = ILdI2" oppure "1c = BranchF"
oppure "04 = FLdRfVar". Chiaro?
- In mezzo a questi esempi, una particolare attenzione va data al comando BranchX (che in
ASM equivale a JZ o JNZ), dove X pu� essere T (true) o F (false).
- Ricordatevi, SEMPRE, che ASM � diverso da P-CODE, quindi un'istruzione che sembra
scontata in ASM, in P-CODE pu� volere dire tutt'altra cosa. Vi faccio un esempio. In
Sice, quando si analizza il p-code, per controllare la vostra posizione dovete sempre
monitorare ESI (codice ESAdecimale): questo vi serve per sincronizzare la vostra posizione
in Sice con quella nel disassembler, dato che il codice che vi appare � totalmente
diverso. Sempre relativamente a Sice, va detto che anche i breakpoint vanno maneggiati in
maniera diversa: una volta caricato il simbolo � necessario mettere il bpx su questo
simbolo PRIMA DI AVVIARE IL PROGRAMMA, ponendo "bpx doexdisp"; poi si avvia il
programma e, non appena si torna in softice si pone un "bpm" (non
"bpx") sull'indirizzo desiderato (esempio: "bpm 413bd8").
- Per questi motivi prestate attenzione alle istruzione che contengono parti tipo MOV EAX,
004175F6: in EAX non troverete nulla, ma se guardate sul disassembler potreste incontrare
qualcosa di molto utile...
- INIZIO REVERSING --> Dopo aver disassemblato il file cercate la stringa di errore
"Il codice Utente inserito non � valido"; la troverete all'indirizzo 413C33. Il
codice precedente � il seguente:
-
- Proc: 413c80
413BD8: 04 FLdRfVar local_008C
413BDB: 21 FLdPrThis
413BDC: 0f VCallAd (object 6 )
413BDF: 19 FStAdFunc local_0088
413BE2: 08 FLdPr local_0088
413BE5: 0d VCallHresult ����
413BEA: 6c ILdRf local_008C
413BED: 0a ImpAdCallFPR4: _rtcR8ValFromBstr
413BF2: 74 FStFPR8 local_009C
413BF5: 04 FLdRfVar local_0092
413BF8: 6f FLdFPR8 local_009C
413BFB: e8 CI4R8
413BFC: 59 PopTmpLdAdStr local_0090
413BFF: 05 ImpAdLdRf: 421030
413C02: 24 NewIfNullPr 4027cc (classProtection)
413C05: 0d VCallHresult
FORM::LoadProp
<-- Attenzione qui (vedere spiegazione)
413C0A: 6b FLdI2 local_0092
413C0D: 08 FLdPr local_param_0008
413C10: 8e MemStI2 local_param_0034
413C13: 2f FFree1Str local_008C
413C16: 1a FFree1Ad local_0088
413C19: 08 FLdPr local_param_0008
413C1C: 89 MemLdI2 local_param_0034
413C1F: f4 LitI2_Byte: 0x0 0 (.)
413C21: c6 EqI2
413C22: 1c BranchF:
413C64
<-- Salta se... e dove salta? Dopo la stringa di errore!
413C25: 27 LitVar_Missing
413C28: 27 LitVar_Missing
413C2B: 27 LitVar_Missing
413C2E: f5 LitI4: 0x10 16 (....)
413C33: 3a LitVarStr: ( local_00AC ) Il codice Utente inserito non �
valido. <-- Stringa di errore
-
- Controllate l'indirizzo 413C22: contiene un salto a 413C64, che si trova dopo il
messaggio di errore, quindi prima di questa istruzione , da qualche parte, avviene il
controllo per il codice esatto. E questo controllo avviene a 413C05. Mi chiedete:
"Come si fa a dirlo?". Riposta: innanzitutto perch� tutta l'altra parte di
codice � composta da istruzioni inutili (servono al programma, ma a noi non interessano)
e poi perch� a questo indirizzo si trova una chiamata ad una dll esterna (faccio
notare che poteva essere anche agli indirizzi 413BE5 e 413BED, ma nel 90% dei casi la call
che nasconde i seriali � quella che precede il jump. Quindi dobbiamo mettere nei
breakpoint a questa chiamata.
- CTRL-D per entrare in Sice, digitiamo "bpx doexdisp" e facciamo partire il
setup. Subito riappare Sice, togliamo i breakpoint ("bc *" o "bd *") e
poniamo il nostro "bpm 413C05"; appare la richiesta del seriale, dove noi
metteremo un numero a caso minore di 11 lettere altrimenti vi collassa il programma (bug?)
e premiamo "Ok". Softice poppa (se non � cos� molto probabilemte non avete
caricato il simbolo: rileggete il tutorial e controllate). Incominciamo a steppare con F10
cercando un "Call EAX" o "Call[EAX]", che indicano la chiamata alla
Call che stiamo cercando. Quando la trovate entrate con F8 all'interno della Call e
vedrete il seguente codice:
- Se vi ricordate cosa ho detto all'inizio, capirete che quel 00416A54 � in realt� una
Proc (se scrivete "D 00416A54) non vedrete nulla. Quindi dobbiamo cercare questo
indirizzo nel listato disassemblato, che ci appare cos�:
Proc: 416a54
............................
416956: 0a ImpAdCallFPR4: _rtcR8ValFromBstr
41695B: e8 CI4R8
41695C: 71 FStR4 local_00E8
41695F: 32 FFreeStr
416966: 36 FFreeVar
41696F: 00 LargeBos
416971: 6c ILdRf local_00E8
416974: 6c ILdRf local_00E4
416977: 80 ILdI4: local_param_000C
41697A: 0b ImpAdCallI2:
pp_writecf
<-- Chiamata a pppwin32.dll
41697F: 70 FStI2 local_013A
416982: 3c SetLastSystemError
416983: 6b FLdI2 local_013A
416986: 70 FStI2 local_0086
...........................
4169A3: 1a FFree1Ad local_0140
4169A6: 1c BranchF:
416A48
<--- Salta l'errore, quindi il controllo del codice � andato
bene.
............................
4169D4: 27 LitVar_Missing
4169D7: 27 LitVar_Missing
4169DA: f5 LitI4: 0x10 16 (....)
4169DF: 1b LitStr: Errore nel controllo del codice Utente:
-
- L'istruzione che ci interessa si trova in 41697A, dove viene eseguito il controllo del
seriale. Ancora una volta: "Come si fa a dirlo?". Riposta: bisogna guardasi
attorno. Se notate, all'indirizzo 4169DF, appare la stringa di un errore durante il
controllo del codice: il salto avviene all'indirizzo 4169A6. Questo salto non indica se il
codice � giusto o sbagliato, ma che il controllo per determinare se � giusto o
sbagliato, � stato eseguito correttamente; quindi poco prima deve essere stato eseguito
il check del serial. Come detto prima, la call che precede il salto � nel 90% dei
casi, la "colpevole".
- Quindi dobbiamo mettere un "bpm 41697A", ma per farlo dobbiamo uscire dal
Setup e rifare i passaggi iniziali ("bpx doexdisp", avviare il programma, quando
Sice poppa, "bc *" o "bd *", mettere "bpm 41697A");
inseriamo il nostro serial e premiamo "Ok". Sice poppa, F10 e noi ci ritroviamo
in questo codice:
-
- PUSH EAX
PUSH ECX
PUSH EDX
CALL 02BB43F0
MOVSX EAX,AL
ADD ESP, AC
- RET
-
- L'unica strada � entrare in quella call (vedete nient'altro di interessante?) e
FINALMENTE (ve lo dico io!) siamo dentro la call che contiene il controllo del seriale. I
pi� esperti possono tentare di trovarlo da soli, mentre per chi vuole saperlo subito, il
controllo termina in questo codice:
- cmp esi, ecx
- JZ (Salta
a POP EDI, evitando lo
XOR)
<-- Se il seriale � esatto salta lo Xor
- CMP AL, 12
JLE XXXXXXX
<-- � il jump che compie il ciclo.
XOR AL, AL
POP EDI
POP ESI
POP EBX
RET
- � fatta!!! Ormai � tutto chiaro: il
primo CMP (quello che evita lo XOR), � quello che contiene i due seriali: il nostro (in
ECX) e quello corretto (in ESI). Il secondo CMP � quello che ci rimanda indietro
modificando il seriale fino a che non compie 12 giri.
- Quindi dobbiamo vedere cosa c'� in ESI e in ECX, ma siamo in p-code, non dimenticatelo:
se facessimo "d ESI" o "d ECX" vedremmo solo
"?????????????", che indicano che i numeri che sono dentro ECX e ESI non si
trovano quelli indirizzi. Ma neanche con "? ESI" "? ECX", poich� Sice
ci fornirebbe indicazioni che non ci servono (abbiamo caricato i simboli per exdec,
ricordate?): infatti apparirebbe qualcosa tipo "Unsigned Long = ..........". E
allora? Dobbiamo utilizzare che ci permetta di vedere in memoria cosa c'�. Prima di
caricare il simbolo per exdec, il comando era "?"; ora il comando �
"?#": punto interrogativo con attaccato il cancelletto (ALT GR + �).
- Scrivete dunque "?# ESI" per vedere il seriale corretto e "?# ECX"
per vedere il vostro inserito.
-
Niutron
Spero siate riusciti a capire come ho agito; certamente, questo tutorial non verr� mai
preso come guida al p-code, per� ho tentato di fare il meglio possibile. Se avete
domande, (o se volete segnalare errori o aggiunte) c'� il mio indirizzo e-mail. Cerco
inoltre volonterosi reverser che contribuiscano alla creazione di un KeyGen. Ancora una
volta ringrazio il mio supervisore JosephCo_, senza il quale non avrei mai capito il
funzionamento di questa protezione. Grazie Jo!