去年在Perfare大佬的安利下试了下用Riru来Hook奇奇怪怪的游戏,结果发现效果还行,既不需要挂IDA调试卡半天,也不需要绕过反调试或者脱壳(没错,说的就是你,辣鸡CrackProof),只需要先dump一下内存,把函数地址拿出来hook就好了,但是后面发现每次版本更新都要改一次地址有点麻烦,于是Perfare大佬指引了个使用Il2cpp的导出函数获取地址的姿势。

要调il2cpp里面的导出函数的话,首先需要dlsym并且拿到so的handle。但是这个so不是我们打开的,所以首先想到的是从linker找到solist这个变量,然后一路solist->next就能拿到libil2cpp的soinfo。于是找了个轮子改了下。

    char osVersion[PROP_VALUE_MAX+1];
    int osVersionLength = __system_property_get("ro.build.version.release", osVersion);
    int osVersionInt = atoi(osVersion);
    LOGI("osVersion: %d", osVersionInt);
    const char* linkerName = NULL;
    if(osVersionInt >= 10){
#if defined(__arm__) || defined(__i386__)
        linkerName = "/apex/com.android.runtime/bin/linker";
#elif defined(__aarch64__) || defined(__x86_64__)
        linkerName = "/apex/com.android.runtime/bin/linker64";
#endif
    }else{
#if defined(__arm__) || defined(__i386__)
        linkerName = "/system/bin/linker";
#elif defined(__aarch64__) || defined(__x86_64__)
        linkerName = "/system/bin/linker64";
#endif
    }
    void* handle = enhanced_dlopen(linkerName, RTLD_LAZY);
    if(handle == nullptr){
        LOGE("cannot open linker");
        return nullptr;
    }
    soinfo* solist = *(soinfo**)enhanced_dlsym(handle, "__dl__ZL6solist");
    while(soinfo* i = solist->next){
        LOGD("so name: %s", i->soname_);
    }

熟练地编译安装重启运行,结果不出所料,程序崩了。看了下backtrace,发现是读*solist的时候就直接丢了个SIGSEGV。但是换成Android 7却没有问题,看起来是Google限制了Android 10上调用这个变量。

于是大佬又建议我直接hook do_dlopen,结果Hook倒没问题,结果Hook上了以后啥反应都没有。最后按照EdXposed的做法,换成__loader_dlopen,就好了。代码也很简单,直接dlopen然后dlsym即可。

    void *dl = dlopen("libdl.so", RTLD_LAZY);
    void* __loader_dlopen = dlsym(dl, "__loader_dlopen");
    hook_each((unsigned long)__loader_dlopen, (void*)dlopen_, (void**)&dlopen_backup);

其中几个函数定义如下,WInlineHookFunction来自Whale

typedef void* (*dlopen_type)(const char* name,
                             int flags,
                             //const void* extinfo,
                             const void* caller_addr);
dlopen_type dlopen_backup = nullptr;
void* dlopen_(const char* name,
              int flags,
              //const void* extinfo,
              const void* caller_addr){

    void* handle = dlopen_backup(name, flags, /*extinfo,*/ caller_addr);
    if(!il2cpp_handle){
        LOGI("dlopen: %s", name);
        if(strstr(name, "libil2cpp.so")){
            il2cpp_handle = handle;
            LOGI("Got il2cpp handle at %lx", (long)il2cpp_handle);
        }
    }
    return handle;
}
void hook_each(unsigned long rel_addr, void* hook, void** backup){
    LOGI("Installing hook at %lx", rel_addr);
    unsigned long addr = /*base_addr + */rel_addr;

    //设置属性可写
    void* page_start = (void*)(addr - addr % PAGE_SIZE);
    if (-1 == mprotect(page_start, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC)) {
        LOGE("mprotect failed(%d)", errno);
        return ;
    }

    WInlineHookFunction(
            reinterpret_cast<void*>(addr),
            hook,
            backup);
    mprotect(page_start, PAGE_SIZE, PROT_READ | PROT_EXEC);
}

拿到il2cpp的handle以后就简单了,只需要调用libil2cpp的API即可

il2cpp_domain_get_ il2cpp_domain_get = (il2cpp_domain_get_)dlsym(il2cpp_handle, "il2cpp_domain_get");
il2cpp_domain_get_assemblies_ il2cpp_domain_get_assemblies = (il2cpp_domain_get_assemblies_)dlsym(il2cpp_handle, "il2cpp_domain_get_assemblies");
il2cpp_assembly_get_image_ il2cpp_assembly_get_image = (il2cpp_assembly_get_image_)dlsym(il2cpp_handle, "il2cpp_assembly_get_image");
il2cpp_class_from_name_ il2cpp_class_from_name = (il2cpp_class_from_name_)dlsym(il2cpp_handle, "il2cpp_class_from_name");
il2cpp_class_get_method_from_name_ il2cpp_class_get_method_from_name = (il2cpp_class_get_method_from_name_)dlsym(il2cpp_handle, "il2cpp_class_get_method_from_name");
sleep(2);
LOGD("hack game begin");
Il2CppDomain* domain = il2cpp_domain_get();
unsigned long ass_len = 0;
const Il2CppAssembly** assembly_list = il2cpp_domain_get_assemblies(domain, &ass_len);
while(strcmp((*assembly_list)->aname.name, "Assembly-CSharp") != 0){
    LOGD("Assembly name: %s", (*assembly_list)->aname.name);
    assembly_list++;
}
const Il2CppImage* image = il2cpp_assembly_get_image(*assembly_list);
Il2CppClass* clazz = il2cpp_class_from_name(image, "Namespace", "Classname");

hook_each((unsigned long)il2cpp_class_get_method_from_name(clazz, "Your Method", 1)->methodPointer, (void*)hook, (void**)&backup);

本文中的代码都可以在这里找到

分类: 逆向

2 条评论

kkcc · 2021年9月1日 上午10:49

大佬用了你这种写法 为什么游戏会闪退?

    双草酸酯 · 2021年9月2日 上午10:00

    完全没有上下文的问题你想让我怎么回答…

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注