finding ctors
so i encountered a crackme that does a PTRACE_TRACEME and checks to see if LD_PRELOAD is set.
if we try putting a breakpoint in main, we notice that the crackme quits sometime before actually hitting main(), thus the breakpoint doesn’t get to resolve. same results when we try ptrace hijacking. we can conclude that before the crackme’s main() function, a routine (or some routines) are being executed. these routines happen to be called “constructors” and they are found at section .ctors
they are made by using the gcc “constructor” function attribute[1]. for destructors, the function attribute “destructors” is used. the problem is, the crackme author modified the elf header in such a way that it’s impossible for some tools (objdump in particular) to view the program’s internals like the disassembly and section informations (like ctors)
misha@heaven ~/rccrackme $ objdump -s -j .ctors rccracker rccracker: file format elf32-i386
so we basically need to find the program’s ctors and see if there are other stuffs it does besides just detecting “tracing” and “ptrace wrappers”. there are some ways of approaching this and the first and most obvious would be to reconstruct the relevant elf sections of the crakme needed for “bsd based” tools like objdump to work. but this requires tedious analysis and binary patching. the second way is if we can find the location of ctors just by following the standard libc start up.
so we’re basically familiar with how libc gives control to main()… in _start[2], a few arguments are being pushed including the main() functions address, then finally, a call to _libc_start_main()[3]. libc_start_main() basically calls the “constructors”, main(), then “destructors” in that order.
so how do we reach the constructors that are get executed? i tried making another program that calls a simple constructor then followed the disassembly listing to _do_global_ctors_aux(). the flow goes…
(note: i followed this on a dynamic linked hello world program)
_start[2] --> __libc_start_main[3] --> __libc_csu_init[4] --> _init[5] --> _libc_global_ctors[6]
so what i did was to trace the crackme disassembly till i reach the address of libc_global_ctors[6] which is at 0x080487C8. it initilizes a pointer to a register and enters a loop calling each ((contructor)) function, so the first addresss it calls must be the first “constructor” function.. i followed the default “call eax” to the address 0x08048654. and there, i found the crackme calling “something” thru the dynamic linker, possibly, getenv(), i can only guess since the return value is a char* to the value assigned to LD_PRELOAD.
then a few instructions further, a call to the check_ptrace(int x) i want to get over.
LOAD:080486A6 mov [esp], 68h ; succeeding function accepts an argument LOAD:080486AD call sub_8048630 ; our ptrace_check() function
— following the call —
LOAD:08048630 arg_0 = dword ptr 4 LOAD:08048630 LOAD:08048630 mov eax, [esp+arg_0] ; eax = 0x68 LOAD:08048634 push ebx ; save ebx LOAD:08048635 xor ecx, ecx ; ecx = 0 LOAD:08048637 push ecx ; save ecx LOAD:08048638 shr eax, 2 ; eax = 0x1a = sys_ptrace LOAD:0804863B pop edx ; edx = 0 LOAD:0804863C mov ebx, ecx ; ebx = 0 LOAD:0804863E push ebx ; zero on stack LOAD:0804863F pop esi ; esi = 0 LOAD:08048640 mov edi, esi ; edi = 0 LOAD:08048642 inc esi ; esi = 1 LOAD:08048643 int 80h ; call kernel LOAD:08048645 test eax, eax ; did ptrace fail? LOAD:08048647 jge short loc_8048650 ; NO! great! LOAD:08048649 add eax, 2 ; (-1) + 2 = 1 LOAD:0804864E int 80h ; call sys_exit LOAD:08048650 LOAD:08048650 loc_8048650: LOAD:08048650 pop ebx ; restore former ebx value LOAD:08048651 retn ; return
from this point on, it’s just a matter of changing the return value of sys_ptrace(). put a breakpoint in 0x08048645. and change eax to from 0xffffffff to 0 to defeat the ptrace_check().
misha@heaven ~/rccrackme $ gdb rccracker (no debugging symbols found) Using host libthread_db library \"/lib/libthread_db.so.1\". niel@gdb $ hb *0x08048645 Hardware assisted breakpoint 1 at 0x8048645 niel@gdb $ r warning: shared library handler failed to enable breakpoint (no debugging symbols found) Error while running hook_stop: Invalid type combination in ordering comparison. Breakpoint 1, 0x08048645 in ?? () niel@gdb $ set $eax = 0 niel@gdb $ continue --===[> Macabre's rc CrackMe <]===-- Program exited normally.
assuming this is the only anti-debugging trick employed in the crackme, we can now “gdb step” our way through completion.
[1] http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
[2] sysdeps/i386/elf/start.S
[3] sysdeps/generic/libc-start.c :: STATIC int LIBC_START_MAIN ()
[4] csu/elf-init.c :: void __libc_csu_init ()
[5] sysdeps/i386/init-first.c :: void _init()
[6] elf/soinit.c :: __libc_global_ctors ()
