hack the password!
features: a anti-libbfd, anti-disassembling, anti-debugging, and a bit of obfuscation
have a lot of fun … ;}
0xf001
Difficulty: 5 - Professional problem to solve
Platform: Unix/linux etc.
Language: Assembler
get it here
as you’ll find out soon after starting,
1) the crackme is filled with anti-disassembly constructs.
2) along the way, there are lots and lots (and lots) or jump via ret/double rets. it’s simply just a matter of fixing/telling the disassembler the correct offset where to start disassembly (IDA in my case)
3) there is a short encrpytion thingy goin on
4) the crackme hides the details of setting up the parameters for a system call among the many jump via ret/double rets
5) patches an int 0x80 before calling it. patches it back after calling it.
example of jump via “ret” in the crackme
0x8048080 call loc_8048086 ; put 0x08048085 to esp
0x8048085 db 0E9h ; garbage
0x8048086 pop edx ; edx = 0x08048085
0x8048087 add edx, 0Bh ; edx = 0x08048090
0x804808D push edx ; on stack
0x804808E retn ; jump to 0x08048090
“double ret”
0x080480E0 add edx, 0xe ; fix 2nd jump address
0x080480E6 push edx ; put on stack
0x080480E7 push 0x80480cc ; 1st jump address
0x080480EC retn ; return once
0x080480CC retn ; return twice
we can basically grasp the pattern immediately after a few tries…. going further we reach a “sign of life” of some sort.. a lodsb instruction.
0x08048130 mov al, [+0x8048c90]
0x08048135 lodsb ; esi initially at 0x08048196
which will put the first byte at 0x08048196 (0xCC) to al, then increment esi. a few blocks further…
0x08048154 xor al, 0x58 ; byte morph ...
…another series of ret/double rets… then we have
0x08048174 stosb ; initially at 0x08048196
eventually, a “loop”
0x08048193 loop +0xa0 (0x8048235) ; 0xa80 times
tell tale signs of decryption don’t you agree? so if we do the math, 0x08048196 + 0xa80 = 0x08048C16. what i want to achieve as of the moment is a copy of the decrpted block so that i can still follow ald’s debugging session in ida. we can take a memory dump of the crackme. i on the other hand chose to make a helper program[1] that will decrypt the encrypted block.
another set of jump via ret/double ret series…
we basically continue till we reach a shl eax, 1 instruction.
08048783 D1E0 shl eax, 1
from the current value of eax = 0xd, the shift left by one bit will make eax = 0x1a (26 decimal) which happens to be the syscall number for sys_ptrace()
series of jump via ret again…
former value of ecx = 0x10 .. via shl cl, 0x3 becomes ecx = 0x80
0804893C C0E103 shl cl, 0x3
further along the way, this instruction patches the byte pointed to by edx (0x08048A4E) with cl = 0x80 (which by the way, is also the linux interrupt vector)
880A mov byte [edx], cl
still following the trail… we now reached the instruction at address 0x080489C4. which is a jz 0x08048653. if we follow this jump (in the disassembler), we’ll reach the dead end which is where the crackme will try returning to 0xf001. we don’t want our debugging session disrupted, so let’s skip that instruction. eventually, (after yet another series of jump via rets) we reach an int 0x80 instruction at 0x08048A4D. remember the mov byte [edx], cl instruction a while ago? the crackme patches this location at runtime in order for a ptrace() system call to resolve.
we can easily defeat this (ald in my case) by giving eax = 0 value.
going further we eventually encounter
08048AA7 BA4E8A0408 mov edx, 0x8048a4e ; this will later be overwitten then restored...
…. more jump via rets …
08048AD7 880A mov byte [edx], cl
so the crackme patches the int 0x80 byte (to cover its tracks). then after a helluva lot of nuisances….
0x08048826 or al, al ; the crackme tries to toggle the zero flag
and further down the listing…
0x08048853 0F85FAFDFFFF jne near +0xfffffdfa (0x8048653)
wait a minute! we’ve already seen this memory address once right? it’s the path leading to 0xf001! so let’s skip that instruction and we’ll safely reach the next set of surprises.
things get interesting starting at 0x080481B5. we encounter more and more of those jump via ret thingies along the way but at least now we now the pattern on how they work…
we encounter another int 0x80 patch so we know something’s gonna turn up soon. the details on how parameters are being setup is hidden among tons of repetitive jump via ret/double calls. don’t forget that the crackme also patches int 0x80 again after every call. we can also determine that address 0x080485bc is the address responsible for the flow of the crackme after every int 0x80 payload. i opted to simply check the registers (parameters) before every int 0x80 call.
ecx = 0x080481D5 (oxfoo1m3 started ;])
ecx = 0x0804820F (3nt4 p455w0rD:)
we now enter the fourth int 0x80 installment (0x0804835C). this time, instead of entering the usual routine (setup int 0x80) the crackme does an int 0x80 directly. checking the registers, we have:
…
eax = 0x00000003 ebx = 0x00000000 ecx = 0x08048223 edx = 0x0000000B
…
sys_read 0x0b characters from stdin and save to buffer at 0x08048223. i guess that brings us to the fifth phase… roughly like the one below.
at 0x080483e2, we see the mov esi, 0x08048223 instruction… and another interesting instruction at 0x08048412 (mov edi, 0x8048223).
08048472 AC lodsb ; load byte (esi) to al
0804849E 30D0 xor al, dl ; dl is strlen(input)
080484CB FEC2 inc dl
08048582 3A01 cmp al, byte [ecx] ; aha!
by virtue of common sense.. i made a keygen[2] immediately to test things out.
works like a charm.
…
~/oxfoo1m3 $ ./cracker
password is : fucktheduck
~/oxfoo1m3 $ ./oxfoo1m3
oxfoo1m3 started ;]
3nt4 p455w0rD:
fucktheduck
u made it!
…
thanks to 0xf001 for this wonderful crackme.
references:
[1] decrypt_foolme.c
[2] cracker.c