�������������������������������������������������������������������������������������������������
�                                [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"
 * 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 database, 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'.. 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 dissassemblato 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 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
.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(VARIANT: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