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
-
-
-
- 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.
- IDA immancabile
- Compilatore C per il keygen e il decrypter
www.lockless.com
Just a CrackMe
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
-
Saluto tutti quelli che ho conosciuto all' HackMeeting
2002 e tutti quelli che mi conoscono.
Fai la cosa giusta !