💾 Archived View for 0x80.org › gemlog › 2016-02-19-practice-tiny-crackme.gmi captured on 2022-04-28 at 17:40:39. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2021-12-03)
-=-=-=-=-=-=-
Found this nice and small crackme at crackmes.de[1]. Author comment :
1: http://crackmes.de/users/yanisto/tiny_crackme/
This is my second linux crackme. It has a very small size (<400 bytes of bytecode) but implements a few tricks all the same : - Elf headers corrupted, - "Cyphered" binary, - CRC checking, - Anti ptrace, - Anti gdb. Solution will be published later on nuxed.org. Enjoy it ! ++nisto.
Let us start solving it. We run the binary
[~/tiny-crackme]$ strace -ifx ./tiny-crackme .... [00200082] ptrace(PTRACE_TRACEME, 0, 0x1, 0) = -1 EPERM (Operation not permitted) [002002a8] write(0, "Sorry but the process seems to b"..., 52Sorry but the process seems to be traced... Bye... ) = 52 [0020031a] _exit(0) = ? [????????] +++ exited with 0 +++
so a ptrace with PTRACE_TRACEME is called at 0x200082 we open in IDA and break there.
LOAD:00200084 test eax, eax LOAD:00200086 jz short check_flag LOAD:00200088 mov ecx, offset str_sorry ; "Sorry but the process seems to be trace"... LOAD:0020008D mov dl, 34h LOAD:0020008F call write
We need eax=0 to bypass the ptrace, if we pass it we go to check_flag which is
LOAD:0020009C do_check_flag: ; CODE XREF: LOAD:check_flagj LOAD:0020009C push ebx LOAD:0020009D mov ecx, offset dwinput LOAD:002000A2 mov edx, 4 LOAD:002000A7 call read LOAD:002000AC call compute_answer LOAD:002000B1 xor ebx, dwinput LOAD:002000B7 jz short success
this function expects an input of 4 bytes then it calls compute_answer, where ebx register is set with a value, and then xors it with our input and if ebx=0 we win. Let us see what computer_answer does.
LOAD:002002C9 compute_answer proc near ; CODE XREF: LOAD:002000ACp LOAD:002002C9 jmp do_compute_answer LOAD:002002C9 compute_answer endp LOAD:002002C9 LOAD:002002C9 ; --------------------------------------------------------------------------- LOAD:002002CE dw 0C8B0h LOAD:002002D0 db 43h ; C LOAD:002002D1 LOAD:002002D1 ; =============== S U B R O U T I N E ======================================= LOAD:002002D1 LOAD:002002D1 LOAD:002002D1 do_compute_answer proc near ; CODE XREF: compute_answerj LOAD:002002D1 xor eax, eax LOAD:002002D3 mov ebx, eax LOAD:002002D5 mov ecx, 2DFh LOAD:002002DA shr ecx, 2 LOAD:002002DD mov esi, offset start LOAD:002002E2 LOAD:002002E2 ACC: ; CODE XREF: do_compute_answer+14j LOAD:002002E2 lodsd LOAD:002002E3 add ebx, eax LOAD:002002E5 loop ACC LOAD:002002E7 xor ebx, 5508046Bh LOAD:002002ED retn LOAD:002002ED do_compute_answer endp
This function set esi=&start and enters a loop from ecx=0xb7 until ecx=0, each time it gets a dword from esi and increment it by 4. Finally after the loop it
xors ebx with 0x5508046b, and leaves, then xors it with our input. The range of esi is [0x200008,0x200008+(0xb7*4)] which our input is part of, so our input affects the final result in ebx, also patching the binary in this range affects the results.
To compute the answer we need to dump all dword in the range mentioned above, with our input set to zero or whatever. The data we need to dump is :
LOAD:00200008 public start LOAD:00200008 start proc near ; DATA XREF: do_compute_answer+Co LOAD:00200008 B3 2A mov bl, 2Ah LOAD:0020000A E9 31 00 00 00 jmp loc_200040 ...... ...... ...... LOAD:00200292 DA C0 EF BE dword_200292 dd 0BEEFC0DAh ; DATA XREF: LOAD:00200058r LOAD:00200296 00 00 00 00 dwinput dd 0x44434241h ; DATA XREF: LOAD:0020009Do LOAD:00200296 ; LOAD:002000B1r LOAD:0020029A ; =============== S U B R O U T I N E ======================================= LOAD:0020029A LOAD:0020029A ; Attributes: thunk LOAD:0020029A LOAD:0020029A write proc near ; CODE XREF: LOAD:0020006Dp LOAD:0020029A ; LOAD:0020008Fp ... LOAD:0020029A E9 01 00 00 00 jmp do_write LOAD:0020029A write endp ...... ...... ...... LOAD:002002E3 01 C3 add ebx, eax LOAD:002002E5 E2 ......
We dump the above region, and we bruteforce the following dwords
....+.... + 0xYYXXBEEF ; 4241 + 0x01e9JJZZ ; 4443 ...+..
We need to figure out what input 0xXXYYZZJJ at 0x00200296 will result in ebx being zero at the end. The following code bruteforces it.
#!/usr/bin/env python2 import string from struct import pack,unpack from itertools import permutations def with_input(d, val): r = [] x = val&0xffff y = (val&0xffff0000) >> 16 for n in d: if n == 0x4241beef: r.append(x << 16 | 0xbeef) elif n == 0x01e94443: r.append(0x01e90000 | y) else: r.append(n) return r def check(d, n): acc = reduce(lambda x,y: (x+y)&0xffffffff, with_input(d, n)) ^ 0x5508046b return True if acc^n==0 else False d = open("export_results.txt", "rb") d = [unpack("<L", d.read(4))[0] for x in xrange(0xb7)] charset = string.ascii_lowercase + string.digits gen = permutations(charset, 4) for g in gen: t = ''.join(g) n = unpack("<L", t)[0] if check(d, n): print "Found ", t
running this gives possible inputs
[~/tiny-crackme]$ python2 solver.py Found b20o Found b40i Found b60k Found f04m Found f24o Found f64k [~/tiny-crackme]$ ./tiny-crackme Tiny_crackme - nisto's crackme #2 --------------------------------- This one has a particularly small size... Hope u'll get some fun with it. You can join me at yanisto@nuxed.org or on #secdev@freenode.net Enter the Password : f64k -> Success !! Congratulations... -> You can send me your solution/comments at the above mail addr...
done.