yanisto’s Naive_crackme
Here is my first linux ELF crackme…
Not very hard to solve but not for tyros either…Solution will be published later on nuxed.org.
Enjoy ! ++nisto
Difficulty: 2 - Needs a little brain (or luck)
Platform: Unix/linux etc.
Language: Assembler
misha@heaven ~/yanisto $ file naive-crk naive-crk: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped misha@heaven ~/yanisto $ strings naive-crk ptraced !! ---snipped some weird characters--- Sorry, it ain't the good pass or the is file patched---snipped some weird characters---
i fired up ida, pressed ‘ctrl e’ and went to the crackme’s start routine. and from there, i encountered one of the first tricks employed. the author inserted some garbage bytes to (possibly) fool disassemblers. ida saw this and did the necessary modifications.
ida:
.text:08048883 start proc near
.text:08048883 ; FUNCTION CHUNK AT .text:080483B2 0000000F BYTES
.text:08048883 ; FUNCTION CHUNK AT .text:080483C4 0000008D BYTES
.text:08048883 ; FUNCTION CHUNK AT .text:0804846B 00000020 BYTES
.text:08048883 ; FUNCTION CHUNK AT .text:080484AF 00000098 BYTES
.text:08048883 ; FUNCTION CHUNK AT .text:0804886C 00000017 BYTES
.text:08048884 nop
.text:08048885 jmp loc_80483C4
this single byte is effectively, a nop instruction. whereas, in objdump, this is how it looked like:
8048881: ff (bad) 8048882: ff 90 90 e9 3a fb call DWORD PTR [eax-80025200] 8048888: ff (bad)
the jz instruction at 0x080483C4 is simply a means of jumping over more garbage. to fix it, follow the jmp, press ‘d’, then at address 0x080483CB (0x080483C9 + 2 = 0x080483CB), press ‘c’ to tell ida to view the data starting there as ‘code’. after doing this, the block would now make sense, outlined below for reference.
.text:080483C9 db 12h ; garbage byte .text:080483CA db 15h ; garbage byte .text:080483CB xor ebx, ebx .text:080483CD xor eax, eax .text:080483CF mov eax, dword_8048453 ; 0x31DB3104 .text:080483D4 mov edx, 0A1h .text:080483D9 mov ecx, edx .text:080483DB mov bl, cl .text:080483DD rol bl, 4 .text:080483E0 ror cl, 4 .text:080483E3 or bl, cl .text:080483E5 ror eax, 2 .text:080483E8 and eax, 0FEh .text:080483ED xchg al, bl .text:080483EF xor ebx, 0C0h .text:080483F5 xor esi, esi .text:080483F7 and cl, 5 .text:080483FA push eax ; 0x1A on the stack .text:080483FB mov esi, offset byte_8048451 .text:08048400 lodsw ; eax = 0x5e60 .text:08048402 xor ax, 0DEADh ; eax = 0x80cd .text:08048406 mov edi, offset loc_8048418 ; buffer .text:0804840B stosw ; 0x80cd to buffer .text:0804840D pop eax ; restore eax = 0x1a .text:0804840E loc_804840E: .text:0804840E xor dx, 0A1h ; edx = 0 .text:08048413 xor bl, 80h ; ebx = 0 .text:08048416 xor esi, esi ; esi = 0
… succeeding two bytes were patched earlier (specifically, at .text:0804840B)
ok, note that the value 0x80cd is stored a few bytes further into the block. 0x80CD when translated to intel asm instruction, is just equal to int 0x80. we know that it’s a PTRACE_TRACEME check because eax is 0x1a (26 decimal) and ebx = 0.
misha@heaven ~/yanisto $ grep -i __nr_ptrace /usr/include/asm/unistd.h #define __NR_ptrace 26
i now resume analysis at address 0x0804846B where the control is passed if the call to ptrace() succeeded.
first encountered a function that xors some predefined characters by 0x1137 (decrypt_welcome_string()). it prints the welcome string after decryption and calls a sys_read() to get characters and save them to a buffer (8 characters).
the welcome string:
Naive_crackme - nisto's crackme #1
----------------------------------
This is my 1st crackme...
It uses regular tricks and some others... hehe...
Hope u'll get some fun with it.
You can join me at yanisto@nuxed.org or on #secdev@freenode.net
Enter the Password :
i noticed that no comparison whatsoever was done with the user input, (well, at least not in the current function anyway) instead, upon return, another function was called which does some calculation, possibly a checksum() function to detect patching within a proximity. but here’s the twist, the user input buffer is also located within the said proximity. making a predefined spot in the block dynamic.
the checksum() is a continuous xor operation of a dword to succeeding blocks of dword values.
.text:08048454 checksum(): .text:08048454 xor ebx, ebx .text:08048456 xor ecx, ecx .text:08048458 mov esi, offset dword_804800C ; start address .text:0804845D checksum_loop: .text:0804845D lodsd ; load next dword .text:0804845E xor ebx, eax ; xor it with the former xor result .text:08048460 add ecx, ebx .text:08048462 cmp esi, offset byte_8048418 ; are we at the end? .text:08048458 jbe short checksum_loop ; no, continue with checksumming .text:0804846A retn
when the function returns ebx, is compared to a predefined value, 0x19160493. the block that is being checksummed roughly looks like:
[ constant values block ] checksum: 0x14a13e9f
[ user input buffer ]
[ constant values block ] checksum: 0x5df8c4c2
the buffer for user input is located in between these two blocks, therefore, the end result of the checksum() is highly dependent on the 8 characters of user input. i consulted my friend grainne yap about the checksum function which i encountered and she told me that XOR is associative, we can get the values of the two blocks with constant values and join them together. then finally, xor that with variable user input that will result to 0x19160493. also take note that the user input buffer has some constant bytes of it’s own, due to the fact that the user input doesn’t fall under a dword boundary relative to the dword increments of the checksum() function. so if we have an 8 character user input “stefanie“, the user buffer block would look something like:
note: values in hex are constants.
t | s | 0x80 | 0xcd n | a | f | e 0x00 | 0x2e | e | i
so we have two variable dwords and the dword with an additional constant value 0x002e80cd.
computing for the xor is a chore, so i used ald to make the crackmes’ own checksum() function compute the xor checksums of the two blocks with constant values for me.
[ constant values block ] checksum: 0x14a13e9f [ user input buffer ] index [2][1][4][3] [ user input buffer ] index [6][5][8][7] [ additional constant ] checksum: 0x002e80cd [ constant values block ] checksum: 0x5df8c4c2
the checksum of the 3 constant values is 0x49777A90. and i also know that the end result must be 0x19160493. i made a generator and tried things out.
Naive_crackme - nisto's crackme #1
----------------------------------
This is my 1st crackme...
It uses regular tricks and some others... hehe...
Hope u'll get some fun with it.
You can join me at yanisto@nuxed.org or on #secdev@freenode.net
Enter the Password : Pih:19kD
Yeahhhh evil hacker
U managed to break my poor crackme…
Send me your code and process to get it… I’m curious…
The next one will be much harder i hope cuz’ I’m still a tyro
First, i wanted to grant you a Renault Clio 16S but, i’m sure u’d rather appear
on some kind of hall of fame… It’s ok for me
++ nisto…
parting words:
i also changed the e_entry field of the crackme to point to the rather standard _start routine to see how it’ll run, i haven’t studied it that deep so far, but something tells me this is another one of the crackmes traps.

What /is/ ida anyway … ?
Comment by chaosite — November 19, 2006 @ 11:52 pm
ida (or Interactive DisAssembler) by ilfak guilfanov is a dissasembler and debugger, argued by some to be the “best-out-there” package for code analysis.
you can find out more about it in these two sites:
official site: http://www.datarescue.com/
author’s blog: http://www.hexblog.com/
cheers
Comment by sleepy jenkins — November 20, 2006 @ 5:23 am