Soluzione lezione tre UIC
(codice SMC)


28/09/1999

by "syscalo"

 

 

UIC's Home Page

Published by Quequero

Those who do not understand Unix are condemned to reinvent it

Bravo sys, anche stavolta hai fatto un bel lavoro ma mancano spiegazioni a funzioni importanti come le istruzioni di divisione e conversione di doubleword

UIC: the most fun university in the world.
UIC's form E-mail: x4sys@iname.com UIC's form

Difficoltà

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

 

Scaricate qui l'allegato

 

Il programma serve a dimostrare come funziona il codice automodificante. Noi dovremo riuscire a trovare il seriale corrispondente al nostro nome ed infine dobbiamo crackare il programma in modo che accetti la registrazione con qualsiasi nome e seriale.


Soluzione lezione 3 UIC
(codice SMC)
Written by syscalo

Introduzione

Questa lezione tratta il crack ed il reverse di programmi che contengono codice automodificante (per capire in cosa consiste leggere la spiegazione di Quequero)

Tools usati

Only SoftIce.

URL o FTP del programma

http://linox.tecnoprogress.it/prj/corsoterzo.html

Notizie sul programma 

Il programma richiede nome e codice seriale. Se i dati inseriti non sono corretti non viene visualizzato alcun messaggio, questo impedisce di impostare breakpoint su MessageBox o similari, ma a noi non importa :). Inoltre presenta un'altra piccola particolarità; la generazione del seriale dal nome avviene con un algoritmo per le prime quattro lettere e con un altro per le successive quattro. In totale l'algoritmo lavora al max sulle prime 8 lettere ma si possono inserire anche nomi più lunghi che genereranno codici diversi. Questo è dovuto al fatto che nell'algoritmo che lavora sulle lettere dalla quinta all'ottava c'è l'istruzione "xor al,[0040210F]" dove la locazione 0040210F contiene la lunghezza del nome inserito. Da qui si capisce chiaramente che se noi inseriamo "Quequero" otteniamo il seriale "4648;577" mentre se inseriamo "QuequeroCheCiTiraMatti" otteniamo il seriale "4648=75?". Per un'ulteriore dimostrazione che la differenza dei codici dopo l'ottava lettera dipende solo dalla lunghezza del nome (non dalle lettere) vediamo che per "Quequero?" viene generato il seriale "4648:688" che coincide con il seriale generato per "Quequero¿".

Essay

Ok iniziamo a prendere confidenza con il programma: apriamo softice (ctrl+D) ed impostiamo un breakpoint sull'acquisizione del nome e del seriale (bpx GetWindowTextA), usciamo da softice (F5) inseriamo nome e seriale nel programma e premiamo il tasto register. Eccoci in softice! Premiamo F12 per tornare dalla CALL, disabilitiamo il breakpoint con "bd 0". Ora ci troviamo davanti al codice del programma appena dopo la chiamata a GetWindowTextA. (un consiglio, procedete nel programma con F8, tranne nelle chiamate alle GetWindowTextA e GetWindowTextLenghtA (qui usate F10), o vi perderete il bello del SMC :)

La funzione chiamata durante il programma con "call 0040160C" la trovate alla fine del listato.

Per tenere sott'occhio l'acquisizione del nome e del seriale, e soprattutto il seriale corretto, procedete nel seguente modo: in softice se non è visibile la finestra dei dati premete alt+F2 (se non sapete qual'è la finestra dei dati premete più volte alt+F2 e vedrete una finestra aprirsi e chiudersi, bhe quella deve essere visibile :) poi date il comando "d 0040209B". In questo modo avrete visibile il range di memoria per leggere tutte le informazioni utili. Per avere il giusto seriale dovete leggere dalla locazione 004020C7 (ovviamente il valore sarà visibile solo dopo aver eseguito tutto l'algoritmo spiegato sotto forzando i salti come verrà indicato :); i valori che vedete a questa locazione andranno così "decodificati":

-per i primi 4 valori prendeteli e xorateli ognuno con 81 e poi con 40.

-per i successivi 4 valori prendeteli e xorateli ognuno con 82, fate due shift a sx e due rotazioni a dx (per eseguire queste operazioni convertite il valore in binario e poi per lo shift aggiungete due zeri a dx ed eliminate due valori a sx, per la rotazione cancellate due valori a dx e riscriveteli a sx; poi riconvertite in esadecimale)

es. : se dalla conversione in binario avete 10110100 con lo shift diventa 11010000 e con la rotazione diventa 00110100.

I valori così ottenuti rappresentano il codice ASCII in esadecimale dei chr del vostro seriale...ora non vi resta che trovare i chr corrispondenti :)

Ovviamente non è detto che avrete per forza 8 valori, possono essere meno, dipende dal nome che avete inserito.

Ci siamo, partiamo ad analizzare il programma:

push 14 |
push 0040209B |locazione dove viene salvato il nome
push dword ptr [0040204C] |
call GetWindowTextA |chiamata alla funzione per acquisire il nome
mov edx,eax salva in edx il # di chr letti (ritornati in eax dall GetWindowTextA)
push 28 |
push 004020AF |locazione dove viene salvato il nome
push dword ptr [0040204C] |
call GetWindowTextA |chiamata alla funzione per acquisire il nome (con questa chiamata rilegge il nome per la seconda volta ma lo salva in una locazione diversa)
mov ebx,[004020AF] carica in ebx il 1° chr del nome
mov esi,004015C1 |questa istruzione e la prossima servono a predisporre i dati per l'esecuzione di movsb (A)
mov edi,00401265 |
mov ecx,1 ecx deve contenere il # di volte che deve essere eseguita la repz
repz movsb Con questa istruzione si va a modificare il codice del programma nel seguente modo: movsb copia il byte indirizzato da ds:esi nel byte indirizzato da es:edi e il tutto viene ripetuto (tramite la repz) fino a quando ecx è uguale a zero.
nop
nop
push 14 |
push 0040209B |locazione dove viene salvato il nome inserito
push dword ptr [0040204C] |
call GetWindowTextA |chiamata alla funzione per acquisire il nome (rilegge il nome per la terza volta e lo sovrascrive a quello della prima lettura)
push dword ptr [0040204C] |
call GetWindowTextLenghtA |chiamata alla funzione per determinare la lunghezza del nome
inc eax in eax c'è il valore tornato dalla GetWindowTextLenghtA e qui viene incrementato di 1 (B)
00401266: mov esi,0040157C |per le seguenti quattro istruzioni vedere (A)
mov edi,00401277 |
mov ecx,38 |questa volta la repz viene ripetuta 56 volte (38 esedecimale = 56 decimale)
repz movsb |

Arrivati a questo punto dobbiamo fermarci un attimo. Qui (in softice) vediamo delle istruzioni che sono le stesse che vedremmo disassemblando il programma. Ora, supponendo di essere arrivati all'istruzione "repz movsb" procedendo con F8 vedremo magicamente cambiare sotto i nostri occhi le istruzioni successive! Le istruzioni riportate di seguito sono quelle già modificate (le altre non ci interessano per niente).

00401277: mov [0040210F],eax salva in 0040210F il # di chr del nome+1 (vedere (B))
xor ecx,ecx azzera ecx
0040127E: mov al,[ecx+0040209B] carica in al i chr del nome dal primo (la locazione da cui prende il chr corrisponde alla terza acquisizione fatta)
   mov edi,0B |da qui inizia una serie di istruzioni per la crittazione dei chr del nome...
cdq |
   idiv edi |
add eax,edx |
shl eax,1 |
mov dl,al |
call 0040160C |qui viene chiamata una procedura che fa in modo di modificare il valore per mantenerlo in un certo range di valori (questa procedura lavora sul registro dl)
xor eax,eax |
mov al,dl |
xor al,81 |
xor al,40 |
mov [ecx+004020C7],al |...e qui terminano, con il salvataggio del valore crittato a partire dalla locazione 004020C7
inc ecx incrementa conteggio chr
cmp ecx,4 |
jnz 0040217E |esegue le istruzioni precedenti sui primi 4 chr poi prosegue
nop
nop
nop
nop
004012AF: mov esi,00401516 |per le seguenti quattro istruzioni vedere (A)
mov edi,004012C0 |
mov ecx,5C |questa volta la repz viene ripetuta 92 volte (5C esedecimale = 92 decimale)
repz movsb |

A questo punto ci ritroviamo nella stessa situazione descritta sopra. Valgono le stesse considerazioni. Ricordo che il codice seguente è quello già modificato.

004012C0: mov ecx,4 carica 4 in ecx per far partire il seguente algoritmo dal 5° chr del nome
004012C5: xor eax,eax azzera eax
mov al,[ecx+0040209B] carica in al i chr del nome dal 5°
cmp ecx,[0040210F] confronta ecx con il # di chr del nome+1 (vedere (B))
jz 0040131C esegue le istruzioni seguenti fino alla fine del nome (dopo salta a 0040131C)
mov edi,8 |da qui inizia una serie di istruzioni per la crittazione dei chr del nome...
cdq |
idiv edi |
add al,dl |
shl al,3 |
ror al,1 |
add al,6 |
shl al,1 |
xor al,[0040210F] |Questa è l'istruzione che xora il chr con la lunghezza del nome (è quella che causa la differenza di seriale tra due nomi di lunghezza diversa e maggiore di 8)
mov edi,3 |
cdq |
idiv edi |
add al,dl |
mov dl,al |
call 0040160C |qui viene chiamata una procedura che fa in modo di modificare il valore per mantenerlo in un certo range di valori (questa procedura lavora sul registro dl)
mov al,dl |
xor al,82 |
shl al,2 |
ror al,2 |
mov [ecx+004020C7],al |...e qui terminano, con il salvataggio del valore a partire dalla locazione 004020CA (004020C7+4)
xor eax,eax azzera eax
inc ecx incrementa il conteggio dei chr
cmp ecx,8 |
jnz 004012C5 |esegue le istruzioni precedenti per max 8 chr (i successivi non vengono considerati)
nop
nop
nop
nop
nop
0040131C: mov esi,004014E5 |per le seguenti quattro istruzioni vedere (A)
mov edi,0040133F |
mov ecx,22 |questa volta la repz viene ripetuta 34 volte (22 esedecimale = 34 decimale)
repz movsb |

Anche qui stessa cosa di sopra (e dai che questa è la penultima volta :)

0040132D: push 18 |con le seguenti tre istruzioni predispone la lettura del seriale
push 004020AF |confrontando gli indirizzi si vede che sovrascrive il seriale alla seconda acquisizione del nome
push dword ptr [00402050] |
call GetWindowTextA |legge il seriale
xor ecx,ecx azzera ecx
00401341: xor eax,eax azzera eax
mov al,[ecx+004020AF] carica in al i chr del seriale dal primo
xor al,81 |queste due istruzioni crittano il chr del seriale
xor al,40 |
mov bl,[ecx+004020C7] carica in bl dal primo i chr del nome crittati
cmp al,bl confronta il chr crittato del nome con il chr crittato del seriale
jnz 00401170 se sono diversi salta a 00401170 ( SERIALE SBAGLIATO) Questo jnz va forzato a non saltare; per fare questo date il comando "r fl z" ovviamente prima di eseguire l'istruzione e solo se softice segnala che è previsto il salto, magari avete indovinato il valore :)
inc ecx incrementa conteggio chr
cmp ecx,4 |
jnz 00401341 |esegue le istruzioni precedenti per i primi 4 chr
nop
nop
00401363: mov esi,004015C3 |per le seguenti quattro istruzioni vedere (A)
mov edi,00401374 |
mov ecx,49 |questa volta la repz viene ripetuta 73 volte (49 esedecimale = 73 decimale)
repz movsb |

Ok questa è l'ultima volta...

00401374: mov ecx,4 carica 4 in ecx per far partire il seguente algoritmo dal 5° chr del nome
00401379: xor eax,eax azzera eax
mov al,[ecx+004020AF] carica in al i chr del seriale dal quinto
cmp ecx,[0040210F] confronta ecx con il # di chr del nome+1 (vedere (B))
jz 004013A5 ripete le istruzioni seguenti fino alla fine del nome+1 (poi salta a 004013A5)
xor al,82 |con le prossime tre istruzioni critta il chr del seriale
shl al,2 |
ror al,2 |
mov bl,[ecx+004020C7] carica in bl i chr del nome crittati dal quinto
cmp al,bl confronta il chr crittato del nome con il chr crittato del seriale
jnz 00401170 se sono diversi salta a 00401170 (SERIALE SBAGLIATO) Questo jnz va forzato a non saltare; per fare questo date il comando "r fl z" ovviamente prima di eseguire l'istruzione e solo se softice segnala che è previsto il salto, magari avete indovinato il valore :)
inc ecx incrementa conteggio chr
cmp ecx,8 |
jnz 00401379 |esegue le istruzioni precedenti per al max 8 chr
004013A5: push 20 |con le prossime 4 istruzioni prepara la chiamata alla MessageBoxA
push 00402061 |
push 00402113 |
push 00 |
call MessageBoxA |visualizza il messaggio SERIALE ESATTO

 

Questa è la procedura che viene chiamata ad ogni istruzione "call 0040160C":

0040160C: cmp dl,7A confronta dl (valore passato prima della chiamata) con 7A
jg 00401617 se è maggiore salta a 00401617
cmp dl,30 confronta dl con 30
jl 00401621 se è minore salta a 00401621
00401616: ret ritorna dalla chiamata
00401617: sub dl,0A sottrae 0A a dl
cmp dl,7A |
jg 00401617 |continua fino a quando dl è maggiore di 7A
jmp 00401616 salta a 00401616 (ritorno dalla chiamata)
00401621: add dl,0A somma 0A a dl
cmp dl,30 |
jl 00401621 |continua fino a quando dl è minore di 30
jmp 00401616 salta a 00401616 (ritorno dalla chiamata)

Siamo giunti alla fine del programma. Ma non è ancora finita: ora abbiamo il nostro seriale esatto ma siccome non vogliamo trovarne uno per ogni nostro amico crackiamo il programma in modo che accetti qualsiasi nome e qualsiasi seriale :)

Dobbiamo tenere conto che stiamo lavorando su SMC quindi il codice che viene eseguito non è uguale al codice disassemblato e quindi nemmeno all'eseguibile che noi andremo a modificare. Per fregare questo tipo di protezione dobbiamo annotare gli opcode delle istruzioni da modificare dopo che il codice è stato modificato (e quindi abbiamo il programma descritto sopra). Inoltre, in questo caso, è utile annotare gli opcode dall'istruzione interessata in avanti (normalmente si prendono un po' di byte prima e un po' dopo). Io ho pensato di modificare le istruzioni immediatamente successive alle "repz movsb" (cioè dopo che il codice è stato modificato) in jmp alla successiva istruzioni "repz movsb" (ovviamente qualche istruzione prima, cioè quelle necessarie all'esecuzione della repz). Per trovare l'opcode delle istruzioni da sostituire dovete procedere nel seguente modo: in softice date il comando "a indirizzo_istruzione" (es. "a 00401277"), scrivete "jmp indirizzo_a_cui_saltare" (es. "jmp 004012AF") e premete invio. Ora annotate l'opcode della nuova istruzione.

Ok schematizziamo il tutto:

-consideriamo il repz prima dell'istruzione all'indirizzo 00401277; vogliamo modificare la "mov [0040210F],eax" in "jmp 004012AF" (004012AF è l'indirizzo della successiva repz). Per fare ciò annotiamo gli opcode partendo proprio dalla mov: A30F|214000 va sostituito con EB36|214000 (la sostituzine andrà fatta con l'editor esedecimale)

Proseguiremo allo stesso modo per i seguenti casi:

-sostituiamo l'istruzione "mov ecx,4" all'indirizzo 004012C0 con una "jmp 0040131C": B904|00000033C08A819B con EB5A|000...

-sostituiamo l'istruzione "push 18" all'indirizzo 0040132D con una "jmp 00401363": 6A18|68AF204000FF3550 con EB34|68AF... (qui è importante considerare anche il 50 sottolineato perchè se non includiamo questo con l'editor esadecimale troveremo tre volte lo stesso valore e non sapremo quale modificare)

-sostituiamo l'istruzione "mov ecx,4" all'indirizzo 00401374 con una "jmp 004013A5": B904|00000033C08A81AF con EB2F|000...

Ok, fatte queste modifiche arriveremo direttamente alla chiamata al messaggio di congratulazioni qualsiasi dato inseriremo :)

Per le modifiche con l'editor esadecimale, se non accetta nel campo ricerca l'intera lunghezza dei valori indicati non c'è problema, inserite fino dove arriva e date l'invio; quando avrà trovato il valore leggete i byte successivi e confronataeli con quelli indicati. Se sono uguali siete nel punto giusto.

Ok ho capito, non avete voglia di fare tutto questo lavoro? Va bhe, vi allego il generatore del seriale :)) ma come dice Que siete dei lavativi :)...almeno leggete la spiegazione del codice...

unit Unit1;
interface
uses
Forms, Classes, Controls, StdCtrls;
type
        TForm1 = class(TForm)
        Edit1: TEdit;
        Button1: TButton;
        Edit2: TEdit;
        Label1: TLabel;
        procedure Button1Click(Sender: TObject);
        private
            procedure codifica(chr:integer);
            procedure codifica2(chr:integer);
        public
end;
{Il codice che interessa a voi parte da qui}

var
    Form1: TForm1;
    nome: string;        {variabile che contiene il nome inserito}
    mezzo: byte=0;    {variabile per il passaggio del chr crittato}
    lungh: integer;      {variabile che contiene la lunghezza del nome}
implementation
procedure TForm1.Button1Click(Sender: TObject); {questa procedura viene chiamata quando premete il tasto Seriale}
var
    i: integer;          {variabile per il conteggio dei chr (fa la funzione di ecx)}
    cr: integer;        {variabile per il passaggio del codice ASCII dei chr}
begin
        i:=0;
        Edit2.Text:='';
        if Edit1.Text<>'' then    {esegue le istruzioni seguenti solo se si è inserito un nome}
        begin
                nome:=Edit1.Text;     {assegna il nome inserito alla variabile nome}
                lungh:=Edit1.GetTextLen;     {carica in lungh il # di chr del nome}
                inc(lungh);         {incrementa di 1 lungh (corrisponde all'istruzione "inc eax" dopo la GetWindowTextLenghtA)}
                while((nome[i+1]<>'') and (i<4)) do    {esegue le istruzioni seguenti solo per i primi 4 chr o fino alla fine del nome se sono meno di 4}
                begin
                        cr:=Ord(nome[i+1]);     {carica in cr il codice ASCII dei chr del nome a partire dal primo}
                        codifica(cr);         {chiama la procedura di codifica dei primi 4 chr}
                        Edit2.Text:=Edit2.Text+Chr(mezzo);     {visualizza il seriale (lo fa chr per chr ma noi lo vediamo tutto insieme)}
                        inc(i);         {incrementa il conteggio dei chr (corrisponde alla "inc ecx")}
                end;
                if((i<4)and(nome[i+1]='')) then    {se il nome è più corto di quattro lettere aggiunge il chr 2 al seriale}
                    Edit2.Text:=Edit2.Text+'2'     {il chr 2 corrisponde alla codifica del codice ASCII 0}
                else         {se invece i chr sono almeno 4 esegue le istruzioni seguenti}
                begin
                    while((nome[i+1]<>'')and(i<8)) do {esegue le istruzioni seguenti fino alla fine del nome+1 e al max per 8 chr}
                    begin
                            cr:=Ord(nome[i+1]);     {carica in cr il codice ASCII dei chr del nome a partire dal quinto}
                            codifica2(cr);     {chiama la procedura di codifica dei chr dal quinto all'ottavo}
                            Edit2.Text:=Edit2.Text+Chr(mezzo);     {visualizza il seriale (lo fa chr per chr ma noi lo vediamo tutto insieme)}
                            inc(i);     {incrementa il conteggio dei chr (corrisponde alla "inc ecx")}
                    end;
                    if((i<8)and(nome[i+1]='')) then    {se il nome è meno di 8 chr aggiunge la codifica del codice ASCII 0}
                    begin
                            codifica2(0);     {qui non possiamo mettere il valore fisso come per il 2 prima perchè cambia in base alla lunghezza del nome}
                            Edit2.Text:=Edit2.Text+Chr(mezzo);     {visualizza il seriale (lo fa chr per chr ma noi lo vediamo tutto insieme}
                    end;
                end;
        end;
end;

{procedura per la codifica dei primi 4 chr-le istruzioni sono uguali a quelle del programma; sono state omesse la istruzioni "xor al,81h" e "xor al,40h" perchè avremmo dovuto rieseguirle per ottenere il codice ASCII del seriale}
procedure TForm1.codifica(chr:integer);
asm
                    mov eax,chr
                    xor edx,edx
                    mov edi,0Bh
                    cdq
                    idiv edi
                    add eax,edx
                    shl eax,1
                    cmp al,7Ah    {le seguenti 12 istruzioni sostituiscono la chiamata "call 0040160C"}
                    jg @sottrai
                    cmp al,30h
                    jl @somma
                    jmp @continua
@sottrai:      sub al,0Ah
                    cmp al,7Ah
                    jg @sottrai
                    jmp @continua
@somma:     add al,0Ah
                    cmp al,30h
                    jl @somma
@continua:   mov mezzo,al    {salva il chr elaborato nella variabile mezzo per la visualizzazione successiva}
end;

{procedura per la codifica dei chr dal quinto all'ottavo-le istruzioni sono uguali a quelle del programma; sono state omesse le istruzioni "xor al,82h", "shl al,02h" e "ror al,02h" perchè avremmo dovuto rieseguirle per ottenere il codice ASCII del seriale}
procedure TForm1.codifica2(chr:integer);
asm
                     mov eax,chr
                     xor edx,edx
                     mov edi,08h
                     cdq
                     idiv edi
                     add al,dl
                     shl al,03h
                     ror al,01h
                     add al,06h
                     shl al,01h
                     xor al,byte ptr lungh    {corrisponde all'istruzione "xor al,[0040210F]"}
                     mov edi,03h
                     cdq
                     idiv edi
                     add al,dl
                     cmp al,7Ah    {le seguenti 12 istruzioni sostituiscono la chiamata "call 0040160C"}
                     jg @sottrai
                     cmp al,30h
                     jl @somma
                     jmp @continua
@sottrai:       sub al,0Ah
                     cmp al,7Ah
                     jg @sottrai
                     jmp @continua
@somma:      add al,0Ah
                     cmp al,30h
                     jl @somma
@continua:    mov mezzo,al    {salva il chr elaborato nella variabile mezzo per la visualizzazione successiva}
end;
end.

Note finali

Un ringraziamento va a Quequero per questo programma perchè quando ho visto modificarsi il codice in softice ho iniziato a ridere come un deficiente (forse che sono un po' matto...bhe ci vuole anche questo nella vita :-)

Un saluto va ovviamente a tutti gli iscritti alla UIC e al newsgroup ahccc (e soprattutto ad un certo Parsifal ;-)

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.
Capitoooooooo????? Bhè credo di si ;)))) 

 

 

UIC's page of reverse engineering, scegli dove andare:

Home   Anonimato   Assembly    ContactMe  CrackMe   Links   
NewBies   News   Forum   Lezioni  
Tools   Tutorial 

UIC