iOS注入UnityFramework

/ 7

Unity版本变化

新版 Unity 打包 iOS 版本后,Unity 编写的代码会编译生成一个名为 UnityFramework.frameworkFramework,存放在 xxx.app/Frameworks 目录下:

而 Unity 老版本打包的不会:

获取基址

使用 Unity 开发的手游,我们一般会使用 IDAIl2CppDumper 分析出我们需要的函数虚拟内存地址后,调用 MSHookFunction 函数进行注入。

但是新版本 Unity 打包生成的二进制位置发生变化,这就会涉及动态库加载顺序和基址的问题。(这里说的二进制文件就是指 Mach-O 文件,也叫 image 或 镜像)。

在 iOS 中一般加载的第一个镜像就是 APP 的默认镜像,我们获取默认镜像的偏移基址,可以直接用下面方式:

_dyld_get_image_vmaddr_slide(0);

如果要获取指定镜像的偏移基址,就需要遍历加载的所有镜像,找到我们要获取的镜像:

intptr_t get_image_vmaddr_slide(const char * image_name) {
    uint32_t count = _dyld_image_count();
    for (int i = 0; i < count; i++) {
        const char *path = _dyld_get_image_name(i);
        const char *name = strrchr(path, '/');
        // printf("name = %s, path = %s", name, path);
        if (name != NULL && strcmp(image_name, name) == 0) {
            return _dyld_get_image_vmaddr_slide(i);
        }
    }
    return -1;
}

但是很可能我们编写的 Tweak 的 dylib 会比我们想要注入的 Framework 加载更早,所以需要稍作处理:

static void didFinishLaunching(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef info) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        intptr_t base_addr = get_image_vmaddr_slide("/UnityFramework");
        printf("didFinishLaunching-base_addr = 0x%lx", base_addr);
    });
}

void launchEvent() {
    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, &didFinishLaunching, (CFStringRef)UIApplicationDidFinishLaunchingNotification, NULL, CFNotificationSuspensionBehaviorDrop);
}

__attribute__((constructor)) static void initialize() {
    launchEvent();
}

完整代码

#import <substrate.h>
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <string.h>
#import <stdio.h>
#import <UIKit/UIKit.h>

int (*old_get_Gem)();
int new_get_Gem() {
    return 99999;
}

int (*old_get_Coin)();
int new_get_Coin() {
    return 300000000;
}

// 根据镜像名称获取镜像的偏移基址
intptr_t get_image_vmaddr_slide(const char * image_name) {
    uint32_t count = _dyld_image_count();
    for (int i = 0; i < count; i++) {
        const char *path = _dyld_get_image_name(i);
        const char *name = strrchr(path, '/');
        // printf("name = %s, path = %s", name, path);
        if (name != NULL && strcmp(image_name, name) == 0) {
            return _dyld_get_image_vmaddr_slide(i);
        }
    }
    return -1;
}

static void didFinishLaunching(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef info) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        intptr_t base_addr = get_image_vmaddr_slide("/UnityFramework");
        printf("didFinishLaunching-base_addr = 0x%lx", base_addr);
        MSHookFunction((void *)(base_addr + 0x1E15468), (void *)&new_get_Gem, (void **)&old_get_Gem);
        MSHookFunction((void *)(base_addr + 0x1E1545C), (void *)&new_get_Coin, (void **)&old_get_Coin);
    });
}

void launchEvent() {
    CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), NULL, &didFinishLaunching, (CFStringRef)UIApplicationDidFinishLaunchingNotification, NULL, CFNotificationSuspensionBehaviorDrop);
}

__attribute__((constructor)) static void initialize() {
    launchEvent();
}