运行时iOS补丁程序说明

22

当应用程序运行时,如何修改单个汇编指令?

我正在为现有应用程序编写一个Mobile Substrate(移动端代码注入框架)插件。在插件的构造函数(MSInitialize)中,我需要能够重写应用程序代码中的单个指令。我的意思是,可能有多个位置在应用程序地址空间中需要被修改,但在每个实例中,只需修改单个指令。我已经为该应用程序禁用了ASLR,并且知道要修补的指令的精确内存地址,并且我有新指令的十六进制字节(作为char[],但这不重要,如果必要可以更改)。我只需要找出如何执行更改。

我知道iOS使用数据执行预防(DEP)来确定可执行内存页不能同时也是可写的,反之亦然,但我知道在越狱设备上是可能绕过此机制的。我还知道iDevices使用的ARM处理器具有指令缓存,需要更新以反映更改。但是,我甚至不知道从哪里开始做。

因此,回答那肯定会被问到的问题,我什么都没尝试过。这不是因为我懒惰,而是因为我完全不知道如何完成此操作。任何帮助都将不胜感激。

编辑:

如果可能的话,我的最终目标是在Mobile Substrate插件中钩住App Store应用程序。以前,为了修改该应用程序,必须先破解它以解密应用程序二进制文件,以便可以对其进行打补丁。我想让人们不必破解该应用程序,因为这可能会导致盗版,而我强烈反对盗版。我无法正常使用Mobile Substrate,因为所有工作都是在C++中完成,而不是Objective-C,并且应用程序已被剥离,没有符号可用于MSHookFunction


3
为什么这个问题被踩了?我没有清楚地解释我的问题吗?还是因为我提到了可怕的“越狱”一词? - C0deH4cker
3
我不知道,这是一个好问题,+1。 - user529758
我找到了这个问题和答案 - 他们建议使用vm_write()来写入进程的内存。 - user529758
@H2CO3 那个方法对我不起作用。我认为他们正在讨论写入数据内存,而不是可执行内存。我添加了赏金。 - C0deH4cker
1
顺便问一下,如果可执行文件被剥离了,你想如何找出需要修改的内容/位置? - user529758
显示剩余10条评论
3个回答

9

我完全忘记了我曾经提出过这个问题,现在我会展示我最终得到的答案。注释应该解释了它是如何以及为什么能够工作。

#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;
}

4

如果你已经越狱并且使用了一个不错的越狱工具(例如,如果mobilesubstrate正常运作),那么可以使用vm_protect实现w^x。


3
从处理器寄存器写入指令内存是有点棘手的,正如其他人在上面所说。特别是对于iPhone来说,因为苹果试图保密处理器细节。
内存访问权限是第一个问题。可执行内存通常不可写。然而,如果这个问题被克服了,那么就需要进行一些小的操作才能将数据从处理器寄存器传输到指令流水线中。一般来说,有同步指令,它们在其之前和之后强制执行内存访问的特定顺序,并且有缓存命令,它们强制将脏写数据写入内存并清除干净的、可能过时的读取数据。这两者都高度依赖于处理器的详细实现。
Arm在网上有很好的手册,详细解释了特定处理器的这些内容。然而,处理器内部是否遵循公共Arm手册所说的,我不知道。
这里提供一个理解Arm处理器内存同步模型的起点: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0092b/ch04s03s04.html 该链接详细介绍了如何通过控制寄存器写入来刷新指令缓存。在Arm处理器中编写自修改代码是完全可行的,因为在手册中有一句话说,有时是不可避免的,并且必须得到支持。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接