去年在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
完全没有上下文的问题你想让我怎么回答…