We have a binary
$ file reverse_me.bin
reverse_me.bin.back: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, corrupted section header size$ readelf -l reverse_me.bin
Elf file type is EXEC (Executable file)
Entry point 0x8048054
There are 1 program headers, starting at offset 52Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x00344 0x00344 RWE 0x1000$ ./reverse_me.bin
Get the flag
Yeah, let’s get the flag…
$ strace ./reverse_me.bin
execve(“./reverse_me.bin”, [“./reverse_me.bin”], [/* 17 vars */]) = 0
ptrace(PTRACE_TRACEME, 0, 0, 0) = -1 EPERM (Operation not permitted)
write(1, “no gdb down there buddy…\n”, 27no gdb down there buddy…
) = 27
_exit(0) = ?
The ELF header has been heavily modified and we have a hard time setting a breakpoint on the Entry Point… some code is run before at start that detect gdb (ptrace) and exits..
$ gdb reverse_me.bin
gdb$ break *0x8048054
Breakpoint 1 at 0x8048054
gdb$ run
no gdb down there buddy…Program exited normally.
Catching to the rescue.. ptrace is a function but also, ultimately, a syscall
gdb$ catch syscall
Catchpoint 1 (any syscall)
gdb$ run
————————————————————————–[regs]
EAX: 0xFFFFFFDA EBX: 0x00000000 ECX: 0x00000000 EDX: 0x00000000 o d I t s Z a P c
ESI: 0x00000000 EDI: 0xBFFFF80C EBP: 0x0AE8DB34 ESP: 0xBFFFF80C EIP: 0x08048187
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0000 SS: 007B
————————————————————————–[code]
0x8048187: cmp eax,0x0
0x804818a: jne 0x80481fb
0x804818c: push 0x4
0x804818e: pop eax
0x804818f: cdq
0x8048190: xor ebx,ebx
0x8048192: inc ebx
0x8048193: push 0xa
——————————————————————————–Catchpoint 1 (call to syscall ‘ptrace’), 0x08048187 in ?? ()
gdb$ conti
————————————————————————–[regs]
EAX: 0xFFFFFFFF EBX: 0x00000000 ECX: 0x00000000 EDX: 0x00000000 o d I t s Z a P c
ESI: 0x00000000 EDI: 0xBFFFF80C EBP: 0x0AE8DB34 ESP: 0xBFFFF80C EIP: 0x08048187
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0000 SS: 007B
————————————————————————–[code]
0x8048187: cmp eax,0x0
0x804818a: jne 0x80481fb
0x804818c: push 0x4
0x804818e: pop eax
0x804818f: cdq
0x8048190: xor ebx,ebx
0x8048192: inc ebx
0x8048193: push 0xa
——————————————————————————–Catchpoint 1 (returned from syscall ‘ptrace’), 0x08048187 in ?? ()
There we have a check for eax, normally ptrace would return 0 but it’s 0xFFFFFFFF since gdb is already active. Let’s change it to 0 and continue.
gdb$ set $eax = 0
gdb$ conti
Catchpoint 1 (call to syscall ‘write’), 0x080481ac in ?? ()
gdb$ conti
Get the flagCatchpoint 1 (returned from syscall ‘write’), 0x080481ac in ?? ()
$ conti
Execution seems to be in a infinite loop there. Break it…
^C
Program received signal SIGINT, Interrupt.
————————————————————————–[regs]
EAX: 0x00000040 EBX: 0x00000001 ECX: 0xBFFFF7FC EDX: 0x0000000D o d I t s z a p c
ESI: 0x00000000 EDI: 0xBFFFF80C EBP: 0x0AE8DB34 ESP: 0xBFFFF7F8 EIP: 0x080481AC
CS: 0073 DS: 007B ES: 007B FS: 0000 GS: 0000 SS: 007B
————————————————————————–[code]
0x80481ac: xor eax,eax
0x80481ae: push 0x41
0x80481b0: pop eax
0x80481b1: shr eax,1
0x80481b3: shl eax,1
0x80481b5: jmp 0x80481ac
0x80481b7: push 0x4
0x80481b9: pop eax
——————————————————————————–
0x080481ac in ?? ()
Infinite loop with the unconditional jump. We try to set EIP just after the jmp (bit of guessing)
gdb$ set $pc = 0x80481b7
gdb$ conti
Catchpoint 1 (call to syscall ‘write’), 0x080481d0 in ?? ()
gdb$ conti
flag:
Catchpoint 1 (returned from syscall ‘write’), 0x080481d0 in ?? ()
gdb$ conti
Catchpoint 1 (call to syscall ‘write’), 0x080481fb in ?? ()
gdb$ conti
tcatch_syscall_always_winCatchpoint 1 (returned from syscall ‘write’), 0x080481fb in ?? ()
flag: tcatch_syscall_always_win
I couldn’t agree more.
Another way to solve this is using IDA and the x86emu plugin. It’s much longer because you will need to go over the multiple decryption loops before finally arriving at the ptrace syscall and the check, but it’s interesting by itself as an introduction to x86emu plugin.