BiSHoP crackme R2
(Piccolo Decripter che patcha anche L'EntryPoint e un piccolo BruteForce)

Data

by "Guzura"

 

28/6/2002

UIC's Home Page

Published by Quequero

Morire per uno sbaglio...

Bella guz, completo come sempre. Interessante il decrypter

Meglio evitare...

....

E-mail: [email protected]

....

Difficoltà

Livello UnPoPiùDiSchiappa

 

Un crackme abbastanza facile ma che si adatta perfettamente a spiegare come si può realizzare un decrypter (semplice) per un file.Poi vediamo anche un bruteforce molto banale e qualche anti-sice trick.


BiSHoP R2
(Decrypt e BruteForce)
Written by Guzura

 
Allegato
 

Introduzione

Il sistema utilizzato per decryptare il Crakme è abbastanza classico e riflette il metodo per Cryptare che si trova nei tut di Kill3x sul formato PE e consiglio anche oltre a questi la lettura del "The Portable Executable File Format from Top to Bottom" by Randy Kath dove spiega molto bene il formato PE e mette a disposizione una DLL (con source) per manipolare il PE.

Tools usati

IDA immancabile
Compilatore C per il keygen e il decrypter

URL o FTP del programma

www.lockless.com

Notizie sul programma

Just a CrackMe  

Essay


Come al solito la prima cosa da fare è lanciare il crackme... Per vedere cosa combina e notare che rileva Sice.
Quindi disassembliamolo con IDA e vediamo cosa caviamo fuori (Ho già cambiato le label).
_rdata segment para public 'DATA' use32

InitFromStart: ;L'EntryPoint ci sbatte direttamente Qui dopo un jump
00401000 mov ebx, offset Start1
00401005 mov edi, offset End1
0040100A call DecriptRoutine
0040100F mov ebx, offset Start2
00401014 mov edi, offset DecriptRoutine
00401019 call DecriptRoutine
0040101E jmp short near ptr Start2
00401020 ; ---------------------------------------------------------------------------
00401020 push 0
...;Per il momento non interessante
00401042 jmp short near ptr unk_401057
00401042 ; ---------------------------------------------------------------------------
 
00401044 Start1 db 77h ; w ; DATA XREF: .rdata:00401000o
00401045 db 7Fh ; &127;
...;Prima zona da decrittare da Start1 a End1
00401055 db 3Dh ; =
00401056 End1 db 0BFh ; + ; DATA XREF: .rdata:00401005o

00401057 loc_401057: ; CODE XREF: .rdata:00401042j
00401057 push eax
00401058 call sub_40126C

0040105D Start2 db 0B7h ; À ; CODE XREF: .rdata:0040101Ej
...;Seconda zona da decrittare da Start2 a DecriptRoutine
0040106B ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
0040106B
DecriptRoutine proc near ; CODE XREF: .rdata:0040100Ap
0040106B ; .rdata:00401019p ...
0040106B mov ax, [ebx]
0040106E xor ax, 1577h
00401072 xchg al, ah
00401074 mov [ebx], ax
00401077 add ebx, 2
0040107A cmp ebx, edi
0040107C jl short DecriptRoutine
0040107E retn
0040107E DecriptRoutine endp

Iniziamo con le cose da notare, intanto i nomi delle sezioni sono invertite il codice è nella sezione _rdata e i dati nella sezione .text questo usando una classica espressione Mantovana "Manda wdasm32 a SMAIALARSI" tanto noi usiamo IDA.

Altra cosa come vedete tutto inizia arrivando a 401000 dove vengono messi nello stack l'address di inizio e di arrivo da decrittare (da 401044 a 401056) una prima zona e poi ne viene decrittata un'altra (da 401054 a 40106B) subito dopo e fatto questo si salta proprio alla seconda zona appena decrittata.
La funzione di Decrypt è abbastanza semplice.
Prima di proseguire vorrei far notare che la routine di analisi del seriale non è criptata quindi se non avete Sice non "SAREBBE" necessario decryttare (o Dumpare).
Vediamo come possiamo fare per decryttare il file senza eseguirlo (vi riporto una parte del codice del decrypter che ho scritto in C):
 
/**********************************************/
FileHandle=CreateFile("c:\\grct\\k\\CM23.exe",GENERIC_READ+GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL+FILE_FLAG_SEQUENTIAL_SCAN,0);
FileSize=GetFileSize(FileHandle,NULL);   
MapFileHandle=CreateFileMapping(FileHandle,NULL,PAGE_READWRITE,0,FileSize,NULL);
StartOffset=MapViewOfFile(MapFileHandle,FILE_MAP_WRITE,0,0,0);            
/*
Questa prima parte di codice non fa altro che aprire il file che vogliamo decrittare con CreateFile per poi prenderne la lunghezza con GetFileSize.
Ora vi riporto un commento tratto dal tut di Kille3x che mi sembra chiaro e coinciso:"come vedete ho scelto di utilizzare i MMF per manipolare l'eseguibile, la ragione e' che in questo modo posso gestire gli offset direttamente come scostamenti in memoria essendo sicuro di avere il file mappato in modo lineare. Questo metodo di procedere e' in sostanza lo stesso che utilizza il loader.. va notato pero' che i MMF hanno una loro piccola pecca, non possono essere ridimensionati una volta creati...
Il puntatore ottenuto dalla MapViewOfFile costituisce ora la nostra ImageBase".
Per ogni riferimento ai parametri guardate sull'SDK e cercate info sui Memory Mapped File
*/
poh=(PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(StartOffset);
poh->AddressOfEntryPoint=0x1020;
/*
Questa seconda parte patcha l'entry point ma la vediamo dopo 
*/           
Start=StartOffset+0x444;
End=StartOffset+0x456;
/*
StartOffset+RawOffset rappresenta quello che sarà l'indirizzo della locazione da cui incominciare la decrittazione dato che per Noi StartOffset è l'equivalente dell'imagebase che si ha quando il programma è eseguito.
Quindi Start e End rappresentano gli indirizzi necessari alla prima decrittazzione
Piccola parentesi sugli RawOffset citando ancora Kill3x "se volessimo ottenere un offset "fisico" (su disco,MMF) dato un VA ?In questo caso dovremmo utilizzare le informazioni relative alla sezione che contiene quell'indirizzo (ovviamente dobbiamo trovarla cercando nella section table verificando che VirtualAddress <= VA <= SVirtualAddress + SVirtualSize), relativizzare l'indirizzo rispetto all'inizio di quella sezione sottraendo l'imagebase e VA della sezione, ottenendo cosi' un offset che andremo a sommare all'offset fisico della sezione stessa:
RAW OFS = (VA - ImageBase - SVirtualAddress) + PointerToRawData
0x834 = (0x401234 - 0x400000 - 0x1000 ) + 0x600
".
IDA fa questa operazione per noi fornendoci direttamente i RAW OFF.
Per noi ImageBase=StartOffsett dato che usiamo un MMF (memory mapped file)
(ci interessa per iniziare la decrittazzione :401044 a cui corrisponde RAW OFFSET:0x444...)
*/  
 
__asm{
                pushf
                mov ebx,Start
                mov edi,End
            Su:
                mov ax,[ebx]
                xor ax,0x1577
                xchg al,ah
                mov [ebx],ax
                add ebx,2
                cmp ebx,edi
                jl Su

                popf
            }
/*
Questa è in pratica la stessa routine di decrittazione che trovate nel crackme
Analogo discorso per la seconda parte da decrittare (sotto)
/*
Start=StartOffset+0x45D;
End=StartOffset+0x46B;
   
__asm{
                pushf
                mov ebx,Start
                mov edi,End
            Su1:
                mov ax,[ebx]
                xor ax,0x1577
                xchg al,ah
                mov [ebx],ax
                add ebx,2
                cmp ebx,edi
                jl Su1

                popf
            }        
if(FlushViewOfFile(StartOffset,FileSize)) MessageBox(hwndDlg,"Salvato","OK",MB_OK);//FlushViewOfFile aggiorna il file dopo averlo modificato
UnmapViewOfFile(StartOffset);//Ovvio chiudiamo tuttigli handle aperti
CloseHandle(MapFileHandle);
CloseHandle(FileHandle);

/**********************************************/
 
In allegato trovate sia il sorgente che l'eseguibile per decrittare ma è meglio se date un'occhiata ai tut di Killo :)
Lanciamo l'eseguibile che decrittera il crackme e vediamo un po cosa appare (ovviamente dobbiamo ridisasemblare il nuovo file decrittato con IDA) per ottenere questo (riporto le PARTI DECRITTATE)
 
00401044 loc_401044: ; CODE XREF: .rdata:00401067j;INIZIO PRIMO DECRIT
00401044 ; DATA XREF: .rdata:00401000o
00401044 push 0
00401046 push offset aCalendraV1_2 ; "Calendra v1.2"
0040104B push offset aNoDebuggingPle ; "No debugging please."
00401050 push 0
00401052
00401052 loc_401052: ; DATA XREF: .rdata:00401005o
00401052 call near ptr 0BFF638D9h ;FINE PRIMO DECRIT

00401057 loc_401057: ; CODE XREF: .rdata:00401042j
00401057 push eax
00401058 call sub_40126C

0040105D loc_40105D: ; CODE XREF: .rdata:0040101Ej;INIZIO SECONDO DECRIT
0040105D ; DATA XREF: .rdata:0040100Fo
0040105D xor eax, eax
0040105F mov ah, 43h
00401061 int 68h ; - APPC/PC
00401063 cmp ax, 0F386h
00401067 jz short loc_401044
00401069 jmp short loc_401020
0040106B ;FINE SECONDO DECRIT


Quello che appare è abbastanza interessante perchè c'era nascosto il primo degli ANTISICE-TRICK dell'INT68 (Ralph Brown List per riferimenti)
Vi ricordo che dopo le due routine di decrittazzione si salta all'indirizzo :40105D dove si verifica se c'è SICE che se è rilevato vi fa apparire un MBoxA.

Lo svolgimento corretto del programma vorrebbe che SICE non fosse caricato e noi saltassimo a :401020 (:40106B jmp 401020) quindi perchè non patchare l'ENTRY POINT DEL PROGRAMMA "DECRITTATO" PER FARLO PARTIRE DA :401020 (lo possiamo fare solo sul decrittato ovviamente a patto che in precedenza non siano stati riliveti parametri necessari per la continuazione del prog; se per esempio in precedenza ci fosse stata una GetModuleHandleA non avremme potuto agire così)
 
Per Patchare l'entry point ho inserito nel codice del decripter le seguenti righe di codice
poh=(PIMAGE_OPTIONAL_HEADER)OPTHDROFFSET(StartOffset);
poh->AddressOfEntryPoint=0x1020;
 
OPTHDROFFSET è una macro che mi permette di ottenere un puntatore all' OPTINAL HEADER che al suo interno ha il campo EntryPoint che a differnza di prima è un RVA (NOTATE LA DIFFERENZA)
Kill3x dice :"RVA, che e' appunto un scostamento relativo alla imagebase: quindi se volete leggere il valore di una DWORD che sta ad un RVA = 1234 basta che gli sommiate l'imagebase ed otterrete il suo Virtual Address (VA) cioe' l'indirizzo nello spazio di indirizzamento del processo:
VA = RVA + ImageBase
0x401234 = 0x1234 + 0x400000
"
La macro usata è presa dalla DLL che Randy Kath usa per spiegare il formato PE (vedi riferimenti all'inizio)
Nel nostro caso vogliamo iniziare a :0x400000+0x1020 = 0x401020
Potreste obbiettare che per patchare l'entry point facevo prima a usare il PEditor!!! Vero ma mettiamo che voi dovete fare una patch da passare a un vostro amico...

Ora il nostro CrackMe patchato e decrittato partira da :401020 quindi ricominciamo l'analisi da li (che ora è quella di un Crackme Classico abbastanza semplice) :)
Vi riporto il codice con gia le LABEL cambiate per comodità:
loc_401020:
00401020 push 0
00401022 call GetModuleHandleA
00401027 mov ds:ModuleHandle, eax
0040102C push 0
0040102E push offset DialogBoxProcFunction
00401033 push 0
00401035 push 65h
00401037 push ds:ModuleHandle
0040103D call DialogBoxParamA
00401042 jmp short loc_401057 ;to ExitProcess


Va fatta notare un'altra cosa IDA non riconosce le API chiamate anche se la IAT e IT non è smaialata (a mio avviso per il fatto che sono invertiti i nomi delle sezioni) ma ci mettete un attimo a cambiarveli a mano come se rinominaste una SubRoutine normale).
 
Ora ci basta cercare a partire dalla DialogBoxProcFunc dove si verifica il seriale; non ci mettete molto ad arrivare Qui:
004010FD mov ebx, offset GetDlgItemInt
004010F0 call RilevaBreakPoint
004010F5 cmp al, 0CCh ; Verifico se ho messo un bpx su un API
004010F7 jz NoDebug
004010FD mov ebx, offset GetDlgItemText
00401102 call RilevaBreakPoint
00401107 cmp al, 0CCh
00401109 jz NoDebug
0040110F mov ebx, offset QUI
00401114 cmp byte ptr [ebx], 0CCh
00401117 jz NoDebug
0040111D call sub_4011EC
00401122 mov ds:B4FC7BCD, eax
00401127 push 0
00401129 push 0
0040112B push 3E8h
00401130 push dword ptr [ebp+8]
QUI: ; DATA XREF: .rdata:0040110Fo
00401133 call GetDlgItemInt
00401138 test eax, eax
0040113A jz IcorrectSerial  ;Seriale non messo...
00401140 call DevoUscireConEax0
00401145 test eax, eax
00401147 jnz IcorrectSerial

Prima di vedere se il Serial è corretto vengono eseguiti altri ANTI-DEBUG Check
La Routine RilevaBreakPoint va a vedere se a un dato indirizzo c'è un INT 3 (opcode CC) quindi va a verificare se avete messo un BPX GetDlgItemText o GetDlgItemInt o al''indirizzo :401133.
 
Vediamo ora la routine DevoUscireConEax0 che va a valutare il seriale
DevoUscireConEax0 proc near ;
004011FC xor ecx, ecx
004011FE mov ebx, eax ; Serial in Ebx
00401200 movzx edx, ax ;
00401203 shl edx, 1 ;
00401205 mov cl, dl ;
00401207 and cl, 0Fh
0040120A and eax, 0FFFh
0040120F mov esi, edx
00401211 and esi, 0FFFFh
00401217 shl ebx, 8
0040121A xor bl, dh
0040121C test ecx, ecx
0040121E jz short loc_401227
00401220 Loop1: ; CODE XREF: DevoUscireConEax0+29j
00401220 cdq
00401221 mul esi
00401223 xor eax, ebx
00401225 loop Loop1
00401227 loc_401227:
00401227 mov ebx, ds:B4FC7BCD
0040122D xor eax, ebx ; Eax deve essere = 0 per essere Serial correct
0040122F retn
DevoUscireConEax0 endp
Quello che veramente ci interessa è che il seriale è preso attraverso GetDlgItemInt quindi è immediatamento convertito in una DWORD dall'API stessa.
Morale: Si puo BruteFortizzare la DWORD stessa e poi ritrasformarlra in una stringa ASII di INTERI (piu facile a farsi che a dirsi).Alternativamente potreste reversarvi l'algo se ne avete voglia.
Vi riporto il codice relativo al BruteForce in C.
 
__asm{
                    pushf
            Riprendi:    
                    mov eax,Serial
                    inc eax
                    mov Serial,eax
                   
                    xor ecx, ecx
                    mov ebx, eax
                    movzx edx, ax
                    shl edx, 1
                    mov cl, dl
                    and cl, 0x0F
                    and eax, 0xFFF
                    mov esi, edx
                    and esi, 0xFFFF
                    shl ebx, 8
                    xor bl, dh
                    test ecx, ecx
                    jz Qui
            Loop1:
                    cdq
                    mul esi
                    xor eax, ebx
                    loop Loop1
            Qui:
                mov ebx, 0xB4FC7BCD
                    xor eax, ebx
                    cmp eax,0
                    jne Riprendi


            Exit:    
                    popf
            }
            wsprintf(Buf,"%u",Serial);
            MessageBox(hwndDlg,Buf,"Serial",MB_OK);
e
I commenti mi sembrano superflui unica cosa è che ci sono piu seriali validi.
 
Vi lascio con le postille:
1)Il metodo usato per decrittare un file è tanto semplice quanto efficace dovete solo avere l'accortezza di studiarvi un attimo la routine da ricopiare (quella di decrittazione).
2)Perchè non provate a usare questo sistema con il LeimCript (di Andreageddon)

By Guzura

 

Note finali

Saluto tutti quelli che ho conosciuto all' HackMeeting 2002 e tutti quelli che mi conoscono.

Disclaimer

Fai la cosa giusta !