Modifying Machine Code in Executables Link to heading
In this post, we’ll walk through a simple but fun reverse-engineering exercise: taking a compiled C program, locating the machine instructions responsible for printing characters, and modifying those bytes directly in the executable.
This is a great way to build intuition around how compilers translate code, how functions map to assembly, and how tools like objdump and xxd help us inspect and patch binaries.
The Source Program Link to heading
We’ll start with a minimal C program that prints "ab\n":
#include <stdio.h>
int main() {
putchar('a');
putchar('b');
putchar('\n');
return 0;
}
Compile it normally:
gcc -o main main.c
Inspecting the Binary with objdump
Link to heading
Let’s look at the generated machine code:
objdump -d main
This produces a lot of output (as expected), but the portion we care about is the main symbol. We can isolate it:
objdump --disassemble=main -f main
The disassembly for main looks like this:
0000000000001139 <main>:
1139: 55 push %rbp
113a: 48 89 e5 mov %rsp,%rbp
113d: bf 61 00 00 00 mov $0x61,%edi
1142: e8 e9 fe ff ff call 1030 <putchar@plt>
1147: bf 62 00 00 00 mov $0x62,%edi
114c: e8 df fe ff ff call 1030 <putchar@plt>
1151: bf 0a 00 00 00 mov $0x0a,%edi
1156: e8 d5 fe ff ff call 1030 <putchar@plt>
115b: b8 00 00 00 00 mov $0x0,%eax
1160: 5d pop %rbp
1161: c3 ret
Each call to putchar is preceded by a mov $VALUE, %edi, where %edi contains the character to print.
0x61>'a'0x62>'b'0x0a> newline
If we want the program to print "ac" instead of "ab", we need to modify the instruction at 0x1147 so that it loads 0x63 (ASCII 'c') instead of 0x62.
Producing a Hex Dump Link to heading
To patch the executable, we’ll create a hex dump:
xxd main > main.asm
Here’s the relevant portion (trimmed for clarity):
00001140: 0000 e8e9 feff ffbf 6200 0000 e8df feff ........b....... < HERE
The byte at address 0x1147 falls 14 bytes (0xE) into this block.
That 62 is the value we want to change.
We want this:
00001140: 0000 e8e9 feff ffbf 6300 0000 e8df feff ........c.......
Notice that only one byte changes: 0x62 > 0x63.
Writing the Modified Binary Back Link to heading
Save the modified hex dump, then run:
xxd -r main.asm modified_main
Now run it:
./modified_main
ac
Success. We changed the program’s behavior without recompiling, simply by patching the machine code. This is the foundation for advanced topics like reverse engineering, binary instrumentation, and exploit development. If you’d like to extend this example, such as patching instructions of different sizes, modifying control flow, or injecting new code, let me know. I’m happy to build on this.