Patching a Mac OS X binary

While it's assumed you have the sources to a binary and can always recompile if you desire to change something, there are cases when sources are not available and other solutions like code injection via the input manager are not feasible.


In this small article, I will show you how to patch a binary that prints a value - our patch will change this value.

This article is relevant to PowerPC processors - it deals with assembly and therefore the information provided here is not directly relevant to the Intel architecture.

Here's our test program (just prints a number on screen):

cristi:~/Programming/test diciu$ cat printNumber.c
#include <stdio.h>
int main()
{
int t;

t = 5;
printf("t = %d\n", t);
return 0;
}



cristi:~/Programming/test diciu$ gcc -o printNumber printNumber.c
cristi:~/Programming/test diciu$ ./printNumber
t = 5


Wow, it works. Now we need to learn a bit about the executable gcc has made.
otool can tell us the address and offset for our text segment.


cristi:~/Programming/test diciu$ otool -l ./printNumber
./printNumber:
Load command 0
cmd LC_SEGMENT
[..]
Section
sectname __text
segname __TEXT

addr 0x00002444
size 0x000008ac
offset 5188
align 2^2 (4)
reloff 0
nreloc 0
flags 0x80000400
reserved1 0
reserved2 0


To find out the in memory address of the instruction that attributes t it's value 5, we'll run our program inside gdb. We choose to break in printf - you can also break in main cause we're it's the main function we're disassembling...


(gdb) bt
#0 0x90105960 in printf$LDBL128 ()
#1 0x00002b84 in main ()
(gdb) f 1
#1 0x00002b84 in main ()
(gdb) disass
Dump of assembler code for function main:
0x00002b50 : mflr r0
0x00002b54 : stmw r30,-8(r1)
0x00002b58 : stw r0,8(r1)
0x00002b5c : stwu r1,-96(r1)
0x00002b60 : mr r30,r1
0x00002b64 : bcl- 20,4*cr7+so,0x2b68
0x00002b68 : mflr r31
0x00002b6c : li r0,5
0x00002b70 : stw r0,56(r30) // store content of r0 to r30 + 56
0x00002b74 : addis r2,r31,0 // load r3 with pointer to format string step 1
0x00002b78 : addi r3,r2,664 // load r3 with pointer to format string step 2
0x00002b7c : lwz r4,56(r30) // r4 = r30 + 56 (address of t)
0x00002b80 : bl 0x2ba0 // call printf
0x00002b84 : li r0,0
0x00002b88 : mr r3,r0
0x00002b8c : lwz r1,0(r1)
0x00002b90 : lwz r0,8(r1)
0x00002b94 : mtlr r0
0x00002b98 : lmw r30,-8(r1)
0x00002b9c : blr
End of assembler dump.


Note: while you can alter stuff directly in gdb, gdb cannot write the modified executable because OS X uses on demand paging that makes it impossible to alter the binary while it's being run.

Let's figure out the offset for our load instruction in the binary file.

offset_in_file = address_in_memory - addr + offset

offset_in_file = 0x00002b6c - 0x00002444 + 5188 = 0x1B6C

We'll use xxd to get a hex dump for our binary.

xxd ./printNumber > printNumber.hex


edit printNumber.hex (go to offset 0x1B6C):


0001b50: 7c08 02a6 bfc1 fff8 9001 0008 9421 ffa0 |............!..
0001b60: 7c3e 0b78 429f 0005 7fe8 02a6 3800 0005 |>.xB.......8...
0001b70: 901e 0038 3c5f 0000 3862 0298 809e 0038 ...8<_..8b.....8


replace 05 with 06.

Recreate binary from the altered xxd file and let the OS know it's executable:

cristi:~/Programming/test diciu$ xxd -r printNumber.hex > ./printNumberPatched
cristi:~/Programming/test diciu$ chmod +x printNumberPatched


Run it:

cristi:~/Programming/test diciu$ ./printNumberPatched
t = 6


That's all folks.