in the name of zero

December 12, 2006

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 ()

Comments »

The URI to TrackBack this entry is: http://gnurbs.blogsome.com/2006/12/12/finding-ctors/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>


Get free blog up and running in minutes with Blogsome | Theme designs available here