Skip to content

Instantly share code, notes, and snippets.

@jtpaasch
Created November 27, 2018 19:11
Show Gist options
  • Select an option

  • Save jtpaasch/95dcede948c5e03e504a936ba2c5b249 to your computer and use it in GitHub Desktop.

Select an option

Save jtpaasch/95dcede948c5e03e504a936ba2c5b249 to your computer and use it in GitHub Desktop.

Analyzing binaries

Useful tutorials:

LDD

Show the dynamically linked libraries:

ldd /path/to/exe

Show unused dynamically linked libraries:

ldd -u /path/to/exe

LTRACE

List all calls to all linked libraries:

ltrace -c /path/to/exe

Filter it down to just a particular shared library:

ltrace -c -l 'libc.so.*' /path/to/exe

GDB

Starting and exiting GDB

Start gdb:

gdb /path/to/exe

Start with a graphical-like interface:

gdb -tui /path/to/exe

That gives you a prompt:

(gdb) _

The underscore shows where you can type.

To exit:

(gdb) quit

Getting info about the program

Find the entry point:

(gdb) info file

See the address of a function (e.g., 'main'):

(gdb) info address main

See the args for a call:

(gdb) info args

See the stack frame:

(gdb) info frame

Running a program

To run the program (in GDB):

(gdb) run

To run the program with arguments:

(gdb) set args arg1 arg2
(gdb) show args
(gdb) run

A shorter version:

(gdb) run arg1 arg2

Seeing assembly/registers

To see the assembly:

(gdb) layout asm

To see the registers:

(gdb) layout regs

Setting breakpoints

Set a breakpoint at a function:

(gdb) break main

Set a breakpoint at an address:

(gdb) break *0x400566

See the breakpoints:

(gdb) info breakpoints

Delete a breakpoint on a function:

(gdb) clear main

Delete a breakpoint on an address:

(gdb) clear *0x400566

Stepping

To continue:

(gdb) continue

To step:

(gdb) step

To go to the next breakpoint:

(gdb) next

At assembly level, there are instruction-level step and next:

(gdb) stepi
(gdb) nexti

Registers

Useful information is here: https://ftp.gnu.org/old-gnu/Manuals/gdb/html_node/gdb_60.html

See the registers:

(gdb) info registers

GDB has names for four special registers.

  • The register that contains the program counter is $pc. On my machine, that's register rip.
  • The register that contains the stack poitner is $sp. On my machine, that's register rsp.
  • The register tha contains the frame pointer is $fp. On my machine, that's register rbp.
  • The register that contains the processor status is $ps. On my machine, that's the register labeled eflags.

Examining the program

The x command examines something in a program, and the p command prints something. Info: http://visualgdb.com/gdbreference/commands/x

Examine the instruction at the program counter:

(gdb) x/i $pc

Examine the next five instructions from the program counter:

(gdb) x/5i $pc

Disassemble a function:

(gdb) disass main

PLT/GOT

The GOT (Global Offset Table) is the actual table of offsets to the external libraries. The linker fills these in.

The PLT (Procedure Lookup Table) is a linking table. It has a stub for each external function call. It begins with code that triggers the linker to look up the address. Then it gets filled in with the actual address.

In the ELF file there are three important sections:

  • .got: this is the table of addresses that are looked up by the linker.
  • .plt: this has the code that calls exteral functions which is either code to trigger the linker, or it is code to jump to the resolved address.
  • .plt.got: this is the linking table for the PLT. It contains an address back to the PLT first, then after an address is resolved, it contains the real address.

Compile the program:

make

Open the program with GDB:

gdb ./main

Look at the entry point and sections:

(gdb) info file

Note where the PLT and GOT tables start.

Show the disassembly:

(gdb) disass main

Find the line where puts@plt is called, and set a breakpoint on that address:

(gdb) break *0x400542

Run the program:

(gdb) run

Examine the instruction we're looking at:

(gdb) x/i $pc

It should say something like this:

=> 0x40052 <main+11>: callq 0x400430 <puts@plt>

This says that we're an offset of 11 from the start of main, and we're about to make a call to address 0x400430. This has a name: it's <puts@plt>, meaning it's the puts code in the PLT, at address 0x400430. Notice that this address is inside the PLT table!

Step forward one instruction (this should take us through the call, to 0x400430):

(gdb) si

Now examine the instruction we've jumped to:

(gdb) x/i $pc

We are at the spot in the PLT for puts. You should see something like this:

=> 0x400430 <puts@plt>: jmpq *0x200be2(%rip)  #0x601018

That says: jump to *0x200be2(%rip), which it computes as 0x601018. Note: that's an address in the GOT table (use info file again to confirm this).

The processor is not going to jump to 0x601018. Rather, it's going to fetch the address to jump to from 0x601018.

We can look at the instruction at that address:

(gdb) x/i 0x601018

On my machine, I see something like this:

0x601018:    ss add $0x40,%al

We can see the value that this computes:

(gdb) x/x 0x601018

That should say something like this:

0x601018:    0x00400436

So the instruction at address 0x601018 will evaluate to 0x00400436. Hence, the jmpq instruction is going to jump to 0x400436.

Let's step through the jump:

(gdb) si

Examine the next 2 instructions:

(gdb) x/2i $pc

You should see something like this:

=> 0x400436 <puts@plt+6>:   pushq  $0x0
   0x40043b <puts@plt+11>:  jmpq   0x400420

What's 0x400420? The beginning of the PLT table.

Step through these two instructions:

(gdb) si
(gdb) si

Now we should be at 0x400420, the beginning of the PLT table.

Inspect the next instructions:

(gdb) x/2i $pc

I see something like this:

=> 0x400420:    pushq  0x200be2(%rip)        # 0x601008
   0x400426:    jmpq   *0x200be4(%rip)        # 0x601010

So we're going to push the value 0x200be2(%rip), which is here computed as 0x601008, Then we jump to the value that gets computed at *0x200be4(%rip), which is 0x601010.

This is going to take us into the system linker, to find the real location of puts. Step through the next instructions using si multiple times, until we get a return from the linker. You'll see it go through the dynamic linker libraries, e.g., dl-runtime.c and dl-lookup.c.

(gdb) si
(gdb) si
...
(gdb) si

Example 2

Here's a program:

#include <unistd.h>
#include <stdio.h>

int main() {
  char * args[] = {"/bin/ls", NULL};
  printf("pid: %d\n", getpid());
}

Compile it and load it into GDB:

gcc -g --no-pie main.c -o main
gdb ./main

See the source code:

(gdb) list

Set a break point at main and run:

(gdb) break main
(gdb) r

Check the function's disassembly:

(gdb) disas main

Mine looks like this:

Dump of assembler code for function main:
   0x00000000004005a7 <+0>: push   %rbp
   0x00000000004005a8 <+1>: mov    %rsp,%rbp
   0x00000000004005ab <+4>: sub    $0x20,%rsp
=> 0x00000000004005af <+8>: mov    %fs:0x28,%rax
   0x00000000004005b8 <+17>:    mov    %rax,-0x8(%rbp)
   0x00000000004005bc <+21>:    xor    %eax,%eax
   0x00000000004005be <+23>:    lea    0xcf(%rip),%rax        # 0x400694
   0x00000000004005c5 <+30>:    mov    %rax,-0x20(%rbp)
   0x00000000004005c9 <+34>:    movq   $0x0,-0x18(%rbp)
   0x00000000004005d1 <+42>:    callq  0x400490 <getpid@plt>
   0x00000000004005d6 <+47>:    mov    %eax,%esi
   0x00000000004005d8 <+49>:    lea    0xbd(%rip),%rdi        # 0x40069c
   0x00000000004005df <+56>:    mov    $0x0,%eax
   0x00000000004005e4 <+61>:    callq  0x4004b0 <printf@plt>
   0x00000000004005e9 <+66>:    mov    $0x0,%eax
   0x00000000004005ee <+71>:    mov    -0x8(%rbp),%rdx
   0x00000000004005f2 <+75>:    xor    %fs:0x28,%rdx
   0x00000000004005fb <+84>:    je     0x400602 <main+91>
   0x00000000004005fd <+86>:    callq  0x4004a0 <__stack_chk_fail@plt>
   0x0000000000400602 <+91>:    leaveq 
   0x0000000000400603 <+92>:    retq   

Note the call instruction:

0x00000000004005d1 <+42>:    callq  0x400490 <getpid@plt>

What's at 0x400490? It's in the PLT table. You can see the tables:

(gdb) info file

Also, you can dump it and check it out:

objdump -s ./main > main.dump
less main.dump

Then you can search for Contents of section .plt.

So what's 0x400490 in the PLT table look like?

(gdb) disas 0x400490

I see this:

Dump of assembler code for function getpid@plt:
   0x0000000000400490 <+0>: jmpq   *0x200b82(%rip)        # 0x601018
   0x0000000000400496 <+6>: pushq  $0x0
   0x000000000040049b <+11>:    jmpq   0x400480

That first jump goes to 0x601018. Where's that? When I look at the sections again, I can see that this is an address in the GOT table (specifically, it's in the .got.plt section). Let's examine what it says:

(gdb) x/x 0x601018

That gives me this:

0x601018: 0x400496

This means the initial value stored in the GOT table for 0x601018 points to 0x400496. If I look in my sections, I can see that 0x400496 is an address in the PLT table.

So, when the main function calls getpid at 0x4005d1, it actually calls a stub routine in the PLT table at (0x400490). There, in that stub, we first find an instruction to jump to whatever value the GOT table has at 0x601018. The value it has is 0x400496, which is again in the PLT table.

What's at 0x400496? It's the instruction right after 0x400490, the beginning of the PLT stub for getpid. I.e., it's the middle line here:

Dump of assembler code for function getpid@plt:
   0x0000000000400490 <+0>: jmpq   *0x200b82(%rip)        # 0x601018
   0x0000000000400496 <+6>: pushq  $0x0
   0x000000000040049b <+11>:    jmpq   0x400480

This pushes 0 onto the stack, and then jumps to 0x400480. What's 0x400480? For me, this is the very first address in the PLT table. What's there?

(gdb) disas 0x400480

It says there's no function there. So let's try to look at the instruction:

(gdb) x/i 0x400480

For me, it says:

0x400480:    pushq  0x200b82(%rip)        # 0x601008

So it pushes 0x601008 onto the stack. What's the next instruction? Try each one, until we see a jump:

(gdb) x/i 0x400481
(gdb) x/i 0x400482
...
(gdb) x/i 0x40086

Finally, at 0x400486, I see a jump:

=> 0x400486:    jmpq   *0x200b84(%rip)        # 0x601010

So the PLT was putting some stuff on the stack, and doing some calculations. Now we're going to jump to the value stored at 0x601010.

What's the value stored at 0x601010? I can see that this is in the .got.plt section. What's the value?

(gdb) x/x 0x601010

I see this:

0x601010:   0xf7dec680

So we're going to jump there. What's at 0xf7dec680?

(gdb) x/x 0xf7dec680

It says I can't access that address:

0xf7dec680: Cannot access memory at address 0xf7dec680

We must be in the dynamic linker?

Step through some instructions:

(gdb) si
(gdb) si
...

It willl take a long time to step through all the moves that the linker does. Instead, set a breakpoint after the getpid function.

(gdb) list main

That shows:

1   #include <unistd.h>
2   #include <stdio.h>
3   
4   int main() {
5     char * args[] = {"/bin/ls", NULL};
6     printf("pid: %d\n", getpid());
7     // sleep(60);
8   }

I can set a breakpoint on line 7:

(gdb) break 7

Now I can continue to that breakpoint:

(gdb) continue

That goes on, and prints out the PID, then stops:

Continuing.
pid: 13315


Breakpoint 2, main () at main.c:8
8   }

At this point, the dynamic linker should have filled in the GOT table with the real address of printf on my system. Let's check it.

Where was the PLT stub? It was 0x400490. What's the value now?

(gdb) x/x 0x400490

Now I see this:

0x400490 <getpid@plt>:  0x0b8225ff

Hmm. That's the same value I saw before. I'm stuck.

Let's see what's mapped to that address. We know the process ID is 13315, because the program printed it out. But we could also look for it by searching for the PID of the main program, because GDB is simply executing that program:

ps aux | grep main

I can see it is the second item listed:

jt       13313  0.0  0.1 102604 41272 pts/0    S+   09:44   0:00 gdb ./main
jt       13315  0.0  0.0   4508   848 pts/0    t    09:44   0:00 /path/to/main

Now that I know the PID, what's the memory map for that process:

less /proc/13315/maps

Another attempt

You can see the relation sections in the elf file:

readelf --relocs ./main

There we can see that getpid will be at 0x601018.

We can see the sections:

readelf --sections ./main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment