Binary Vivisection (Part 3)

Welcome back for the 3rd and final installation in our series which covers the Vivisect framework.  For this post, we'll be focusing on VDB, which is a powerful and scriptable debugger written in Python.  With that in mind, let's get started and we'll begin by using the program "conditional" which is an elite and imaginative chunk of code which prompts you for your age and outputs a response based on your input.  It is however useful for conditional branches and demoing VDB.

The debugger can be executed from within Vivisect by running "vdb" in the console, or it can be started directly by just executing ./vdbbin.  If we just call it from the command line, this will launch the VDB CLI and the "gui" command can be used to start the VDB GUI (as you might have guessed...).  Similar to the static component of Vivisect, running "help" will return a list of available options and commands accessible to you from within the debugger.

vdb1

With the debugger, you have a few options for processes.  One option is to attach to an existing process by the given PID, and the second option is to start a new process within the debugger using the exec command.  For now, we'll just start the program and attach to the PID.

vdb > attach 29141
Attaching to 29141
vdb > Attached to : 29141
Loading Binary: /home/user/vivisect_20130901/conditional
Loading Binary: /lib/i386-linux-gnu/libc-2.15.so
Loading Binary: [vdso]
Loading Binary: /lib/i386-linux-gnu/ld-2.15.so
Thread: 29141 NOTIFY_BREAK

Now that we're attached, the VDB tracer will automatically break and let you decide what you want to do next.  Entering the "go" command continues the program's execution.

> go
Running Tracer (use 'break' to stop it)
PID 29141 exited: 0 (0x00000000)

So let's do something more interesting.  Let's restart the program under the debugger, and insert a breakpoint using the "bp" command.  In the last part of the tutorial, we inspected some code blocks and determined what stack variable was the user input and labeled it Vivisect workspace.  As a refresher, this code is pasted below:

.text:0x08048464  
.text:0x08048464  FUNC: int cdecl conditional.main( ) [1 XREFS]
.text:0x08048464  
.text:0x08048464  Stack Variables:
.text:0x08048464           -20: int myAge
.text:0x08048464           -44: int local44
.text:0x08048464           -48: int local48
.text:0x08048464  
.text:0x08048464  55               push ebp
.text:0x08048465  89e5             mov ebp,esp
.text:0x08048467  83e4f0           and esp,0xfffffff0
.text:0x0804846a  83ec20           sub esp,32
.text:0x0804846d  b8a0850408       mov eax,loc_080485a0
.text:0x08048472  890424           mov dword [esp + local48],eax
.text:0x08048475  e8e6feffff       call printf_08048360    ;printf_08048360()
.text:0x0804847a  b8b6850408       mov eax,loc_080485b6
.text:0x0804847f  8d54241c         lea edx,dword [esp + 28]
.text:0x08048483  89542404         mov dword [esp + local44],edx
.text:0x08048487  890424           mov dword [esp + local48],eax
.text:0x0804848a  e811ffffff       call __isoc99_scanf_080483a0    ;__isoc99_scanf_080483a0()
.text:0x0804848f  8b44241c         mov eax,dword [esp + myAge]
.text:0x08048493  83f863           cmp eax,99
.text:0x08048496  7f0e             jg loc_080484a6

What we're really interested in here is the second from the last line,0x08048493 which compares the value of EAX, against 99, where EAX contains the user supplied information.  Let's put a breakpoint at 0x08048493 and enter "22" as our age and do some investigating.

> go
Running Tracer (use 'break' to stop it)
> break
Thread: 29394 NOTIFY_BREAK
> bp 0x08048493
 [ Breakpoints ]
[0] 0x08048493 Breakpoint: 0x08048493 enabled: True fast: False
> go
Running Tracer (use 'break' to stop it)
Thread: 29394 Hit Break: [0] 0x08048493 Breakpoint: 0x08048493

We've hit our first breakpoint and let's confirm our suspicions that EAX does in fact contain the age we provided.  To do this, simply use the eval command and as an argument, an expression or value to evaluate.

> eval eax
eax = 0x00000016 (22)

There you have it, 22.

Another useful function is VDB's ability to provide disassembly for a given virtual address such as depicted in below.  In this case, we want to disassemble memory at the same virtual address as our breakpoint: vdb > dis 0x08048493

vdb2

Now let's say we want to know what the memory address is for our input, 22.  To do this, we can use the search function and simply do a literal string search with "search 22" which outputs the following:

> search 22
searching all memory...
matches for: 3232 ('22')
0xb7772f52: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7773d42: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777bdb: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777d6a: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777dae: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777e06: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777e4c: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777e91: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb7777ebc: -r-x /lib/i386-linux-gnu/libc-2.15.so
0xb77cc000: -rw- 
0xbf83e330: -rw- tid:29394 sp-880 (stack)
done (11 results).

So out input is at 0xb77cc000 and if we want to view memory at this location, it's simply done with "memory <virtual address>."

> mem 0xb77cc000
0xb77cc000  32 32 0a 00 00 00 00 00 00 00 00 00 00 00 00 00   22..............
0xb77cc010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc020  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc040  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc050  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc060  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc070  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc080  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0b0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0c0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0d0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0e0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0xb77cc0f0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................

Before we finish up, let's take a look at dealing with execution flow by managing eflags.  Like any functional debugger, VDB lets you access a program's eflags register bits which you can increment and decrement using either the eflags command from within VDB, or the eflags buttons in the VDB GUI.

vdb3

To change the program's execution, let's exit the current debugged process and restart.  One useful thing to note is the "restart" command in VDB, but I believe that only works if you spawn a process from VDB using the "exec" command.

Now, look back up a bit at the large chunk of disassembly and take a look at the last line at 0x08048496.  This is a conditional jump based on the result of the previous comparison instruction.  Based on dynamic analysis, and the static analysis we did in part 2, we know that if we enter our age as 102, the program will tell us that we're very old (insulting, I know).

root@ubuntu:~/vivisect_20130901# ./conditional 
Please enter your age102
You are really old

Ok, that's enough elite dynamic analysis for now.  Moving on...

> bp 0x08048496
 [ Breakpoints ]
[0] 0x08048496 Breakpoint: 0x08048496 enabled: True fast: False 
> go
Running Tracer (use 'break' to stop it)
Thread: 29477 Hit Break: [0] 0x08048496 Breakpoint: 0x08048496

We've hit out breakpoint and eval'ing EAX tells us that out input is 102.

> eval eax
eax = 0x00000066 (102)

At this point, our program is paused, and its next operation will be a jmp to loc_080484a6 which does another comparison against "100" and performs another conditional jump, which tells us that we're very old.  But wait, we don't want that!  In the GUI, click ZF to change the value from zero to one and the resume the program's execution with "go."  Your terminal should now have the below output.

You are pretty young!
Please enter your age102
You are pretty young!

Well, if you've used a debugger, none of this is new to you, but this is how it's done in VDB.  If there's one takeaway from this series, it's that everything that's been covered can be scripted and automated using the API and there's still so much we didn't cover!  While it's true that there are other tools out there that provide APIs to do similar things, these tools can be very expensive and it's always fun to do something awesome with FOSS.

Thanks for following along!

  
Read the rest of the Binary Vivisection Series