22

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

我正在为现有应用程序编写一个 Mobile Substrate 调整。在调整的构造函数 ( MSInitialize) 中,我需要能够重写应用程序代码中的各个指令。我的意思是,我希望修改应用程序地址空间中的多个位置,但在每个实例中,只需要修改一条指令。我已经为应用程序禁用了 ASLR,并且知道要修补的指令的确切内存地址,并且我有新指令的十六进制字节(作为 char[],但这并不重要,可以根据需要进行更改)。我只需要弄清楚如何执行更改。

我知道 iOS 使用数据执行保护 (DEP) 来指定可执行内存页也不能​​是可写的,反之亦然,但我知道可以在越狱设备上绕过这一点。我还知道 iDevices 使用的 ARM 处理器有一个指令缓存,需要更新以反映变化。但是,我什至不知道从哪里开始这样做。

所以,为了回答这个肯定会被问到的问题,我没有尝试过任何事情。这不是因为我懒惰;相反,这是因为我完全不知道如何做到这一点。任何帮助都将不胜感激。

编辑:

如果它有帮助,我的最终目标是在 Mobile Substrate 调整中使用它来挂钩 App Store 应用程序。以前,为了修改这个应用程序,必须先破解它来解密应用程序,这样才能修补二进制文件。我想让人们不必破解该应用程序,因为这可能导致盗版,我强烈反对。我不能正常使用 Mobile Substrate,因为所有工作都是用 C++ 完成的,而不是 Objective-C,并且应用程序被剥离,没有任何符号可以使用MSHookFunction

4

3 回答 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;
}
于 2014-01-07T06:26:47.403 回答
4

vm_protect 到 w^x,假设你已经越狱了一个不错的越狱(例如,如果 mobilesubstrate 工作)

于 2012-12-19T20:44:48.957 回答
3

正如上面其他人所说,从处理器寄存器写入指令存储器有点棘手。尤其是对于 iPhone,因为 Apple 试图对处理器细节保密。

内存访问权限是第一个问题。可执行内存通常不可写。但是,如果克服了这一点,则需要进行一些操作才能将数据从处理器寄存器中取出并输入指令流水线。一般来说,有同步指令,它强制在它们之前和之后的内存访问上具有特定的顺序,以及缓存命令,它强制将脏数据写入内存并清除干净的和可能陈旧的读取数据。这两者都高度依赖于处理器的详细实现。

Arm 在网络上有很好的手册,详细解释了特定处理器的这些内容。但是,iPhone 内部的处理器是否按照公开的 Arm 手册所说的那样做,我不知道。

这是一个开始了解一个处理器的 Arm 内存同步模型的地方:http: //infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0092b/ ch04s03s04.html 然后继续告诉如何通过写入控制寄存器来刷新指令缓存。为 Arm 处理器编写自修改代码当然是可能的,因为在那本手册的某处,我发现了一个声明,说它有时是不可避免的并且必须得到支持。

(我并不是说这是一个答案。但它不适合评论。)

于 2012-12-01T15:57:00.030 回答