Menu

Giochi di prestigio con SMARTCHECK

ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßÛ
²                                [x] ringZ3r0  Proudly Presents [x]                             ²
±                   ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ                ±
³            Giochi di prestigio con SMARTCHECK            ³
Kill3xx
05 Gennaio ,1999
--==[   PREMESSA   ]==---------------------------------------------------------------------------
LE INFORMAZIONI CHE TROVATE ALL'INTERNO DI QUESTO FILE SONO PER PURO SCOPO DIDATTICO.
L'AUTORE NON INCORAGGIA CHI VOLESSE UTILIZZARLO PER SCOPI ILLEGALI.
--==[  DIFFICOLTA' ]==---------------------------------------------------------------------------
scala : *=Novizio, **=Apprendista, ***=Esperto, ****=Guru
target: **1/2
--==[  TOOLS USATI ]==---------------------------------------------------------------------------
* SmartCheck 6.0 (Numega)
* SoftIce 3.23   (Numega)
* IDA ver 3.80b  (Ilflak / DataRescue)
* HIEW 6.01
--==[ INTRODUZIONE ]==---------------------------------------------------------------------------
Per iniziare direi che e' necessaria un breve digressione sul funzionamento e soprattutto sull'
utilizzo di SmartCheck. SmartCheck e' fondamentalmente un avanzato ApiHooker, cioe' un programma
che si aggancia (hook) alle chiamate alle API di sistema o alle funzioni di una DLL cercando di
carpire tutte le informazioni possibili e sui parametri passati e sul programma chiamante: questo
gli permette di analizzare runtime la presenza di errori generati dal passaggio di parametri non
validi (ad exp. perche' non inizializzati), di trovare memory leak, ecc. o semplicemente di
verificare cosa avviene dietro le quinte del nostro programma VB una volta "compilato" ed
eseguito. Inoltre, in presenza dei sorgenti, SMCHK ci consente di trovare direttamente nel nostro
codice dove sta l'istruzione colpevole, il parametro sbagliato, ecc..Indubbiamente un tool molto
potente! "A cosa puo' servirci?" Beh a moltissimo direi :)
Chi ha esperienza di reverse con programmi VB sa che il problema piu' grosso con questi target e'
l'origine stessa di VB, che nasce come linguaggio interpretato: anche senza arrivare al caso
estremo di programmi interamente in PCODE (VB3/4 e VB5/6 non "compilati") il codice "nativo"
dell'eseguibile e' una sorta di collante intorno a chiamate alle DLL runtime di VB. Questo si
rivela un problema sia nel caso intendessimo debuggare il codice con Sice, sia nel caso di un
approccio dead listing, in quanto siamo costretti a seguire un dedalo di chiamate ad API che
costituiscono i vari layer con cui il VB incapsula i costrutti ad alto livello del linguaggio.
SmartCheck entra in gioco proprio a questo punto: ci permette di avare una sorta di "fotografia"
dell'esecuzione di un programma, consentendoci di seguire il workflow del programma stesso
comprese API invocate, messaggi, hook, chiamate alle interfacce OLE,ecc.
A questo punto studiando i "log" possiamo trovare i punti "d'attacco" con cui iniziare il debug,
le api migliori su cui mettere i breakpoint, i file e le entry del registro coinvolte e molto
altro.. nel caso estremo potremmo addirittura trovarci il "serial number" bello in chiaro :)
--==[ CONFIGURAZIONE DI SMARTCHECK ]==-----------------------------------------------------------
La configurazione di SmartCheck non e' un'operazione molto complessa ma e' di vitale importanza
in
quanto coinvolge il modo stesso di operare di SMTCHK, e quindi le informazioni che poi possiamo
ottenere: innanzitutto caricate il programma da debuggare con FILE->OPEN.. in questo modo avrete
la possibilita' di impostare anche i settaggi relatili al target corrente:
TAB "ERROR DETECTION"
*
1000
listbox "type of errors": permette di selezionare i tipi di errori che volgiamo vengano
tracciati: una configurazione tipica e' Memory ,Pointer, Api errors ad ON e leaks ad OFF
* checkbox "report errors immediatly" : impostatelo ad off o vi verra' un crampo alla mano a
suon di confermare tutti gli errori che si presentano :)
* button "advanced": da qui accedete ad un dialog in cui potete impostare alcune fondamentali
opzioni del "motore di debug" di SMTCHK:
* "report error caused by other errors" : quwsta opzione vi consente di tracciare anche gli
errori in modo "ricorsivo".. mettetela ad ON in quanto potrete loggare anche cosa accade nel
caso il programma utilizzi informazioni errate che generano errori (ad exp.con i keyfile
potrebbe essere la lunghezza errata, gli attributi, la data, ecc... insomma avete capito :)
* "report error even if no source..": beh mi pare scontanto l'ON ;)
* "report each error once" : lasciatelo ad OFF; noi vogliamo piu' informazioni possibili
* "check all heap block..": settatelo ad OFF, rallenta solo le prestazioni senza che nella
maggioranza dei casi possiamo trarne vantaggio (a meno che no stiate debaggando un vostro
programma ;)
* "guard byte" : non ci interessa in quanto legato al check dello heap
* "cache program events" : scontato l'ON a meno che non abbiate un pentium3000 nel qual caso
non ve ne puo' fregare di piu' ;)
* "suppress system and api call" : settatelo ad ON; questo vi consente di tracciare anche le
API di sistema (RegQueryValueExA,ReadFile,ecc..) indispensabile!
* "defer program results..": settatelo ad ON, in questo modo il log avvera' in background e
potrete utilizzare il programma in modo trasparente
TAB "REPORTING"
questa opzione permette di impostare quali eventi devono essere riportati nei log, e quando
deve iniziare il log: quindi la configurazione tipica sara'impostare tutto ad ON tranne che
"MouseMove event over OCX controls" = OFF,  questo perche' vogliamo che SMTCHK riporti ogni
possibile errore genarato da VB per avere, lo ripeto, piu' informazioni utili possibili.
TAB "FILES TO CHECK"
questo tab permette di impostare quali EXE,DLL debbano essere coinvolti nel logging (e quindi
nell'APIhook) in modo da filtrare le informazioni non necessarie. In genere vanno attivati
tutti gli EXE/DLL che sono utilizzati dal programma e che non fanno parte di DLL/OCX di runtime
a noi ben note e/o che sappiamo per certo non possono essere coinvolti nella protezione (vedi
COMDLG32.OCX,DAO35.DLL,NETBIOS.DLL,ecc.)
TAB "ERROR SUPPRESSION"
qui e' possibile specifiare le API o le DLL escluse dall'hooking e quindi dal report.Si tratta
di tarare il "motore" di SMTCHK a seconda del programma debuggato in modo che ignori specifiche
chiamate a funzioni od ad intere DLL, che sappiamo per certo irrilevanti nello schema di
protezione. E' ovvio che tipicamente, escluderemo i vari OCX standard, le DLL del DAO, ecc.
mentre e' meglio lasciare le varie DLL MFC spesso utilizzate x funzioni di supporto.
In ogni caso SMTCHK crea un file di contenente le vari API escluse x cui non e' necessario
specificarle ad ogni esecuzione.
--==[ UN ESEMPIO PRATICO ]==---------------------------------------------------------------------
Bene, dopo la lunga (pure pallosa ;) ma devorosa premessa vediamo di testare la vera potenza di
SMTCHK su un target "reale". Per questo scopo ho scelto un programma commerciale di cui non faro'
il nome eheh ;).. xche' presenta una schema abbastanza elaborato di serial che a mio giudizio
costituisce un classico caso "da manuale" di protezione VB.
Questo target utilizza un unlock code in cui sono contenute le informazioni sul tipo e numero di
licenze, la data di validita', ecc.. nel caso installiate la trial questa sara' valida per i
classici 60gg, con una sola licenza user e nessuna licenza server. All'avvio si presenta uno
splash screen che ci illustra le operazioni in corso: browsing data, updating da
1000
tabase, e ofcoz
"checking license" ;).. nel menu help e'presente la dialog x inserire il serial con  verifica
immediata. Ora mandiamo avanti la data e vediamo cosa succede: ok ok, il programma ci informa
gentilmente che e' scaduto il periodo trial e ci presenta la dialog per inserire il serial..
direi che abbiamo abbastanza materiale per stendere un piano d'attacco ..splash form,license
form.. lets go :)
Runniamo il prog da SmartCheck.. lasciamo che il prog visualizzi il messaggio.. clikkiamo per
registrarci.. modifichiamo con solito sistema "a cazzo" (TM) il  serial.. serial invalido! :)
bene ora dovremmo aver un bel log da analizzare:
SMRCHK vi presenta una window divisa in due panel.. a SX il log vero e proprio in forma concisa,
mentre a DX le informazioni in forma estesa: leggi parametri e cosa fondamentale l'address della
call nella forma MODULENAME!RVA
// newbie corner //------------------------------------------------------------------------------
questa forma ci mostra il Relative Virtual Address della call.. cioe' offset relativo all'address
in cui viene mappata l'immagine di MODULENAME che contiene il codice (=IMAGEBASE; per gli .exe
solitamamente corrispondente alla preferred IMAGEBASE presente nel PE Header (=40000), per le DLL
spesso e' diversa in quanto sono piu' soggette a collisioni e quindi a rilocazione, specie se
hanno sezioni shared). Questa forma assomiglia a quella usata in sICE, MODULENAME!SECTION+OFFSET,
solo che non e' esplicitato il nome della section. Questo formato consente di indicare un address
in un modulo a prescindere dalla eventuale rilocazione. Se vogliamo ottenere l'address effettivo
in memoria dobbiamo semplicemente sommare la IMAGEBASE. Prendiamo ad exp. SPIINIT.ED82: dobbiamo
fare:
IMAGEBASE + RVA
34040000  + F7B5 = 3404F7B7
se invece volessimo calcolare l'offset su file, ad esempio per usare la pratica scorciatoia
dell'INT3 hardcoded, possiamo ottenerlo facendo questo semplice calcolo:
RAWOFFSET section + (ADDRESS  - IMAGEBASE) - RVA section
400               + (3404F7B5 - 34040000 ) - 1000        = EBB5
dove section e' la section che contiene quell'address (tipicamente .TEXT o CODE) e RAWOFFSET
l'offset fisico nel file in cui inizia detta section. Tutte queste informazioni si possono essere
ricavate usando un PE dumper o in modo piu' pratico usando PEBROWSER e leggendo le info nel PE
header. Se non ci avete capito un cz cominciate studiare il formato PE.. vi fara' molto comodo :)
-----------------------------------------------------------------------------------------------//
--==[ GIOCHI DI PRESTIGIO ]==--------------------------------------------------------------------
Quando iniziate ad analizzare i log si SMTCHK vi conviene usare un apprioccio topdow, mi spiego:
nel menu VIEW sciegliete "Show error and specific events", poi la voce Specific Events.. questo
vi consente di analizzare i singoli aspetti del programma: la creazione di form, controlli, gli
eventi come i click, le call alle api,ecc.. L'idea e' quella di dare uno sguardo d'insieme per
trovare velocemente un punto che ci puo' interessare e quindi passare ad una visione piu'
dettagliata. Ad exp in questo caso noi cerchiamo la splash form, o la form x inserire la licenza,
quindi una buona idea e quella di attivare per ora solo "Object events" + "Methods, properties.."
+ "Form Creation": in questo modo vedremo solo i log relativi alla creazione di form, settaggi
di proprieta' (ad exp. una caption bar), o le invocazioni in risposta ad un click nel menu, nei
button, ecc... Di fatti in un attimo troviamo:
3341     FSPISplash (Form) created
7515     FSPISplash.Show
20996    LoadStatus.Caption <-- "Verifying License..." (String)
bene abbiamo scovato un buon punto di inizio.. ora possiamo attivare le altr voci per analizzare
in dettaglio i log: quindi attiviamo le opzioni di  visualizzazione
"Visual Basic and.." -> (visualizza le a VB e WIN di alto livello (MID,LEFT,REAFILE,ecc.)
"Value Coercion"     -> (visualizza le conversioni implicite che VB fa'
1000
.. eheh so parecchie ;)
"API Calls form.."   -> (visualizza anche le api di basso livello di VB e WIN (ex. vbaSubVarVal)
A questo punto avremo un bel po da leggere :)
Appena sotto a LoadStatus.Caption troviamo due strane call:
22413       MethCallEngine()
22417        MethCallEngine()
questo non e' altro che un entrypoint x l'engine di VB, quindi dobbiamo seguire le call
espandendo i rami di queste entry per vedere quello che avviene realmente quando viene eseguito
il codice della splash form. Scrollando da questo ci salta subito all'occhio questo blocco:
24623          __vbaStrToAnsi(String:"Software...", LPSTR *:0073F470) returns DWORD:514078
24633          RegOpenKeyExA(HKEY:80000001, LPSTR:00514078, DWORD:00000000,
.....
24686          __vbaStrToAnsi(String:"LicUser", LPSTR *:0073F470) returns DWORD:514C90
24696          RegQueryValueExA(HKEY:C728D988, LPSTR:00514C90, DWORD:00000000,
.....
25146          __vbaStrCopy(String:"Register...", LPBSTR:0073F3A4) returns DWORD:512F10
25151          __vbaStrCat(String:"5", String:"Software...") returns DWORD:610F54
25156          __vbaStrMove(String:"Software...", LPBSTR:0073F3A8) returns DWORD:610F54
25160          __vbaStrToAnsi(String:"Software...", LPSTR *:0073EF78) returns DWORD:514078
25170          RegOpenKeyExA(HKEY:80000002, LPSTR:00514078, DWORD:00000000,
Ehhe, e' chiaro cosa sta facendo.. legge i dati della licenza dal registro: esaminando i
parametri vediamo subito dove si trovano le informazioni..dall'SDK:
80000001 = HKEY_LOCAL_USER;
80000002 = HKEY_LOCAL_MACHINE
una rapida occhiata al regisytro ci conferma il tutto.. Ora sappiamo anche dove e' contenuta la
protezione nella dll SPIINIT.DLL.. andiamo sempre meglio ;)
continuando a scorrere i log incontriamo questo blocco che manipola il trial serial :
25333     Len(String:"191041-2...") returns LONG:31
25334     Len(String:"191041-2...") returns LONG:31
25335     Long (31) --> Integer (31)
25336     Mid$(String:"191041-2...", long:1, VARIANT:Integer:1)
.....     ... stesso pattern x 6 ...
25488     Mid$(String:"191041-2...", long:7, VARIANT:Integer:1)
25513     Mid$(String:"191041-2...", long:1, VARIANT:Integer:6)
25521     Mid$(String:"191041-2...", long:8, VARIANT:Missing)
25533     Left$(String:"191041", long:1)
25571     Len(String:"191041") returns LONG:6
25572     IsNumeric(VARIANT:ByRef String:"191041") returns Boolean:True
25580     String ("191041") --> Long (191041)
26386     Len(String:"00698") returns LONG:5
26387     Len(String:"00698") returns LONG:5
26388     Long (5) --> Integer (5)
26389     Mid$(String:"00698", long:1, VARIANT:Integer:1)
.....     ... stesso pattern x 3 ...
26488     Mid$(String:"00698", long:5, VARIANT:Integer:1)
26514     Mid$(String:"00698", long:1, VARIANT:Integer:5)
26521     Mid$(String:"00698", long:7, VARIANT:Missing)
26533     Left$(String:"00698", long:1)
26575     Len(String:"00698") returns LONG:5
26576     IsNumeric(VARIANT:ByRef String:"00698") returns Boolean:True
26584     String ("00698") --> Long (698)
Anche qui possimo fare alcune interessanti considerazioni: innanzitutto possiamo dire che si
tratta di un loop che:
1) chiama una proc contenente un'altro loop che decodifica i blocchi del serial uno alla volta
2) verifica che il blocco decodificato sia un numero e quindi lo converte come LONG
perche' un loop direte voi.. semplice dico io :) : SMTCHK riporta questi caller:
25333 SPIINIT!1C8FB |
25336 SPIINIT!1C92B | find_block loop
25533 SPIINIT!1CA36 |
25571 SPIINIT!ED5F     | decode_loop
25572 SPIINIT!ED82     |
26386 SPIINIT!1C8FB |
26389 SPIINIT!1C92B |
26533 SPIINIT!1CA36 |
26575 SPIINIT!ED5F     |
26576 SPIINIT!ED82     |
ed quindi facile riconoscere i pattern e dedurre la presenza di un loop.
Come vedete SMTCHK ci consente una visione molto precisa di quello che sta accandendo durante
l'esecuzione, basta solo saper decifrare le informazioni contenute nei log.
Cmq siccome siamo dei tipi precisi andiamo a verificare il dissa
1000
ssemblato in IDA e.. sorpresa:
.text:3404ED38                 call    j___vbaStrCopy
.... more ....
.text:3404ED45                 call    sub_0_3405C843 ; <-- decode loop
.... more ....
.text:3404ED5F                 call    j___vbaLenBstr
.text:3404ED64                 test    eax, eax
.text:3404ED66                 jz      loc_0_34050559
.... more ....
.text:3404ED81                 push    eax
.text:3404ED82                 call    j_rtcIsNumeric
.text:3404ED87                 test    ax, ax
.text:3404ED8A                 jz      loc_0_34050559
.text:3404ED90                 push    dword ptr [ebp-5Ch]
.text:3404ED93                 call    j___vbaI4Str
.text:3404ED98                 movsx   ecx, bx
.text:3404ED9B                 mov     edx, [ebp-34h]
le nostre supposizioni erano giuste (no dico dubitavate di me?!?.. vi stronco in caso
affermativo!:))
Appena sotto questo snippet ne troviamo un'altro interessante:
26592  __vbaVarDup(VARIANT:String:"000000", VARIANT:Empty) returns DWORD:73F374
26597   Format(VARIANT:ByRef Long:191041, VARIANT:String:"000000", Integer:1, Integer:1)
.....  ... stesso pattern x 3 ...
26673  __vbaVarDup(VARIANT:String:"00000", VARIANT:Empty) returns
26679  Format(VARIANT:ByRef Long:698, VARIANT:String:"00000", Integer:1, Integer:1)
seguito da:
26694  __vbaVarCat(VARIANT:String:"191041", VARIANT:String:"-") returns DWORD:73F354
26700  __vbaVarCat(VARIANT:String:"191041-", VARIANT:String:"28251")
26728  __vbaVarCat(VARIANT:String:"191041-2...", VARIANT:String:"-") returns DWORD:73F294
26734  __vbaVarCat(VARIANT:String:"191041-2...", VARIANT:String:"00698") returns DWORD:73F264
autoesplicativo.. con i valori decodificati il programma si costruisce una versione correttamente
formattata del serial (questo puo' sembrare assurdo ma se guardate i log anche per la dialog di
licenza, vi accorgerete che il codice del check e' come e' logico aspettarsi lo stesso.. quindi
i programmatori hanno codificato la procedura a prescindere dalla fonte dei dati.. registro o
editboxs. Benissimo ora sappiamo anche la struttura del serial NNNNNN-NNNNN-NNNNNN-NNNNN-NNNNN
Continuiamo xche' ora viene il bello pero':
26826          Format(VARIANT:ByRef Long:191041, VARIANT:String:"000000",
.....          ... stesso pattern x 3 ...
26907          Format(VARIANT:ByRef Long:698, VARIANT:String:"00000", Integer:1
.....
26922          __vbaVarCat(VARIANT:String:"191041", VARIANT:String:"28251") returns DWORD:73F334
.....          ... stesso pattern x 2 ...
26938          __vbaVarCat(VARIANT:String:"19104128...", VARIANT:String:"00698")
un'altra volta la formattazione ma questa volta senza "-" ?!?!... gia' ma guardate ora cosa fa
il programma:
27008  Mid(VARIANT:ByRef String:"19104128...", long:6, VARIANT:Integer:2)
27013  Mid(VARIANT:ByRef String:"19104128...", long:15, VARIANT:Integer:2)
27018  Mid(VARIANT:ByRef String:"19104128...", long:1, VARIANT:Integer:2)
27023  Mid(VARIANT:ByRef String:"19104128...", long:10, VARIANT:Integer:5)
27028  Mid(VARIANT:ByRef String:"19104128...", long:17, VARIANT:Integer:2)
27033  Mid(VARIANT:ByRef String:"19104128...", long:21, VARIANT:Integer:3)
27038  Mid(VARIANT:ByRef String:"19104128...", long:8, VARIANT:Integer:2)
27044  Mid(VARIANT:ByRef String:"19104128...", long:24, VARIANT:Integer:2)
27050  Mid(VARIANT:ByRef String:"19104128...", long:3, VARIANT:Integer:3)
27056  Mid(VARIANT:ByRef String:"19104128...", long:26, VARIANT:Integer:2)
27062  Mid(VARIANT:ByRef String:"19104128...", long:19, VARIANT:Integer:2)
.....
27074  __vbaVarCat(VARIANT:String:"1210", VARIANT:String:"19") returns DWORD:73F304
.....
27119  __vbaVarCat(VARIANT:String:"12101951...", VARIANT:String:"34") returns DWORD:73F184
eheh un bel giochetto di prestigio... questo blocco lo potremmo chiarare BuildInternalSerial :
il programma rimappa dei sottoblocchi del serial e ne costruisce una versione nuova che e' poi
quella che viene usata x leggere le informazioni della licenza a loro volta contenuti in
sottoblocchi di
1000
questo InternalSerial, in pratica abbiamo questa tabella di corrispondenza fra
le posizioni:
internal 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
original 06 07 15 16 01 02 10 11 12 13 14 17 18 21 22 23 08 09 24 25 03 04 05 26 27 19 20
Ora abbiamo piu' o meno le idee chiare sul tipo di protezione che hanno addottato i programmatori
si tratta di vedere in dettaglio i singoli blocchi.
Siccome 'sto benedetto tutorial e' gia' bello lungo.. ne analizzeremo solo alcuni, in particolare
il controllo sul checksum e quello sulla expiration date... per gli altri vi daro' solo una breve
spiegazione del procedimento usato.
--==[ IL CHECKSUM ]==----------------------------------------------------------------------------
Dopo aver scoperto i giochi di prestigio sul serial, vediamo in dettaglio la routine del checksum
e soprattutto come da SMTCHK possiamo averne una chiara visione senza ricorre a IDA o sIce:
di seguito abbiamo:
27223  Mid(VARIANT:ByRef String:"12101951...", long:20, VARIANT:Integer:2)
27228  String ("61") --> Integer (61)
questo e' una sorta di magic che come vedremo viene usato x calcolare i parametri finali della
licenza.
27248  Mid(VARIANT:ByRef String:"12101951...", long:25, VARIANT:Integer:2)
27253  String ("83") --> Integer (83)
.....
27273  Mid(VARIANT:ByRef String:"12101951...", long:27, VARIANT:Integer:1)
27278  String ("4") --> Integer (4)
come vediamo vengono letti separatamente i blocchi 25..26 e 27 che vedremo sono i valori di
raffronto per il checksum.. poi segue un bel papirozzo:
27296          Len(String:"12101951...") returns LONG:27
27297          Long (24) --> Integer (24)
27298          Mid(VARIANT:ByRef String:"12101951...", long:1, VARIANT:Integer:1)
27303          __vbaI2ErrVar(VARIANT:String:"1") returns DWORD:610001
.....          ... stesso pattern x 22 ...
27821          Mid(VARIANT:ByRef String:"12101951...", long:24, VARIANT:Integer:1)
27826          __vbaI2ErrVar(VARIANT:String:"9") returns DWORD:610009
spiegazione: chiaramente un loop che legge i caratteri uno alla volta da 1 a 24 (=len - 3) e li
trasforma in int: non ci vuole molta fantasia a capire che sotto c'e' una qualche somma o cmq
operazione aritmetica che calcola il checksum.. purtroppo SMTCHK non puo' aiutarci oltre ma
almeno ci ha detto dove cercare.. ora IDA fara' il resto ;)
.text:3404F802                 push    19h
.text:3404F804                 lea     eax, [ebp-2A0h]
.text:3404F80A                 push    eax
.text:3404F80B                 lea     eax, [ebp-0B0h]
.text:3404F811                 push    eax
.text:3404F812                 call    j_rtcMidCharVar
.text:3404F817                 lea     eax, [ebp-0B0h]
.text:3404F81D                 push    eax
.text:3404F81E                 call    j___vbaI2Var
.text:3404F823                 mov     [ebp-20h], eax ; <-- check1 = 83
.... more ....
.text:3404F863                 push    1Bh
.... more ....
.text:3404F873                 call    j_rtcMidCharVar
.text:3404F878                 lea     eax, [ebp-0B0h]
.text:3404F87E                 push    eax
.text:3404F87F                 call    j___vbaI2Var
.text:3404F823                 mov     [ebp-24h], eax ; <-- check2 = 4
.... more ....
.text:3404F8A1                 call    j___vbaLenBstr
.text:3404F8A6                 mov     ecx, eax
.text:3404F8A8                 sub     ecx, 3
.text:3404F8AB                 jo      loc_0_34050978
.text:3404F8B1                 call    j___vbaI2I4
.text:3404F8B6                 mov     [ebp-40Ch], eax
.text:3404F8BC                 mov     dword ptr [ebp-44h], 1 ; |
.text:3404F8C3                 mov     eax, [ebp-44h]         ; | for count=1 -> len(ISer)-3
.text:3404F8C6                 cmp     ax, [ebp-40Ch]         ; |
.text:3404F8CD                 jg      loc_0_3404F96A         ; |
.... more ....
.text:3404F904                 lea     eax, [ebp-0B0h]
.text:3404F90A                 push    eax
.text:3404F90B                 call    j_rtcMidCharVar
.
1000
text:3404F910                 lea     eax, [ebp-0B0h]
.text:3404F916                 push    eax
.text:3404F917                 call    j___vbaI2ErrVar
.text:3404F91C                 imul    ax, [ebp-44h]
.text:3404F921                 jo      loc_0_34050978
.text:3404F927                 movsx   eax, ax
.text:3404F92A                 add     eax, [ebp-64h]
.... more ....
.text:3404F96A                 mov     eax, [ebp-64h]
.text:3404F96D                 cdq
.text:3404F96E                 push    0Bh
.text:3404F970                 pop     ecx
.text:3404F971                 idiv    ecx
.text:3404F973                 cdq
.text:3404F974                 push    64h
.text:3404F976                 pop     ecx
.text:3404F977                 idiv    ecx
.text:3404F979                 movsx   eax, word ptr [ebp-20h]
.text:3404F97D                 cmp     edx, eax               ; check1
.text:3404F97F                 jnz     loc_0_3405055C
.text:3404F985                 mov     eax, [ebp-64h]
.text:3404F988                 cdq
.text:3404F989                 push    0Ah
.text:3404F98B                 pop     ecx
.text:3404F98C                 idiv    ecx
.text:3404F98E                 movsx   eax, word ptr [ebp-24h]
.text:3404F992                 cmp     edx, eax               ; check2
.text:3404F994                 jnz     loc_0_3405055C
ora sappiamo che :
CHECKSUM = sum[MID(x,1)*x] con x=1 to 24
VAL(MID(25,2) = (CHECKSUM IDIV 0B) MOD 64
VAL(MID(27,1) = CHECKSUM MOD 0A
--==[ L'EXPIRATION DATE ]==----------------------------------------------------------------------
Nel check che controlla se la licenza e' scaduta incontriamo un'altro giochetto di prestigio che
ovviamente SMTCHK ci svela senza troppa fatica: riassumendo il programma legge dal registro il
valore nella entry "Register5" e ne decodifica il contenuto..in pratica contiene i valori in HEX,
tradotti in ASCII, che compongono la stringa con la data di installazione.. per e 04-FEB-99 e
la confronta con la data di scadenza codificata nel serial.  Ovviamente condisce la cosa con un
bello scramble della stringa basato su una key hardcoded.
Ma guardiamo l'output di SMTCHK:
intanto vediamo che la key di "encryption" compare assolutamente in chiaro in SMTCHK :)
28264  Len(String:":u89h4)h...") returns LONG:14
28505  Mid(VARIANT:ByRef String:"000A0041...", long:1, VARIANT:Integer:4)
28510  __vbaVarCat(VARIANT:String:"H", VARIANT:String:"000A") returns DWORD:73EF30
28515  __vbaStrVarVal(VARIANT:String:"&H000A") returns DWORD:5279F4
28516  Val(String:"H000A") returns double:10 (displayed as single-precision floating point)
28528  Double (10) --> Long (10)
.....  ... stesso pattern x tante volte ;) ...
Mid(VARIANT:ByRef String:"000A0041...", long:33, VARIANT:Integer:4)
28774  __vbaVarCat(VARIANT:String:"H", VARIANT:String:"0058") returns DWORD:73EF30
28779  __vbaStrVarVal(VARIANT:String:"&H0058") returns DWORD:527974
28780  Val(String:"H0058") returns double:88 (displayed as single-precision floating point)
28792  Double (88) --> Long (88)
questo in pratica decodifica da ASCII a HEX passando per la notazione coll'"&H" del VB..purtroppo
in SMTCHK non e' visibile la parte dell'encryption, che si basa in sostanza su uno xor, ma non
e' molto importante perche' lo scopo di capire come si svolge il check lo raggiungiamo lo stesso:
28807  Chr(Integer:57)
28813  __vbaVarCat(VARIANT:String:"9", VARIANT:String:"") returns
.....  ... more ...
28983  Chr(Integer:48)
28989  __vbaVarCat(VARIANT:String:"0", VARIANT:String:"4-Feb-99") returns DWORD:73EF40
seguito da un'inequivocabile sequenza:
29045  IsDate(VARIANT:ByRef String:"04-Feb-9...") returns Boolean:True
29242  String ("04-Feb-99") --> Date (2/4/99)
29343  Double (36225) --> Date (3/6/99)
resta da capire quel 36225 da dove viene.. cioe' lo sappiamo dal serial decode:), ma da dove:
osserviamo questo blocco che segue poco dopo il controllo del checksum:
27897  Mid(VARIANT:ByRef String:"12101951...", long:1, VARIANT:Integer:1)
27902  Mid(VARIAN
ce7
T:ByRef String:"12101951...", long:6, VARIANT:Integer:6)
27907  __vbaVarCat(VARIANT:String:"1", VARIANT:String:"951222") returns DWORD:73F334
27912  __vbaI4ErrVar(VARIANT:String:"1951222") returns DWORD:1DC5F6
e appena sotto
28003  Mid(VARIANT:ByRef String:"12101951...", long:15, VARIANT:Integer:5)
28008  __vbaVarSub(VARIANT:String:"00820", VARIANT:Integer:257) returns DWORD:73F354
28028  __vbaVarSub(VARIANT:Double:563, VARIANT:Integer:531) returns DWORD:73F344
28029  Double (32) --> Long (32)
28030  SysFreeString(BSTR:005279F4)
28034  Long (32) --> Integer (32)
28035  Double (2000.03) --> Integer (2000)
28036  DateSerial(Integer:2000, Integer:1, Integer:32)
28039  __vbaDateVar(VARIANT:Date:2/1/00) returns DWORD:6106DC
Riassumendo, come ci mostra SMTCHK, il pragramma usa i blocci 1 + 6..11 e 15..19 per qualche
strano  calcolo.. passando in ida risulta chiaro che in sostanza sono rispettivamente la data
iniziale e la expiration date: in pratica tra 28003 e 28028 il programma esegue:
license date    = VAL(MID(1,1)+MID(6,6))-120746
expiration date = VAL(MID(15,5))-((Magic^2 IDIV 7)-257)).. e e poi non ditemi che SMTCHK non e'
utile :)
Cmq il check avviene con 36255 perche' io ho alterato solo i blocchi 15..19 ma ovviamente i
checksum non e' valido e quindi il programma passa in versione trail.. quindi i canonici 60gg
dopo la data di installazione, poco male visto che sappiamo come correggere il controllo sul
checksum.
--==[ NOTE FINALI ]==----------------------------------------------------------------------------
Il resto dei parametri del serial sono calcolati nello stesso modo ad exp. le user license:
27975  Mid(VARIANT:ByRef String:"12101951...", long:12, VARIANT:Integer:3)
27980  __vbaVarSub(VARIANT:String:"036", VARIANT:Integer:5) returns
che corrisponde in IDA a : VAL(MID(12,3) IDIV 2) - 5
e cosi':  server lic= VAL(MID(4,2)-(Magic MOD 0A), lic type = VAL(MID(22,3)-(Magic MOD 1f),
ecc...
Ovviamente il significato di questi valori lo potete scoprire solo da sIce.. ma sicuramente
SmartCheck vi ha ridotto di moltissimo il lavoro di stepping e di comprensione del codice.
Ora potete ricostruire l'InternalSerial e da questo applicando il mapping alla rovescia il serial
finale senza aver passato ore in sICE a pigiare il tasto F8, con buona pace dei vostri occhi :)
Come vedete SMTCHK e' un tool davvero eccezzionale: ci ha permesso di districarci all'interno di
una protezione che, se avessimo usato solo i tool "normali", ci avrebbe portato via molto piu'
tempo e fatica; inoltre ci ha consententito non solo di capire come VB lavora sottobanco (ottima
cosa per imparare quali BreakPoint sono piu' indicati a seconda del tipo di protezione) ma ci ha
fornito una visione dettagliata dell'esecuzione del programma dandoci la possibilta' di fare una
sorta di pre-reverse che alla fine non era molto distante dal codice effettivamente eseguito.
Chiudo 'sto pacco di tut (azz e' proprio lungo :P ) con la speranza di aver chiarito a molti
newbie le idee sull'uso di SmartCheck e soprattutto su come usarlo in modo proficuo.. se non ci
sono riuscito pazienza :) .. anche perche' non tornero' sull'argomento per parecchio tempo dopo
sto romanzo :)))
byz Kill3xx

Top: Sicurezza su Internet - Back
Previous: Esempio di DLL reversing - Riconfigurare il comportamento di explorer modificando alcune funzioni della libreria SHELL32.DLL - Up: Sicurezza su Internet - Next: Reversing Delphi - Tutorial interattivo

This page last updated: Friday 08 November 2002 at 3:59pm

Please send any comments on this page to Astalalista.

Labelled with ICRA

This page is powered by Copyright Button(TM).
Click here to read how this page is protected by copyright laws.

Use this button to
NOT browser button


begin banner Exchange zone

Member of BannerPower Rotation System

Home Main Portal Translate Tell A Friend Create Page Create Site Search Site Search WWW E-mail Login Insert A Link Free for All Free Dating Forum disabled-come soon Guestbook Links Vote For Us disabled-come soon Team Building Can Improve Businesses. | Beaded Necklace | Vertical Blinds | Nyc Hair Transplant Center | Blinds