lldb for disassembly and HexFiend to edit this example. Gdb, otool, and objdump also work fine for quick and dirty disassembly.
Here's a toy code snippet, wat-arg.c
, that should be easy to binary edit:
#include <stdio.h>
int main(int argc, char **argv) {
if (argc > 1) {
printf("got an arg\n");
} else {
printf("no args\n");
}
}
If we compile this and then launch lldb on the binary and step into main, we can see the following machine code:
$ lldb wat-arg
(lldb) breakpoint set -n main
Breakpoint 1: where = original`main, address = 0x0000000100000ee0
(lldb) run
(lldb) disas -b -p -c 20
; address hex opcode disassembly
-> 0x100000ee0: 55 pushq %rbp
0x100000ee1: 48 89 e5 movq %rsp, %rbp
0x100000ee4: 48 83 ec 20 subq $32, %rsp
0x100000ee8: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
0x100000eef: 89 7d f8 movl %edi, -8(%rbp)
0x100000ef2: 48 89 75 f0 movq %rsi, -16(%rbp)
0x100000ef6: 81 7d f8 01 00 00 00 cmpl $1, -8(%rbp)
0x100000efd: 0f 8e 16 00 00 00 jle 0x100000f19 ; main + 57
0x100000f03: 48 8d 3d 4c 00 00 00 leaq 76(%rip), %rdi ; "got an arg\n"
0x100000f0a: b0 00 movb $0, %al
0x100000f0c: e8 23 00 00 00 callq 0x100000f34 ; symbol stub for: printf
0x100000f11: 89 45 ec movl %eax, -20(%rbp)
0x100000f14: e9 11 00 00 00 jmpq 0x100000f2a ; main + 74
0x100000f19: 48 8d 3d 42 00 00 00 leaq 66(%rip), %rdi ; "no args\n"
0x100000f20: b0 00 movb $0, %al
0x100000f22: e8 0d 00 00 00 callq 0x100000f34 ; symbol stub for: printf
As expected, we load a value, compare it to 1 with cmpl $1, -8(%rbp)
, and then print got an arg
or no args
depending on which way we jump as a result of the compare.
$ ./wat-arg
no args
$ ./wat-arg 1
got an arg
If we open up a hex editor and change 81 7d f8 01 00 00 00; cmpl 1, -8(%rbp)
to 81 7d f8 06 00 00 00; cmpl 6, -8(%rbp)
, that should cause the program to check for 6 args instead of 1
$ ./wat-arg
no args
$ ./wat-arg 1
no args
$ ./wat-arg 1 2
no args
$ ./wat-arg 1 2 3 4 5 6 7 8
got an arg
Simple! If you do this a bit more, you'll soon get in the habit of patching in 90
3 to overwrite things with NOPs. For example, if we replace 0f 8e 16 00 00 00; jle
and e9 11 00 00 00; jmpq
with 90
, we get the following:
0x100000ee1: 48 89 e5 movq %rsp, %rbp
0x100000ee4: 48 83 ec 20 subq $32, %rsp
0x100000ee8: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
0x100000eef: 89 7d f8 movl %edi, -8(%rbp)
0x100000ef2: 48 89 75 f0 movq %rsi, -16(%rbp)
0x100000ef6: 81 7d f8 01 00 00 00 cmpl $1, -8(%rbp)
0x100000efd: 90 nop
0x100000efe: 90 nop
0x100000eff: 90 nop
0x100000f00: 90 nop
0x100000f01: 90 nop
0x100000f02: 90 nop
0x100000f03: 48 8d 3d 4c 00 00 00 leaq 76(%rip), %rdi ; "got an arg\n"
0x100000f0a: b0 00 movb $0, %al
0x100000f0c: e8 23 00 00 00 callq 0x100000f34 ; symbol stub for: printf
0x100000f11: 89 45 ec movl %eax, -20(%rbp)
0x100000f14: 90 nop
0x100000f15: 90 nop
0x100000f16: 90 nop
0x100000f17: 90 nop
0x100000f18: 90 nop
0x100000f19: 48 8d 3d 42 00 00 00 leaq 66(%rip), %rdi ; "no args\n"
0x100000f20: b0 00 movb $0, %al
0x100000f22: e8 0d 00 00 00 callq 0x100000f34 ; symbol stub for: printf
Note that since we replaced a couple of multi-byte instructions with single byte instructions, the program now has more total instructions.
$ ./wat-arg
got an arg
no args
Other common tricks include patching in cc
to redirect to an interrupt handler, db
to cause a debug breakpoint, knowing which bit to change to flip the polarity of a compare or jump, etc. These things are all detailed in the Intel architecture manuals, but the easiest way to learn these is to develop the muscle memory for them one at a time.
Have fun!
I don't actually recommend doing this in an emergency if you haven't done it before. Pushing out a known broken binary that leaks details from future releases is bad, but pushing out an update that breaks your updater is worse. You'll want, at a minimum, a few people who create binary patches in their sleep to code review the change to make sure it looks good, even after running it on a test client.
Another solution, not quite as "good", but much less dangerous, would have been to disable the update server until the new release was ready.
[return]