A:link { TEXT-DECORATION: underline } A:visited { TEXT-DECORATION: underline } A:active { COLOR: red; TEXT-DECORATION: underline } A:hover { COLOR: #8080ff; TEXT-DECORATION: underline }

ZaiRoN's crackme Serial Fishing

Data

by Spider

 

01/03/2001

UIC's Home Page

Published by Quequero

 Qual è l'unico libro che comincia nel mezzo?

Qualche mio eventuale commento sul tutorial :)))

La Divina Commedia!...

....

Home page: http://bigspider.cjb.net
E-mail: spider_xx87@hotmail.com
Server IRC: irc.azzurra.it
Canali:  #crack-it  #asm
Nickname: ^Spider^

....

Difficoltà

(X)NewBies ( )Intermedio ( )Avanzato ( )Master

 


Un bel crackme nome/serial by ZaiRoN. Scaricate qui il sorgente del KeyGenerator.


ZaiRoN's crackme Serial Fishing
Written by Spider

Introduzione

Il programma vittima è un crackme di ZaiRon. Vediamo le rulez:

 - NO patching
 - NO brute forcing
 - keygen it!

Bene... vediamo cosa possiamo fare! :-)

Tools usati

Tools usati:

- SoftICE qualunque versione
- un disassembler: spesso il codice è più chiaro da IDA che da softice :-)

URL o FTP del programma


http://quequero.cjb.net sezione crackme. E' il crackme numero 42.

Essay


Bene, iniziamo.  Mettiamo un nome (Spider) ed un seriale a caso (123123123) e da sice mettiamo un breakpoint su GetWindowTextA e GetDlgItemTextA. Premiamo OK e SoftICE poppa sullo schermo. Siamo alla riga successiva alla chiamata a GetDlgItemTextA che si occupa di prelevare il nome immesso; poche righe più tardi troviamo la seconda chiamata che si occupa invece di prelevare il serial inserito.
All'offset 004010AA troviamo un "call 0040115E". Vediamo cosa c'è a quell'indirizzo:

:0040115E xor ecx, ecx ;azzera ecx (usato come contatore)
:00401160 xor eax, eax ;azzera eax

* Jump at Address:
|:00401178(U)
|
:00401162 cmp byte ptr [ecx+esi], 30h ;se l'ASCII del char è minore di 30h...
:00401166 jb 0040119E                 ;...serial errato
:00401168 cmp byte ptr [ecx+esi], 3Ah ;se non è minore di 3Ah...
:0040116C jnb 0040117A                ;...salta a 0040117A
:0040116E sub byte ptr [ecx+esi], 30h ;sottrae 30h
:00401172 inc ecx                     ;incrementa ecx per passare al prossimo char

* Jump at Address:
|:0040119C(U)
|
:00401173 cmp ecx, 00000009           ;se abbiamo già controllato 9 caratteri
:00401176 je 004011A3                 ;salta al "ret"
:00401178 jmp 00401162                ;altrimenti ripete il ciclo

* Jump at Address:
|:0040116C(C)
|
:0040117A cmp ecx, 00000002           ;se il carattere non numerico è il terzo
:0040117D je 0040118B                 ;salta a 40118B
:0040117F cmp ecx, 00000005           ;se è il sesto
:00401182 je 0040118B                 ;salta a 40118B
:00401184 cmp ecx, 00000008           ;se è il nono
:00401187 je 0040118B                 ;salta a 40118B
:00401189 jmp 0040119E                ;altrimenti... serial errato

* Jump at Addresses:
|:0040117D(C), :00401182(C), :00401187(C)
|
:0040118B cmp byte ptr [ecx+esi], 41h ;se il carattere è minore di 'A' (41h)...
:0040118F jb 0040119E                 ;...serial errato
:00401191 cmp byte ptr [ecx+esi], 45h ;se il carattere è maggiore di 'E' (45h)...
:00401195 ja 0040119E                 ;...serial errato
:00401197 sub byte ptr [ecx+esi], 37h ;sottrae 37h al carattere
:0040119B inc ecx                     ;incrementa ecx
:0040119C jmp 00401173                ;salta a 00401173

* Jump at Addresses:
|:00401166(C), :00401189(U), :0040118F(C), :00401195(C)
|
:0040119E mov eax, 00000001           ;eax viene messo ad 1 quando il serial è errato

* Jump at Address:
|:00401176(C)
|
:004011A3 ret

Questa  procedura lavora solo sui primi 9 caratteri del serial. Sono considerati caratteri validi soltanto i caratteri numerici (da '0' a '9', ossia da 30h a 39h), eccetto per il terzo, il sesto e il nono carattere che oltre a valori numerici possono avere i valori 'A' (41h), 'B' (42h), 'C' (43h), 'D' (44h), 'E' (45h). Nel caso di valori numerici viene sottratto 30h, quindi il valore finale sarà un numero compreso tra 0 e 9; nel caso di valori letterali viene invece sottratto 37h, e il valore finale sarà un numero compreso tra 10 e 14 (in pratica se ne considera il valore esadecimale. Ad esempio 0Ah esadecimale equivale a 10 decimale). Chiaro?

Bene, immettiamo un nuovo seriale, stando attenti a rispettare le condizioni imposte dalla procedura appena vista. Io ho usato il serial '123123123'. Dopo la call di poco fa troviamo un "test eax, eax" e un "jne" che, nel caso eax fosse stato messo ad 1, considera il seriale errato senza bisogno di ulteriori controlli.

Subito dopo avremo davanti una procedura che esegue una serie di calcoli sui caratteri del serial (opportunamente modificato dalla precedente procedura)  e confronta il risultato finale con i caratteri del nome inserito. Vengono presi 3 caratteri del serial per volta e il ciclo è ripetuto tre volte. In ogni ripetizione del ciclo il risultato dei calcoli viene confrontato con un carattere del nome inserito (il primo carattere per il primo ciclo, il secondo per il secondo ciclo e il terzo carattere per il terzo ciclo [ma và...]). Per semplificare i calcoli, assegniamo delle lettere ai caratteri del serial coinvolti nel calcolo: a per il primo carattere, b per il secondo e c per il terzo.

:004010B7 xor edi, edi         ;azzera edi   \___ edi ed edx vengono usati entrambi 
:004010B9 mov edx, 00000000    ;azzera edx   /    come contatori, ma per scopi diversi
:004010BE push edx

* Jump at Address:
|:0040110D(U)
|
:004010BF movzx ebx, byte ptr [edi+0040301B]    ;ebx = b
:004010C6 movzx ecx, byte ptr [edi+0040301A]    ;ecx = a

:004010CD mov esi, ecx                          ;esi = a
:004010CF imul eax, ebx, 3                      ;eax = 3b
:004010D2 add esi, eax                          ;esi = a+3b
:004010D4 mov eax, ebx                          ;eax = b
:004010D6 mul ebx                               ;eax = b2
:004010D8 add esi, eax                          ;esi = a+3b+b2
:004010DA mov eax, ecx                          ;eax = a
:004010DC mul ecx                               ;eax = a2
:004010DE add esi, eax                          ;esi = a+3b+a2+b2
:004010E0 imul ebx, ecx                         ;ebx = ab
:004010E3 imul ebx, 2                           ;ebx = 2ab
:004010E6 add esi, ebx                          ;esi = a+3b+a2+b2+2ab
:004010E8 mov eax, esi                          ;eax = a+3b+a2+b2+2ab
:004010EA mov edx, 2
:004010EF div dl                                ;eax = (a+3b+a2+b2+2ab)/2
:004010F1 imul eax, 0Eh                         ;eax = (a+3b+a2+b2+2ab)*7
:004010F4 add al, byte ptr [edi+0040301C]       ;eax = (a+3b+a2+b2+2ab)*7+c
:004010FA pop edx
:004010FB cmp al, byte ptr [edx+00403007]       ;confronta con un char del nome
:00401101 jnz short SerialErrato                ;se sono diversi... serial errato :)
:00401103 add edi, 3                            ;passa ai 3 char successivi del serial
:00401106 cmp edi, 9                            ;se il ciclo è stato eseguito 3 volte...
:00401109 jz short 0040110F                     ;...salta a 0040110F
:0040110B inc edx                               ;incrementa edx
:0040110C push edx
:0040110D jmp short 004010BF

Bene... o forse male: ci troviamo di fronte ad un'equazione a 3 incognite. Dobbiamo infatti trovare 3 valori a, b e c tali che (a+3b+a2+b2+2ab)*7+c = 83 (83 è il valore decimale di 'S'), e poi lo stesso con 112 ('p') e con 105 ('i'). Le equazioni a 3 incognite hanno infinite soluzioni, ma... un momento! a, b e c hanno un range molto limitato: a e b hanno valori compresi tra 0 e 9, mentre c ha un valore compreso tra 0 e 13, come abbiamo visto prima. Pertanto le combinazioni sono poche, potremmo anche andare a tentativi per trovare i valori delle incognite, ma... meglio far fare tutto al nostro caro PC... Codiamo un piccolo bruteforcer per trovare la giusta combinazione. Ecco il source (scritto in VC++):



#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{

    char name[] = "Spi";   //ci bastano i primi 3 caratteri
    char serial[10];
    unsigned int a;
    unsigned int b;
    unsigned int c;
    unsigned int count;

    for (count = 0;count < 9;count = count + 3) {
        for (a = 0; a < 10; a++) {
            for (b = 0; b < 10; b++) {
                for (c = 0; c < 14; c++) {
                    if (((a+3*b+b*b+a*a+a*b*2)*7+c) == name[count/3]) {
                        serial[count + 0] = a + 48;
                        serial[count + 1] = b + 48;
                        if (c < 10) {
                            serial[count + 2] = c + 48;
                        }
                        else {
                            serial[count + 2] = c + 48 + 7;             
                        }
                    }
                }
            }
        }
    }
    for (count = 0; count < 10; count++) {
        serial[17-count] = serial[count];
    }
    serial[9] = 0;

    MessageBox(NULL,serial,"I've found it!",MB_ICONINFORMATION);

    return 0;
}


Bene. Compiliamo, eseguiamo... DING! Ecco la nostra bella MessageBox che ci comunica di aver trovato il serial! Nel mio caso è 02D120217. Abbiamo quindi trovato i primi 9 caratteri del nostro seriale :-)

Osserviamo adesso il codice immediatamente successivo alla procedura di poco fa:

*Jump at Address:
|:00401109(C)
|
:0040110F push 0000001E
:00401111 push 0040301A
:00401116 push 00000BBC
:0040111B push dword ptr [00403031]

* Reference To: GetDlgItemTextA
|
:00401121 Call 004014E4
:00401126 cmp eax, 00000012
:00401129 ja 00401149

Il crackme richiama un'altra volta GetDlgItemTextA per prelevare il seriale, dato che quello prelevato prima era stato modificato per il check. Confronta poi la lunghezza del serial con 12h (18 decimale), e se è maggiore salta alla solita locazione di wrong serial. La lunghezza del seriale è quindi presumibilmente di 18 caratteri.

Dopo questo codice, ha inizio una lunghissima procedura che non riporto a causa delle dimensioni. Inizialmente non avevo capito a cosa servisse; solo steppandola e risteppandola più volte ho intuito la sua funzione. In realtà è semplicissimo: controlla che i secondi 9 caratteri (dal decimo in poi) siano esattamente l'inverso dei primi 9. Pertanto se i primi 9 caratteri erano 02D120217, il serial completo sarà 02D120217712021D20. Inseriamo il serial, premiamo ok e... "Good work!".

Spider

Note finali


Saluti a ZaiRoN che ha fatto il crackme, e poi ovviamente un saluto va a tutti gli studenti e frequentatori di UIC, Ringz3r0, Itassembly, #crack-it e #asm.

Disclaimer


I crackme vanno COMPRATI e non rubati. Dopo aver trovato il serial dovete inviare all'autore i soldi necessari (ZaiRoN, poi dividiamo, ok? :-P).

Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.


Home

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