Windows Phone 7 - Dotfuscator XAP Reversing
From UIC
Contents |
| Windows Phone 7 - Dotfuscator XAP Reversing | |
|---|---|
| Author: | IndieAlex |
| Email: | |
| Website: | |
| Date: | 26/11/2010 (dd/mm/yyyy) |
| Level: |
|
| Language: | Italian |
| Comments: | Il mio primo tutorial |
Introduction
Il recente rilascio del nuovo OS Mobile targato Microsoft porta con se, tra le tante novità, anche vecchi problemi. La natura autodescrittiva degli assembly .NET ha portato indubbi vantaggi (uno su tutti, la fine del DLL Hell) ma anche qualche svantaggio. Decompilazioni e disassemblati perfettamente leggibili sono stati terreno fertile per il proliferare di offuscatori di codice. Ma è davvero sufficiente offuscare il proprio sorgente per nasconderlo ad occhi indiscreti? L'infrastruttura .NET implementata nel nuovo Windows Phone 7 come si comporta da questo punto di vista? Farà eccezione? Cerchiamo di dare una risposta a tutti questi quesiti nel seguente tutorial (o almeno ci proviamo).
Tools
Notizie sul Programma
Programma offuscato con Dotfuscator 4.9 Pro. Tutte le opzioni di offuscamento attivate tranne Linking e Removal. Richiede il Windows Phone Developer Tools. Per testare il crackme avviare Application Deployment, caricare il file .xap, selezionare l'emulatore nel Target e cliccare su Deploy.
Per seguire il tutorial potete scaricare il CrackMe da qui.
Un'altra versione dello stesso CrackMe ma con offset diversi (nel caso voleste provare da soli a deoffuscare il codice) e con SupportedOrientations="PortraitOrLandscape" (tanto per fare figo) potete scaricarla da qui.
Essay
In realtà sarebbe stato più corretto intitolarlo "Deoffuscare Dotfuscator" ma suonava male così ho cercato un pretesto, come il nuovo OS Mobile di MS, per parlare un po' di deoffuscazione del codice .NET. A mia discolpa però c'è da dire che gli offuscatori di codice .NET più rognosi attualmente in circolazione, che usano tutti quei bei tricks che fanno crashare Reflector, Ildasm, OllyDbg, ecc., difficilmente potranno essere usati su WP7 attivando le opzioni di offuscamento più avanzate. Per farla breve, attualmente Dotfuscator rappresenta la soluzione più stabile su WP7 quindi analizzeremo questa cercando di offrire un approccio diverso al problema rispetto a tutte quelle informazioni che già si trovano in rete e che offrono soluzioni impacchettate e deoffuscamenti automatizzati tramite tool sviluppati da terzi. Ma veniamo al dunque, un programma per WP7 consiste in una dll contenente codice, risorse e metadata. Il compilatore impacchetta questa dll, insieme alle eventuali risorse esterne dell'applicazione, in un file con estensione .xap. Come si può esaminare questo formato? Niente di più facile... come per i vecchi file .cab usati per distribuire applicazioni Windows Mobile basta aprire il file .xap con Winzip, Winrar, o il programma di archiviazione per file compressi che preferite. E' confortante sapere che anche patchando la dll poi basta ricostruire l'archivio .xap perchè tutto funzioni senza problemi. Ora, estraiamo la dll ed esaminiamola con Reflector. La classe che ci interessa è MainPage (WP7 lavora con le pagine e non con le Form). Essendo un piccolo crackme trovare il metodo che genera il seriale non presenta particolari difficoltà ma in programmi più corposi la ricerca potrebbe richiedere certamente più tempo... comunque Reflector ci semplifica non poco la vita da questo punto di vista. Dunque, selezionando il metodo bool a(string A_0) Reflector ci mostra questo:
{
// This item is obfuscated and can not be translated.
}
quindi passiamo alla visualizzazione del codice IL. Reflector ci mostra tutto il codice del metodo. A questo punto il tutorial potrebbe anche finire qui perchè, nonostante il codice offuscato, basta seguirne il flusso e calcolarsi il seriale senza neanche sporcarsi le mani. Prima di guardare il codice del metodo sottolineo che, per comodità e per migliorare la leggibilità, ho usato il listato disassemblato con Simple Assembly Explorer (SAE d'ora in poi) che converte gli offset in numeri di riga. Quindi, in questo caso, quando parliamo di numeri di riga in realtà stiamo parlando di offset. Ancora, per migliorare la leggibilità ho usato un controllo PasswordBox così da distinguere meglio la nostra key con il product code conservato nel TextBlock. Ma vediamo il listato:
{
.maxstack 4
.locals init (
[0] class [mscorlib]System.Text.StringBuilder V_0,
[1] char V_1,
[2] char[] V_2,
[3] string V_3,
[4] string V_4,
[5] int32 V_5,
[6] int32 V_6)
0 L_0000: ldc.i4.0 // L'intero V_6 serve solo per
1 L_0001: stloc.s V_6 // caricare valori da usare
2 L_0003: ldloc.s V_6 // nello switch come indici.
3 L_0005: switch 4 //
4 L_000e: br.s 7 -> newobj System.Void System.Text.StringBuilder::.ctor() //
5 L_0010: ldloc.s V_6 // Il metodo parte dalla prossima
6 L_0012: switch 18, 73, 90, 42, 48, 86, 96, 52, 26, 38, 59, 67 // istruzione:
7 L_0047: newobj System.Void System.Text.StringBuilder::.ctor()
8 L_004c: stloc.0
9 L_004d: ldarg.0 // this
10 L_004e: ldfld System.Windows.Controls.TextBlock WP7CrackMe01.MainPage::j // Carica il valore della textbox
11 L_0053: callvirt System.String System.Windows.Controls.TextBlock::get_Text() // in V_4
12 L_0058: stloc.s V_4 //
13 L_005a: ldc.i4.0 //Carica zero nell'intero
14 L_005b: stloc.s V_5 // V_5
15 L_005d: ldc.i4 0 // Carica zero in V_6
16 L_0062: stloc.s V_6 // consulta lo switch
17 L_0064: br.s 5 -> ldloc.s V_6 // e prosegue alla riga 18 -->
18 L_0066: br.s 49 -> ldc.i4 7 // Salta alla riga 49 -->
19 L_0068: ldloc.0 //
20 L_0069: ldloc.1 // Inserisce il char V_1
21 L_006a: callvirt System.Text.StringBuilder::Append(System.Char) // in V_0 (StringBuilder)
22 L_006f: pop
23 L_0070: ldc.i4 8 // Carica 8 in V_6
24 L_0075: stloc.s V_6 // consulta lo switch e
25 L_0077: br.s 5 -> ldloc.s V_6 // prosegue alla riga 26 -->
26 L_0079: br 74 -> ldloc.s V_5 // Salta alla riga 74 -->
27 L_007e: ldloc.0 //
28 L_007f: ldloc.1 // Converte il carattere
29 L_0080: call System.Int32 System.Convert::ToInt32(System.Char) // in intero
30 L_0085: ldc.i4.1 //
31 L_0086: sub // Sottrae 1
32 L_0087: call System.Char System.Convert::ToChar(System.Int32) // Converte intero a char
33 L_008c: callvirt System.Text.StringBuilder::Append(System.Char) // Inserisce il char in V_0
34 L_0091: pop
35 L_0092: ldc.i4 9 // Carica 9 in V_6
36 L_0097: stloc.s V_6 // consulta lo switch e
37 L_0099: br 5 -> ldloc.s V_6 // prosegue alla riga 38 -->
38 L_009e: br.s 74 -> ldloc.s V_5 // Salta alla riga 74 -->
39 L_00a0: ldc.i4 3 // Carica 3 in V_6
40 L_00a5: stloc.s V_6 // consulta lo switch e
41 L_00a7: br 5 -> ldloc.s V_6 // prosegue alla riga 42 -->
42 L_00ac: ldloc. 1 // Salta alla riga 19 se
43 L_00ad: ldc.i4.s 0x30 // il carattere è '0'
44 L_00af: beq.s 19 -> ldloc.0 //
45 L_00b1: ldc.i4 4 // Carica 4 in V_6
46 L_00b6: stloc.s V_6 // consulta lo switch e
47 L_00b8: br 5 -> ldloc.s V_6 // prosegue alla riga 48 -->
48 L_00bd: br.s 87 -> ldc.i4 2 // Salta alla riga 87 -->
49 L_00bf: ldc.i4 7 // Carica 7 in V_6
50 L_00c4: stloc.s V_6 // consulta lo switch e
51 L_00c6: br 5 -> ldloc.s V_6 // prosegue alla riga 52 -->
52 L_00cb: ldloc.s V_5 // Salta a 60 finchè ci sono
53 L_00cd: ldloc.s V_4 // caratteri nella stringa V_4
54 L_00cf: callvirt System.Int32 System.String::get_Length() //
55 L_00d4: blt.s 60 -> ldloc.s V_4 // (Ri)Comincia il ciclo -->
56 L_00d6: ldc.i4 10 // Carica 10 in V_6
57 L_00db: stloc.s V_6 // consulta lo switch e
58 L_00dd: br 5 -> ldloc.s V_6 // prosegue alla riga 59 -->
59 L_00e2: br.s 97 -> ldloc.0 // Ciclo finito, salta a 97 -->
60 L_00e4: ldloc.s V_4 // Prende il carattere
61 L_00e6: ldloc.s V_5 // all'indice V_5 e lo mette
62 L_00e8: callvirt System.Char System.String::get_Chars(System.Int32) // in V_1
63 L_00ed: stloc.1 //
64 L_00ee: ldc.i4 11 // Carica 11 in V_6
65 L_00f3: stloc.s V_6 // consulta lo switch e
66 L_00f5: br 5 -> ldloc.s V_6 // prosegue alla riga 67 -->
67 L_00fa: ldloc.1 //
68 L_00fb: ldc.i4.s 0x41 // Salta alla riga 19 se
69 L_00fd: beq 19 -> ldloc.0 // il carattere è 'A'
70 L_0102: ldc.i4 1 // Carica 1 in V_6
71 L_0107: stloc.s V_6 // consulta lo switch e
72 L_0109: br 5 -> ldloc.s V_6 // prosegue alla riga 73 -->
73 L_010e: br.s 39 -> ldc.i4 3 // Salta alla riga 39 -->
74 L_0110: ldloc.s V_5 // Incrementa V_5
75 L_0112: ldc.i4.1 //
76 L_0113: add //
77 L_0114: stloc.s V_5 //
78 L_0116: ldc.i4.1 // ...
79 L_0117: br.s 82 -> brfalse.s 83 -> ldc.i4 5 // ...
80 L_0119: ldc.i4.0 // ...
81 L_011a: br.s 82 -> brfalse.s 83 -> ldc.i4 5 // ...
82 L_011c: brfalse.s 83 -> ldc.i4 5 // Prosegue alla riga 83 -->
83 L_011e: ldc.i4 5 // Carica 5 in V_6
84 L_0123: stloc.s V_6 // consulta lo switch e
85 L_0125: br 5 -> ldloc.s V_6 // prosegue alla riga 86 -->
86 L_012a: br.s 49 -> ldc.i4 7 // Salta alla riga 49 -->
87 L_012c: ldc.i4 2 // Carica 2 in V_6
88 L_0131: stloc.s V_6 // consulta lo switch e
89 L_0133: br 5 -> ldloc.s V_6 // prosegue alla riga 90 -->
90 L_0138: ldloc.1 // Salta alla riga 19 se
91 L_0139: ldc.i4.s 0x2d // il carattere è '-'
92 L_013b: beq 19 -> ldloc.0 //
93 L_0140: ldc.i4 6 // Carica 6 in V_6
94 L_0145: stloc.s V_6 // consulta lo switch e
95 L_0147: br 5 -> ldloc.s V_6 // prosegue alla riga 96 -->
96 L_014c: br 27 -> ldloc.0 // Salta alla riga 27 -->
97 L_0151: ldloc.0 //
98 L_0152: callvirt System.String System.Object::ToString() // Converte lo StringBuilder
99 L_0157: callvirt System.Char[] System.String::ToCharArray() // in un array di char
100 L_015c: stloc.2 // e lo mette in V_2
101 L_015d: ldloc.2 // Carica V_2
102 L_015e: call System.Void System.Array::Reverse(System.Array) // Inverte tutti i char
103 L_0163: ldloc.2
104 L_0164: newobj System.Void System.String::.ctor(System.Char[]) // Dall'array appena invertito
105 L_0169: stloc.3 // crea una nuova stringa V_3
106 L_016a: ldloc.3 // Carica V_3
107 L_016b: ldarg.0 // this
108 L_016c: ldfld System.Windows.Controls.PasswordBox WP7CrackMe01.MainPage::i // Prende la key da noi
109 L_0171: callvirt System.String System.Windows.Controls.PasswordBox::get_Password() // inserita
110 L_0176: call System.Boolean System.String::op_Equality(System.String,System.String) // Confronta V_3 e la nostra key
111 L_017b: ret // Ritorna valore restituito da op_Equality
}
La banalità del codice che genera il seriale è del tutto irrilevante dato che, come abbiamo già spiegato, la natura autodescrittiva degli assembly .NET permette di avere listati perfettamente leggibili ed inoltre, quando si ha a che fare con applicazioni .NET, spesso l'obbiettivo è la decompilazione dell'essembly o del metodo interessato. Decompilazione appunto, e se volessimo decompilare il metodo? In realtà non è difficile, basta eliminare tutte le istruzioni iniziali inutili compreso lo switch e tutte le istruzioni tipo
16 L_0062: stloc.s V_6 // consulta lo switch
17 L_0064: br.s 5 -> ldloc.s V_6 // e prosegue alla riga 18 -->
inutili rimbalzi che consultano lo switch per poi proseguire alla riga successiva. Si può fare il tutto manualmente con Reflexil (plugin di Reflector) o SAE che offre molte più funzionalità ed è anche più comodo. Ma se dovessimo trovarci di fronte ad un metodo con centinaia o migliaia di istruzioni? In questo caso sarebbe meglio usare un tool che ci permetta di automatizzare il tutto. CFF Explorer è adatto a questo scopo grazie alla possibilità di caricare i nostri script che andranno a patchare l'assembly. Quindi, per prima cosa, dobbiamo segnarci gli opcode che vogliamo eliminare. A questo scopo possiamo usare lo stesso CFF Explorer (o Ildasm). Per quanto riguarda le istruzioni iniziali fino allo switch (compreso) possiamo dire a CFF Explorer che vogliamo eliminare tutte le istruzioni a partire dall'offset del corpo del metodo fino all'offset dello switch. Entrambi i valori li troviamo guardando il metodo che ci interessa disassemblato in CFF Explorer. Quindi, la prima cosa che scriviamo nel nostro script è:
che nopperà tutte le istruzioni fino allo switch. In seguito guardiamo gli opcode dei rimbalzi. Abbiamo due tipi di blocchi diversi da eliminare. Il primo del tipo:
0x13, 0x06,
0x2B, ND
l'altro del tipo:
0x13, 0x06,
0x38, ND, ND, ND, ND
ora segnamoci la dimensione del metodo (che troviamo sempre in CFF Explorer) perchè la sommeremo all'offset del metodo così da rimanere nello scope dello stesso durante la ricerca. Nel nostro script:
0x13, 0x06,
0x38, ND, ND, ND, ND
}
sign2 = { 0x20, ND, ND, ND, ND,
0x13, 0x06,
0x2B, ND
}
Offset = SearchBytes(hFile, 0x598, sign1)
Offset2 = SearchBytes(hFile, 0x598, sign2)
while Offset != null do
if Offset > 0x598 + 0x17C then
break
end
FillBytes(hFile, Offset , #sign1, 0)
Offset = SearchBytes(hFile, Offset + 1, sign1)
end
while Offset2 != null do
if Offset2 > 0x598 + 0x17C then
break
end
FillBytes(hFile, Offset2 , #sign2, 0)
Offset2 = SearchBytes(hFile, Offset2 + 1, sign2)
end
if SaveFile(hFile)== true then
MsgBox("dejungled")
end
Apriamo l'assembly patchato con reflector o SAE. Ora è molto più facile individuare i blocchi da eliminare. Rimuoviamo tutti i nop (evitando i pop dopo StringBuilder::Append) e TA DAAAAAAAAA: This item is obfuscated and can not be translated. Ma Porc... Eh si, il flusso del codice è talmente incasinato che è impossibile per Reflector riuscire a decompilarlo. Dobbiamo fixarlo e renderlo appunto un flusso invece di quel ping pong tra blocchi di istruzioni che è adesso. Si noterà che ci sono parecchie istruzioni di salto (condizionato e non) accoppiate, non va bene. Innanzitutto possiamo eliminare quel blocco inutile:
45 L_006a: br.s 48 -> brfalse.s 49 -> br.s 27 -> ldloc.s V_5
46 L_006c: ldc.i4.0
47 L_006d: br.s 48 -> brfalse.s 49 -> br.s 27 -> ldloc.s V_5
48 L_006f: brfalse.s 49 -> br.s 27 -> ldloc.s V_5
Ora dobbiamo risistemare il blocco IF che confronta il carattere preso dalla stringa ad ogni ciclo con 'A', '0' e '-' perchè è davvero inguardabile.
24 L_003c: ldc.i4.s 0x30
25 L_003e: beq.s 9 -> ldloc.0
26 L_0040: br.s 45 -> ldloc.1
27 L_0042: ldloc.s V_5
28 L_0044: ldloc.s V_4
29 L_0046: callvirt System.Int32 System.String::get_Length()
30 L_004b: blt.s 32 -> ldloc.s V_4
31 L_004d: br.s 49 -> ldloc.0
32 L_004f: ldloc.s V_4
33 L_0051: ldloc.s V_5
34 L_0053: callvirt System.Char System.String::get_Chars(System.Int32)
35 L_0058: stloc.1
36 L_0059: ldloc.1
37 L_005a: ldc.i4.s 0x41
38 L_005c: beq 9 -> ldloc.0
39 L_0061: br.s 23 -> ldloc.1
40 L_0063: ldloc.s V_5
41 L_0065: ldc.i4.1
42 L_0066: add
43 L_0067: stloc.s V_5
44 L_0069: br.s 27 -> ldloc.s V_5
45 L_006b: ldloc.1
46 L_006c: ldc.i4.s 0x2d
47 L_006e: beq 9 -> ldloc.0
48 L_0073: br 14 -> ldloc.0
Come si può notare l'istruzione alla riga 34 prende il char, lo confronta prima con 0x41 ('A'), poi salta alla riga 23 e lo confronta con 0x30 ('0'), salta alla riga 45 e lo confronta con 0x2d ('-'). Quindi con SAE selezioniamo le istruzioni da 23 a 26, click destro/Move e selezioniamo la riga 39 (Type: After), click su Ok e il primo mini-blocco è andato. Eliminiamo l'istruzione di salto alla riga 35 (ex 39) che ora è inutile e proseguiamo. selezioniamo le righe da 44 a 47 (non guardate più il listato sopra, ormai dopo aver spostato il primo mini-blocco gli opcode sono ad offset differenti), click destro/Move e selezioniamo la riga 38 (Type: After), click su Ok. Eliminiamo l'istruzione di salto alla riga 38 e adesso possiamo constatare che è tutto molto più ordinato. Apriamo con Reflector e questa volta TA DAAAAAAAAAA: Ok, Reflector lo decompila ma che roba è...? Bisogna sistemare ancora qualcosina. Vediamo un pò:
23 L_0041: ldloc.s V_5
24 L_0043: ldloc.s V_4
25 L_0045: callvirt System.Int32 System.String::get_Length()
26 L_004a: blt 28 -> ldloc.s V_4
27 L_004f: br 47 -> ldloc.0
28 L_0054: ldloc.s V_4
29 L_0056: ldloc.s V_5
30 L_0058: callvirt System.Char System.String::get_Chars(System.Int32)
31 L_005d: stloc.1
32 L_005e: ldloc.1
33 L_005f: ldc.i4.s 0x41
34 L_0061: beq 9 -> ldloc.0
35 L_0066: ldloc.1
36 L_0067: ldc.i4.s 0x30
37 L_0069: beq 9 -> ldloc.0
38 L_006e: ldloc.1
39 L_006f: ldc.i4.s 0x2d
40 L_0071: beq 9 -> ldloc.0
41 L_0076: br 14 -> ldloc.0
42 L_007b: ldloc.s V_5
43 L_007d: ldc.i4.1
44 L_007e: add
45 L_007f: stloc.s V_5
46 L_0081: br 23 -> ldloc.s V_5
L'istruzione alla riga 22 salta alla riga 42 per caricare l'intero V_5, incrementarlo e ritornare alla riga 23, rimbalzo senza senso. Selezioniamo le righe da 23 a 27, click destro/Move e selezioniamo la riga 46 (Type: After), click su Ok. Eliminiamo i salti alle righe 41 e 46 ormai inutili. Ora, lasciamo stare Reflector perchè ci sono un paio di altre cose da sistemare:
10 L_001c: ldloc.1
11 L_001d: callvirt System.Text.StringBuilder System.Text.StringBuilder::Append(System.Char)
12 L_0022: pop
13 L_0023: br 37 -> ldloc.s V_5
14 L_0028: ldloc.0
15 L_0029: ldloc.1
16 L_002a: call System.Int32 System.Convert::ToInt32(System.Char)
17 L_002f: ldc.i4.1
18 L_0030: sub
19 L_0031: call System.Char System.Convert::ToChar(System.Int32)
20 L_0036: callvirt System.Text.StringBuilder System.Text.StringBuilder::Append(System.Char)
21 L_003b: pop
22 L_003c: br 37 -> ldloc.s V_5
23 L_0041: ldloc.s V_4
24 L_0043: ldloc.s V_5
25 L_0045: callvirt System.Char System.String::get_Chars(System.Int32)
26 L_004a: stloc.1
27 L_004b: ldloc.1
28 L_004c: ldc.i4.s 0x41
29 L_004e: beq 9 -> ldloc.0
30 L_0053: ldloc.1
31 L_0054: ldc.i4.s 0x30
32 L_0056: beq 9 -> ldloc.0
33 L_005b: ldloc.1
34 L_005c: ldc.i4.s 0x2d
35 L_005e: beq 9 -> ldloc.0
36 L_0063: br 14 -> ldloc.0
37 L_0068: ldloc.s V_5
38 L_006a: ldc.i4.1
39 L_006b: add
40 L_006c: stloc.s V_5
41 L_006e: ldloc.s V_5
42 L_0070: ldloc.s V_4
43 L_0072: callvirt System.Int32 System.String::get_Length()
44 L_0077: blt 23 -> ldloc.s V_4
Ora nel nostro ciclo se il char è 'A', '0' o '-' il flusso salta alla riga 9 solo per inserire il char per poi proseguire alla riga 37. Anche questo non ha senso dato che l'istruzione callvirt StringBuilder::Append alla riga 11 è all'interno del blocco IF (il char è 'A', '0' o '-') e non verrà mai chiamata prima che il blocco IF stesso abbia completato il confronto. Selezioniamo le istruzioni dalla riga 9 alla riga 13, click destro/Move e selezioniamo la riga 36 (Type: After), click su Ok. Eliminiamo li salto alla riga 36 (ex 13) ormai inutile. Lasciamo da parte ancora una volta Reflector, tanto avrete già capito che bisogna sistemare l'altro StringBuilder::Append che sarebbe il blocco ELSE (il char non è 'A', '0' o '-').
10 L_001c: ldloc.1
11 L_001d: call System.Int32 System.Convert::ToInt32(System.Char)
12 L_0022: ldc.i4.1
13 L_0023: sub
14 L_0024: call System.Char System.Convert::ToChar(System.Int32)
15 L_0029: callvirt System.Text.StringBuilder System.Text.StringBuilder::Append(System.Char)
16 L_002e: pop
17 L_002f: br 36 -> ldloc.s V_5
18 L_0034: ldloc.s V_4
19 L_0036: ldloc.s V_5
20 L_0038: callvirt System.Char System.String::get_Chars(System.Int32)
21 L_003d: stloc.1
22 L_003e: ldloc.1
23 L_003f: ldc.i4.s 0x41
24 L_0041: beq 32 -> ldloc.0
25 L_0046: ldloc.1
26 L_0047: ldc.i4.s 0x30
27 L_0049: beq 32 -> ldloc.0
28 L_004e: ldloc.1
29 L_004f: ldc.i4.s 0x2d
30 L_0051: beq 32 -> ldloc.0
31 L_0056: br 9 -> ldloc.0
32 L_005b: ldloc.0
33 L_005c: ldloc.1
34 L_005d: callvirt System.Text.StringBuilder System.Text.StringBuilder::Append(System.Char)
35 L_0062: pop
36 L_0063: ldloc.s V_5
37 L_0065: ldc.i4.1
38 L_0066: add
39 L_0067: stloc.s V_5
40 L_0069: ldloc.s V_5
41 L_006b: ldloc.s V_4
42 L_006d: callvirt System.Int32 System.String::get_Length()
43 L_0072: blt 18 -> ldloc.s V_4
L'altro StringBuilder::Append si trova alla riga 15 e, ancora una volta, non ha alcun senso averlo in quella posizione (sopra il blocco IF) dato che dovrebbe trovarsi all'interno del blocco ELSE (il char non è 'A', '0' o '-') . Selezioniamo le istruzione dalla riga 9 alla riga 17, click destro/Move e selezioniamo la riga 31 (Type: After), click su Ok. Eliminiamo li salto alla riga 22 (ex 31) ormai inutile. Bene, ora Reflector dovrebbe mostrare il codice perfettamente decompilato.
Note Finali
Dotfuscator è uno degli offuscatori più semplici da reversare ma è anche il più stabile ed il più utilizzato dagli sviluppatori per proteggere il proprio codice .NET. Ci sono offuscatori molto più ostici a dire il vero e, in un prossimo tutorial magari, non sarebbe male vedere alcune delle tecniche di offuscamento più avanzate, come implemetarle nei nostri programmi e come fixarle o eluderle. Per il momento, aver dato una occhiata al codice MSIL è stato un buon allenamento. Ringrazio quequero per l'incoraggiamento e per la disponibilità, Ntoskrnl per aver creato un tool favoloso come il CFF Explorer e tutta la UIC (così non dimentico nessuno ;p).
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.

