crp-’s 888
make it print ‘OK’, no patching please
![]()
Difficulty: 2 - Needs a little brain (or luck)
Platform: Unix/linux etc.
Language: Assembler
the first few instructions from e_entry are quite easy to follow.. we now come to the part of the crackme where real work is being done.
LOAD:08048247 loc_8048247: LOAD:08048247 push 18149275h LOAD:0804824C sub [esp+4+var_4], 10101010h LOAD:08048253 call loc_8048259 ; push 0x08048258 on stack LOAD:08048253 ; --------------------------------------------------------------------------- LOAD:08048258 byte_8048258 db 74h LOAD:08048259 ; --------------------------------------------------------------------------- LOAD:08048259 loc_8048259: LOAD:08048259 add [esp+4+var_4], 8 ; 0x0804825 + 8 = 0x08048260 (address of retn) LOAD:08048260 retn
the routine pushes two addresses on the stack the first of which is the address (0x08048265) the second address (0x08048258) is pushed by the call loc_8048259 instruction, which is made to point to the address of the retn instruction itself making it execute two times. it basically, updates EIP with it’s own address then updates EIP (again) with the real address where to return control.
the next block of instruction (starts at 0x08048265) saves argc on a buffer, pushes the address 0x0804286 (remember this address) on the stack and finally, does another jmp via return address modification to address 0x08048223 which takes care of setting up a signal handler for the SIGTRAP signal.
LOAD:08048223 loc_8048223: LOAD:08048223 xor eax, eax LOAD:08048225 mov al, 32h LOAD:08048227 mov ecx, 2 LOAD:0804822C LOAD:0804822C loc_804822C: LOAD:0804822C dec al ; sys_signal (eax = 48) LOAD:0804822E loop loc_804822C LOAD:08048230 sub ebx, ebx LOAD:08048232 mov bl, 5 ; ebx = 5 = SIGTRAP LOAD:08048234 mov ecx, offset loc_804820B LOAD:08048239 dec ecx ; ecx = 0x0804820A (SIGTRAP handler)
lets jump to the crackme’s SIGTRAP handler to see what it does…
LOAD:0804820A inc dword_804837C ; increment handler_count LOAD:08048210 mov dword_8048380, 5B54D103h ; handler_code to another variable LOAD:0804821A sub eax, eax ; eax = 0 LOAD:0804821C inc eax ; eax = 1 LOAD:0804821D neg eax ; eax = -1 LOAD:0804821F js short loc_8048223 ; setup signal() again, after every handler call. LOAD:08048221 int 3 ; Trap to Debugger
so in a nutshell, the crackme’s own SIGTRAP handler increments a variable then stores a predefined “identification” code to another variable and then setups up the signal() routine again.. (possibly, to restart the crackme routine…)
then control transfers to address 0x0804286 (pushed before on the stack remember?) thru a “jmp via ret” technique which eventually transfers control to 0x0804829a with the registers having values eax = 0x5 and ebx = 0x000482D2. a hardcoded value (0xd4a08f90) is saved onto a buffer and then another “jmp via ret” to the next instruction immediately after the block.
now we come accross a routine (0x080482aa) that patches the address 0x000482D2 with a 0xCC (trap/tracepoint) opcode. .. more jmps happens then finally an increment to eax which holds the memory address 0x080482D1 followed by a jmp to it. now this memory address was patched before with the value 0xCC remember? the crackme is trying to invoke its own SIGTRAP handler!
let’s view that address under gdb shall we?
misha@heaven ~/crp/888 $ gdb 888
(no debugging symbols found)
Using host libthread_db library \"/lib/libthread_db.so.1\".
niela@gdb $ context-on
niela@gdb $ hb *0x080482D2
Hardware assisted breakpoint 1 at 0x80482d2
niela@gdb $ r
warning: shared library handler failed to enable breakpoint
(no debugging symbols found)
_______________________________________________________________________________
eax:080482D2 ebx:00000005 ecx:0804820A edx:00000000 eflags:00000206
esi:00000000 edi:00000000 esp:BFC15774 ebp:00000000 eip:080482D2
cs:0073 ds:007B es:007B fs:0000 gs:0000 ss:007B o d I t s z a P c
[007B:BFC15774]---------------------------------------------------------[stack]
BFC157A4 : 5B 7B C1 BF 88 7B C1 BF - 97 7B C1 BF A3 7B C1 BF [{...{...{...{..
BFC15794 : 17 77 C1 BF 27 77 C1 BF - 32 77 C1 BF 4D 7B C1 BF .w..'w..2w..M{..
BFC15784 : DB 76 C1 BF EB 76 C1 BF - FB 76 C1 BF 05 77 C1 BF .v...v...v...w..
BFC15774 : 03 76 C1 BF 00 00 00 00 - 1B 76 C1 BF C8 76 C1 BF .v.......v...v..
[007B:080482D2]---------------------------------------------------------[ data]
080482D2 : CC A1 80 83 04 08 85 C0 - 74 49 2D 4F 4F 50 53 FF ........tI-OOPS.
080482E2 : D0 68 66 83 04 08 68 25 - 82 04 08 68 A1 81 04 08 .hf...h%...h....
[0073:080482D2]---------------------------------------------------------[ code]
0x80482d2: int3
0x80482d3: mov eax,ds:0x8048380
0x80482d8: test eax,eax
0x80482da: je 0x8048325
0x80482dc: sub eax,0x53504f4f
0x80482e1: call eax
——————————————————————————
Error while running hook_stop:
Invalid type combination in ordering comparison.
Breakpoint 1, 0x080482d2 in ?? ()
now, inside gdb, an interrupt 3 will suspend the program being debugged and control would be given to the debugger. the question is, how would this fool debuggers such as gdb if we are not careful anyway??
i quote the gdb info page: (gdb.info)
this was how i understood it:5.3 Signals
===========…
GDB has the ability to detect any occurrence of a signal in your
program. You can tell GDB in advance what to do for each kind of
signal.
…When a signal stops your program, the signal is not visible to the
program until you continue. Your program sees the signal then, if
`pass’ is in effect for the signal in question _at that time_. In
other words, after GDB reports a signal, you can use the `handle’
command with `pass’ or `nopass’ to control whether your program sees
that signal when you continue.
…You can also use the `signal’ command to prevent your program from
seeing a signal, or cause it to see a signal it normally would not see,
or to give it any signal at any time.
the int 3 will send a signal (motioning for it to stop) but gdb will catch this before the crackme’s SIGTRAP handler. unless you tell debuggers such as GDB to explicitly pass a caught signal to the program being debugged upon, things could go wrong… in this case, a SIGTRAP handler not getting executed…
after the handler executes, control is then passed to address 0x08042d3, the next instruction after the 0xCC opcode.
earlier, i inspected the SIGTRAP handler and it saves a hardcoded SIGTRAP identification code to a buffer (0x08048380). i can now see that this value is being passed to eax, tested for “zero” (which means that the crackme’s own SIGTRAP handler wasn’t called). this value is subtracted by another hard code value and then finally, eax is being called. so we have:
0x05b54D103 - 0x53504F4f = 0x080481B4 (resume execution at this address)
the block (from 0x080481B4) starts off with a pop instruction, which clears the stack of the return address of the call instruction at address 0x80482e1, saving the address (0x080482e3) to a buffer.in enters a loop wherein it continously pops off the stack until it reaches a terminating NULL. granted that the argc value has already been popped off before (at address 0x08048266 specifically), the first value it will remove is actually argv[0], the crackme’s absolute pathname. it’s trying to reach the envp variables of the program.
what’s it trying to do with the envp variables?
LOAD:080481D2 call sub_8048052 ; strlen LOAD:080481D7 cmp eax, 4 LOAD:080481DC jl short fixed_loc ; strlen < 4 LOAD:080481DE cmp eax, 40h LOAD:080481E3 jg short loc_80481CB ; strlen > 64 LOAD:080481E5 mov ecx, [ebx] ; strncpy(ecx, ebx, 4); LOAD:080481E7 and ecx, 0FFFFFFh ; null terminate the 3 characters LOAD:080481ED cmp ecx, 79656Bh ; strcmp(ecx, “key”, 4); LOAD:080481F3 jnz short loc_80481CB ; no match LOAD:080481F5 push ebx ; save string on stack LOAD:080481F6 push offset loc_80481CB ; return address LOAD:080481FB jmp loc_804813E ; process “key” envp variable
sub_8048052 is a custom strlen() function that initializes the address of the next envp variable to be processed before returning the actual length of the current envp variable in question. a conversion is being done based on the value of at least two “key” environment variables,. translated to C, the conversion is roughly :
#define BTMASK 0x80000000
#define ECX 0x0a
for (i=0; i<strlen (value); i++ ) {
if (!isdigit(value[i])) {
ebx = BTMASK | value[i];
goto fin;
}
ebx *= ECX;
ebx += (value[i]-0x30);
} fin: /* ... */
which is simply an atoi(const char *nptr).
following the code, i came across another block of that sets up yet another signal handler for the signal SIGFPE. skip analysis of the handler code at the moment and continue at address 0x0804819d.
first, a stamp (that was ‘or’ed by 1 and 2 before) is being continously tested with decrementing values starting from 3 which would state that at least two “keyX=value” pairs have been processed. the crackme then forces an FPE (floatin point exception) by doing a division by zeroat address 0x80481b1, which would trigger the SIGFPE handler.
what does the handler do?
a) it nops out the div ecx instruction at 0x080481b1
b) both key1 and key2 values are being compared to 0x1000. (must be greater)
c) key values are multiplied… product is stored at edx:eax
d) product is divided by 0x10001 quotient is eax, remainder is edx
e) remainder (edx) is decremented and zero flag determines crack state.
