I can see you gdb

So, as soon as the gllin binary was released for download, I came up with an evil plan – will for sure blog about it after it is executed. But first (as part of usual preparation for an evil plan) I needed to find out whether in a normal program under Linux the heap is executable, or rather what section is executable and writable. While attempting this I made a funny and completely unuseful observation which I’m going to share with you now. Here’s the test program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void sayhello(int world_number) {
        int local;
        static int stat;

        printf("Hello World %i! Local variable at %p and static at %p\n",
                        world_number, &local, &stat);

int main(int argc, char *argv[], char **envp) {
        void (*say[2])(int i) = { sayhello, malloc(0x1000) };

        memcpy(say[1], say[0], 0x50);
        return 0;

After “make hello” I have the ELF under ./hello and load it into gdb and inspect:

 $ gdb ./hello
GNU gdb 6.6
(gdb) break sayhello
Breakpoint 1 at 0x40068f: file hello.c, line 9.
(gdb) run
Breakpoint 1, sayhello (world_number=0) at hello.c:9
(gdb) up 
#1  ... in main (argc=1, argv=..., envp=...) at hello.c:17
(gdb) disassemble say[0] (say[0] + 15)
Dump of assembler code from 0x400684 to 0x400693:
0x0000000000400684 <sayhello+0>:        push   %rbp
0x0000000000400685 <sayhello+1>:        mov    %rsp,%rbp
0x0000000000400688 <sayhello+4>:        sub    $0x20,%rsp
0x000000000040068c <sayhello+8>:        mov    %edi,0xffffffffffffffec(%rbp)
0x000000000040068f <sayhello+11>:       lea    0xfffffffffffffffc(%rbp),%rdx
End of assembler dump.
(gdb) disassemble say[1] (say[1] + 15)
Dump of assembler code from 0x602010 to 0x60201f:
0x0000000000602010:     push   %rbp
0x0000000000602011:     mov    %rsp,%rbp
0x0000000000602014:     sub    $0x20,%rsp
0x0000000000602018:     mov    %edi,0xffffffffffffffec(%rbp)
0x000000000060201b:     int3
0x000000000060201c:     lea    0xfffffffffffffffc(%rbp),%edx
End of assembler dump.

You may now ask yourself the same question that I asked myself: WTF? or I may first explain what is happening above and you may ask the question then. We loaded the program into the debugger. The program was supposed to greet the world once and then call a copy of sayhello we made with memcpy(). We set a breakpoint at the start of the function and run the program. When it enters sayhello, it hits the break and we have a chance to look at the copy of the function. We step out of the sayhello frame so that we can access the say array. We disassemble the start of the original function and the start of its copy, and we see that they differ (!). Someone is messing in MY functions?! Or memcpy() is perhaps broken?!

No, it’s just gdb. When we set a breakpoint at sayhello it inserted the extra instruction (which I would have maybe recognised if I used x86 asm more often) to get notified in the right moment. We copied the function together with the breakpoint and we hit the original breakpoint. gdb then hid it from out eyes (first disassembly) but it didn’t know that we had secretly made a copy (second disassembly) and we now have a pretty little breakpoint of our own.

So what useful did we learn? Nothing really. That checksumming the program in runtime may sometimes work.

Good news is that memcpy() is fine and the world is safe. Pheww..


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: