Yado's Krypton v0.5:
Reversing and automatic unpacking!

Data

by anonymous

 

31/01/2004

UIC's Home Page

Published by Quequero


Tanto che va quequero al lardo...
 

Complimentoni, ci son volute quasi due ore per leggerlo tutto ma bravo davvero!

...che ci lascia lo zampino -.-

....

Home page se presente:

....

Difficoltà

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

 

The story of the yado's krypton!
In principio Yado creò un crackme... tutto sommato di semplice soluzione. Il suo nome era Krypton. Ancora l'umanità era all'oscuro di quella che era la verità assoluta riguardo a quel nome! Tempo dopo nacque il Krypton 2. Un essere spaventoso! Nulla a che vedere con la sua prima versione! Un eseguibile in grado di danneggiare irreparabilmente i pc dei poveri utenti a forza di reset! Era l'avvento dello yado's Krypton - The krypter! Era nato! Anche se in una versione non ancora al massimo delle sue facoltà... aveva già provocato la morte di innumerevoli innocenti. Qualcuno doveva fermarlo! Il turno fu di AndreaGeddon, che, col suo impeccabile tutorial, spiegò passo passo la strategia di guerra utilizzata per annientare quel mostro. Ma il krypton non si diede per vinto! Riuscì a perfezionare la sua potenza distruttiva e la sua forza psichica! Adesso l'impresa sarà ancora più ardua! Siamo arrivati al Krypton - The krypter v0.5! Terribile macchina da guerra in grado di trasformare il più innocuo degli eseguibili in un vero e proprio inferno di bogus code!
Un uomo! Un giovane ragazzo deciso a distruggere per sempre questo incredibile nemico, affrontò il viaggio all'interno di quelle istruzioni! Oltrepassati 3 layer di dekrypting riuscì a ricostruire la import table per poi trovarsi di fronte ad altri 5 terrificanti mostri! Gli FF15! Il ragazzo riuscì con l'astuzia ad ingannare quell'applicazione, distruggendone tutte le parti che difettavano del bene umano! L'ora era giunta! Il krypton venne spazzato via! Non rimase più nulla... se non il ricordo di quel programma, dato dall'uguaglianza del virtual offset e del raw offset, vago ricordo di un mapping del file all'interno della memoria... ormai letta e stravolta del terribile ma allo stesso tempo sconfitto Krypton 0.5!
Così anche questa volta l'umanità potrà godere un altro periodo di pace e tranquillità... ma per quanto tempo? o_O


Yado's Krypton v0.5
Reversing and automatic unpacking!
Written by anonymous

Allegati

Introduzione

Se non capite 'na mazza di assembly vi conviene cambiare aria...
Ready to die...
FIGHT!

Tools usati

I tools li scrivo uno alla volta perché non ricordo bene tutto quello che serve per unpackare quei benedetti eseguibili.

1) Masm32 o qualsiasi assemblatore
2) il Krypton 0.5
3) IDA disassembler
4) PEditor
5) Hex workshop
6) Qualche eseguibile qua è là...
7) Un cervello funzionale
8) Un altro cervello... per sicurezza... non si sa mai: uno solo potrebbe non bastare!
9) Un altro cervello ancora (beh... la prudenza non è mai troppa :)
10) Un paio di palle cubiche come i dadi del gioco dell'oca! :S

URL o FTP del programma

www.lockless.com  

Notizie sul programma

E' la quinta versione del krypter... che c'è da dire? E' un krypter per eseguibili quindi va unpackato!  

Essay

Ciao a tutti. Innanzitutto è inutile che andate di fretta perché il lavoro è lungo e al primo errore il pc rimane freezato. Spieghiamo come stanno un po' le cose. Oggi inizio a scrivere questo tutorial... è il 31 dicembre 2003... e sto aspettando un po' di tempo per recarmi a casa dei nonni a fare il canonico cenone di fine anno. Lasciando perdere il 2004 pensiamo al krypton.
Dobbiamo unpackare un eseguibile... per farlo ci serve un eseguibile packato. A questo punto, l'eseguibile lo scrivo da solo. Prendo il compilatore assembly (io uso il masm32) e scrivo "l'eseguibile minimo". Questo vuol dire che ci metto la sezione data, la sezione code con quante meno istruzioni possibili... tanto da permettere al krypton il packing corretto dell'eseguibile stesso. Capirete che è molto più semplice procedere all'unpacking conoscendo dimensioni e istruzioni dell'eseguibile da packare. L'eseguibile non packato potete scaricarlo da qua. Comunque... il codice ve lo mostro direttamente:
; *********************************************
; * cavia.asm
; *
; *********************************************


.386
.model flat, stdcall
option casemap: none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
    msgtext db "Simple code by anonymous", 0
    appname db "anonymous", 0

.code
main:
        push 0
        push offset appname
        push offset msgtext
        push 0
        call MessageBox

        push 0
        call ExitProcess

        call GetModuleHandle
        call GetTickCount

end main

Le ultime due chiamate non servono a nulla... oddio... non servono a nulla per quanto riguarda il codice ma servono per garantire un corretto packing dell'eseguibile in quanto il krypton richiede un certo numero di importazioni dal kernel (se non ricordo male...) comunque con questo codice siste a posto. A questo punto packiamo il file. Per il packing io seleziono queste opzioni:

1. Enable k-exec
2. k-exec on api
3. tutti i livelli del k-execution
4. k-protect on api

Seleziono il mio piccolo eseguibile e lo crypto. Se lo testate per l'esecuzione potete assicurarvi che funziona vedendo la messagebox che appare sullo schermo. Mi sa che possiamo procedere con il reversing.
Prima di tutto è opportuno dire che con il krypton 0.5 o con qualsiasi altro krypter che si rispetti, sferrare un attacco rapito (del tipo settando bpx o provare a dumpare...) non è assolutamente pensabile! Così facendo infatti altro non farete che fare il gioco del krypter e andare a finire dove vuole lui proprio per ingannarvi. Se esiste un modo per unpackare questi eseguibili è stepparci dentro col softice dall'inizio alla fine. Fatevene una ragione.
Adesso io vi suggerirei di giocare un po' di intelletto. Provate a iniziare a steppare col softice all'interno dell'eseguiibile packato. Come potete notare il codice è automodificante... ed è proprio quella la difficoltà del krypter, in quanto, dentro tutto a quel casino è molto difficile capire il codice effettivamente utile. Per questo vi consiglio di prendere carta e penna e di riscrivervi tutte le istruzioni necessarie saltando tutto il bogus code. Però però però... se andate a leggere dentro al krypton... nei ringraziamenti viene citato un certo iNX. Vi spiego meglio come stanno le cose... tutto quel casino che vedete nel codice... non pensate che l'abbia scritto yado facendosi tutti i conticini su dove sarebbe andato a finire l'eip dopo un salto del codice ammucchiato... vi spiego: solitamente la cosa è suddivisa in due fasi, ovvero il normale programma + lo scramble. Nella maggior parte dei casi, salvo eccezioni, il codice originale viene scritto dichiarando tra un'istruzione e l'altra un certo numero di bytes (solitamente di nop). Una volta compilato l'eseguibile, viene utilizzata una seconda applicazione, ovvero lo scrambler, che, nei punti di spazio dichiarati tra le istruzioni va ad infilarci del codice bogus che serve solo a confondere il reverser in questione ovvero me in questo caso :). Il problema di fronte a cui mi pongo io è il seguente: lo scrambler è un programma... e per quanto polimorfico possa essere, deve seguire una certa logica... ora se io riuscissi a capire questa logica, riuscirei a scrivere qualcosa in grado di ripristinare tutti quei nop al posto di tutto quel bogus code? Beh... se non ci riesco per lo meno capisco bene come è sistemato quel codice inutile e vi garantisco che alla fine evitarlo viene naturale.

Ok... oggi è il primo dell'anno. Oggi ho analizzato molto bene quello che succede con lo scrambler del krypton. La conclusione? Hehehe... si... l'ho fatto fuori! In poche parole, il krypton si trasforma completamente... dal programma impossibile e complicato da unpackare alla canonica routine assembly da interpretare. Infatti la difficoltà del programma era proprio in quella marea di codice ad capocchiam da steppare. Vediamo un po' come funziona in linea teorica: il tutto inizia con un JMP che ci porta su un'istruzione, ovvero PUSH ECX. Subito dopo notiamo che c'è una chiamata a una procedura che è sempre la stessa, ovvero:

pop ecx
pushfd
add ecx, -19
popfd
jmp ecx

Questa qua è il nostro mito. Siccome dovrò citarla spesso all'interno del tutorial, per convenzione la chiameremo "famosa". ^^' Adesso però spieghiamo un po' come funziona il resto del codice. Prendiamo come esempio l'inizio dell'inferno, ovvero il bogus code che si trova a partire da 00447015, che inizia con il nostro bel jump. Vediamo lo scheletro principale di tutto lo scrambling:

_______:00447015 jmp short loc_0_44705A ; questo è il jmp che dà inizio alle danze
_______:00447015 start endp             ; e che ci porta esattamente qua -+
_______:00447015                        ;                                 |
_______:00447015 ; -------------------------------------------------------|---------
_______:00447017 dd 584E69DFh, 0F07259DFh, 73DF01EBh, 9C59DFEBh, 9DE7C183 |
_______:00447017 dd 0DEBE1FFh, 0FFF0E851h, 0B1DFFFFFh, 9A3F22A3h, 0E883F9 |
_______:00447017 dd 0DF47EB06h, 0DF584E69h, 0EBF37959h, 0EE78DF01h        |
_______:0044704F db 0DFh                                                  |
_______:00447050                                                          |
_______:00447050 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ SUBROUTINE ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦|¦¦¦¦¦¦¦¦¦
_______:00447050                                                          |
_______:00447050                                                          |
_______:00447050 sub_0_447050 proc near ; CODE XREF: _______:0044705B p   |
_______:00447050 pop ecx                                                  |
_______:00447051 pushf                                                    |
_______:00447052 add ecx, 0FFFFFFE7h                                      |
_______:00447055 popf                                                     |
_______:00447056 jmp ecx                                                  |
_______:00447056 sub_0_447050 endp                                        |
_______:00447056                                                          |
_______:00447058 ; -------------------------------------------------------|---------
_______:00447058 jmp short loc_0_447067                                   |
_______:0044705A ; -------------------------------------------------------|---------
_______:0044705A                                                          |
_______:0044705A loc_0_44705A: ; CODE XREF: start+15 j <------------------+
_______:0044705A push ecx              ; ecx viene pushato
_______:0044705B call sub_0_447050     ; qua viene chiamata la famosa proc :)
_______:00447060 db 3Eh                ; <- 00407060 dovrebbe essere l'eip di...
_______:00447060 mov ebx, eax          ;    ritorno dopo la chiamata a quella proc...
_______:00447063 db 66h

Allora questo è lo scheletro principale dell'esecuzione del bogus code. All'indiricco 0044705B viene chiamata la nostra famosa procedura. Di conseguenza, in un normale listato assembly, dopo il ret all'interno della procedura chiamata si dovrebbe ritornare all'esecuzione del codice principale partendo da 00447060 (esattamente dopo la call...). Infatti, dopo che una procedura viene chiamata, in [esp] c'è il valore di ritorno di questa procedura. Una volta saputo questo, possiamo continuare l'analisi del nostro codice commentando la nostra famosa procedura:

_______:00447050 sub_0_447050 proc near ; CODE XREF: _______:0044705B p
_______:00447050 pop ecx        ; questo mette in ecx il valore di ritorno della proc
_______:00447051 pushf          ; pusha tutti i flags per evitare inconvenienti
_______:00447052 add ecx, -19h  ; sottrae ad ecx -19h
_______:00447055 popf           ; ripoppa i flags precedentemente pushati
_______:00447056 jmp ecx        ; salta ad ecx
_______:00447056 sub_0_447050 endp

Vediamo meglio anche qua: la prima istruzione è "pop ecx". Guardiamo un po' di cosa si tratta... qualche rigo fa abbiamo detto che dopo una chiamata a procedura, in [esp] c'è l'indirizzo di ritorno della procedura chiamata. Facendo "pop ecx", si mette in ECX questo indirizzo e lo stack viene modificato (ma queste cose dovreste già saperle -.-). La pushf è solo un'istruzione preventiva in quanto sottraendo 19h ad ecx si potrebbe andare incontro ad un cambiamento dei flags, rischiando così di cambiare il normale corso dell'applicazione... così con la pushf vengono salvati per poi essere ripristinati dopo la sottrazione. Con "add ecx, -19h" viene sottratto 19h da ecx... quindi il normale valore di ritorno viene modificato portando l'esecuzione del codice a 19h bytes prima di quella originale. Popf ripristina i flags mentre "jump ecx" sostituisce il ret poiché all'inizio della subroutine è stata poppata una DWORD dallo stack, quindi al salto lo stack risulterà compensato. In pochissime parole, quella chiamata è uno pseudo-salto che ci porta all'interno di altro bogus, prima della procedura famosa ^^'. Rivediamo ora lo scheletro principale dopo queste nuove informazioni :D

_______:00447015 jmp short loc_0_44705A ; questo è il jmp che dà inizio alle danze
_______:00447015 start endp             ; e che ci porta esattamente qua -+
_______:00447015                        ;                                 |
_______:00447015 ; -------------------------------------------------------|---------
_______:00447017 dd 584E69DFh, 0F07259DFh, 73DF01EBh, 9C59DFEBh, 9DE7C183 |
_______:00447017 dd 0DEBE1FFh, 0FFF0E851h, 0B1DFFFFFh, 9A3F22A3h, 0E883F9 |

_______:00447017 dd 0DF47EB06h, 0DF584E69h, 0EBF37959h, 0EE78DF01h        |
_______:0044704F db 0DFh
                                               ^ |
_______:00447050                                                        | |
_______:00447050 ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ SUBROUTINE ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦|¦|¦¦¦¦¦¦¦¦¦
_______:00447050                                                        | |
_______:00447050                                                        | |
_______:00447050 sub_0_447050 proc near ; CODE XREF: _______:0044705B p | |
_______:00447050 pop ecx                                                | |
_______:00447051 pushf                                                  | |
_______:00447052 add ecx, 0FFFFFFE7h                                    | |
_______:00447055 popf                                                   | |
_______:00447056 jmp ecx                                                | |
_______:00447056 sub_0_447050 endp                                      | |
_______:00447056                                                        | |
_______:00447058 ; -----------------------------------------------------|-|---------
_______:00447058 jmp short loc_0_447067                                 | |
_______:0044705A ; -----------------------------------------------------|-|---------
_______:0044705A                                                        | |
_______:0044705A loc_0_44705A: ; CODE XREF: start+15 j <----------------|-+
_______:0044705A push ecx              ; ecx viene pushato              |
_______:0044705B call sub_0_447050 --+ ; qua viene chiamata la famosa pr|c :)
_______:00447060 db 3Eh -------------+----------------------------------+  di...
_______:00447060 mov ebx, eax          ;    ritorno dopo la chiamata a quella proc...
_______:00447063 db 66h

Mooooolto bene. Quella in azzurro è la freccia che ci porta vicino al punto che ci interessa. Dico vicino perché là dentro IDA ha visualizzato tutto quel codice come una serie di DWORD. Per vedere bene di cosa si tratta, diciamo ad ida di visualizzare quel codice non in DWORD ma in singoly bytes in modo da trasformare il tutto il codice a partire dal punto esatto, in modo da capire di cosa si tratta. Andando all'inizio di quel blocco e premendo il tasto Undefine si ottiene la visualizzazione in bytes di tutta la massa di codice:

_______:00447015 ; -----------------------------------------------------------------
_______:00447017 db 0DFh  ; ¯
_______:00447018 db 69h   ; i
_______:00447019 db 4Eh   ; N
_______:0044701A db 58h   ; X
_______:0044701B db 0DFh  ; ¯
_______:0044701C db 59h   ; Y
_______:0044701D db 72h   ; r
_______:0044701E db 0F0h  ; ­
_______:0044701F db 0EBh  ; Ù
_______:00447020 db 1     ;
_______:00447021 db 0DFh  ; ¯
_______:00447022 db 73h   ; s
_______:00447023 db 0EBh  ; Ù
_______:00447024 db 0DFh  ; ¯
_______:00447025 db 59h   ; Y
_______:00447026 db 9Ch   ; £
_______:00447027 db 83h   ; â
_______:00447028 db 0C1h  ; -
_______:00447029 db 0E7h  ; þ
_______:0044702A db 9Dh   ; Ø
_______:0044702B db 0FFh  ;
_______:0044702C db 0E1h  ; ß
_______:0044702D db 0EBh  ; Ù
_______:0044702E db 0Dh   ;
_______:0044702F db 51h   ; Q
_______:00447030 db 0E8h  ; Þ
_______:00447031 db 0F0h  ; ­
_______:00447032 db 0FFh  ;
_______:00447033 db 0FFh  ;
_______:00447034 db 0FFh  ;
_______:00447035 db 0DFh  ; ¯
_______:00447036 db 0B1h  ; ¦
_______:00447037 db 0A3h  ; ú
_______:00447038 db 22h   ; "
_______:00447039 db 3Fh   ; ?
_______:0044703A db 9Ah   ; Ü
_______:0044703B db 0C0h  ; +
_______:0044703C db 0F9h  ; ¨
_______:0044703D db 83h   ; â
_______:0044703E db 0E8h  ; Þ
_______:0044703F db 6     ;
_______:00447040 db 0EBh  ; Ù
_______:00447041 db 47h   ; G
_______:00447042 db 0DFh  ; ¯
_______:00447043 db 69h   ; i
_______:00447044 db 4Eh   ; N
_______:00447045 db 58h   ; X
_______:00447046 db 0DFh  ; ¯
 ______:00447047 db 59h   ; Y  ; questa è la riga su cui salta la proc. famosa.
_______:00447048 db 79h   ; y
_______:00447049 db 0F3h  ; ¾
_______:0044704A db 0EBh  ; Ù
_______:0044704B db 1     ;
_______:0044704C db 0DFh  ; ¯
_______:0044704D db 78h   ; x
_______:0044704E db 0EEh  ; ¯
_______:0044704F db 0DFh  ;

Verifichiamo: seguendo lo scheletro principale del bogus, dopo la chiamata alla famosa, si dovrebbe arrivare su 00447060. Tutto quello che dobbiamo fare è sottrarre 19h per vedere dove si arriva:

 00447060 - ; questo dovrebbe essere il regolare indirizzo di ritorno
       19 = ; togliamo 19h
-----------
 00447047   ; e otteniamo il nostro caro indirizzo di arrivo :D

Siamo quindi nella tecnica del Delta offset: il codice utile è sistemato in modo tale da trovarsi 19h bytes prima del codice bogus eseguito per trovarsi al punto giusto al momento del salto. Bando alle chiacchiere... andiamo con ida su quel diavolo di indirizzo e premiamo la Code per convertire il tutto in codice. Il risultato è questo:

_______:00447047 ; -----------------------------------------------------------------
_______:00447047 pop ecx                 ;
_______:00447048 jns short loc_0_44703D  ; eccolo qua! :D
_______:0044704A jmp short loc_0_44704D ----+
_______:0044704A ; -------------------------|---------------------------------------
_______:0044704C db 0DFh ; ¯                |
_______:0044704D ; -------------------------|---------------------------------------
_______:0044704D                            |
_______:0044704D loc_0_44704D: ; CODE XREF: |______:0044704A j
_______:0044704D js short loc_0_44703D <----+
_______:0044704D ; -----------------------------------------------------------------

Prima istruzione: "pop ecx" che serve per compensare lo stack (poiché prima della chiamata alla famosa c'è un push ecx... ricordate?). Subito dopo abbiamo un JNS che salta ad un determinato indirizzo... poi abbiamo un JMP in caso di salto non eseguito e poi un JS... che salta allo stesso identico indirizzo del JNS di prima... questo fatto deve farvi riflettere un po'. Ve lo spiego subito... o grazie al JNS o grazie al JS, il salto verrà sempre fatto in base a come sn settati i flags. Detto in altre parole, anche questo sistema serve per confondervi le idee. Siccome lo scrambler, al momento dell'encrypting dell'eseguibile, non conosce come sono settati i flags al momento del passaggio da quel punto, per assicurarsi di arrivare al punto del "codice utile" è costretto a settare due jumps in modo che uno dei due per forza alla fine salterà al punto desiderato. JNS e JS servono per confondervi le idee... noi abbiamo preso in esame la prima parte del bogus code, ma successivamente incontrerete una roba simile ma con i tipi di jump diversi ad esempio troverete un JNO insieme ad un JO, un JNP insieme ad un JP, un JNZ insieme ad un JZ... non so se mi spiego... beh se non mi spiego i problemi sono vostri non miei. ^^' Si giunge alla conclusione che la destinazione di quei salti è il punto in cui si trova il nostro benedettissimo "codice utile"! Infatti se da ida premiamo INVIO su quel salto là arriviamo esattamente qua:

_______:0044703D loc_0_44703D: ; CODE XREF: _______:00447048 j
_______:0044703D ; _______:0044704D j
_______:0044703D sub eax, 6                              ; istruzione utile :D
_______:00447040 jmp short near ptr dword_0_447071+18h   ; ricomincia un altro bogus!

Grosso modo, per tutto il programma le istruzioni son sempre disposte in questo modo: codice utile - codice bogus - codice utile - codice bogus, bla bla bla bla...
E così abbiamo capito come funziona tutta 'sta roba qua. Ora vogliamo o no farci un generic remover per tutta quella sfilza di bytes in modo da steppare un codice scorrevole e comprensibile? Sii! Vediamo in linea teorica come si può fare (che poi alla fine io dico "vediamo se si può fare" quando in realtà l'ho già fatto -.-). Innanzitutto impariamo a capire dove è che inizia questo codice... abbiamo detto che inizia con un JMP e fin qua ci siamo ma dobbiamo saper riconoscere se quel JMP è del bogus oppure è un normale jump che fa parte del codice utile. Niente di più facile e di più statico! Se osservate bene l'inizio di ogni bogus (intendo osservare gli opcodes) visualizzerete sempre una roba di questo tipo:

EB XX jmp short loc_0_44705A ; questo è il salto iniziale
DF
69
4E
58
DF
59

In ogni punto di inizio del bogus troverete sempre l'opcode EB che è quella del JMP seguita da un'opcode variabile che indica la lunghezza del salto. Dopo queste due opcode ce ne sono altre che sono sempre DF 69 4E ecc... (a noi ce ne interessano poche per verificare se siamo in bogus o meno). Quindi... nel file dobbiamo cercare tutti gli EB. Dopo di ciò dobbiamo controllare che la word che segue saltando un byte sia uguale a 69DF. Se tutto rientra nel caso, siamo in opcodes e siamo quindi pronti per rimuoverlo dall'esecuzione. Sposteremo il nostro puntatore di tanti bytes più avanti quanti ne sono indicati dall'opcode che indica la lunghezza del salto. Una volta arrivati a questo punto (siamo sul "pop ecx" prima della famosa chiamata) basta avanzare di 6 bytes ("pop ecx": 1 byte + 5 bytes per la call... arriviamo a 6) per arrivare a quello che dovrebbe essere il normale valore di ritorno di questa call. Sottraiamo 19h da questo indirizzo ed arriviamo al gruppo di bytes che contiene quei salti associati. Prendiamo il primo (tanto tutti e due saltano alla stessa posizione)... ci estrapoliamo la lunghezza di questo salto, la sottraiamo dal nostro attuale indirizzo ed arriviamo al codice utile. Prendiamo l'offset del codice utile, sottraiamo l'offset di destinazione e otteniamo come risultato la lunghezza del salto da effettuare per arrivare direttamente dal salto iniziale al codice utile. Da questa togliamo 2 perchè sono il numero degli opcodes del jump e sostituiamo il nostro valore con il vecchio XX e il gioco sarà fatto! Ora... siccome qua in mezzo a questa spiegazione non capirete un'emerita mazza, vi scrivo il mio codice asm (fa skifo... non è ottimizzato per niente...) poi ve lo debuggate come diavolo volete per vedere di capire quanto meglio le operazioni che ho fatto:

; *********************************************
; * descrambler.asm
; *
; *********************************************


.386
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\comdlg32.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\comdlg32.lib

.data
    ofn OPENFILENAME <>
    FileName db 0 dup (255)
    hFile dd 0
    FileSize dd 0
    hInstance dd 0
    AppName db "descrambler by anonymous", 0
    _file_error_message db "Can't open selected file!", 0
    lpData dd 0
    mdw dd 0

    jump_offset dd 0
    questo_offset dd 0

    outfile db "c:\windows\desktop\outfile.exe", 0

.code
main:
        mov ecx, sizeof OPENFILENAME
        xor eax, eax
        lea edi, ofn
        rep stosb

        mov [ofn.lStructSize], sizeof OPENFILENAME
        mov [ofn.lpstrFile], offset FileName
        mov [ofn.nMaxFile], 255

        push 0
        call GetModuleHandle
        mov [ofn.hInstance], eax

        mov [ofn.Flags], OFN_HIDEREADONLY or OFN_EXPLORER

        push offset ofn
        call GetOpenFileName
        cmp eax, 0
        jz _quit

        push 0
        push 0
        push OPEN_EXISTING
        push 0
        push 0
        push GENERIC_READ
        push offset FileName
        call CreateFile
        mov [hFile], eax
        inc eax
        jz _file_error

        push 0
        push [hFile]
        call GetFileSize
        mov [FileSize], eax

        push PAGE_EXECUTE_READWRITE
        push MEM_COMMIT
        push eax
        push 0
        call VirtualAlloc
        mov [lpData], eax

        push 0
        push offset mdw
        push [FileSize]
        push [lpData]
        push [hFile]
        call ReadFile

        push [hFile]
        call CloseHandle

        mov ecx, [FileSize]
        mov eax, [lpData]

    _loop1:
        cmp ecx, 0
        jz _end_loop1

        cmp byte ptr [eax], 0ebh
        jz _is_eb
        dec ecx
        inc eax
        jmp _loop1

    _is_eb:
        cmp word ptr [eax+2], 69dfh
        jz _is_bogus
        dec ecx
        inc eax
        jmp _loop1

    _is_bogus:
        pushad

        mov [jump_offset], eax ;indirizzo del salto iniziale
        xor edx, edx
        mov dl, [eax+1]        ;dl = lunghezza del salto
        add eax, edx           ;eax = offset "push ecx" prima della proc famosa
        add eax, 6             ;eax = fake retval
        sub eax, 19h           ;eax = jump_zone
        add eax, 3             ;eax punta al secondo salto
        xor ebx, ebx
        mov bl, [eax+1]
        not bl
        sub eax, ebx
        inc eax
        mov ebx, [jump_offset] ;vengono presi i due indirizzi per il delta-offset
        sub eax, ebx           ;al contiene l'esatto valore :-)
        sub al, 2
        mov ebx, [jump_offset]
        mov byte ptr [ebx+1], al ;mettiamo il valore al suo posto :D

        popad

        inc eax
        dec ecx
        jmp _loop1

    _end_loop1:
        push 0
        push 0
        push CREATE_ALWAYS
        push 0
        push 0
        push GENERIC_WRITE
        push offset outfile
        call CreateFile
        mov [hFile], eax

        push 0
        push offset mdw
        push [FileSize]
        push [lpData]
        push eax
        call WriteFile

        push [hFile]
        call CloseHandle

    _quit:
        push 0
        call ExitProcess

    _file_error:
        push MB_ICONERROR or MB_OK
        push offset AppName
        push offset _file_error_message
        push 0
        call MessageBox
        jmp _quit

end main

Questo è il mio codice utilizzato momentaneamente per rimuovere tutto quel codice inutile. Se ho fatto bene i calcoli, selezionando il file packato, sul desktop dovrebbe venire salvato un file di nome outfile.exe che dovrebbe eseguirsi senza grossi problemi. Infatti il file viene salvato e... CA**O! Non funziona! O meglio dà errore ad un determinato punto! Hmmm... deve esserci qualche errore nel codice... no impossibile sono troppo bravo per sbagliare... non ci resta che steppare (tanto per la prima parte è facilissimo... il codice è comprensibilissimo e il tempo si riduce drasticamente :DD). Steppando, in pochissimo tempo si giunge in un punto in cui c'è un JMP EAX. Ovviamente un reverser, quando vede quest'istruzione, capisce che è sulla buona strada! :D Steppando (o anche guardando con ida), vi sarete resi conto che fino a quel punto non ci sono controlli o tricks speciali da evitare o cose del genere... quindi saltate ad EAX che vi porta su 00404000. Adesso è ora di guardare la section table dell'eseguibile packato:

SECTION VIRTUAL SIZE VIRT. OFFSET RAW SIZE RAW OFFSET CHARACT.
YADO 0000003C 00001000 00000000 00000000 E0000020
YADO 000000C6 00002000 00000000 00000000 C0000040
YADO 00000023 00003000 00000000 00000000 C0000040
krypton 00043000 00004000 00000000 00000000 E0000020
_!_!_!_ 00014000 00047000 000132CE 00000400 E0000020

Diamo anche uno sguardo all'entry point... quello dell'exe packato è uguale a 47000. Il programma parte quindi dall'ultima sezione(_!_!_!_). Quando noi arriviamo al nostro JMP EAX, veniamo spediti su 00404000, ovvero all'inizio della sezione "krypton". Ora accendete il cervello... al momento dell'avvio del processo, questa sezione non ha nè raw size nè raw offset. Questo può indicare solo una cosa: che è stata creata a runtime dalla sezione _!_!_!_. A sua volta questo implica che ci sono stati sicuramente degli spostamenti di bytes e chissà quali altre operazioni (non mi sono steppato niente per capire... sono andato avanti e basta). Per questo, il lavoro svolto dal mio descrambler, per quella sezione, avrà di certo generato qualche errore. Ora ci sono 2 opzioni: 1) quella di effettuare la correzione manuale dei salti sbagliati (io l'ho fatto non sono molti, ma ve lo sconsiglio); 2) dato che fino a 00404000 non ci sono stati imprevisti di esecuzione, io suggerirei di partire dal packato originale e di fare, col softice, "g 00404000" in modo da arrivare in quel punto senza problemi. Una volta arrivati là, blocchiamo il processo per prepararci a dumparlo. Vediamo, tutto inizia così:

8B 0C 24            mov ecx, [esp+0]

Questa è la prima riga a 00404000. Innanzitutto prendiamo nota dei primi 2 opcode (8B 0C). Per bloccare il processo, inseriamo dei nostri opcodes, e precisamente EB FE che corrispondono all'istruzione "jmp eip". In questo modo, effettuiamo un jump sempre sullo stesso punto, così il processo rimane bloccato. Per inserire gli opcode, facciamo col softice "ew eip FEEB" (FEEB sarebbero le opcode EB FE scritte in notazione intel in formato DWORD). Ora che il nostro processo rimarrà bloccato, usciamo fuori dal softice e mettiamo le mani sul PEditor! Clicchiamo sul pulsante "tasks" per ottenere la lista di tutti i processi correntemente attivi sul sistema. Dalla lista, scorriamo in basso fino ad incontrare il percorso del processo del nostro eseguibile (bloccato su quel punto). Tasto destro del mouse -> dump -> dump full. Salviamo il file in una cartella... dove volete... e poi con il tasto terminate process, interrompiamo il processo precedentemente bloccato. Che cosa abbiamo fatto? Si chiama dumping... se non sapete che cosa significa, lasciate questo tutorial e datevi fuoco. Bene... ora andiamo a ritoccare manualmente il file dumpato. Innanzitutto la sezione _!_!_!_ non serve più perché l'abbiamo tutta steppata. Quella sezione serve per ricostruire tutta la section table dell'eseguibile (dopo ci daremo uno sguardo). Settiamo l'entry point su 4000 (ovvero il punto in cui ci eravamo fermati, in modo da ricominciare direttamente da là). Però però... dobbiamo ripristinare le opcode che avevamo modificato per bloccare il processo... niente di più semplice. Prima di tutto diamo uno sguardo alla section table che il file assume dopo l'esecuzione della sezione _!_!_!_:

SECTION VIRTUAL SIZE VIRT. OFFSET RAW SIZE RAW OFFSET CHARACT.
YADO 0000003C 00001000 0000003C 00001000 E0000020
YADO 000000C6 00002000 000000C6 00002000 C0000040
YADO 00000023 00003000 00000023 00003000 C0000040
krypton 00043000 00004000 00004300 00004000 E0000020
_!_!_!_ 00014000 00047000 00014000 00047000 E0000020

Come potete vedere, si giunge facilmente alla conclusione che il programma è stato unpackato e che ora bisogna steppare a partire dalla sezione krypton! Siccome per dumpare abbiamo usato il PEditor, l'eseguibile risulterà perfettamente allineato. Per cui, se dobbiamo patchare i bytes di 00404000, il raw offset sarà 4000. Non credo vi servano commenti per patchare due bytes, no? Quindi mano all'hex workhsop e patchate con 8B 0C. Se ora ricominciamo a steppare, il programma funzionerà benissimo, per cui, riprocessiamo questo file col mio descrambler per steppare anche questo pezzo di codice senza troppi problemi! Ovviamente anche in questo caso, a causa delle manovre del krypton, l'eseguibile modificato dal mio programma inevitabilmente crasherà ad un determinato punto, ma prima di arrivare là ne passerà di tempo per cui stepperemo senza grosse difficoltà. Alla fine a noi è questo che interessa! Adesso però scordatevi completamente tutti i trucchetti e i descrambling possibili perché dovete solo armarvi di carta e penna per scrivere ogni istruzione e commentare rigo per rigo! Ecco il mio codice commentato (miticooo! :) [attenzione il mio codice lo scrivo molto schematizzato per farvi capire meglio la situazione. Il tutto non rispetterà esattamente il vero codice del krypton]

00404000:   mov    ecx, [esp]            ; ecx = retval nel kernel
            call   ...
            mov    [ebp+0044780C], ecx
            push   eax
            push   esi
00443F66:   xor    edx, edx              ; azzera EDX
            dec    ecx                   ; decrementa ECX (nel kernel)
            mov    dx, [ecx+3C]          ; mette in DX una word dal kernel
            test   dx, F800              ; controlla se dx è uguale a F800
            jnz    00443F66
            cmp    ecx, [edx+ecx+34]     ; se è uguale fa anche questo controllo
            jnz    00443F66
            mov    [ebp+0044994B], ecx   ; tutto va bene solo se in ECX si ha
                                         ; l'indirizzo in memoria del kernel
                                         ; che poi verrà salvato in [ebp+0044994B] :)

Questa è una delle procedure importanti. Come ho accennato prima, vi scriverò soltanto il codice principale del krypton, limitandomi nella scelta a quello che serve per la comprensione del funzionamento del kryptrer. Capirete pure che è disumano copiare tutto il programma no? ^^' Vediamo ora il punto in cui viene chiamata una VirtualAlloc per capire a cosa serve:

            mov    eax, [ebp+0044992C] ; eax = indirizzo della VirtualAlloc
            cmp    byte ptr [eax], CC  ; questo è un anti-bpx: se avete messo un
            jz     00445F69            ; bpx sulla VirtualAlloc, il codice salta
            ...                        ; ad errore e vi porta dove dice lui.
            call   [ebp+0044992C]      ; altrimenti chiama normalmente VirtualAlloc.
            mov    [ebp+00449953], eax ; eax = 006A0000 viene salvato in una var.

Questa procedura viene ripetuta per due volte (però in due punti diversi). In effetti vengono allocate 2 porzioni di memoria per il momento vuote. Andando più avanti nel codice possiamo accorgerci a che cosa vengono usate (tenendo presente che sul mio sistema la seconda VirtualAlloc ha dato come risultato 006B0000):

            mov    esi, eax
            mov    esi, edi
            mov    eax, 0044794B
            add    eax, ebp        ; EAX contiene un indirizzo del programma
            mov    ecx, 0003FEBE   ; ECX prende una dimensione da elaborare
            xor    ebx, ebx        ; viene azzerato EBX
004459A4:   mov    bl, [eax]       ; BL prende un carattere dal codice
            ror    bl, cl          ;
            xor    bl, cl          ;
            add    bl, cl          ; in questo pezzo di codice elabora il carattere..
            xor    bl, cl          ;
            ror    bl, cl          ;
            mov    [edi], bl       ; il carattere elaborato viene messo in [EDI]
            inc    edi             ; incrementa EDI
            inc    eax             ; incrementa EAX
            loopnz 004459A4        ; finisce se ECX = 0 decrementandolo ogni volta

In pratica... la seconda VirtualAlloc ha allocato dello spazio per il momento vuoto. Nella prima istruzione della routine sopra parte subito dopo la seconda VirtualAlloc, quindi con EAX che punta nella zona di memoria appena allocata (nel mio caso 006B0000). Quindi, osservando il codice, questo spazio viene riempito prendendo dei bytes del codice del programma e decryptandoli secondo quella piccola routine. Secondo voi questo che cosa significa? Significa che il mio programma farà cilecca nel punto in cui il krypton salterà dentro quell'allocazione di memoria! Infatti il mio descrambler va alla ricerca del bogus code cercando tutti i bytes che lo compongono. Ovviamente all'inizio, siccome quei bytes devono ancora essere decryptati, il mio programmino non troverà un'emerita mazza! Ma siccome noi abbiamo (per definizione) un'intelligenza, facciamo un'altra semi-release del decrambler che andrà a decryptare subito quel pezzo di codice e toglierà il bogus. Dobbiamo anche ricordarci di noppare tutta questa procedura di decrypting perché noi oltre che a togliere il bogus da quel pezzo di file, decrypteremo anche il tutto. Vediamo un po' di farci i nostri bei calcoletti: prima EAX prende un indirizzo, ovvero 0044794B. Poi a questo gli viene aggiunto EBP. Alla fine si avrà EAX = 00404008. Siccome il file è stato dumpato col peditor, il raw offset corrispondente sarà 00004008. Quindi io ho fatto un programmino che esegue la stessa identica routine di decrypting a partire da 00404008 fino a 00404008 + 0003FEBE. Ora che abbiamo il codice già decifrato, dobbiamo noppare le operazioni di decrypting della routine del file. La routine inizia a 004459A4. Gli indirizzi su cui si trovano le istruzioni sono questi:

004459C6    ror bl, cl
004459EA    xor bl, cl
00445A0E    add bl, cl
00445A31    xor bl, cl
00445A56    ror bl, cl

Ogni istruzione di queste occupa 2 bytes. Per cui ogni istruzione deve essere rimpiazzata da 2 NOP (90h). I raw offset corrispondenti sono questi:

004459C6    ->    000459C6
004459EA    ->    000459EA
00445A0E    ->    00045A0E
00445A31    ->    00045A31
00445A56    ->    00045A56

Ok. La prima routine è stata rimossa dal codice. Per questo, arrivati a quel punto, l'eseguibile si limiterà a copiare quei bytes all'interno della memoria allocata dalla VirtualAlloc. Ora, siccome abbiamo sul file i bytes del codice già decifrati, possiamo tranquillamente usare il descrambler (con quale piccola modifica) in modo da farlo funzionare solo sul nuovo pezzo di codice per rimuovere il bogus anche da là in mezzo. Ovviamente lascio a voi effettuare le modifiche allo scrambler... non posso perdere ogni volta tutto il tempo a scrivere il codice. ^^'
Ho effettuato il descrambling, ma come al solito qualcosina va storta... se devo essere sincero non capisco come mai non va tutto alla perfezione, comunque, per fortuna sono solo piccolezze da aggiustare. Ogni volta che il programma mi dà errore, prendo il file con il bogus, vado allo stesso indirizzo dell'errore per vedere dove devo effettuare la correzione. Vabè, le correzioni non sono difficili da inserire. Andando avanti col codice arriviamo ad un punto importante, ovvero all'indirizzo 006BB32A:

mov si, 'FG'
mov di, 'JM'
int 3

Fate molta arrenzione perché questo codice fa eccezione: se quando viene eseguito l'int 3, SI e DI hanno quei valori là, l'int 3 diventa un ottimo sistema per vedere se nel sistema è presente un debugger (come il SoftICE in questo caso). In situazioni normali, questa chiamata darebbe errore, ma siccome il debugger è presente, non succederà assolutamente nulla. Il nostro compito è quello di far comportare questo codice come se ci fosse il debugger, ovvero facendo dare errore al codice. Come fare? Beh, basta cambiare il valore di SI e di DI a runtime in modo che l'int 3 sia una semplicissima chiamata a interruzione. Così facendo dà errore. Attenzione... non dobbiamo steppare sull'int 3 col softice ma dobbiamo andare a continuare il nostro codice sulla SEH (structured exception handler, se non sapete cos'è, andate a farvi benedire). Comunque siamo magnanimi: come fare per vedere l'indirizzo della seh? Dal softice digitare:

dd fs:0

Così nella data window del softice appariranno tutte le dword della struttura fs:[0000]. Prendiamo la prima dword e facciamo:

dd ds:xxxxxxxx (dove al posto delle x ci mettete la dword)

Ancora una volta compariranno altre dword nella data window. Di queste, la seconda a partire da sinistra corrisponde all'indirizzo del gestore di eccezioni. Quindi, per andare là sopra, prima di eseguire l'int 3, dopo aver cambiato DI e SI, digitiamo "g" seguito dall'indirizzo della procedura.
Continuando l'esecuzione del codice, arriviamo in un punto dove possiamo vedere del codice sistemato in questo modo:

pushfd                    ; pusha i flag nello stack
or byte ptr [esp+1], 1    ; abilita il trap flag
popfd                     ; ripoppa i flags col trap flag abilitato
nop                       ; single step

A prima vista non sembra nulla di particolare. In realtà fa eccezione anche questo. Si tratta del trap flag. Se questo flag è abilitato, lo step che viene eseguito col debugger provocherà un'eccezione. Per ovviare anche a questo inconveniente, basterà mettere un breackpoint nella riga successiva alla nop in modo da non steppare col softice su quel codice e tutto andrà per il verso giusto. Arrivati a questo punto, devo darvi una tristissima notizia... continuando di questo passo, dovrete abbandonare il file senza bogus code e prendere sotto stepping il file kryptato con tutte le protezioni di questo mondo. Questo perché ci sono dei controlli relativi all'integrità del codice (violata più di una volta col descrambler ecc... bla bla) e poi successivamente anche vengono eseguite delle istruzioni con un tipo di bogus code diverso. Leggendo la spiegazione data prima su quel tipo di bogus, potete facilmente intuire che sarebbe disumano creare tutte le procedure per rimuovere altro bogus differente (anche perché queste istruzioni saranno allocate in memoria e subiranno modifiche a runtime). Detto questo... il descrambler ci ha facilitato la vita fino ad ora... ma è arrivato il momento di subire la vera potenza del krypton... non vi preoccupate perché ovviamente la faticaccia l'ho fatta io... a voi vi dico direttamente le cose principali perché i dettagli non sono "spiegabili"... se ne avete voglia (see see... -.-) steppatevi il programma dalla prima all'ultima istruzione.
Continuando a steppare, trovate altri due int 3 agli indirizzi 006B0BEE e 006B6638. Continuando il lavoro arriverete ad una chiamata a CreateThread. Arrivati qua dovete mettere un breackpoint sul thread creato (006B6D35) e premere F5 per continuare lo stepping da là dentro. Arrivati a 006BCDAF troverete un'altro int 3. Poco dopo abbiamo 2 chiamate a CreateFile: una che scrive su disco un file che si chiama kkk.tmp. Vediamo un po'... se andate a vedere di che file si tratta, scoprirete che è un driver... precisamente è l'antisoftice. E' facile... al momento della chiamata andate a cambiare il nome di questo file in modo che la seconda CreateFile (chiamando il file col nome originale) non trovi più questo file fallendo la chiamata al driver.
Adesso che il gioco sembra quasi fatto, arriva una parte inaspettata... come tutorial, vi dico io che cosa succede. Voi vi aspetterete delle routine di decrypting per il codice... beh si... è così... ma queste routine non sono sempre le stesse, o per meglio dire... sono polimorfiche. Precisamente succede che le istruzioni di decrypting possono essere spostate, posposte o anteposte le une alle altre. Il modo migliore è quello di copiare per intero la routine che si trova nel file kryptato per poi eseguirla nel nostro codice (ovviamente l'indirizzo della routine non cambia, quindi dobbiamo solo prelevare i bytes). Ricapitolando velocemente, Vediamo di cosa abbiamo bisogno per decryptare completamente il file:

  1. Copiare tutte le routine che servono per decifrare le sezioni di codice

  2. Individuare l'indirizzo della IT originale

  3. Individuare e copiare la routine di ripristino della IT

  4. Aggiustare le FF15 (maledetti... :)

  5. Rimuovere il loader del krypton dall'eseguibile

  6. Aggiustare gli Header del PE in base alle nuove modifiche

Procedendo per ordine, vediamo di individuare tutte le routine di decrypting per le sezioni di codice e di data. Innanzitutto bisogna dire che ci troviamo all'interno dello spazio allocato in memoria (006B0000). Ovviamente, ogni volta che dobbiamo copiare qualche informazione, dobbiamo ricavare il raw offset ed applicare la routine di decrypting applicata precedentemente dal mio descrambler per questa stessa porzione di memoria (infatti ricordate che alla fine il nostro decrypter automatico processerà il file originale e non quello senza bogus code). Ovviamente come avete letto in precedenza, non sempre sarà possibile utilizzare il descrambler... ma ora basta con le chiacchiere... passiamo ad analizzare il dekrypting!
Prima di tutto dobbiamo attendere che l'esegubile si unpacki da solo. Quindi dobbiamo creare un processo, scriverci sopra un EBFE (jmp eip) dumpare e proseguire col dekrypting. Ovviamente dobbiamo tracciarci tutto il krypton. Io vi semplifico (per modo di dire) il lavoro dicendovi che la parte utile si trova tutta nell'area di memoria allocata (nel mio caso 006E0000). Prima però di proseguire, devo informarvi di un'altra cosa: da un certo punto in poi sn stato costretto per ovvi motivi a cambiare eseguibile. Comunque il mio lavoro sarà molto schematizzato e non corrispondera _esattamente_ al lavoro del krypton in quanto cambio qualche indirizzo per farvi capire meglio il tutto. E ora... preparatevi... tenete i nervi saldi e state pronti allo spettacolo (mi ripeto, ho dovuto cambiare eseguibile, ma se volete debuggare cavia.exe, gli indirizzi sono gli stessi, ma dovete sostituire il prefisso 006E con 006B perché cavia.exe è più piccolo dell'eseguibile che ho usato io). Mi preoccupo per la vostra incolumità... ma potete farcela. Mi raccomando:

L ' I M P A T T O   S A R A '   T E R R I B I L E !

    006E0000 (spazio allocato in memoria)

    006E0000    call    006EB122
    006EB122    call    006EB128
    006EB128    pop     ebp
                sub     ebp, 00412A72
                mov     [ebp+004142DA], eax
                mov     eax, 00413C30
                add     eax, ebp
                push    eax
                push    dword ptr fs:[0000]
                mov     fs:[0000], esp
                mov     word ptr [ebp+00410412], FEEB
                lea     eax, [ebp+00412AC0]
                mov     ebx, [eax+02]
                add     ebx, ebp
                mov     [eax+02], ebx
                lea     eax, [ebp+00412ADB]
                mov     ebx, [eax+02]
                add     ebx, ebp
                mov     [eax+02], ebx
                sub     eax, eax
                call    006EB17E
    006EB17E    push    dword ptr fs:[eax]
                mov     fs:[eax], esp
                pushfd                       
; pusha i flag
                or      byte ptr [esp+1], 01 
; abilita il trap flag
                popfd 
                       ; ripoppa i flag col trap flag abilitato
                nop 
                         ; single step
               
pop     dword ptr fs:[eax]
                pop     eax
                dec     byte ptr [006EC975]
                js      006EC883              ; questo salto non deve avvenire perché
                                              ; altrimenti vuol dire che avete steppato
                                              ; sul single step col trap flag abilitato
               
call    006EB1A2
    006EB1A2    pop     ebp
                sub     ebp, 00412AEC
                mov     eax, 0040794B
                add     eax, ebp              ; eax = 006E0000
               
mov     ebx, 00449B70
                add     ebx, ebp
    _loop01     add     ecx, [eax]
                inc     eax
                cmp     eax, ebz
                jl      _loop01
                mov     edi, 00410234
                add     edi, ebp
                mov     [edi], ecx
                lea     eax, [ebp+004142BF]
                lea     ebx, [ebp+00412C7D]
                mov     eax, [eax]
                rol     eax, 02
                mov     [ebx], al
                mov     byte ptr [ebx+01], 90
                mov     si, 'FG'              ; magic value
                mov     di, 'JM'              ; magic value
                int     3                     ; questo deve saltare a SEH

   
SEH: 006EC2E5
    006EC2E5    call    006EC2EA
    006EC2EA    pop     ebp
                sub     ebp, 00413C35
                mov     eax, fs:[0000]
                mov     esp, [eax]
                pop     dword ptr fs:[0000]
                mov     eax, 00408479
                mov     ecx, 0040794B
                add     ecx, ebp
                mov     [ecx], 000000E8
                mov     [ebp+004142BB], ecx
                lea     esi, [ebp+0041429F]   ; questa è la reale posizione della prima
                                              ; routine di dekrypting del codice (002D86B5)
               
lea     edi, [ebp+00407BB4]
                mov     ecx, 6                ; dimensione della routine
               
repz movsb
                push    eax
                repz movsb
                sub     eax, eax
                push    dword ptr fs:[0000]
                mov     fs:[0000], esp
                mov     ecx, [ebp+004142BB]
                jmp     ecx                   ; ECX = 006E0000

   
006E0000    call    006E0005
    006E0005    pop     ebp
                mov     eax, ebp
                sub     ebp, 00447950
                movzx   esi, byte ptr [ebp+004142DE] ; numero di sezioni da dekryptare
                test    esi, esi
                jnz     ????????
                mov     edi, ebp
                mov     word ptr [ebp+00410412], FEEB
    006E01C1    mov     ebx, [ebp+004142DA]    ; ImageBase
               
mov     eax, [edi+004142E3]    ; Virtual Offset della sezione da dekryptare
               
add     ebx, eax               ; EBX = 00401000 (.text)
               
mov     ecx, [edi+004142E3+4]  ; Dimensione della sezione

    /* Apro un blocco di commenti per spiegarvi meglio come stanno le cose: edi+004142E3
       è una tabella che viene costruita durante l'encrypting del file originale. Questa
       tabella contiene tutti i virtual offset delle sezioni da dekryptare con tutte le
       rispettive dimensioni. Nel mio eseguibile la situazione è strutturata in questo
       modo:

               edi+004142E3+00 = 00001000 -> VOffset prima sezione
               edi+004142E3+04 = 00000400 -> Dimensione della prima sezione
               edi+004142E3+08 = 00002000 -> VOffset seconda sezione
               edi+004142E3+0C = 00000400 -> Dimensione della seconda sezione
               edi+004142E3+10 = 00003000 -> VOffset terza sezione
               edi+004142E3+14 = 00000800 -> Dimensione della terza sezione

       In questo modo, il krypton ha uno schema ben preciso di quelle che sono il numero
       di sezioni da dekryptare e per ognuna di esse ha un punto di partenza e il numero di
       bytes che bisogna processare. Una volta fatto questo esame, ritorniamo al codice,
       precisamente alla prima routine di dekrypting delle sezioni dell'eseguibile. */


                mov     al, [ebp+004142C5] ; questa è una chiave usata per il dekrypting
    006E0269    ror     byte ptr [ebx], cl
                xor     [ebx], al
                add     [ebx], al
                nop                        ; fate attenzione a questa NOP
               
inc     al
                inc     ebx
                dec     ecx
                jnz     006E0269

                add     edi, 8             ; passa alla sezione successiva
               
dec     esi                ; ESI contiene il numero di sezioni
               
jnz     006E01C1           ; ritorna sopra per dekryptare la sez. successiva

               
sub     eax, eax
                mov     [eax], ebx         ; ovviamente questo fa eccezione, quindi
                                           ; dobbiamo saltare a SEH

   
SEH: 006E0B2E
    006E0B2E    call    006E0B33
    006E0B33    pop     ebp
                sub     ebp, 0040847E
                mov     eax, fs:[0000]
                mov     esp, [eax]
                pop     dword ptr fs:[0000]
                lea     eax, [ebp+0040873B]
                push    eax
                mov     dword ptr fs:[0000], esp
    006E0BEE    mov     si, 'FG'                 ; magic value da evitare
                mov     di, 'JM'                 ; qua fate quello che volete
                int     3                        ; stesso trucchetto di prima e salto a SEH

   
SEH: 006E0DF0
    006E0DF0    call    006E0DF5
    006E0DF5    pop     ebp
                sub     ebp, 00408740
                mov     eax, fs:[0000]
                mov     esp, [eax]
                pop     dword ptr fs:[0000]
                mov     eax, 00449994
                add     eax, ebp
                push    eax                 ; EAX punta a "KERNEL32.DLL"
               
call    [ebp+00449977]      ; GetModuleHandle
               
mov     [ebp+00410216], eax
                mov     eax, 00410381
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103F6], eax
                mov     eax, 0041038E
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103FA], eax
                mov     eax, 00410347
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103E2], eax
                mov     [ebp+00415D7B], eax
                mov     eax, 00410353
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103E6], eax
                mov     [ebp+00415D7F], eax
                mov     eax, 0041035D
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103EA], eax
                mov     [ebp+00415D83], eax
                mov     eax, 0041039F
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103FE], eax
                mov     [ebp+0041B6CD], eax
                mov     eax, 00410369
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103EE], eax
                mov     [ebp+00415D87], eax
                mov     eax, 00410375
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+004103F2], eax
                mov     eax, 004103A5
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+00410402], eax
                mov     eax, 004103B2
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+00410406], eax
                mov     eax, 004103C3
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+0041040A], eax
                mov     eax, 004103D4
                add     eax, ebp
                push    eax                         ; nome API
               
push    dword ptr [ebp+00410216]    ; hKernel
               
call    [ebp+0044996F]              ; GetProcAddress
               
mov     [ebp+0041040E], eax

                cmp     byte ptr [ebp+004437C7], FF
                jnz     006E6510
    006E6510    lea     eax, [ebp+0040E0AD]
                push    eax
                push    dword ptr fs:[0000]
                mov     fs:[0000], esp
    006E6638    mov     si, 'FG'                    ; da evitare come al solito
                mov     di, 'JM'
                int     3                           ; questo ci porta ancora a SEH

   
SEH: 006E6762
    006E6762    call    006E6768
    006E6768    pop     ebp
                sub     ebp, 0040EB02
                mov     eax, 0040DC2E
                add     eax, ebp
                push    eax
                push    00
                push    00
                mov     eax, 0040E680
                add     eax, ebp
                push    eax                           ; EAX = 006E6D35
                push    00
                push    00
                call    [ebp+00410402]                ; CreateThread -> da qua in poi bisogna
                                                     
; passare alla ThreadProc

   
THREAD PROC: 006E6D35
    006E6D35    call    006E6D3B
                pop     ebp
                sub     ebp, 0040E685
                mov     eax, 004102A6
                add     eax, ebp
                push    eax                           ; EAX punta a "ADVAPI32.DLL"
               
push    eax
                mov     eax, [ebp+0040997F]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+0044997F]                ; LoadLibrary
               
mov     [ebp+0041021A], eax
                mov     ebx, 004102B3
                add     ebx, ebp
                mov     esi, 004102DF
                add     esi, ebp
                mov     edi, [ebp+0041021A]           ; hADVAPI32
   
_get_api    push    ebx                           ; nome API
               
push    edi
                push    eax
                mov     eax, [ebp+0044996F]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+0044996F]                ; GetProcAddress
               
add     [esi], eax
    _next_api   inc     ebx
                cmp     byte ptr [ebx], 00
                jnz     _next_api
                inc     ebx
                add     esi, 04
                cmp     byte ptr [esi], FF
                jnz     _get_api
                mov     eax, 00449994
                add     eax, esp
                push    eax                            ; EAX punta a "KERNEL32.DLL"
               
push    eax
                mov     eax, [ebp+00449977]            ; GetModuleHandle
               
cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+00449977]                 ; GetModuleHandle
               
mov     [ebp+00410216], eax
                mov     eax, 00413A84
                add     eax, ebp
                push    eax                            ; nome API
               
mov     eax, [ebp+00410216]            ; hKernel
               
push    eax
                push    eax
                mov     eax, [ebp+0044996F]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+0044996F]                 ; GetProcAddress
               
add     [ebp+004102EC], eax
                push    eax
                mov     eax, [ebp+004102EC]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+004102EC]                 ; GetCommandLine
               
push    eax                            ; lpCommandLine
               
mov     edi, eax
                xor     al, al
                mov     ecx, -1
                repnz scasb
                neg     ecx
                dec     ecx
                pop     esi
                mov     edi, 00410252
                add     edi, ebp
                xor     ebx, ebx
    _mk_key     mov     bl, [esi]
                cmp     bl, 5C
                jnz     006E7B8A    ; a questo indirizzo il char '\' viene cambiato in '-'
                mov     [edi], bl
                inc     esi
                inc     edi
                loopnz  _mk_key
                mov     byte ptr [edi], 00
                mov     eax, 004102A2
                add     eax, ebp
                push    eax
                mov     eax, 0041029E
                add     eax, ebp
                push    eax
                push    00
                push    001F0003
                push    00
                push    00
                push    00
                mov     eax, 00410241
                add     eax, ebp
                push    eax                        ; nome della chiave di registro
               
mov     eax, [ebp+0041022A]
                push    eax
                push    eax
                mov     eax, [ebp+004102DF]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+004102DF]             ; RegCreateKeyExA
               
mov     eax, [ebp+0041023D]
                push    eax
                mov     eax, 00410234
                add     eax, ebp
                mov     ebx, [eax]
                mov     [ebp+00411787], ebx
                push    eax
                push    03
                push    00
                mov     eax, 0041022E
                add     eax, ebp
                push    eax
                ...
                EAX = C69ABDD0               ; questo non so cosa sia... il debugger non
 
              
...                          ; mi ha fatto vedere... suppongo una mov... boh :)
               
push    eax
                push    eax
                mov     eax, [ebp+004102E3]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax
                call    [ebp+004102E3]                ; RegSetValueExA
               
movzx   esi, byte ptr [ebp+004142DE]  ; Numero delle sezioni da dekryptare
               
test    esi, esi
                jz      006E878C
                mov     edi, ebp
    _dekr       mov     ebx, [ebp+004142DA]           ; ImageBase
               
mov     eax, [edi+004142E3]           ; La tabella coi valori descritta sopra
               
add     ebx, eax
                mov     ecx, [edi+004142E3+4]         ; Dimensione del codice da dekryptare
   
006E8632    rol     byte ptr [ebx], cl
                xor     [ebx], cl
                inc     ebx
                dec     ecx
                jnz     006E8632
                add     edi, 8                        ; Sezione successiva
               
dec     esi                           ; Le sezioni sono finite?
               
jnz     _dekr
                mov     edx, [ebp+004142DA]           ; ImageBase
                mov     esi, [ebp+004142CE]           ; Import Table
                add     esi, edx
                mov     byte ptr [ebp+00407D9D], 8B   ; 8B è una MOV
               
mov     eax, [esi+0C]
                or      eax, eax
                jz      006E9EE6                      ; Errore
               
add     eax, edx                      ; EAX punta a "KERNEL32.DLL" dalla IT
               
mov     ebx, eax
                push    eax                           ; "KERNEL32.DLL"

               
push    eax
                mov     eax, [ebp+00449977]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+00449977]                ; GetModuleHandle
               
or      eax, eax
                jnz     006EA8C9
    006EA8C9    cmp     word ptr [ebp+00410412], FEEB
                jnz     006E08FB
                mov     [ebp+004142C6], eax
                mov     dword ptr [ebp+004142CA], 00000000
                cmp     dword ptr [ebp+00447813], 00000000
                jnz     006E8DC0
    006E8DC0    mov     edx, [ebp+004142DF]           ; 00001000
                add     edx, [ebp+004142DA]           ; ImageBase
               
mov     [ebp+00447813], eax
                ...
                EDX = 00414646 ; Non ho visto nemmeno questo... un altro mov?
               
...            ; Qualsiasi cosa sia non ci interessa per il reversing :)
               
add     edx, ebp
                push    edx
                push    dword ptr fs:[0000]
                mov     dword ptr fs:[0000], esp
                push    esi
    006E8DDF    mov     edx, [ebp+004142DA]            ;ImageBase
               
mov     eax, [esi]                     ; ESI = 00402050 (import table)
               
or      eax, eax
                jnz     006E8EC0
    006E8EC0    add     eax, edx
                add     eax, [ebp+004142CA]            ; nAPI
               
mov     ebx, [eax]
                mov     edi, [esi+10]
                add     edi, edx
                add     edi, [ebp+004142CA]            ; nAPI
               
push    edi
                push    eax
                mov     edi, [ebp+004117BE]
                sub     eax, edi
                cmp     edi, 00000000
                jg      006E9114
                mov     [ebp+004117BE], eax
                pop     eax
                pop     edi
                test    ebx, ebx
                jz      006E9E77
                test    ebx, 11000000
                jnz     006E93E0
                test    ebx, 80000000
                jnz     006E93E0
                add     ebx, edx
                add     ebx, 2
                cmp     word ptr [ebx], 'ataF'
                jnz     006E93D5
                mov     [ebp+00411783], ebx
    006E9462    nop                                    ; inizio ricostruzione della import tab.
                nop                                    ; attenzione a questi due nop
                ror     byte ptr [ebx], 2              ; EBX punta al nome dell'api
                inc     ebx
                cmp     byte ptr [ebx], 00             ; il nome dell'api è stato decryptato?
                jnz     006E9462
                mov     ebx, [ebp+00411783]
                nop
                and     ebx, 0FFFFFFF
                push    ebx                            ; nome dell'api
               
push    dword ptr [ebp+004142C6]       ; hModule
               
call    [ebp+0044996F]                 ; GetProcAddress
               
cmp     dword ptr [ebp+00411783]
                jz      006E960B
                mov     ecx, [ebp+00411783]
    006E95B4    nop                                    ; Attenzione :)))
               
nop
                nop
                shl     byte ptr [ecx], cl
                xor     [ecx], al
                inc     ecx
                cmp     byte ptr [ecx], 00
                jnz     006E95B4

    /* La routine che vedete qua sopra è un anti-dumping: cancella i nomi delle api */

               
or      eax, eax
                jz      006E08FB
                cmp     byte ptr [ebp+004142BA], FF
                jnz     006E9DE3
                pushad
                mov     ebx, [ebp+00449953]
                mov     [ebp+0041177A], eax
                lea     edi, [ebp+0041178F]
                add     dword ptr [ebp+004117B6], 0000001F
                add     ebx, [ebp+004117B6]
                mov     [ebp+004117BA], ebx
                mov     eax, ebx
    006E9810    mov     cl, [edi]
                mov     [ebx], cl
                inc     ebx
                inc     edi
                cmp     dword ptr [edi], 00000000
                jnz     006E9810
                mov     ecx, eax
                add     ecx, 1B
                mov     [eax+02], ecx
                mov     [eax+11], ecx
                mov     [eax+0B], ecx
                mov     ebx, eax
                mov     eax, [ebp+00411787]
                mov     ecx, 41C64E6D
                mul     ecx
                add     eax, 00003039
                and     eax, 7FFFFFFF
                mov     [ebp+00411787], eax
                cmp     eax, 3F000000
                jge     006E9C98
                mov     [ebx+06], eax
                mov     [ebx+15], eax
                xor     [ebx+1B], eax
                popad
                mov     eax, [ebp+004117BA]
                mov     [edi], eax
                add     dword ptr [ebp+004142CA], 04
                jmp     006E8DC0                         ; salta sopra

               
add     esi, 14
                mov     edx, [ebp+004142DA]              ; ImageBase
                jmp     006E0452

    006E0452    mov     eax, [esi+0C]
                or      eax, eax
                jz      006E9EE6

    006E9EE6    pop     esi
                jmp     006E94F1

    006E94F1    mov     eax, [ebp+004117BE]
    006E9F55    mov     byte ptr [esi], 00
                inc     esi
                cmp     esi, eax
                jnz     006E9F55
                mov     eax, 00413AA3
                add     eax, ebp
                push    eax                     ; EAX punta a "GetVolumeInformationA"
               
mov     eax, [ebp+0044994B]     ; hKernel
               
push    eax

                push    eax
                mov     eax, [ebp+0044996F]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+0044996F]          ; GetProcAddress
               
add     [ebp+00413AC1], eax
                mov     eax, 005C3A43           ; "C:\"
               
mov     ebx, 004499A5
                add     ebx, ebp
                mov     [ebx], eax
                mov     ebx, 004499A5
                add     ebx, ebp
                push    00
                push    00
                mov     eax, 004499B9
                add     eax, ebp
                push    eax
                mov     eax, 004499BD
                add     eax, ebp
                push    eax
                mov     eax, 004499C9
                add     eax, ebp
                push    eax
                push    00000104
                mov     eax, 0044369B
                add     eax, ebp
                push    eax
                push    ebx                    ; "C:\"

                push    eax
                mov     eax, [ebp+00413AC1]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+00413AC1]        ; GetVolumeInformationA
               
mov     mov eax, 0044369B
                add     eax, ebp              ; EAX punta a "MATTIA" ovvero alla
                                              ; label del vostro hard disk
                mov     eax, [eax]
                mov     ebx, 004499C9
                add     ebx, ebp
                mov     ebx, [ebx]
                add     eax, ebx
                mov     edi, 00410234
                add     edi, ebp
                mov     [edi+04], eax
                xor     ebx, ebx
                mov     eax, 00410252
                add     eax, ebp
    006EA861    mov     edi, 00410234
                add     edi, ebp
                mov     ecx, 8
    006EA8F9    mov     bl, [eax]
                add     [edi], bl
                inc     eax
                cmp     byte ptr [eax], 00
                jz      006EAAD7
                inc     edi
                dec     ecx
                test    ecx, ecx
                jnz     006EA8F9
                cmp     byte ptr [eax], 00
                jnz     006EA861

    006EAAD7    ...
                EAX = 8
                ...
                push    eax
                mov     eax, 00410234
                add     eax, ebp
                push    eax
                push    03
                push    00
                mov     eax, 0041022E
                add     eax, ebp
                push    eax
                mov     eax, [ebp+0041029E]

                push    eax
                mov     eax, [ebp+004102E3]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+004102E3]            ; RegSetValueExA
               
mov     eax, [ebp+0041029E]
                push    eax

                push    eax
                mov     eax, [ebp+004102E7]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+004102E7]            ; RegCloseKey
               
lea     eax, [ebp+00413F30]
                cmp     byte ptr [eax], 33
                jnz     006EB0AB
    006EB0AB    lea     eax, [ebp+00410F02]  ; Da qua inizia una routine utile dopo per
                                             ; aggiustare uno dei 5 tipi di FF15 (FF15-00)
               
mov     ecx, 0000087C    ; Dimensione dell'iterazione
                sub     ebx, ebx         ; Azzera EBX
    006ECAA5    add     ebx, [eax]  ; Praticamente questa routine calcola un valore che verrà
                dec     ecx         ; usato come chiave di xoring sul codice. Questo valore
                inc     eax         ; viene calcolato con una funzione simile a quella di un
                test    ecx, ecx    ; CRC. Per questo è importante che il codice sia integro
                jnz     006ECAA5    ; ed esente da breakpoint al momento dell'iterazione
               
lea     eax, [ebp+004142BA]
                sub     ecx, ecx
                mov     eax, [eax]
                mov     cl, al
                add     ebx, ecx
                mov     [ebp+0041C4C0], ebx    ; la chiave viene salvata

               
mov     eax, [ebp+004142DA]    ; ImageBase
               
mov     [ebp+0041C4DD], eax
                lea     ebx, [ebp+0040794B]
                lea     ecx, [ebp+00449B70]
    006ECBB6    mov     byte ptr [ebx], 00
                inc     ebx
                cmp     ebx, ecx
                jnz     006ECBB6               ; questo fa eccezione perché viene
                                               ; sovrascritto anche il codice corrente.
                                               ; quindi si passa a SEH! :D

   
SEH: 006ECCFB
    006ECCFB    mov     eax, fs:[0000]
                mov     esp, [eax]
                pop     dword ptr fs:[0000]
                call    006ECD0D
    006ECD0D    pop     ebp
                sub     ebp, 00414658
                lea     eax, [ebp+00415D8B]
                push    eax
                push    dword ptr fs:[0000]
                mov     fs:[0000], esp
    006ECDAF    mov     si, 'FG'
                mov     di, 'JM'
                int     3                    ; Esattamente come prima! Tutti a SEH!

   
SEH: 006EE440
    006EE440    mov     eax, fs:[0000]
                mov     esp, [eax]
                pop     dword ptr fs:[0000]
                call    006EE452
    006EE452    pop     ebp
                sub     ebp, 00415D9D
                mov     cx, ss
                xor     cl, cl
                jecxz   006EE4B2
                push    00
                push    00
                push    02
                push    00
                push    00
                push    40000000
                lea     ebx, [ebp+00415D6B]
                push    ebx                    ; EBX punta a "KKK.TMP"
               
call    [ebp+00415D7B]         ; CreateFile (crea kkk.tmp sul disco)
               
mov     [ebp+00415D73], eax
                push    00
                lea     ebx, [ebp+00415D77]
                push    ebx
                push    000012FB
                lea     ebx, [ebp+00414A63]
                push    ebx
                push    eax                    ; hFile
               
call    [ebp+00415D7F]         ; WriteFile (scrive il contenuto del file)
               
push    [ebp+00405D73]         ; hFile
               
call    [ebp+00415D83]         ; CloseHandle (chiude l'open handle del file)

    /* E' arrivato il momento di un'altro blocco di commenti! Come potete notare dal codice
       sopra, il krypton crea sull'hard disk un file che si chiama kkk.tmp. Se provate ad
       interrompere il debugging e ad aprire questo file, potete facilmente accorgervi che
       si tratta di un driver, precisamente di un VxD. E' l'anti-softice. Questo driver non
       si deve debuggare ma semplicemente eviteremo che esso venga installato :) Come? Beh
       per adesso è stato creato su disco. Questo implica che dopo troveremo un'altra chiamata
       a CreateFile col compito di installare il driver. Per evitare che questo accada, basta,
       prima di eseguire la chiamata, modificare il nome del file che deve essere aperto.
       Nella CreateFile, seguendo il modello LIFO dello stack, il punutatore al nome corrispon-
       de all'ultima DWORD pushata. Per questo, per modificare il nome del file, dovete digi-
       tare nel softice "e *esp". Modificate il nome del file e premete F10 per far fallire
       la chiamata. Mi raccomando, dopo il fallimento, rimettete a posto il nome del file per
       non incorrere in qualche controllo di integrità del codice. Arriviamo subito al punto */

               
push    00
                push    04000000
                push    00
                push    00
                push    00
                lea     ebx, [ebp+00415D5f]
                push    ebp                     ; Puntatore al nome del driver (da modificare)
    006EE922    call    [ebp+00415D7B]          ; CreateFile (questa chiamata deve fallire)
              
 push    eax                     ; hFile
                call    [ebp+00415D6B]          ; CloseHandle
               
lea     ebx, [ebp+00415D6B]
                push    ebx                     ; EBX punta a "KKK.TMP"
               
call    [ebp+00415D87]          ; DeleteFile
               
cmp     byte ptr [0041C4DC], FF
                jz      006EEB39
    006EEB39    call    006EEB3E
    006EEB3E    pop     ebp
                sub     ebp, 00416489
                mov     eax, 002DC6C0
                push    40
                push    1000
                push    eax
                push    00

                push    eax
                mov     eax, [ebp+0044992C]
                cmp     byte ptr [eax], CC
                jz      00721F61
                pop     eax

                call    [ebp+0044992C]        ; VirtualAlloc
               
mov     [ebp+0041C4CA], eax
                mov     edi, [ebp+0041C4CA]
                lea     esi, [ebp+00416B27]
                mov     [esi], edi
                add     dword ptr [esi], 4
                lea     esi, [ebp+00416B27]
                mov     edi, [ebp+0041C4CA]
                mov     ecx, 725
                repz movsb
                mov     esi, eax
                mov     eax, 00401000
                mov     ecx, [ebp+00426155]
    006EEFFD    cmp     word ptr [eax], 15FF    ; Inizia la scansione per il primo FF15
               
jnz     006EF0F3
                cmp     dword ptr [eax], 05     ; Questo lo uso per distinguere gli FF15
               
jnz     006EF0F3                ; ad esempio in questo caso lo chiamo FF15-05
               
mov     [eax+02], esi
    006EF0Fe    inc     eax
                dec     ecx
                test    ecx, ecx
                jnz     006EEFFD

OK fermiamoci un minuto! (beh dovrò pur spiegarvi in qualche modo no? :). Alor... dopo tutto questo diavolo di codice (cazz... non è uno scherzo ricopiarlo tutto...) siamo arrivati ai famosi FF15. Voi dite << famosi perché? >> Beh... grazie a dio voi non li avete affrontati come ho fatto io. ^^' Innanzitutto chiariamo. Per la spiegazione partiamo dall'eseguibile originale (ora capirete anche perché sono stato costretto a cambiare eseguibile). Quando viene kryptato, il krypton itera per tutta la sezione di codice alla ricerca di determinate istruzioni (parliamo di istruzioni lunghe 6 bytes che vedremo dopo con calma) e le sostituisce con del suo codice. Questo codice consiste in 2 bytes: FF 15 seguiti da una DWORD che contiene un numero che varia da 0 a 5. FF 15 sono le opcodes di una CALL FAR. Arrivati a questo punto del programma, il loader del krypton, itera per tutta la sezione di codice proprio alla ricerca di queste FF 15. Appena ne trova una vede che DWORD hanno dopo. Questa DWORD io la uso per distinguere i vari FF15. E non solo io! Anche il krypter! Infatti questi numeri sono provvisori: col ciclo riportato sopra, quella dword che contiene il numero dell'ff15 viene sostituita con una dword che contiene l'indirizzo di una procedura. Sarà in questa procedura che avverà il dekrypting delle istruzioni originali che poi saranno rimesse al loro posto. Devo dirvi un'altra cosetta: questo di cui vi ho parlato è il meccanismo utilizzato dagli FF15-00, FF15-01, FF15-02, FF15-03 e FF15-04. Infatti proprio il primo caso fa eccezione. l'FF15-05 corrisponde al K-Execution previsto dal krypter all'interno della sezione di codice. Se leggete la guida del Krypton v0.5, troverete all'interno le istruzioni su come utilizzare questo manual K-Execution. L'FF15-05 è proprio il meccanismo di crittografia che viene usato per proteggere il codice all'interno del blocco del manual k-exec. E' il primo caso... non c'è niente da fare... dobbiamo analizzarlo! Per analizzarlo dobbiamo steppare nella procedura che lui va ad inserire dopo le FF15. Quindi con un "g 00401000" arriviamo col SoftICE all'original entry point e steppiamo fino a quando non incontriamo la FF15 (potete quindi capire che mi son dovuto scrivere altri eseguibili con dentro il manual k-execution per fare questo reversing. Non solo, ho dovuto trovare tutti i casi di istruzione che vengono sostituite dagli altri FF15 per questo mi son scritto parecchi eseguibili. Siccome questo tutorial è stato "interattivo" (ovvero l'ho scritto mentre reversavo e non l'ho incominciato una volta finito il reversing), all'inizio, non conoscendo questo krypter, non potevo immaginare che ci fosse una cosa del genere... (anche se poi vedendola mi son ricordato del krypton3... ma è passato un macello di tempo :). Ora mano alla procedura della FF15! Per analizzarlo dobbiamo prina visionare il codice sorgente dell'esebuibile originale per poi passare ad analizzare l'eseguibile kryptato partendo tranquillamente dall'original entry point. Vediamo 'sto sorgente...

.code
main:
        push    0
        push    offset appname
        push    offset msgtext
        push    0
        call    MessageBox


        db 0EBh, 0Eh
        db 'KDES'
        db 00, 00, 00, 00, 00
        db 00, 00, 00, 00, 00

        mov    eax, ebx
        mov    ecx, eax
        xor    ebx, edx

        db 0EBh, 0Eh
        db 'KDEE'
        db 00, 00, 00, 00, 00
        db 00, 00, 00, 00, 00


        push    0
        call    ExitProcess

        call    GetModuleHandle
        call    GetTickCount


end main

Non è difficile da capire. Vediamo un po' la situazione... ho messo qualche istruzione ad capocchiam tanto per avere qualche punto di riferimento all'interno del codice una volta assemblato. Quello che però veramente ci interessa è la procedura che si trova all'interno della dichiarazione del manual k-execution (usare il KDES/KDEE o usare il KEES/KEEE è la stessa cosa, come reversing non cambia nulla e anche l'algoritmo di unpacking è identico). Vediamo innanzitutto che cosa succede all'OEP dell'eseguibile assemblato:

    00401000    push    00
                push    00403019      ; offset AppName
                push    00403000      ; offset msgtext
                push    00
                call    0040105C      ; MessageBox

   
_old_kdes   push    000169EB      ; [esp+24]
                push    000107C2      ; [esp+20]
                call    [00820000]    ; dword ptr [00820000] = 00820004

                ...
                ...

Ecco qua un po' come si presenta la situazione sotto il softice dopo che arriviamo all'entry point (tenete conto che ormai il numero che vien dopo l'FF15 è stato sostituito con l'indirizzo del puntatore della procedura da chiamare!). La MessageBox non si vede perché il krypton, al contrario di quello che faremo noi, prende gli indirizzi delle api che gli servono e le salva all'interno di una sua import table. Da questa richiamerà tutte le procedure del nostro eseguibile. Quello che a noi interessa ora è il codice che inizia ad _old_kdes. Se fate caso, nel codice originale, al posto di quelle due push, c'era la dichiarazione del manual k-execution. Iniziamo a capire come funziona: la dichiarazione viene sostituita da queste istruzioni qua. Innanzitutto vi vengo incontro dicendovi che i valori pushati sono fondamentali per ripristinare il codice di partenza. Ora non ci resta altro da fare che analizzare.

    00820004    push    eax
                pushfd
                mov     eax, [esp+08]    ; RetVal
               
push    ebx
                push    ecx
                push    esi
                push    edi
                push    ebp
                call    00820014
    00820014    pop     ebp
                sub     ebp, 00416B3B
                mov     esi, [ebp+00416E1F]
                xor     [esp+20], esi        ; In questo si ha [esp+20] = alla
                                             ; dimensione del bytes di codice all'interno
                                             ; del blocco del manual k-execution.
               
xor     [esp+24], si
                mov     ecx, [esp+20]        ; dimensione
               
mov     ebx, [esp+24]
                lea     edi, [ebp+00417248]
                mov     esi, eax
                repz movsb
                mov     ecx, edi
                add     ecx, 6
                mov     dword ptr [edi], 000025FF
                mov     [edi+02], ecx
                mov     [edi+06], esi
                mov     ebx, [esp+24]
                and     ebx, FFFF0000
                cmp     ebx, 00001000
                jnz     00820443
                mov     ecx, [esp+20]
                mov     esi, eax
                dec     esi
    008203B5    inc     esi
                mov     byte ptr [esi], 00
                loopnz  008203B5
                mov     ebx, [esp+24]
                mov     ecx, [esp+20]
                lea     edi, [ebp+00417248]
                xor     eax, eax
                mov     ax, bx
    008206EA    neg     cl                    ; questa è la procedura di dekrypting
                add     [edi], cl             ; del codice.
                xor     [edi], cl
                rol     byte ptr [edi], cl
                neg     cl
                sub     [edi], al
                add     [edi], ah
                xor     [edi], al
                rol     byte ptr [edi], cl
                xor     [edi], ah
                inc     edi
                loopnz  008206EA

Basta fin qua. Alor: a partire da 008206EA inizia la procedura di dekrypting del codice camuffato. EDI punta a questo codice, ECX contiene la dimensione del codice da dekryptare. Per quanto riguarda EAX, possiamo calcolarlo andando a ritroso nella procedura. Questo proviene da [ESP+24] xorato con la dword ptr [ebp+00416E1F]. Quindi non è difficile da dekryptare. Basta prelevare dal codice dell'eseguibile dumpato tutto il materiale che ci serve e ripetere la stessa identica operazione (il codice ve lo posto tutto insieme alla fine del tutorial. Per ora passiamo avanti col reversing). Possiamo ritornare al codice del krypton. Proseguiamo con l'analisi del secondo FF15. Precisamente partiamo da 006BF905.

    006BF905    cmp     byte ptr [ebp+0041C4CE], FF
                jnz     006BFC10
                mov     eax, 00401000
                mov     ecx, [ebp+0041C4E1]          ; dimensione da scansionare
               
lea     ebx, [ebp+0041B83B]
                mov     [ebp+0042FDC1], ebx
                lea     ebx, [ebp+0042FDC1]
    006BFA81    cmp     word ptr [eax], 15FF
                jnz     006BFB4F
                cmp     dwoed ptr [eax+02], 00000000 ; FF15-00
               
jnz     006BFB4F
                mov     [eax+02], ebx
    006BFB4F    inc     eax
                dec     ecx
                test    ecx, ecx
                jnz     006BFA81

Anche qua la stessa cosa del codice di prima: viene ricercato l'FF15 nel file e gli viene piantato dentro l'indirizzo della procedura da eseguire. Questa volta però la cosa sarà più semplice da gestire. Infatti il problema più grosso era l'FF15-05 in quanto la dimensione del blocco di codice da decifrare cambiava e i dati erano forniti da quelle due push prima della call. In questi altri quattro casi abbiamo degli FF15 che dekryptano sempre 6 bytes. Per questo son dovuto andare a tentoni per trovare tutte le istruzioni che venivano sostituite dagli FF15 per creare degli eseguibili da reversare. Vediamo un po'... un'istruzione che viene sostituita con un FF15-00 è ad es:

                mov     ecx, dword ptr [offset]

Ok. Basta creare un eseguibile che contenga quest'istruzione:

.code
main:
        push    0
        push    offset appname
        push    offset msgtext
        push    0
        call    MessageBox


        mov     ecx, dword ptr [_myoff]

        push    0
        call    ExitProcess

    _myoff:
        call    GetModuleHandle
        call    GetTickCount


end main

Questo era il mio eseguibile. L'ho compilato, l'ho kryptato, e sono arrivato di nuovo a 00401000. Vediamo questa volta che cosa cambia nel codice:

    00401000    push    00
                push    00403019    ; offset AppName
               
push    00403000    ; offset msgtext
               
push    00
                call    0041103C    ; MessageBox

   
ffacll      call    [006D8476]  ; FF15-00

               
push    00
                call    0040102A    ; ExitProcess
               
call    00401030    ; GetModuleHandle
               
call    00401036    ; GetTickCound

Vediamo un po': la prima cosa che deve saltarvi all'occhio è che questa volta il codice successivo alla FF15 non è cambiato (invece nel primo caso dovevamo dekryptare anche quello successivo). Allora il lavoro si restringe notevolmente in quanto questa volta, al posto della FF15 (6 bytes) dobbiamo reinserire il codice originale (sempre di 6 bytes), ma questa volta non ci sono push o altri trucchetti prima. Anche qua andiamo ad analizzare la FAR-PROC chiamata dalla FF15:

    006C3EF0    push    ecx
                pushfd
                sub     dword ptr [esp+08], 6 ; così [ESP+08] = ffcall (RetVal)
               
mov     eax, [esp+08]         ; ffcall
               
push    eax
                push    edx
                push    ebp
                push    ebx
                call    006C3F04
    006C3F04    pop     ebp
                sub     ebp, 0041B84F
                not     dword ptr [ebp+0041C4D8]
                test    edx, edx
                jz      006C49E7
                mov     edx, [ecx]             ; ffcall
               
mov     [ebp+0041C4E5], edx
                mov     edx, [ecx+04]          ; ffcall + 4
               
mov     [ebp+0041C4E5+04], edx
                mov     edx, [ecx+08]          ; ffcall + 8
               
mov     [ebp+0041C4E5+08], edx
                mov     edx, [ecx+0C]          ; ffcall + 12
               
mov     [ebp+0041C4E4+0C], edx
                mov     word ptr [ecx+6], 15FF ; questo non ci interessa affatto
               
mov     edx, [ecx+02]          ; ffcall + 02 (ma non ci interessa)
               
mov     [ecx+08], edx          ; e nemmeno questo c'entra col reversing

               
lea     eax, [ebp+0041C509]    ; offset _nostri_bytes - 04
               
mov     ebx, 0041C4C0
                add     ebx, ebp               ; EBX punta ad una chiave di xoring
    006C41D5    mov     edx, [eax]
                xor     edx, [ebx]
                sub     edx, ecx
                test    edx, edx               ; Verifica
                jz      006C4303
                add     eax, 0A                ; Da questo capiamo che tra i vari FF15-0
                jmp     006C41D5               ; i bytes sono a 10 bytes di distacco.
    006C4303    mov     edx, [eax+4]           ; Nostro codice kryptato
               
xor     edx, [ebx]
                mov     [ecx], edx             ; Rimpiazza la FF15 col codice giusto (4 bytes)
               
mov     dx, [eax+08]           ; Ultimi due bytes per l'istruzione
               
xor     edx, [ebx]
                mov     [ecx+04], dx           ; Rimpiazza gli ultimi 2 bytes
               
pop     ebx
                pop     ebp
                pop     edx
                pop     eax
                popfd
                pop     ecx
                ret

Anche questo FF15 è stato reversato. Passiamo avanti e proviamo gli altri (che poi io li ho già provati tra l'altro ^^'). Il mprossimo caso è quello del jump far. Si tratta dell'FF15-01. Come al solito vediamo il codice sorgente dell'eseguibile originale:

.data
    myoff dd offset _end_code

.code
main:
        push    0
        push    offset appname
        push    offset msgtext
        push    0
        call    MessageBox


        jmp     [myoff]

        push    0
        call    ExitProcess

    _end_code:
        call    GetModuleHandle
        call    GetTickCount


end main

Ora vediamo il codice del krypton al momento della scansione:

    006BFC30    cmp     byte ptr [ebp+004104D7]
                jnz     006BFF26
                mov     eax, 00401000
                mov     ecx, [ebp+00426155]       
; Dimensione del codice da scansionare
               
lea     ebx, [ebp+0041BD6A]
                mov     [ebp+0042FDC5], ebx
                lea     ebx, [ebp+0042FDD5]
    006BFD82    cmp     word ptr [eax], 15FF
                jnz     006BF363
                cmp     dword ptr [eax+02], 00000001
; FF15-01
                jnz     006BF363
                mov     [eax+02], ebx
    006BF363    inc     eax
                dec     ecx
                test    ecx, ecx
                jnz     006BFC30

Amici... è la stessa identica cosa! Questa volta qualconisa cambia nella procedura sostituita... ma alla fine si tratta sempre della stessa roba. Vediamo questa volta di cosa si tratta:

    006C441F    push    edi
                push    ecx
                pushfd
                mov     ecx, [esp+0C]           
; RetVal
               
sub     ecx, 6                   ; ffcall
               
push    edx
                push    ebp
                push    ebx
                call    006C4431
    006C4431    pop     ebp
                sub     ebp, 0041BD7C
                mov     edx, [ecx]
                mov     [ebp+00426159], edx
                mov     edx, [ecx+04]
                mov     [ebp+00426159+04], edx
                mov     edx, [ecx+08]
                mov     [ebp+00426159+08], edx
                mov     edx, [ecx+0C]
                mov     [ebp+00426159+0C], edx
                lea     eax, [ebp+0042617D]     
; puntatore ai nostry bytes (sempre meno 4)
               
mov     ebx, 0042614D
                add     ebx, ebp                
; punta a key di xor. Questa volta è statica
   
_do_verify  mov     edx, [ecx]
                xor     edx, [ebx]
                sub     edx, ecx
                test    edx, edx                
; verifica la posizione della ffcall
               
jz      _is_zero
                add     ecx, 0A                 
; anche qua 10 bytes di stacco tra i codici
               
jmp     _do_verify
    _is_zero    mov     edx, [ecx+04]
                xor     edx, [ebx]
                test    dl, dl
                ...

Sentite basta così (O_O sono pazzo!). La procedura è identica alle altre! (non sono pazzo :) Anche se potete notare qualche differenza. Al contrario delle altre, questa rocedura analizza il nostro codice per poi fare da sè un salto sul punto desiderato con un jmp [eax]. L'unica cosa che dobbiamo tener presente è il primo byte: dovrebbe essere FF ma diventa zero. Quindi dopo il promo xoring, dobbiamo impostare DL = FF. Cambiano ovviamente (ma questo è normale -.-) la chiave di xoring (che questa volta è statica... non c'è bisogno di generarla in base al file) e la posizione dei bytes. Passiamo avanti! FF15-02: Istruzione:

    mov     dword ptr [offset], numero ; 10 bytes

Come al solito vediamo il nostro eseguibile:

.code
main:
        push 0
        push offset appname
        push offset msgtext
        push 0
        call MessageBox

        mov dword ptr [mydd], 333h
        mov dword ptr [mydd2], 444h

       
push 0
        call ExitProcess

        call GetModuleHandle
        call GetTickCount

end main

Facile facile. Vediamo come al solito il codice del krypton:

    006BFF4A    cmp     byte ptr [ebp+0041C4CF], FF
                jnz     006C062C
                mov     eax, 00075300
                push    40
                push    00001000
                push    eax
                push    00

                push    eax
                mov     eax, [ebp+0044992C]
                cmp     byte ptr [eax], CC
                jz      007AF161
                pop     eax

                call    [ebp+0044992C]       
; VirtualAlloc
               
mov     [ebp+0041C4BC], eax
                mov     eax, 00401000
                mov     edx, [ebp+0042FDE5]  
; dimensione
               
mov     edi, [ebp+00401C4BC]  ; AllocMem
               
mov     [ebp+0042FDC9], edi
                lea     ebx, [ebp+0042FE0D]  
; questa loc è importante
    006B02A7    cmp     word ptr [eax], 15FF
                jnz     006B05F6
                cmp     dword ptr [eax+02], 02
                jnz     006B056F
                mov     edi, [ebx]
                mov     [ebp+0041B7CD], edi
                mov     edi, [ebx+04]
                mov     [ebp+0041B7CD+04], edi
                ...
                add     ebx, 8               
; per logica suppongo sia una ADD
                ...
                lea     esi, [ebp+0041B78D]
                mov     edi, [ebp+0042FDC9]
                mov     [esi], edi
                add     dword ptr [esi], 04
                mov     [eax+02], EDI
                mov     ecx, 48
                repz movsb
                mov     [ebp+0042FDC9], edi
    006B056F    inc     eax
                dec     edx
                test    edx, edx
                jnz     006B02A7

Soffermiamoci sul rigo prima di 006B02A7. Se col SoftICE proviamo a visualizzare il contenuto della locazione ebp+0042FE0D troviamo 4 DWORD. Vi anticipo io qualcosa: sono le dword utilizzate dal programma per riemulare l'istruzione mov [xxxxxxxx], xxxxxxxx. Si raggruppano a due a due per ogni FF15-02. Ma andiamo con calma. Prima di tutto vediamo come viene mutato il codice una volta arrivati all'entry point:

    00401000    push    00
                push    00403019   
; offset AppName
               
push    00403000    ; offset msgtext
               
push    00
                call    00401040   
; MessageBox

               
push    eax
                push    ebx
                call    [00B00000] 
; ffcall
               
pop     ebx
                pop     eax

                push    eax
                push    ebx
                call    [00B0000X] 
; ffcall
               
pop     ebx
                pop     eax

                push    00
                call    0040102E   
; ExitProcess

Come vedete il krypter, per recuperare i 4 bytes (perché la nostra istruzione è di 10 bytes mentre la FF15 ne prende solo 6) utilizza due push e due pop che non fanno nulla. Ora che avete capito come sta sistemato il codice passiamo ad analizzare la procedura chiamata dalla FF15:

    00B00004    push    ebp
                push    eax
                push    ebx
                pushfd
                call    00B0000D
    00B0000D    pop     ebp
                sub     ebp, 0041B79A
                lea     eax, [ebp+0041B7CD]
; questa è la locazione che contiene le 4 dword
                mov     ebx, [eax]         
; in ebx la prima dword
               
mov     eax, [eax+04]       ; in eax la seconda dword
               
xor     ebx, eax            ; così otteniemo la lpDword
               
add     eax, ebx            ; così invece otteniemo il valore da inserire
               
mov     [ebx], eax          ; ecco la nostra istruzione eseguita diversamente!
               
and     eax, FFFF0000
                lea     ebx, [ebp+0041B7C9]
                mov     ebx, [ebx]
                shr     ebx, 18
                cmp     ebx, eax
                popfd
                pop     ebx
                pop     eax
                pop     ebp
                ret

Quindi: per ottenere i valori, prendiamo le DWORD a due a due e le mettiamo una in EAX e l'altra in EBX. Xorando EAX con EBX si ottiene la lpDWORD. Aggiungendo questa alla chiave di xoring si ottiene il valore da inserire (notevole eh?). Anche questa è fatta signori! Passiamo all'ultimo punto della nostra incredibile sfaticata! L'FF15-03! Siamo nel caso della cmp:

    cmp dword ptr [xxxxxxxx], xx

Con questo caso abbiamo una piccolissima differenza rispetto agli altri. Non posso farvi l'esempio del codice perché il krypton non trasforma tutte le cmp in FF15. Infatti il programma seleziona le istruzioni da trasformare in base a dei suoi criteri e controllando se la sostituzione è possibile o meno. Vabè... comunque il problema non si pone: ho preso le istruzioni di esempio dall'eseguibile del krypton stesso (sapete che è kryptato con sè stesso no? :P). Ok: si parte da 00330650:

    00330650    cmp     byte ptr [ebp+0041C4D0], FF
                jnz     003336D4
                mov     eax, 75300
                push    40
                push    1000
                push    eax
                push    00

                push    eax
                mov     eax, [ebp+0044992C]
                cmp     byte ptr [eax], CC
                jz      00361F61
                pop     eax

                call    [ebp+0044992C]        ; VirtualAlloc
               
mov     [ebp+0041C4C6], eax
                mov     eax, 401000
                mov     edx, [ebp+00439A53]   ; dimensione del codice
               
mov     edi, [ebp+0041C4C6]   ; allocmem
               
mov     [ebp+0042FDD1], edi
                lea     ebx, [ebp+00439A57]   ; questo è importante come quello del caso
                                              ; precedente. E' qua che si trova tutto.

Raga... mi fermo qua... è identica a quella di prima. A noi per risolverla interessava soltanto la locazione a cui punta EBX per prenderci i valori che ci servono, per il resto, dopo c'è la classica scansione e sostituzione della procedura. Andiamo a guardare invece direttamente la procedura rimpiazzata (anche qua dentro ve la scrivo semplificata perché è piena di istruzioni inutili):

    00DC043F    push    ebp
                push    eax
                push    ebx
                pushfd
                call    00DC0448
    00DC0448    pop     ebp
                sub     ebp, 0041B7E6
                lea     eax, [ebp+0041B7F7]
                mov     ebx, [eax]
                mov     eax, [eax+04]
                xor     ebx, eax                ; questo punta alla locazione in memoria
               
add     eax, ebx
                and     eax, FFFF0000
                shr     eax, 18                 ; così in AL si ottiene il valore

Anche qua possiamo fermarci. Abbiamo quello di cui abbiamo bisogno. Adesso ricapitoliamo un po' la situazione per vedere che cosa dobbiamo fare per un corretto unpacking:
Innanzitutto dobbiamo fare in modo di creare un processo che poi dumperemo una volta arrivato alla fine dell'ultima sezione del krypter (sezione "_!_!_!_", che poi sarebbe quella di unpacking dell'eseguibile). Una volta fatto questo, noi abbiamo l'eseguibile dumpato in modo grezzo. Ci toccherà riallinearlo ma non dovrebbero esserci problemi. Poi dobbiamo ritirare dall'interno dell'eseguibile tutte le routine non-static che poi useremo per dekryptare l'eseguibile. Una volta fatto questo ricostruiremo tutte le sezioni. Dopo le sezioni tocca alla ricostruzione della import table. Non è molto difficile in quanto solo i nomi delle api sono crittografati... il resto rimane come sta. Affinché funzioni però dobbiamo anche ripristinare nell'header dell'eseguibile l'indirizzo della nostra import table. Fatto questo rimangono solo le FF15 (io ho scritto cinque procedure diverse per facilitarvi nella comprensione). Abbiamo finito! Basta rimuovere dall'eseguibile le sezioni del loader, aggiornare l'header, aggiustare l'entry point e aggiustare il sizeof image (per la compatibilità con windowsNT); salvare il file su disco e festeggiare! Ora è arrivato il momento di visualizzare questo benedetto codice di dekrypting (anche qua non sarà un magnifico spettacolo... sono due chilometri -.-).
Pronti? Vi... HEY HEY HEY! Aspettate un attimo! C'è una cosa che devo ancora dirvi. Voi non lo sapete... ma il mio caro amico yado ha inventato dei trucchetti che permettono al programma di individuare la presenza del loader del krypton. Per questo motivo il programma si accorgerà se le ultime sezioni sono state rimosse o meno. Vediamo di svelare questo trucchetto? :D Ho preso il dekrypter pensando che fosse tutto finito ed ho unpackato l'eseguibile del krypton stesso. Il programma partiva! :D Però nel momento in cui ho premuto il tasto "Browse", l'applicazione si è subito chiusa... così sono andato a spiare quello che succedeva all'interno del codice. Siamo esattamente su 00401E48. Vediamo un po di che si tratta:

    YADO:004014E8 loc_0_4014E8: ; CODE XREF: sub_0_4011C8+310 j
    YADO:004014E8     push eax
    YADO:004014E9     mov eax, ds:dword_0_40680C
    YADO:004014EE     xor eax, 112233h
    YADO:004014F3     and eax, 11000000h
    YADO:004014F8     test eax, eax
    YADO:004014FA     jz loc_0_4011A4
    YADO:00401500     mov eax, ds:dword_0_40680C
    YADO:00401505     xor eax, 112233h
    YADO:0040150A     and eax, 0FFFFFFh
    YADO:0040150F     call eax
    YADO:00401511     pop eax

    YADO:00401512     not ds:byte_0_4142BA
    YADO:00401518     not ds:byte_0_40774F
    YADO:0040151E     jmp short loc_0_40152E

Non so se ve rendete conto... ma a 004014EE avviene uno xoring e un and molto strani... poi lo stesso viene ripetuto e poi il tutto viene chiamata una procedura che si trova all'indirizzo ottenuto dalle operazioni fatte su quella dword. E' un trucchetto che serve per individuare la presenza del loader nel file (ne ho avuto la conferma parlando con yado). Volendo anche qualsiasi utente che conosce bene il krypton potrebbe implementare questa cosa nel suo codice. Per questo ho abbozzato una funzioncina che rimuove tutta questa roba qua. Controllo la presenza di quelle DWORD consecutive per vedere se è presente questo codice (il tutto comporta un minimo rischio nel caso si verificasse che un programma abbia quelle dword sistemate allo stesso modo... ma credo sia impossibile... comunque la funzione è facoltativa nel mio programmino). Con una semplice funzioncina ora abbiamo l'unpacker completo! E per la gioia dei bambini...

E C C O   I L   C O D I C E ! ! ! !

;*************************************************************************************
;* KRYPTON-D]i[E v0.5
;* IRC: #crack-it | #fuckinworld | #informazionelibera @ Azzurra.org
;*
;*************************************************************************************

    .386
    .model flat, stdcall
    option casemap: none

    include             \masm32\include\windows.inc    
    include             \masm32\include\kernel32.inc
    include             \masm32\include\user32.inc
    include             \masm32\include\comdlg32.inc

    includelib          \masm32\lib\kernel32.lib
    includelib          \masm32\lib\user32.lib
    includelib          \masm32\lib\comdlg32.lib

    include             resource.inc
    include             data.inc

    WndProc             PROTO hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
    dekrypt             PROTO
    memdekrypt          PROTO
    import_routines     PROTO
    routine1            PROTO
    routine2            PROTO
    routine3            PROTO
    import_rinfo        PROTO

    ff15_05             PROTO
    ff15_00             PROTO
    ff15_01             PROTO
    ff15_02             PROTO
    ff15_03             PROTO
    remove_tricks       PROTO

    FileExists          PROTO lpFileName: LPCSTR
    personalizza        PROTO
 
.const
    AppName             db "KRYPTON-DiE v0.5", 0
    imagebase           dd 00400000h
    jmp_eip             dw 0FEEBh

    kdes                db 0ebh, 0eh
                        db 'KDES'
                        db 00, 00, 00, 00, 00
                        db 00, 00, 00, 00, 00

    error_open_file  	db "ERROR: Can't open selected file!", 0
    error_invalid_pe 	db "ERROR: This is not a valid PE file format!", 0
    error_invalid_kr 	db "ERROR: This file is not protected with krypton 0.5!", 0
    unknown_error       db "ERROR: Unhandled exception - unknown error!", 0
    dekrypted           db "File succesfully dekrypted! :D", 0
    dekrypting          db "Unpacking in progress. Please wait...", 0
    
    last_section_name   db "_!_!_!_", 0
    krypton_sec_name    db "krypton", 0

    rtable              dd 0C95Ah
                        dd offset routine1
                        dd 6
                        
                        dd 8632h
                        dd offset routine2
                        dd 2

                        dd 8658h
                        dd offset routine2+2
                        dd 2
                        
                        dd 9462h
                        dd offset routine3
                        dd 5
                        
	file_exists	db "The selected file already exists:", 13, 10
			db "Do you want to overwrite?", 0
      
.data
    hInstance           dd 0
    hFile               dd 0
    FileSize            dd 0
    lpData              dd 0
    hMainWindow         dd 0
    mdw                 dd 0
    nSections           dw 0

    vsize_last_section  dd 0
    vaddr_last_section  dd 0
    vaddr_kryp_section  dd 0
    file_alignment      dd 0
    

    StartupInfo         STARTUPINFO <>
    ProcessInfo         PROCESS_INFORMATION <>
    ofn                 OPENFILENAME <>
    lpContext           dd 0
    FindData		WIN32_FIND_DATA <>

    ProcessSize         dd 0
    lpDumped            dd 0

    ;**************************************************************************************

    nSec                dd 0 ; Sezioni da decryttare
    DekrypterTableSize  dd 0 ; Dimensione della tabella di informazioni per il dekrypting
    lpDekrypterTable    dd 0 ; Puntatore alla tabella di informazioni per il dekrypting
    dekrypting_key      db 0 ; chiave utilizzata dalla prima routine
    code_section_size   dd 0

    it                  dd 0
    nModules            dd 0
    lpModules           dd 0

    ;**************************************************************************************

    current_log         db 1024 dup (0)
    InitLog             db "Press ''Browse'', select an already encrypted file and then press"
			db " ''Dekrypt'' to enjoy! :D.", 13, 10, 13, 10, 0
    FileName            db 255 dup (0)

    clear               db "Ready.", 0
    Filter              db "Exe filex", 0
                        db "*.exe", 0, 0
    OpenName            db "Select a file...", 0
    outfile             db 512 dup (0)
    suffix              db "_unpacked.exe", 0

.code
Main:
	invoke GetModuleHandle, 0
        mov     [hInstance], eax

        invoke  VirtualAlloc, 0, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE
	mov     [lpContext], eax	; Ho dovuto fare così perché altrimenti il CONTEXT
					; non funzionava correttamente...

        invoke VirtualProtect, addr routine1, 14, PAGE_EXECUTE_READWRITE, addr mdw
        invoke VirtualProtect, addr routine2, 9, PAGE_EXECUTE_READWRITE, addr mdw
        invoke VirtualProtect, addr routine3, 12, PAGE_EXECUTE_READWRITE, addr mdw

	invoke DialogBoxParam, hInstance, IDD_DIALOG1, 0, addr WndProc, 0
        invoke ExitProcess, 0
        
WndProc PROC hWnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
        mov eax, [uMsg]

	.if(ax == WM_CLOSE)
		invoke PostQuitMessage, 0
	        xor eax, eax
	        ret
	.elseif(ax == WM_INITDIALOG)
	        push [hWnd]
	        pop [hMainWindow]
        
	        xor eax, eax
	        ret
	.elseif(ax == WM_COMMAND && wParam == IDC_BROWSE)
		mov [ofn.lStructSize], sizeof OPENFILENAME
	        push [hWnd]
	        pop [ofn.hwndOwner]
        	push [hInstance]
	        pop [ofn.hInstance]
        	mov [ofn.lpstrFilter], offset Filter
	        mov [ofn.lpstrFile], offset FileName
        	mov [ofn.nMaxFile], 255
	        mov [ofn.Flags], OFN_HIDEREADONLY or OFN_EXPLORER
        	mov [ofn.lpstrTitle], offset OpenName
        	invoke GetOpenFileName, addr ofn
		.if(eax == 0)
			xor eax, eax
			ret
        	.endif
        	
        	invoke SetDlgItemText, hWnd, IDC_EDIT1, addr FileName
        	invoke SetDlgItemText, hWnd, IDC_STATUS, addr clear
        
	        xor eax, eax
	        ret
	.elseif(ax == WM_COMMAND && wParam == IDC_INFO)
		invoke MessageBox, hWnd, addr AboutText, addr AppName, 0
		xor eax, eax
		ret
	.elseif(ax == WM_COMMAND && wParam == IDC_DEKRYPT)      
	      invoke dekrypt			; Questa è la procedura di dekrypting
	      xor eax, eax
	      ret
	.endif
	
	xor eax, eax
	ret
        
WndProc ENDP

dekrypt PROC

    ;*********************************************
    ;* Apertura del file
    ;*
    ;*********************************************
 	pushad
 
 	invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr dekrypting
        
        mov     [nModules], 0
        invoke CreateFile, addr FileName, GENERIC_READ or GENERIC_WRITE, \
        		   0, 0, OPEN_EXISTING, 0, 0
        .if(eax == INVALID_HANDLE_VALUE)
        	invoke SetDlgItemText, hMainWindow, IDC_STATUS, offset error_open_file
        	popad
        	xor eax, eax
        	ret
        .endif
        
        mov     [hFile], eax

    ;************************************************
    ;* Lettura del file
    ;*
    ;************************************************

        invoke GetFileSize, eax, 0
        mov [FileSize], eax
        
        invoke VirtualAlloc, 0, eax, MEM_COMMIT, PAGE_EXECUTE_READWRITE
        mov [lpData], eax

        invoke ReadFile, hFile, eax, FileSize, addr mdw, 0
        invoke CloseHandle, hFile
        
    ;************************************************
    ;* Controllo del formato PE
    ;*
    ;************************************************

        mov edi, [lpData]
        assume edi: ptr IMAGE_DOS_HEADER
        mov bx, word ptr [edi]
        .if(bx != 'ZM')
        	invoke VirtualFree, lpData, FileSize, MEM_DECOMMIT
        	invoke SetDlgItemText, hMainWindow, IDC_STATUS, offset error_invalid_pe
        	popad
        	xor eax, eax
        	ret
        .endif
        
        mov ecx, [edi].e_lfanew
        add edi, ecx
        assume edi: ptr IMAGE_NT_HEADERS
        mov ebx, [edi].Signature
        .if(ebx != IMAGE_NT_SIGNATURE)
        	invoke VirtualFree, lpData, FileSize, MEM_DECOMMIT
        	invoke SetDlgItemText, hMainWindow, IDC_STATUS, offset error_invalid_pe
        	popad
        	xor eax, eax
        	ret
        .endif

    ;******************************************************
    ;* Controllo della presenza del krypton
    ;*
    ;******************************************************

        mov edx, [edi].OptionalHeader.FileAlignment
        mov [file_alignment], edx

        mov edx, edi
        add edx, 4
        add edx, sizeof IMAGE_FILE_HEADER
        add dx, [edi].FileHeader.SizeOfOptionalHeader  ; EDX punta alla section table

        movzx eax, [edi].FileHeader.NumberOfSections
        assume edi: nothing
        
        mov [nSections], ax		; prelevo il numero di sezioni dell'eseguibile
        dec eax

        assume edx: ptr IMAGE_SECTION_HEADER
        mov edi, edx

        mov ecx, sizeof IMAGE_SECTION_HEADER
        mul ecx
        add edi, eax

        invoke lstrcmp, edi, addr last_section_name
        .if(eax != 0)
        	invoke VirtualFree, lpData, FileSize, MEM_DECOMMIT
        	invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr error_invalid_kr
        	popad
        	xor eax, eax
        	ret
        .endif

        sub edi, sizeof IMAGE_SECTION_HEADER
        invoke lstrcmp, edi, addr krypton_sec_name
        .if(eax != 0)
        	invoke VirtualFree, lpData, FileSize, MEM_DECOMMIT
        	invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr error_invalid_kr
        	popad
        	xor eax, eax
        	ret
        .endif
        
        assume edi: ptr IMAGE_SECTION_HEADER
        mov eax, [edi].VirtualAddress
        mov [vaddr_kryp_section], eax

        add edi, sizeof IMAGE_SECTION_HEADER

        mov eax, [edi].VirtualAddress
        mov [vaddr_last_section], eax

        mov eax, [edi].Misc.VirtualSize
        mov [vsize_last_section], eax
        
        invoke VirtualFree, lpData, FileSize, MEM_DECOMMIT
        assume edi: nothing

    ;*********************************************************
    ;* Creazione del processo
    ;*
    ;*********************************************************

        invoke GetStartupInfo, addr StartupInfo

        mov edi, [lpContext]
        assume edi: ptr CONTEXT
        mov [edi].ContextFlags, CONTEXT_FULL
        
        invoke CreateProcess, addr FileName, 0, 0, 0, 0, CREATE_SUSPENDED,
        		      0, 0, addr StartupInfo, addr ProcessInfo

        mov eax, [imagebase]
        add eax, [vaddr_last_section]
        add eax, 00000CF1h
        
        invoke WriteProcessMemory, ProcessInfo.hProcess, eax, addr jmp_eip, 2, addr mdw

	.while(1)
		invoke ResumeThread, ProcessInfo.hThread
		invoke Sleep, 1000
		invoke SuspendThread, ProcessInfo.hThread
		invoke GetThreadContext, ProcessInfo.hThread, lpContext

	        mov eax, [imagebase]
	        add eax, [vaddr_last_section]
        	add eax, 00000CF1h

	        mov edi, [lpContext]
        	assume edi: ptr CONTEXT
	        mov ebx, [edi].regEip
	        .if(ebx == eax)
	        	.break
	       	.endif
	.endw

    ;*******************************************
    ;* Dumping del processo
    ;*
    ;*******************************************

        mov eax, [vaddr_last_section]
        add eax, [vsize_last_section]
        mov [ProcessSize], eax
        
        invoke VirtualAlloc, 0, ProcessSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE
        mov [lpDumped], eax
        invoke ReadProcessMemory, ProcessInfo.hProcess, imagebase, lpDumped,
        			  ProcessSize, addr mdw
        invoke TerminateProcess, ProcessInfo.hProcess, 0

    ;***********************************************
    ;* Reallineamento eseguibile dumpato
    ;*
    ;***********************************************

        assume  edi: ptr IMAGE_DOS_HEADER

        mov edi, [lpDumped]
        add edi, [edi].e_lfanew

        assume edi: ptr IMAGE_NT_HEADERS
        mov edx, edi
        add edx, 4
        add edx, sizeof IMAGE_FILE_HEADER
        add dx, [edi].FileHeader.SizeOfOptionalHeader

        assume edi: nothing
        assume edx: ptr IMAGE_SECTION_HEADER

        movzx ecx, [nSections]
        sub edx, sizeof IMAGE_SECTION_HEADER

    	.while(ecx > 0)
	        add edx, sizeof IMAGE_SECTION_HEADER

	        mov esi, [edx].VirtualAddress
        	mov [edx].PointerToRawData, esi

	        mov esi, [edx].Misc.VirtualSize
	        mov [edx].SizeOfRawData, esi
	        dec ecx
	.endw

    ;************************************************
    ;* Prelevamento delle informazioni necessarie
    ;*
    ;************************************************

        invoke memdekrypt	; Questa routine sistema il file per la lettura
				; delle informazioni

        invoke import_routines  ; Questa routine importa tutte le funzioni di
				; dekrypting dall'interno del file dumpato
				; seguendo una tabella costante (rtable)

        invoke import_rinfo	; Questa preleva altre informazioni necessarie

    ;************************************************
    ;* Let's dekrypt!!!
    ;*
    ;************************************************

        mov esi, [nSec] 	;numero delle sezioni da dekryptare
        mov edi, [lpDekrypterTable]

    	.while(esi > 0)
	        mov ebx, [lpDumped]
	        add ebx, [edi]
	        mov ecx, [edi+4]
	        movzx eax, [dekrypting_key]
	        invoke routine1

        	mov ebx, [lpDumped]
	        add ebx, [edi]
        	mov ecx, [edi+4]
	        invoke routine2

	        add     edi, 8
	        dec     esi
	.endw
	
	invoke VirtualFree, lpDekrypterTable, DekrypterTableSize, MEM_DECOMMIT

        ;**************************************************
        ;* Ricostruzione della IT
        ;*
        ;**************************************************

        lea ebx, routine3
        mov bx, [ebx]
        cmp bx, 9090h
        jnz _end_it

        mov ebx, [it]
        add ebx, [lpDumped]
        add ebx, 0Ch

    _count:
        cmp dword ptr [ebx], 0
        jz  _end_count
        inc [nModules]
        add ebx, 14h
        jmp _count

    _end_count:
        xor eax, eax
        mov ecx, [nModules]
        
    _api_loop:
        mov ebx, [it]
        add ebx, [lpDumped]
        add ebx, eax            ;cambiamento di puntatore di elementi
        
        mov ebx, [ebx]
        add ebx, [lpDumped]

    _k_api:
        push ebx
        mov ebx, [ebx]
        
        cmp ebx, 0
        jz _end
        
        add ebx, [lpDumped]
        add ebx, 2		; ebx punta al nome dell'api crittografato
        invoke routine3		; Questa routine ricostruisce il nome dell'api
        pop     ebx
        add     ebx, 4
        jmp     _k_api

    _end:
        pop     ebx
        add     eax, 20
        loopnz  _api_loop

    _end_it:
    
    ;**************************************************
    ;* Aggiustiamo le FF15
    ;*
    ;**************************************************

        invoke ff15_05          ; Manual K-Execution
        invoke ff15_00		; mov ecx, dword ptr [xxxxxxxx]
        invoke ff15_01		; jmp far dword ptr [xxxxxxxx]
        invoke ff15_02		; mov dword ptr [xxxxxxxx], xxxxxxxx
        invoke ff15_03		; cmp dword ptr [xxxxxxxx], xx

 	invoke  IsDlgButtonChecked, hMainWindow, IDC_CHECK
 	.if(eax == BST_CHECKED)
        	invoke remove_tricks
        .endif

    ;**************************************************
    ;* Qualche altra operazione facile facile
    ;*
    ;**************************************************

        mov edi, [lpDumped]
        assume edi: ptr IMAGE_DOS_HEADER
        add edi, [edi].e_lfanew
        assume edi: ptr IMAGE_NT_HEADERS
        mov [edi].OptionalHeader.AddressOfEntryPoint, 1000h ;Aggiornare l'entry point

        mov edi, [lpDumped]
        add edi, dword ptr [edi+3Ch]
        mov eax, [it]
        mov dword ptr [edi+80h], eax          ;Correggere l'indirizzo della Import Table

        sub [edi].FileHeader.NumberOfSections, 2          ;Aggiorna il numero di sezioni
        mov eax, [vaddr_kryp_section]
        mov [edi].OptionalHeader.SizeOfImage, eax

        invoke personalizza

    ;******************************************************
    ;* Salvataggio del file e liberazione
    ;* della memoria allocata
    ;*
    ;******************************************************

	invoke lstrcpy, addr outfile, addr FileName
        
        lea     edi, outfile
        push    edi
        xor     eax, eax
        or      ecx, -1
        repnz scasb
        not     ecx
        dec     ecx
        pop     edi

    _ext_check:
        cmp     byte ptr [edi], '.'
        jz      _end_ext_check
        inc     edi
        loopnz  _ext_check

    _end_ext_check:
    	mov byte ptr [edi], 0
    	invoke lstrcat, addr outfile, addr suffix
    	mov     [ofn.lpstrFile], offset outfile
    	invoke GetSaveFileName, addr ofn
    	
    	.if(eax == 0)
    		invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr dekrypted
    		popad
    		xor eax, eax
    		ret
    	.endif
    	
    	invoke FileExists, addr outfile
    	.if(eax == TRUE)
    		invoke MessageBox, hMainWindow, addr file_exists, \
    				   addr AppName, MB_YESNO or MB_ICONQUESTION
    		.if(eax == IDNO)
    			invoke VirtualFree, addr lpDumped, ProcessSize, MEM_DECOMMIT
		        invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr dekrypted
        		popad
		        xor eax, eax
        		ret
        	.endif
        .endif
            	
    	invoke CreateFile, addr outfile, GENERIC_READ or GENERIC_WRITE,
    			   0, 0, CREATE_ALWAYS, 0, 0        
        mov [hFile], eax
        invoke WriteFile, hFile, lpDumped, vaddr_kryp_section, addr mdw, 0
        invoke CloseHandle, hFile
        
        invoke VirtualFree, addr lpDumped, ProcessSize, MEM_DECOMMIT
        invoke SetDlgItemText, hMainWindow, IDC_STATUS, addr dekrypted
        popad
        xor eax, eax
        ret

dekrypt ENDP

memdekrypt PROC
        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 8
        mov ecx, 3FEBEh

    	.while(ecx > 0)
	        mov bl, [eax]
        	ror bl, cl
        	xor bl, cl
	        add bl, cl
        	xor bl, cl
	        ror bl, cl
        	mov [eax], bl
	        inc eax
        	dec ecx
        .endw
        ret
memdekrypt ENDP

import_routines PROC
        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 8

        xor edx, edx
        mov edi, 4

	.while(edi > 0)
	        push eax
        
        	add eax, [rtable+edx]
	        mov esi, [rtable+edx+4]
        	mov ecx, [rtable+edx+8]

	    	.while(ecx > 0)
		        mov bl, [eax]
        		mov [esi], bl
		        inc eax
        		inc esi
        		dec ecx
	        .endw

	        add     edx, 0Ch
        	dec     edi
	        pop     eax
	.endw
        ret
import_routines ENDP

routine1 PROC
        nop
        nop
        nop
        nop
        nop
        nop
        inc al
        inc ebx
        dec ecx
        jnz routine1
        ret

routine1 ENDP

routine2 PROC
        nop
        nop
        nop
        nop
        inc ebx
        dec ecx
        jnz routine2
        ret

routine2 ENDP

routine3 PROC
        nop
        nop
        nop
        nop
        nop
        inc ebx
        cmp byte ptr [ebx], 0
        jnz routine3
        ret

routine3 ENDP

import_rinfo PROC
        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 1E80Ah
        mov eax, [eax]
        
        .if(eax == 0)
        	mov edi, [lpDumped]
	        add edi, dword ptr [edi+3Ch]
        	mov edx, edi

	        add edx, 4
        	add edx, sizeof IMAGE_FILE_HEADER
	        add dx, [edi].FileHeader.SizeOfOptionalHeader  ; EDX punta alla section table
        
	        assume edx: ptr IMAGE_SECTION_HEADER
        	mov eax, [edx].Misc.VirtualSize
	        assume edx: nothing
	.endif
        mov [code_section_size], eax
        
        ; Vi spiego questo sistema: tra le varie di un beta-tester, è successo che un ese-
        ; guibile non riportasse la dimensione della sezione di codice al solito offset.
        ; Così ho fatto un controllo che, se si verifica questo caso, prende la dimensione
        ; della sezione direttamente dalla section table, anche se in questo caso sarà
        ; un valore arrotondato in base al section allignment perché dalla tabella non
        ; abbiamo il RawSize ma solo il VirtualSize.

        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 0C97Ah
        add eax, 8
        mov al, [eax]
        mov [dekrypting_key], al            ; Ritiro della chiave per la prima routine
                
        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 0C993h
        add eax, 8

        movzx eax, byte ptr [eax]
        mov [nSec], eax                     ; Ritiro del numero di sezioni da decrittare
        
        shl eax, 3
        mov [DekrypterTableSize], eax

        invoke VirtualAlloc, 0, DekrypterTableSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE
        mov [lpDekrypterTable], eax
        
        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 0C998h
        add eax, 8

        mov ecx, [DekrypterTableSize]
        mov edi, [lpDekrypterTable]

    	.while(ecx > 0)          ; Ritiro della tabella di informazioni
	        mov bl, [eax]
        	mov [edi], bl
	        inc eax
        	inc edi
        	dec ecx
        .endw

        mov eax, [vaddr_kryp_section]
        add eax, [lpDumped]
        add eax, 0C983h
        add eax, 8

        mov esi, [eax]
        mov [it], esi

        add esi, [lpDumped]
        mov eax, [esi+0Ch]
        add eax, [lpDumped]
        
        ret
import_rinfo ENDP

ff15_05 PROC
        LOCAL   esp20:  DWORD
        LOCAL   esp24:  DWORD
        LOCAL   RetVal: DWORD
        LOCAL   proce:  DWORD
        LOCAL   ffcall: DWORD
        LOCAL   dimens: DWORD
        LOCAL   key:    DWORD
        
        mov eax, [lpDumped]
        add eax, 1000h
        mov ecx, [code_section_size]
        
    	.while(ecx > 0)
	        cmp word ptr [eax], 15FFh
        	jnz _continue_scan05
	        cmp dword ptr [eax+2], 05
        	jnz _continue_scan05

    ;*********************************************************************
	        pushad
        
	        mov [ffcall], eax

        	mov ebx, [eax-4]
	        mov [esp20], ebx    ;Ottengo esp+20
        	mov ebx, [eax-9]
	        mov [esp24], ebx    ;Ottengo esp+24
        	mov ebx, eax
	        add ebx, 6
        	mov [RetVal], ebx   ;Ottengo RetVal

	        mov eax, [vaddr_kryp_section]
        	add eax, [lpDumped]
	        add eax, 8
        	add eax, 0F1DCh
	        mov [proce], eax    ;Ottengo l'indirizzo della procedura FF15-05 su disco

	        add eax, 2F8h
        	mov eax, [eax]
	        mov ecx, [esp20]
        	xor ecx, eax        ;Ottengo la dimensione di codice da decifrare
	        mov [dimens], ecx

	        mov ecx, [esp24]
        	xor cx, ax
	        mov [key], ecx

	        mov ebx, [key]
        	xor eax, eax
	        mov ax, bx
        	mov ecx, [dimens]
        	mov esi, [ffcall]
	        add esi, 6
        
	    	.while(ecx > 0)
		        mov dl, [esi]
        		neg cl
		        add dl, cl
        		xor dl, cl
	        	rol dl, cl
	        	neg cl
		        sub dl, al
        		add dl, ah
		        xor dl, al
        		rol dl, cl
	        	xor dl, ah
	        	mov [esi], dl
		        inc esi
        		dec ecx
        	.endw

	        mov edi, [ffcall]
	        sub edi, 10
	        lea esi, kdes
	        mov ecx, 13
	        rep movsb        

	        popad
    ;*********************************************************************
    
	    _continue_scan05:
	        inc eax
	        dec ecx
	.endw
        ret

ff15_05 ENDP

ff15_00 PROC
        LOCAL   bytes:   DWORD
        LOCAL   ptrcode: DWORD
        LOCAL   key00:   DWORD

    ;**************************************************
    ;* Ritiro dei valori necessari
    ;*
    ;*************************************************

	pushad
	
        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 14BBEh
        mov [bytes], eax                ;Salvo lpCodice

        xor ebx, ebx
        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 95B7h
        mov ecx, 87Ch

    _getkey00:
        add ebx, [eax]
        inc eax
        dec ecx
        test ecx, ecx
        jnz _getkey00

        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 0C96Fh
        mov eax, [eax]
        xor ecx, ecx
        mov cl, al
        add ebx, ecx
        mov [key00], ebx                ;Salvo la chiave

        mov [ptrcode], 0
        
        mov eax, [lpDumped]
        add eax, 1000h
        mov ecx, [code_section_size]
        
    	.while(ecx > 0)
	        cmp word ptr [eax], 15FFh
        	jnz _continue_scan00
	        cmp dword ptr [eax+2], 0
        	jnz _continue_scan00

    ;**********************************************************************
    	    	pushad

	        mov ecx, eax
        	lea ebx, [key00]
	        mov eax, [bytes]
	        add eax, [ptrcode]

	        mov edx, [eax+4]
        	xor edx, [ebx]
	        mov [ecx], edx
        	mov dx, [eax+8]
	        xor edx, [ebx]
        	mov [ecx+4], dx
        	
	        add [ptrcode], 10
	        popad
    ;**********************************************************************

	    _continue_scan00:
        	inc eax
	        dec ecx
	.endw
	popad
        ret
        
ff15_00 ENDP

ff15_01 PROC
        LOCAL   bytes:   DWORD
        LOCAL   ptrcode: DWORD
        LOCAL   key01:   DWORD
	
	pushad
	
    ;**************************************************
    ;* Ritiro dei valori necessari
    ;*
    ;*************************************************

        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 1E832h
        mov [bytes], eax                ;Salvo lpCodice

        mov ebx, [lpDumped]
        add ebx, [vaddr_kryp_section]
        add ebx, 8
        add ebx, 1E802h
        mov ebx, [ebx]
        mov [key01], ebx                ;Salvo la chiave

        mov [ptrcode], 0
        
        mov eax, [lpDumped]
        add eax, 1000h
        mov ecx, [code_section_size]
        
    	.while(ecx > 0)
	        cmp word ptr [eax], 15FFh
        	jnz _continue_scan01
	        cmp dword ptr [eax+2], 1
        	jnz _continue_scan01

    ;**********************************************************************
	        pushad

	        mov ecx, eax
        	lea ebx, [key01]
	        mov eax, [bytes]
        	add eax, [ptrcode]

	        mov edx, [eax+4]
        	xor edx, [ebx]
	        mov dl, -1
        	mov [ecx], edx
	        mov dx, [eax+8]
        	xor edx, [ebx]
	        mov [ecx+4], dx

	        add [ptrcode], 10
        
	        popad
    ;**********************************************************************

    		_continue_scan01:
	        inc eax
        	dec ecx
	.endw
	popad
        ret        
ff15_01 ENDP

ff15_02 PROC
        LOCAL   ptrcode: DWORD
        LOCAL   lptab: DWORD
        
        pushad       
        mov [ptrcode], 00

        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 284C2h
        mov [lptab], eax

        mov eax, [lpDumped]
        add eax, 1000h
        mov ecx, [code_section_size]

    	.while(ecx > 0)
	        cmp word ptr [eax], 15FFh
        	jnz _continue_scan02
	        cmp dword ptr [eax+02], 02
        	jnz _continue_scan02

    ;*****************************************************
	        pushad

	        mov ecx, eax
        	sub ecx, 2
        
	        mov word ptr [ecx], 05C7h   ; mov [xxxxxxxx], xxxxxxxx
	        mov eax, [lptab]
	        add eax, [ptrcode]
	        mov ebx, [eax]
	        mov eax, [eax+04]
        	xor ebx, eax                ; EAX = VALUE
	        add eax, ebx                ; EBX = LOCATION
        	mov [ecx+02], ebx
	        mov [ecx+06], eax

	        add [ptrcode], 8

	        popad
    ;*****************************************************

    		_continue_scan02:
        	inc eax
        	dec ecx
	.endw
	
        popad
        ret
ff15_02 ENDP

ff15_03 PROC
        LOCAL   ptrcode: DWORD
        LOCAL   lptab: DWORD
        
        pushad	
        mov [ptrcode], 00

        mov eax, [lpDumped]
        add eax, [vaddr_kryp_section]
        add eax, 8
        add eax, 3210Ch
        mov [lptab], eax

        mov eax, [lpDumped]
        add eax, 1000h
        mov ecx, [code_section_size]

    	.while(ecx > 0)
        	cmp word ptr [eax], 15FFh
	        jnz _continue_scan03
        	cmp dword ptr [eax+02], 03
	        jnz _continue_scan03

    ;*****************************************************
	        pushad

	        mov ecx, eax
        
	        mov word ptr [ecx], 3D83h   ; cmp [xxxxxxxx], xx
        	mov eax, [lptab]
	        add eax, [ptrcode]
        	mov ebx, [eax]
	        mov eax, [eax+04]
        	xor ebx, eax                ; EBX = LOCATION
	        add eax, ebx                ; EAX = VALUE
        	and eax, 0FFFF0000h
	        shr eax, 18h
        
	        mov [ecx+02], ebx
        	mov byte ptr [ecx+06], al

        	add [ptrcode], 8

        	popad
    ;*****************************************************

	    _continue_scan03:
	        inc eax
	        dec ecx
	.endw

        popad
        ret
ff15_03 ENDP

remove_tricks PROC
        mov ebx, [lpDumped]
        add ebx, 1000h
        mov ecx, [code_section_size]
                
    _loop:
        cmp dword ptr [ebx+1], 112233h
        jz  _step2
        inc ebx
        dec ecx
        test ecx, ecx
        jnz     _loop
        ret

    _step2:
        cmp     dword ptr [ebx+6], 11000000h
        jz      _step3
        inc     eax
        dec     ecx
        test    ecx, ecx
        jnz     _loop
        ret

    _step3:
        push    ecx
        mov     ecx, 41
        mov     al, 90h
        sub     ebx, 6
        
    _delete:
        mov     [ebx], al
        inc     ebx
        loopnz  _delete
        
        pop     ecx
        
        inc     ebx
        dec     ecx
        test    ecx, ecx
        jnz     _loop

        ret
remove_tricks ENDP

FileExists PROC lpFileName: LPCSTR
        xor eax, eax
        or ecx, -1
        mov edi, [lpFileName]
        repnz scasb
        not ecx
        dec ecx
        .if(!ecx)
        	mov eax, FALSE
        	ret
        .endif
        
        invoke FindFirstFile, lpFileName, addr FindData
        invoke FindClose, eax
        ret
FileExists ENDP

personalizza PROC
	pushad
	
	mov edi, [lpDumped]
	add edi, dword ptr [edi+3Ch]
        mov edx, edi

	add edx, 4
        add edx, sizeof IMAGE_FILE_HEADER
	add dx, [edi].FileHeader.SizeOfOptionalHeader  ; EDX punta alla section table
	movzx ecx, word ptr [nSections]
        sub ecx, 2
	assume edx: ptr IMAGE_SECTION_HEADER
	.while(ecx > 0)
		mov edi, dword ptr [secname]
		mov dword ptr [edx], edi
		mov edi, dword ptr [secname+4]
		mov dword ptr [edx+4], edi
		add edx, sizeof IMAGE_SECTION_HEADER
		dec ecx
	.endw
	xor eax, eax
	mov dword ptr [edx], eax
	mov dword ptr [edx+4], eax
	add edx, sizeof IMAGE_SECTION_HEADER
	mov dword ptr [edx], eax
	mov dword ptr [edx+4], eax
	
	popad
	xor eax, eax
	ret
personalizza ENDP
end Main

Amici miei... aiuto! Alor questo è il risultato. Molte volte ho dichiarato procedure a parte per farvi capire meglio come è stato schematizzato il codice. We mi son fatto tanto di culo per finire 'sta roba... spero di essere riuscito almeno a guadagnare qualche lettura da parte vostra :). Credo che per oggi possa bastare!! Ciao a tutti!

Note finali

Ahhh... finalmente posso scrivere su questo tutorial senza farmi venire un incredibile mal di testa! Prima di tutto un ringraziamento va a Yado ;). Un saluto a Reload85, AndreaGeddon, Quequero (che mi farà mettere l'uic form nel mio sito se no lo prendo a pugni :), CrashRR, Ntoskrn, Quake2^AM, Evilcry, CyberDude e a tutti i membri di #crack-it di #fuckinworld e di #informazionelibera.
Saluto anche la mia professoressa di inglese (non c'è un motivo preciso... la saluto e basta :) poi i miei colleghi Cazzato, Lubello e Cortese, oltre che a tutta la classe :).
Un saluto maligno con minaccia di morte a tutti coloro che o per un motivo o per un altro o addirittura senza un motivo mi vogliono morto :). Tutto il resto è noia! :DDD

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.