iOS Patch program instruction at runtime
Asked Answered
L

3

22

How would one go about modifying individual assembly instructions in an application while it is running?

I have a Mobile Substrate tweak that I am writing for an existing application. In the tweak's constructor (MSInitialize), I need to be able to rewrite individual instruction(s) in the app's code. What I mean by this is that there may be multiple places in the application's address space that I wish to modify, but in each instance, only a single instruction needs to be modified. I have already disabled ASLR for the application and know the exact memory address of the instruction to be patched, and I have the hex bytes (as a char[], but this is uninportant and can be changed if necessary) of the new instruction. I just need to figure out how to perform the change.

I know that iOS uses Data Execution Prevention (DEP) to specify that executable memory pages cannot also be writeable and vice versa, but I know that it is possible to bypass this on a jailbroken device. I also know that the ARM processor used by iDevices has an instruction cache that needs to be updated to reflect the change. However, I do not even know where to begin to do this.

So, to answer the question that would surely otherwise be asked, I have not tried anything. This is not because I am lazy; rather, it is because I have absolutely no clue how this could be accomplished. Any help at all would be greatly appreciated.

Edit:

If it helps at all, my ultimate goal is to use this in a Mobile Substrate tweak that hooks an App Store application. Previously, in order to mod this application, one would have to first crack it to decrypt the app so the binary could be patched. I want to make it so people wouldn't have to crack the app, since that can lead to piracy which I am strongly against. I can't use Mobile Substrate normally because all of the work is done in C++, not Objective-C, and the application is stripped, leaving no symbols to use MSHookFunction on.

Locomotor answered 28/11, 2012 at 5:42 Comment(15)
Why was this question downvoted? Did I not clearly explain my question? Or is it just because I mentioned the dreaded J word (jailbreak)?Locomotor
I don't know, this is a good question, +1.Sideslip
I found this question and answer - they suggest using vm_write() for writing a process' memory.Sideslip
@H2CO3 that method doesn't work for me. I think that they are discussing writing to its data memory, not executable memory. I added a bounty.Locomotor
By the way, if the executable is stripped, how do you want to find out what/where to modify?Sideslip
Why not modify the cracked binary? You could just make a script that dumps the App Store app (via clutch or similar tools), apply your patches, and replace the App Store's binary with your patched one.Mylo
Hold on here. No sensible Os with memory protection would let you write to the current applications code pages, let alone another's. This isn't happening without hacking, or some obscure very insecure calls. Think about it. If I could just inject one or two instructions into some code, I could now overwrite any? That's a gaping security hole that no sensible Os would leave unpatched.Catbird
To bypass execution prevention, you'd have to hack the kernel to change the bits in the page table. Not exactly an easy thing to do.Catbird
Note: the main problem with this is that anybody other code that looks at it's operations dos not see something legitimate -- it sees a code injection attack.Catbird
@Catbird You're wrong, I can modify current process's code without any problems.Salgado
@Locomotor What exact problem/error are you experiencing?Salgado
@mifki: I thought iOS took advantage of paging to force code pages to be read only. Or maybe that's Linux. Thanks.Catbird
@Catbird Yes, they're read-only. But you can control page protection flags, there are vm_protect() and mprotect() functions. You can make your own code writable without any special privileges. Accessing another process will obviously require them.Salgado
@H2CO3 The android version of this app is not stripped, so I can see the symbols there. Just find what I want to change and then figure out where it is in the iOS version.Locomotor
@mifki: thanks. I dint know you could change permissions like that.Catbird
L
9

Completely forgot I asked this question, so I'll show what I ended up with now. The comments should explain how and why it works.

#include <stdio.h>
#include <stdbool.h>
#include <mach/mach.h>
#include <libkern/OSCacheControl.h>

#define kerncall(x) ({ \
    kern_return_t _kr = (x); \
    if(_kr != KERN_SUCCESS) \
        fprintf(stderr, "%s failed with error code: 0x%x\n", #x, _kr); \
    _kr; \
})


bool patch32(void* dst, uint32_t data) {
    mach_port_t task;
    vm_region_basic_info_data_t info;
    mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT;
    vm_region_flavor_t flavor = VM_REGION_BASIC_INFO;

    vm_address_t region = (vm_address_t)dst;
    vm_size_t region_size = 0;

    /* Get region boundaries */
    if(kerncall(vm_region(mach_task_self(), &region, &region_size, flavor, (vm_region_info_t)&info, (mach_msg_type_number_t*)&info_count, (mach_port_t*)&task))) return false;
    /* Change memory protections to rw- */
    if(kerncall(vm_protect(mach_task_self(), region, region_size, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY))) return false;

    /* Actually perform the write */
    *(uint32_t*)dst = data;

    /* Flush CPU data cache to save write to RAM */
    sys_dcache_flush(dst, sizeof(data));
    /* Invalidate instruction cache to make the CPU read patched instructions from RAM */
    sys_icache_invalidate(dst, sizeof(data));

    /* Change memory protections back to r-x */
    kerncall(vm_protect(mach_task_self(), region, region_size, false, VM_PROT_EXECUTE | VM_PROT_READ));
    return true;
}
Locomotor answered 7/1, 2014 at 6:26 Comment(0)
U
4

vm_protect to w^x, assuming you're jailbroken with a decent jailbreak (e.g. if mobilesubstrate works)

Unpolitic answered 19/12, 2012 at 20:44 Comment(0)
D
3

Writing to instruction memory from processor registers is, as others say above, a bit tricky. Especially with iPhones, since Apple tries to keep the processor details secret.

Permissions on memory access are the first problem. Executable memory is not normally writable. However, if this is overcome, then there is a little dance to go through to get data out of the processor registers and into the instruction pipeline. In general, there are synchronisation instructions, which force a specific order on the memory accesses before and after them, and cache commands, which force dirty write data out to memory and flush out clean and possibly stale read data. Both of these are highly dependent on the detailed implementation of the processor.

Arm Has nice manuals on the web that explain these in detail for specific processors. However, whether the processors inside iPhones do what the public Arm manuals say, I have no idea.

Here's a place to start understanding the Arm memory synchronisation model for one processor: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0092b/ch04s03s04.html and that goes on to tell how to flush the instruction cache by a write to a control register. It certainly is possible to write self-modifying code for Arm processors because somewhere in that manual I found a statement that said that it is sometimes unavoidable and the has to be supported.

(I'm not claiming this is an answer. But it wouldn't fit in a comment.)

Domenech answered 1/12, 2012 at 15:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.