C# Framework.NET
Reversing - Episodio 2 |
||
Data |
by "Pbdz" |
|
14/giugno/2004 |
Published by Quequero |
|
Forza Azzurri!! |
Grazie mille pbdz, come al solito hai scritto un ottimo tutorial! |
Trullallerò trullallà! |
.... |
Home
page: http://pbdz.cjb.net E-mail: [email protected] |
.... |
Difficolt� |
( )NewBies (x)Intermedio ( )Avanzato ( )Master |
|
Non avete capito niente del tutorial precedente? Mi fa piacere! Vi do un'altra possibilità! ;-)
C# Framework.NET Reversing
- Episodio 2
I.L. Cracking
Written by Pbdz
Introduzione |
Che dire...sta arrivando l'estate, le ragazze sono tutte delle .....! L'unico modo per non impazzire è tuffarmi in un mare di codice incomprensibile!.
Tools usati |
Per disassemblare consiglierei IDA, altrimenti
basta scrivere sulla nel prompt dei comandi:
ildasm.
Per modificare il file basta un qualsiasi Hex Editor.
URL o FTP del programma |
Ecco il link al mio programma. Per scaricare il framework andate sul sito di zio Bill! ;-)
Notizie sul programma |
AVVISO: Se avete letto il tutorial precedente potete anche saltare questo pezzo! Altrimenti, vabbè..saltatelo lo stesso, ma rimarrete ignoranti! :-)
Cos'�
il Framework.NET?
Questo oscuro oggetto (di casa microsoft) non � altro
che una piattaforma
su cui possono girare determinati programmi, piattaforma anche chiamata CLR
(Common Language Runtime).
Per farvi un esempio basta pensare alle varie dll
che servono per far girare un programma in VB.
Il concetto � lo stesso, se fate un programma in C#
VB.Net ecc...e non avete il framework installato,
il programma non vi funzioner� mai. Il programma non funzioner� perch� conterr�
istruzioni differenti dalle solite... Supponiamo
che faccia un programma in c#, compilando il codice
sorgente ottengo un tipo di file chiamato Assembly,
ha l'estensione .exe, ma non contiene al suo interno
del codice macchina, bens� I.L.
(intermediate language). Questo I.L.
sar� tradotto dal CLR in linguaggio macchina vero e proprio, e quindi
sar� adatto per l'esecuzione. Il vantaggio di questo meccanismo sta nel fatto
che il CLR compila solo il
codice che serve, perci� si risparmia tempo, in pi� una volta che ha compilato
il codice questo viene salvato
e non sar� pi� necessario ricompilarlo al momento
di una futura esecuzione.
Inutile dire che il framework
lo trovate sul sito di ZiA Bill!
:-) Potete scaricare quello "normale" che
pesa una 20ina di Mb o, se volete divertirvi potete
scaricare l'SDK, ma pesa molto di pi�! (sconsigliato
se avete ancora un 56K come me! Consigliato se avete un amico con l'adsl! ;-))
Spero di essere stato abbastanza esaustivo!
Essay |
Ho deciso
di scrivere una guida un po� pi� approfondita perch� il primo tutorial forse era un po� troppo sintetico, ma sapete�sono
quelle cose che scrivi di getto, senza pensare
troppo!!!
Cercher�
di descrivere come interpretare l�intermediate language per risalire alle operazioni pi� comuni, come
dichiarare una variabile, un ciclo for ecc�
Dichiarazione
e assegnazione di una variabile:
int numero;
disassemblato diviene:
.locals init
(int32 V0)
Quando si
dichiara una� o
pi� variabili, questa viene memorizzata in un�array,
qui per esempio abbiamo dichiarato solo una variabile e quindi il suo numero
(posizione nell�array) � 0. Se per esempio avevamo
tre variabili di cui due di tipo int32 e una di tipo STRING avevamo:
.locals init (int32 V0,
������������� int32
V1,
������������� class System.String
V2)
�
tutto chiaro? Spero di si!
Assegnare
un valore ad una variabile:
numero = 5;
Per
assegnare un numero occorrono 2 istruzioni:
ldc.i4.5
Questa istruzione
serve per pushare nello stack
il numero 5 come un int32.
Quando
pusha un numero int32 si usa i4, se si usa un numero
int64 si usa i8, se si pusha un float32 si usa r4, se
si pusha un float64 si usa r8.
La
sintassi ldc.i4.(num) � valida con i numeri fino ad
8. E ancora finch� si usano i numeri fino ad 8 le
istruzioni vengono considerate un unico blocco, mi
spiego meglio, l�istruzione ldc.i4.6 ha un solo opcode che la identifica. Gli opcode
per queste istruzioni vanno da 16(ldc.i4.0) a 1E(ldc.i4.8). Ma
ritorniamo al discorso di prima, come si fa a pushare
un numero maggiore di 8?
Semplice,
si usa questa funzione: ldc.i4.s num, es. ldc.i4.s 0xA in questo caso l�opcode sar�
da 1F0A dove
stloc.0
Questa
funzione non fa altro che caricare nella variabile numero 0 il valore precedentemente pushato.
Operazioni
matematiche semplici:
Facciamo
finta di aver gi� dichiarato 3 variabili di cui la terza deve contenere il
risultato tra la soma/sottrazione/divisione/moltiplicazione tra le prime due.
int numero = 10;
int numero1 = 23;
int risultato;
risultato = numero+numero1;
ecco
come ci viene tradotto:
��
�.locals init (int32 V0,
�����������������
int32 V1,
�����������������
int32 V2)
��� ldc.i4.s 0xA
��� stloc.0
��� ldc.i4.s 0x17
��� stloc.1
Fino a
qui sappiamo cosa succede, giusto? Abbiamo le prime 2 variabili assegnate. Ora
ecco cosa succede:
ldloc.0
ldloc.1
add
stloc.2
Si
capisce gi� cosa combina in questo caso, pusha nello stack la prima e la seconda variabile con l�istruzione ldloc, li somma con l�istruzione add e poi poppa il loro valore nella terza variabile
tramite l�istruzione stloc.2 (2 rappresenta la
posizione nell�array delle variabili�).
Il
procedimento � identico sia per la sottrazione/divisione/moltiplicazione�le
istruzioni sono:
mul������
//moltiplicazione
sub����� //sottrazione
div����
//divisione
Queste
istruzioni prelevano dallo stack gli ultimi 2 valori pushati e ripusha il risultato
che poi verr� inserito nell�altra variabile.
IF-ELSE:
Vediamo
cosa succede quando in un programma sono usate queste
2 condizioni!
Supponiamo
che in un programma ci sia questo codice:
int numero = 1;
if(numero == 1)
� {
��� MessageBox.Show("Condizione
soddisfatta","Messaggio");
� }
else
� {
��� MessageBox.Show("Condizione
non soddisfatta","Messaggio");
� }
Scusate,
non � il massimo che si possa avere! Per� � giusto per rendere la cosa abbastanza semplice. Disassembliamo e avremo:
//Fin qui dichiariamo la solita variabile e le assegnamo
come valore 1.
.locals init (int32 V0)
��� ldc.i4.1
��� stloc.0
//Qui pushamo nello stack
il valore della variabile e il numero 1
��� ldloc.0
---> posizione che la variabile occupa nell�array
��� ldc.i4.1
��� bne.un.s
loc_158�
/*Questa sopra � la funzione chiave, in pratica salta se i due
valori che abbiamo pushato sono diversi. E�
l�equivalente di un jne!
Quella s alla fine
Significa che il salto � corto.
In sintesi possiamo dire
if(var_pushata == 1)
��� [Esegui
codice]� */
//Quello che segue sono le istruzioni che servono a visualizzare
una MessageBox.
//Ve le commento brevemente
��� ldstr
"Condizione soddisfatta" //carica nello stack
il testo della MsgBox
��� ldstr
"Messaggio" //carica nello stack il titolo della MsgBox
//Infine chiama la funzione vera e propria
��� call value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String)
��� pop
��� br.s
loc_168� //questo � un salto incodizionato e serve per terminare tutta la funzione, la s
sta ad indicare che il salto � di tipo short
loc_158:�����������������������������������
��� ldstr
"Condizione non soddisfatta"
��� ldstr
"Messaggio"
��� call value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String)
��� pop
loc_168:�������������������������������
��� ret
Ciclo
While:
l�equivalente
di
int numero = 1;
while(numero < 10)
�� {
���� numero++;
�� }
?
Eccolo:
.locals init
(int32 V0)
��� ldc.i4.1
��� stloc.0
//fin qui tutto normale, sempre la solita variabile con valore 1.
��� br.s
loc_148 //Qui salta alla loc_148, perci� andiamo pure noi!
loc_144:�������������������������������
��� ldloc.0
��� ldc.i4.1
��� add
��� stloc.0
/*Non notate niente di familiare? Dai...date uno sguardo al
paragrafo riguarda le operazioni matematiche! In sintesi tutto sto casino per fare �numero++�.
Credo sia chiarissimo! Da notare che finita questa
operazione va di nuovo a controllare se la nostra variabile � minore di
10, come ogni ciclo while che si rispetti!*/
loc_148:�������������������������������
��� ldloc.0
��� ldc.i4.s 0xA
//fin qui ha pushato nello stack il valore della nostra variabile e il numero 0x0A,
che come tutti sanno equivale a 10 decimale.
��� blt.s
loc_144 //qui c�� un salto condizionato, salta se il primo valore � minore
del secondo! Mi sa che � ora di saltare! :-) Andiamo
alla loc_144!
Ciclo
Do-While:
int numero = 1;
����� do
����� {
����� � numero++;
����� }
while(numero < 10);
Disassemblato avremo:
��� .locals init (int32
V0)
��� ldc.i4.1
��� stloc.0
loc_142:�������������������������������
��� ldloc.0
��� ldc.i4.1
��� add
��� stloc.0
��� ldloc.0
��� ldc.i4.s 0xA
��� blt.s loc_142
��� ret����
/*Se notate bene le istruzioni cambiano
solo nell�ordine�infatti il ciclo
do-while esegue prima
l�istruzione e poi verifica la condizione. Non commento il codice in quanto �
identico al precedente.
Ciclo For:
Ecco un
ciclo for ultra standard!
for(int i=0; i < 10;i++)
�{
MessageBox.Show("Messaggio numero "+i,"Titolo MessageBox");
�}
Ed
ecco la �traduzione�:
.locals init
(int32 V0)
��� ldc.i4.0
��� stloc.0
��� br.s
loc_163 //dopo aver assegnato la variabile salta a loc_163
loc_144:�������������������������������
��� ldstr
"Messaggio numero"
��� ldloc.0
��� box [mscorlib]System.Int32
��� call class System.String [mscorlib]System.String::Concat(class
System.Object, class System.Object)
��� ldstr "Titolo
MessageBox"
��� call value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String)
��� pop���� //Fin qui visualizza la messagebox
��� ldloc.0
��� ldc.i4.1
��� add���� //Incrementa il valore di i di 1������������������ �������������
��� stloc.0
loc_163:
��� ldloc.0
��� ldc.i4.s 0xA
��� blt.s loc_144 //Confronta la
variabile (i) con il numero 10 e salta a loc_144 se i � minore.
Ret
Prendere
il testo da una TextBox:
Considerate questa routine:
private void button1_Click(object sender, System.EventArgs
e)
����������� {
����������������� string nome = nome_txt.Text;
����������������� string
serial = serial_txt.Text;
����������������� MessageBox.Show(zz,
prova);
����������� }
Come potete
osservare dichiaro 2 stringhe e le assegno 2 valori
che provengono da 2 textBox quali, nome_txt e serial_txt. Il
programma in fine visualizza le stringhe con una MessageBox.
Ecco come
si presenta con IDA:
��� .locals init (class System.String V0,� //dichiara le
�����������������
class System.String
V1)� //due stringhe
��� ldarg.0
��� ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox my_app.Form1::serial //carica nello
stack il controllo da usare, in questo
caso
��� callvirt class System.String
[System.Windows.Forms]System.Windows.Forms.Control::get_Text()
//chiama la funzione per prendere il testo dalla
textBox.
��� stloc.0� //inserisce il valore ottenuto nella prima
variabile (posizione 0).
��� ldarg.0
��� ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox my_app.Form1::nome
��� callvirt class System.String
[System.Windows.Forms]System.Windows.Forms.Control::get_Text()
��� stloc.1 //fin qui ha
fatto la stessa cosa ma ha usato l�altra textbox e l�altra
variabile per immagazzinare il valore ottenuto.
��� ldloc.0
//carica
nello stack la prima variabile.
��� ldloc.1
//carica
nello stack la seconda variabile.
��� call
value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String) //chiama la
funzione MessageBox.
��� pop
��� ret
�
Ok, ora che sappiamo qualcosina in più possiamo cimentarci nell'interpretazione del codice di un mio proto-crackme che lo trovate come
allegato a questo tutorial...
Il crackme chiede un nome e un serial per essere registrato, sicuramente (ma va?) il serial viene generato in base al nome inserito,
disassembliamo con IDA o con qualche altro programma che supporti il framework andiamo e portiamoci nella routine button1_Click.
Ecco cosa ci appare:
.method private hidebysig
void button1_Click(class System.Object
sender, class [mscorlib]System.EventArgs
e)
��������������������������������������� // DATA
XREF: InitializeComponent+1F1r
� {
��� .locals init
(class System.String V0,
�����������������
class System.String
V1,
�����������������
class [mscorlib]System.Text.StringBuilder V2,
�����������������
int32[] V3,
����������������� int32
V4 //Questa variabile sarebbe il contatore usato nel
ciclo for per generare il codice)
��� ldarg.0
��� ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox ForUIC2.Form1::textBox1
��� callvirt class System.String
[System.Windows.Forms]System.Windows.Forms.Control::get_Text()
��� stloc.0���������������������������� // Arrivati a
questo punto abbiamo il nome nella nostra variabile di tipo stringa
��� ldarg.0
��� ldfld class [System.Windows.Forms]System.Windows.Forms.TextBox ForUIC2.Form1::textBox2
��� callvirt class System.String
[System.Windows.Forms]System.Windows.Forms.Control::get_Text()
��� stloc.1���������������������������� // Dopo queste altre operazioni abbiamo il codice da noi
inserito nell'altra variabile
��� newobj
void [mscorlib]System.Text.StringBuilder::.ctor()
��� stloc.2���������������������������� // Qui crea l'oggetto
StringBuilder, in c# sarebbe
StringBuilder myString =
new StringBuilder();
��� ldloc.0
�� �callvirt
int32 [mscorlib]System.String::get_Length()
��� conv.ovf.u2.un��������������������� //
Questa istruzione � molto particolare...in pratica come potete osservare
��������������������������������������� // prima di questa istruzione c'�
una chiamata ad una funzione get_Legth()
��������������������������������������� // che si occupa di determinare lunghezza di una stringa,
in questo caso della
��������������������������������������� //
stringa che contiene il nostro nome. La funzione conv.ovf.u2.un converte
��������������������������������������� // il
numero dei caratteri della stringa in un int16 (u2), ed in pi� ha la funzione di
��������������������������������������� //
prevenire un overflow (ovf).
��� newarr
[mscorlib]System.Int32������ // Qui inizializza l'array che conterr�
il codice...questo array
��������������������������������������� // come
potete vedere � di tipo int32.
��� stloc.3���������������������������� // Ora l'array � inizializzato
��� ldc.i4.0�����������������������
����//
Carica la variabile col nostro nome
��� stloc.s
byte_4���������������������
��� br.s
loc_33F����������������������� // Salta al ciclo for
loc_320:�������������������������������
��� ldloc.3���������������������������� // Carica un elemento dell'array
��� ldloc.s
4�������������������������� // carica la posizione l�indice
��� ldloc.0����������������������� �����// carica la
stringa
��� ldloc.s
4�������������������������� // carica la posizione dell'indice
��� callvirt
char [mscorlib]System.String::get_Chars(int32) //trova il codice del carattere contenuto nella string ache si trova nella
posizione indicate dall�indice.
��� ldc.i4.2��������������������������� // Carica una costante numerica, in questo caso 2
��� xor�������������������������������� // xora il codice del carattere
corrente con 2
��� stelem.i4�������������������������� // inserisce il risultato nell� array
��� ldloc.2
��� ldloc.3���������������������������� // carica l'array
��� ldloc.s
4������������ ��������������//
carica l'indice
��� ldelem.i4�������������������������� // carica il valore dell'array
indicato dall'indice.
��������������������������������������� //
Questa funzione ha bisogno di 2 parametri per funzionare,
�������������������������� �������������// perci�
prima vengono pushati l'array
e l'indice.
��������������������������������������� // Il
valore restituito dalla funzione viene inserito nello stack
��� callvirt
class [mscorlib]System.Text.StringBuilder
[mscorlib]System.Text.StringBuilder::Append(int32) // Questa funzione
serve per costruire una stringa,
��������������������������������������� // in
questo caso abbiamo aggiunto il primo carattere...
��� pop�������������������������������� // non c'� molto da dire...in sintesi rimuove l'ultimo
elemento
��������������������������������������� //
inserito nello stack
��� ldloc.s
4�������������������������� // Push indice
��� ldc.i4.1��������������������������� // push del numero 1
��� add�������������������������������� // Incrementa di 1 l�indice
��� stloc.s
4�������������������������� // Aggiorna l�indice �col nuovo valore
loc_33F:�������������������������������
��� ldloc.s
4�������������������������� // Carica il valore corrente dell'indice
��� ldloc.0���������������������������� // Carica la stringa...
��� callvirt int32 [mscorlib]System.String::get_Length()
��� blt.s loc_320���������������������� //
Se l'indice � pi� piccolo della
��������������������������������������� //
lunghezza della stringa salta
��� ldarg.0
��� ldfld
class [System.Windows.Forms]System.Windows.Forms.TextBox
ForUIC2.Form1::textBox2
��� callvirt class System.String
[System.Windows.Forms]System.Windows.Forms.Control::get_Text()
��� ldloc.2���������������������������� // legge il serial da noi inserito
��� callvirt
class System.String [mscorlib]System.Text.StringBuilder::ToString()
��� call bool [mscorlib]System.String::op_Equality(class System.String,
class System.String)
��� brfalse.s loc_373������������������ //
Salta se la chiamata alla funzione precedente restituisce un
��������������������������������������� //
valore di tipo booleano FALSE.
��� ldstr
"Registrato! Bravo!"
��� ldstr
"Pbdz Crackme"
��� call
value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String)
��� pop
��� br.s loc_383
loc_373:������������������������������� // CODE XREF:
button1_Click+
��� ldstr "Codice
sbagliato!!"
��� ldstr
"Pbdz Crackme"
��� call value class [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(class System.String, class System.String)
��� pop
loc_383:������������������������������� // CODE XREF:
button1_Click+81j
��� ret
� }
Note finali |
Ringrazio Quequero perchè mi da la possibilità di esprimermi grazie al suo sito! Un saluto a tutti quelli che credono che programmare significa saper usare "word". Un saluto a tutti quelli che il 29 giugno saranno a Padova a vedere i Metallica! Ah, dimenticavo, ringrazio mia cugina per avermi prestato il portatile!
Disclaimer |
Qui inserirete con questo carattere il vostro piccolo disclaimer, non � obbligatorio per� � meglio per voi se c'�. Dovete scrivere qualcosa di simile a: 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.