4

I've seen references on Stack Exchange and elsewhere to inserting detours into compiled code. My understanding is that essentially a jmp instruction is inserted and then somehow the patched program is linked with additional code that contains the target of the jmp.

As a concrete but (hopefully) simple example, consider a program.

#include <stdio.h>

void hello(void) {
    printf("Hello ");
    printf("world!\n");
}

int main(void) {
    hello();
}

Suppose I have only a binary compiled from this program. No measures have been taken to strip symbols or obfuscate the program in any way. I want to insert a detour to call code compiled from this function.

#include <stdio.h>

void detour(void) {
    printf("detoured ");
}

The output of the patched program should be:

Hello detoured world!

How would I do that? How would I avoid breaking address offsets in the compiled code?

My available compilers are gcc, clang, and icc. My available operating systems are OS X and Ubuntu. Choose whatever you prefer in your answer.

If this can't reasonably be answered here then a brief overview and a pointer to some reading material would also be a good answer.

I'm aware of LD_PRELOAD and ld --wrap. The example was chosen so that those methods do not easily suffice. (You could of course just detour the entire hello() function to one that prints "Hello detoured world!" but lets pretend you don't have the source code and the function is non-trivial.)

Related Questions

I was first going to ask how to simply disassemble and reassemble a compiled program but that has been asked. The response was that it's extremely difficult. My sense from the answers is that it's not a common thing to do. I suspect my assumption that disassembling and reassembling is a necessary step for detouring might not be correct.

Why there are not any disassemblers that can generate re-assemblable asm code?

A similar question on StackOverflow received a lukewarm response. This question is focused just on inserting a detour rather than "modification" in general and I think the audience here will be more receptive.

https://stackoverflow.com/questions/4309771/disassembling-modifying-and-then-reassembling-a-linux-executable

This Reverse Engineering question asks about modifying binaries in general. A lot of different tools and LD_PRELOAD are mentioned. Some answers say it's possible to do this with a hex editor. I think that's the method I'd be most interested in.

How do I add functionality to an existing binary executable?

Recent examples I have seen that refer to doing such a thing

A blog post.

http://charlessolar.com/post/tag/disassemble

This Stack Overflow question.

https://stackoverflow.com/questions/9449845/how-to-link-object-file-to-executable-compiled-binary

2
  • Are you looking to make the detour in a single specific binary? Or are you looking to make a generic detour "patcher" that will work on any binary that contains a function like your hello() function above? Commented Oct 16, 2014 at 14:35
  • I'm looking to understand the process in general. Commented Oct 16, 2014 at 16:01

1 Answer 1

6

I compiled your program on Ubuntu 14.04 and put it on https://mega.co.nz/#!gdRRxRzZ!dw08GEHvXeTxXqurcpMLOxpXVjZa807TJN0PH60h4Rg; you might want to use that binary if you want to retrace the following steps, because if you don't have the exact version of the C compiler and libs, your binary might be different.

The file is a zip that includes the original detour.c, the compiled program (detour.orig), and the patched one (detour.patched).

First, let's disassemble the binary using objdump:

$ objdump -d detour.orig|less
.. stuff omitted ..
000000000040057d <hello>:
  40057d:       55                      push   %rbp
  40057e:       48 89 e5                mov    %rsp,%rbp
  400581:       bf 34 06 40 00          mov    $0x400634,%edi
  400586:       b8 00 00 00 00          mov    $0x0,%eax
  40058b:       e8 d0 fe ff ff          callq  400460 <printf@plt>
  400590:       bf 3b 06 40 00          mov    $0x40063b,%edi
  400595:       e8 b6 fe ff ff          callq  400450 <puts@plt>
  40059a:       5d                      pop    %rbp
  40059b:       c3                      retq

000000000040059c <main>:
  40059c:       55                      push   %rbp
  40059d:       48 89 e5                mov    %rsp,%rbp
  4005a0:       e8 d8 ff ff ff          callq  40057d <hello>
  4005a5:       5d                      pop    %rbp
  4005a6:       c3                      retq
  4005a7:       66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  4005ae:       00 00
00000000004005b0 <__libc_csu_init>:
.. more stuff omitted ..

and also check the data sections:

$ objdump  -s detour.orig | less
.. stuff omitted ..
Contents of section .rodata:
 400630 01000200 48656c6c 6f200077 6f726c64  ....Hello .world
 400640 2100                                 !.
Contents of section .eh_frame_hdr:
.. more stuff omitted ..

As you see, the strings Hello, world are in the read-only data section, at 400634 and 40063b. These offsets are passed to printf and puts in hello. Why puts? Well, the optimizer is clever enough to rewrite a printf of a constant string that ends in '\n' into a puts; you can see the '\n' is omitted from the string in the .rodata section.

Now, we want to insert a puts("detoured"). But there's no space in the hello function, and if we tried to insert some bytes there, everything else in the program would be moved, which we want to avoid. Also, there's no space in the .rodata section for another string, the .eh_frame_hdr starts directly behind it.

However, check address 4005a7. The main function returns at 4005a6, and the C compiler used some padding to get the next function, __libc_csu_init, at a 16-byte boundary. Which means there are a few unused bytes that we can make use of. Looking through the rest of the disassembly, we find some more bytes like that:

4004ba          66 0f 1f 44 00 00
4004e9          0f 1f 80 00 00 00 00
400529          0f 1f 80 00 00 00 00
4005a7          66 0f 1f 84 00 00 00 00 00
400615          66 66 2e 0f 1f 84 00 00 00 00 00

The last one of these, 400615, has the size to fit the "detoured " string in.

Now what we're going to do with the assembly is patch it to:

400590          jmp 4004e9                              e9 54 ff ff ff
4004e9          mov $0x400615, %edi; jmp 400529         bf 15 06 40 00 eb 39
400529          callq 400460; jmp 4005a7                e8 32 ff ff ff eb 77
4005a7          mov 0x40063b, %edi; jmp 400595          bf eb 06 40 00 eb e7

which overwrites the instruction at 400590, places the required instructions in the spare bytes, adds jumps between the parts, and restores the overwritten mov before jumping to the next instruction.

Now, it's time to use a hex editor to apply these patches to the binary (don't forget to move the 'detoured ' string to 400615 as well). The resulting binary should be identical to detour.patched from the zip.

Last, we run the patched program:

$ detour.patched
Hello detoured world!

As you see, the trick is to make us of unused regions within the program, to avoid moving stuff around, which would break offsets. I cheated a bit when i put the "detoured" string into the code section - this would fail if anything wanted to write-access the string. If i wanted writable data, i would need to use the data section, and possibly even extend it - but that case is much more compliated since i'd have to fiddle with the ELF headers and sizes, and i'd probably need specific ELF tools to get it right. The current example needs nothing but a hex editor. I even crafted the hex coded manually from the opcodes; to get a bit more sophisticated, use the radare2 tool.

1
  • 1
    I'd just like to point out that one (probably) could also change the section characteristics of the code section to make the string writeable (keeping the size constraints in main) or add new sections to put code and data in - which would remove all size constrictions. (At least for the PE format that works)
    – user45891
    Commented Oct 16, 2014 at 19:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.