首页
社区
课程
招聘
[原创]内存解析Il2cpp函数地址
发表于: 2020-12-29 22:07 23327

[原创]内存解析Il2cpp函数地址

2020-12-29 22:07
23327

使用函数名拿到U3D非导出函数地址

网上也有很多类似的相关文章
比如 [unity]Real-time Dump一套强劲的 Il2cpp Hook 框架IL2CPP的原理
都可以去参照 去细品

这里我讲讲我用到的东西
分析的入口点从加载 global-metadata.dat 入手
这是一个非常标志性的入口,不管是在源码还是在IDA中都很容易的定位
找到入口位置
IDA中从字符串窗口搜索该关键词也可以轻松的定位到一下代码

源码中的入口
IDA中F5也是随意的改改名字,改改结构体就差不多一个样了

记下未初始化的段的两个地址
两个关键地址
在源码中让后看,可以看下面就开始对此处分配出来的内存填数据了,后面我们用的时候也是无需但是未初始化的问题

这里的s_ImagesTable是一个指向一个 Il2CppImage 列表的开头,
sizeof(Il2CppImage) = 52,可以去头文件查看

这一步我们关注的只有 const char *nameNoExt 以及当前位置的指针位置由此写出

运行一下就可以拿到一下内容
列出Images
这些玩意其实就是我们用Il2CppDumper拿到的哪些dll

使用命名空间,类名,方法名,参数个数拿到我们需要的函数 内存地址以及IDA中静态分析的地址

下面介绍两个主角函数(这里引入的两个函数都是libil2cpp.so的默认导出函数)
位置:D:\Program Files\Unity\Editor\Data\il2cpp\libil2cpp\il2cpp-api.cpp

由此可以写出以下代码进行主动调用

实现通过名称拿到函数地址

静态dumper出来的结果

看到此处的话目的算是勉强达到了,但是还有待优化把,比如一开始的

这两个位置需要我们手动IDA去找

从上述结果中确实看到了我们可以成功的调用函数拿到函数的返回值,但是仅限于我们已知Il2CppImage*的时候有效,也就是仅仅在我们list_Images中列出的地址对应的子函数可以这么操作(使用il2cpp_class_from_name()获取对应的Il2CppClass),所以我们为了通用得考虑新的思路

两个关键结构体:Il2CppImage 和 Il2CppAssembly
两个关键结构体

从list_images中取地址

查看一下image的内存情况

前两个地址对应的是name和nameNoExt
可以看到第三个指针指向的地址的第一个区域由指回来了,就是咋们的Il2CppImage*

结合上图

上面这张图不是重点,重点在上上张图第二三个圈起来的部分,也就是
TypeDefinitionIndex typeStart;
uint32_t typeCount;
这两位,分别记录了偏移位置和方法个数,偏移位置的开始在(还是去参考IDA拿到地址,同样也是bss段的一个指针)

s_MethodInfoDefinitionTable

根据上述SeeHexA(0xcf80ec00)中可知偏移0x0,方法个数0x5c2(即1474个方法),接下来就可以写出以下demo

效果如下
运行结果

上述故意方法数多加一,看到最后空命名空间和<Module>基本就稳了,就是这意思没猜错

能拓展的东西就挺多的,这就涉及最初起点了

Il2CppDumperTool 脱离 Il2CppDumper 以及python脚本更易用

搞一些通用的API Hook,比如

以上展示只是一个简单的分析,其实就上述这样东西还是非常不好用,上述的addr并不能找到所有的方法及其地址,后续就想着让他更加智能一点,就尝试手动去解析一下结构体,然后就得到了一下效果
找关键位置

首先是针对我们需要的三个参数的自动解析,主要涉及到ldr寻址

findins

这里为了实现找到我们需要的参数,主要是通过导出函数il2cpp_init的地址往下找找到bl进去然后再找到ldr,并解析到ldr加载的值,如果出现了“mscorlib.dll”,说明我们找到的bl是正确的,然后我们同理找下层的“global-metadata.dat”即可定位到函数 bool il2cpp::vm::MetadataCache::Initialize(),找到上述位置后,我们稍微看一下就可以发现特征 IL2CPP_CALLOC 函数,老规矩通过这个bl指令去解析到函数地址,记录所有调用的该函数的当前地址放进数组,接下来就是 通过数组访问到 [1] [4] [5] 位置就是我们需要的三个初始参数,从当前位置往上解析它,第一条 mov r0,#xx 的立即数就是我们要找的,好了介绍到这里,上效果:

找参数

这种找法效果还行,能解决大部分问题,也不能解决全部问题,总有些奇葩adr之类的东西,这些就自己手动IDA查看了

然后就是挨着列出
列出namespace和class

列出method

也可以解析的更加详细(其实还可以继续深入解析出方法类型返回值类型的,暂时没有继续深入)
详细method

用新的方法来解析找函数就不存在解析不到函数的问题了(随意解析这个so里面的任意函数)

解析method

随便举个例:断点 public extern void SetActive(GameObject obj,bool value);
1.有了gameObject就可以拿到transform,以及他的名字,再遍历拿到父级名字,整个调用链你都清晰了


2.有了gameObject用以上的函数API可以随意的移动,显示隐藏 。。。。
......

 
 
 
 
typedef struct Il2CppImage
{
    const char* name;
    const char *nameNoExt;
    Il2CppAssembly* assembly;
 
    TypeDefinitionIndex typeStart;
    uint32_t typeCount;
 
    TypeDefinitionIndex exportedTypeStart;
    uint32_t exportedTypeCount;
 
    CustomAttributeIndex customAttributeStart;
    uint32_t customAttributeCount;
 
    MethodIndex entryPointIndex;
 
#ifdef __cplusplus
    mutable
#endif
    Il2CppNameToTypeDefinitionIndexHashTable * nameToClassHashTable;
 
    const Il2CppCodeGenModule* codeGenModule;
 
    uint32_t token;
    uint8_t dynamic;
} Il2CppImage;
typedef struct Il2CppImage
{
    const char* name;
    const char *nameNoExt;
    Il2CppAssembly* assembly;
 
    TypeDefinitionIndex typeStart;
    uint32_t typeCount;
 
    TypeDefinitionIndex exportedTypeStart;
    uint32_t exportedTypeCount;
 
    CustomAttributeIndex customAttributeStart;
    uint32_t customAttributeCount;
 
    MethodIndex entryPointIndex;
 
#ifdef __cplusplus
    mutable
#endif
    Il2CppNameToTypeDefinitionIndexHashTable * nameToClassHashTable;
 
    const Il2CppCodeGenModule* codeGenModule;
 
    uint32_t token;
    uint8_t dynamic;
} Il2CppImage;
var s_ImagesTable = soAddr.add(0x2DF9134).readPointer()
var s_ImagesCount = soAddr.add(0x2DF9130).readPointer().toInt32()
function list_Images(keywords){
    LOG("-------------------------------------------------------------------------------------",LogColor.COLOR_33)
        for(var t=0;t<s_ImagesCount;t++){
            var tt = s_ImagesTable.add(sizeof_Il2CppImage*t)
            var typeCount = tt.add(p_size*4).readPointer().toInt32()
            var name = tt.add(p_size).readPointer().readCString()
            if (keywords != undefined){
                if (name.indexOf(keywords)!=-1){
                    LOG("[*]"+tt+"\t"+name,LogColor.COLOR_36)
                }else{--tmp}
            }else{
                LOG("[*]"+tt+"\t"+name,LogColor.COLOR_36)
            }
            current_off = current_off.add(typeCount*p_size)
        }
        LOG("----------------------------",LogColor.COLOR_33)
        LOG("  List "+(keywords==undefined?tmp:--tmp)+" Images",LogColor.RED)
        LOG("-------------------------------------------------------------------------------------",LogColor.COLOR_33)
}
var s_ImagesTable = soAddr.add(0x2DF9134).readPointer()
var s_ImagesCount = soAddr.add(0x2DF9130).readPointer().toInt32()
function list_Images(keywords){
    LOG("-------------------------------------------------------------------------------------",LogColor.COLOR_33)
        for(var t=0;t<s_ImagesCount;t++){
            var tt = s_ImagesTable.add(sizeof_Il2CppImage*t)
            var typeCount = tt.add(p_size*4).readPointer().toInt32()
            var name = tt.add(p_size).readPointer().readCString()
            if (keywords != undefined){
                if (name.indexOf(keywords)!=-1){
                    LOG("[*]"+tt+"\t"+name,LogColor.COLOR_36)
                }else{--tmp}
            }else{
                LOG("[*]"+tt+"\t"+name,LogColor.COLOR_36)
            }
            current_off = current_off.add(typeCount*p_size)
        }
        LOG("----------------------------",LogColor.COLOR_33)
        LOG("  List "+(keywords==undefined?tmp:--tmp)+" Images",LogColor.RED)
        LOG("-------------------------------------------------------------------------------------",LogColor.COLOR_33)
}
 
 
function findAddrByName(namespaze){
    for(var t=0;t<s_ImagesCount;t++){
        var t_addr = s_ImagesTable.add(52*t)
        var t_name = s_ImagesTable.add(52*t).add(p_size).readPointer().readCString()
        if (t_name == namespaze) return ptr(t_addr)
    }
    return ptr(0x0)
}
 
function showAddr(namespaze,className,functionName,argsCount){
    Interceptor.detachAll()
    Il2CppImage = findAddrByName(namespaze)
    if (Il2CppImage == 0) {
        console.warn("Il2CppImage addr not found!")
        return
    }
    console.warn("---------------------------------------------------------")
    console.error(namespaze+"."+className+"."+functionName)
    console.warn("----------------------------")
    console.log("Il2CppImage\t---->\t "+Il2CppImage)
 
    //Il2CppClass* il2cpp_class_from_name(const Il2CppImage* image, const char* namespaze, const char *name)
    var il2cpp_class_from_name = new NativeFunction(
        Module.findExportByName(soName,"il2cpp_class_from_name"),
        'pointer',['pointer','pointer','pointer'])
 
    //const MethodInfo* il2cpp_class_get_method_from_name(Il2CppClass *klass, const char* name, int argsCount)
    var il2cpp_class_get_method_from_name = new NativeFunction(
        Module.findExportByName(soName,"il2cpp_class_get_method_from_name"),
        'pointer',['pointer','pointer','int'])
 
    var namespaze_t = Memory.allocUtf8String(namespaze)
    var className_t = Memory.allocUtf8String(className)
    var functionName_t = Memory.allocUtf8String(functionName)
 
    var Il2CppClass = il2cpp_class_from_name(Il2CppImage,namespaze_t,className_t)
    console.log("Il2CppClass\t---->\t",Il2CppClass)
    var MethodInfo = il2cpp_class_get_method_from_name(Il2CppClass,functionName_t,argsCount)
    console.log("MethodInfo\t---->\t",MethodInfo)
    console.log("\x1b[36mmethodPointer\t---->\t "+MethodInfo.readPointer() +"\t ===> \t"+MethodInfo.readPointer().sub(soAddr)+"\x1b[0m")
    console.warn("---------------------------------------------------------")
}
function findAddrByName(namespaze){
    for(var t=0;t<s_ImagesCount;t++){
        var t_addr = s_ImagesTable.add(52*t)
        var t_name = s_ImagesTable.add(52*t).add(p_size).readPointer().readCString()
        if (t_name == namespaze) return ptr(t_addr)
    }
    return ptr(0x0)
}
 
function showAddr(namespaze,className,functionName,argsCount){
    Interceptor.detachAll()
    Il2CppImage = findAddrByName(namespaze)
    if (Il2CppImage == 0) {
        console.warn("Il2CppImage addr not found!")
        return
    }
    console.warn("---------------------------------------------------------")
    console.error(namespaze+"."+className+"."+functionName)
    console.warn("----------------------------")
    console.log("Il2CppImage\t---->\t "+Il2CppImage)
 
    //Il2CppClass* il2cpp_class_from_name(const Il2CppImage* image, const char* namespaze, const char *name)
    var il2cpp_class_from_name = new NativeFunction(
        Module.findExportByName(soName,"il2cpp_class_from_name"),
        'pointer',['pointer','pointer','pointer'])
 
    //const MethodInfo* il2cpp_class_get_method_from_name(Il2CppClass *klass, const char* name, int argsCount)
    var il2cpp_class_get_method_from_name = new NativeFunction(
        Module.findExportByName(soName,"il2cpp_class_get_method_from_name"),
        'pointer',['pointer','pointer','int'])
 
    var namespaze_t = Memory.allocUtf8String(namespaze)
    var className_t = Memory.allocUtf8String(className)
    var functionName_t = Memory.allocUtf8String(functionName)
 
    var Il2CppClass = il2cpp_class_from_name(Il2CppImage,namespaze_t,className_t)
    console.log("Il2CppClass\t---->\t",Il2CppClass)
    var MethodInfo = il2cpp_class_get_method_from_name(Il2CppClass,functionName_t,argsCount)
    console.log("MethodInfo\t---->\t",MethodInfo)
    console.log("\x1b[36mmethodPointer\t---->\t "+MethodInfo.readPointer() +"\t ===> \t"+MethodInfo.readPointer().sub(soAddr)+"\x1b[0m")
    console.warn("---------------------------------------------------------")
}
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2021-5-31 17:22 被唱过阡陌编辑 ,原因: github地址修改
收藏
免费 17
支持
分享
最新回复 (25)
雪    币: 355
活跃值: (430)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
学习                            学习
2020-12-30 08:17
0
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
等 加精
2020-12-30 08:50
0
雪    币: 1137
活跃值: (620)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
np,np
2020-12-30 10:38
0
雪    币: 5855
活跃值: (438)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
5
 来膜拜下大牛,以前都是要用il2cppdumper获得偏移,每次app更新就得更新偏移,这下可以自动支持新版了
2020-12-30 18:39
0
雪    币: 199
活跃值: (93)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
np,学习了,好地方
2021-1-1 21:05
0
雪    币: 30
活跃值: (1322)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
好东西,刚想自己实现个,你就放出来了。真的好兴奋啊。
2021-1-4 14:31
0
雪    币: 1422
活跃值: (1573)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
NightGuard 来膜拜下大牛,以前都是要用il2cppdumper获得偏移,每次app更新就得更新偏移,这下可以自动支持新版了
https://github.com/HimekoEx/cxk 了解一下?
2021-1-10 19:07
1
雪    币: 21
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
Il2CppDumper的作者有写过用il2cpp自带的api+反射遍历的方法
https://github.com/Perfare/Riru-Il2CppDumper/blob/master/module/src/main/cpp/il2cpp.cpp#L316
2021-1-13 03:00
1
雪    币: 1441
活跃值: (5640)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
10
 

第一版是参考他的代码写出来的

第二版使用的 frida-il2cpp-bridge 做底层

最后于 2022-9-2 18:24 被唱过阡陌编辑 ,原因:
2021-1-13 08:57
0
雪    币: 2484
活跃值: (2968)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
11
支持一下
2022-7-19 15:16
0
雪    币: 17
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12

这是啥问题 

2022-7-20 23:30
0
雪    币: 1441
活跃值: (5640)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
13
wx_H_535 这是啥问题&nbsp;
line: 1566  @ c52c986608694cf460161da111979476be135afe  

var MethodInfoOffset = 0x0
改成:
var MethodInfoOffset = 0x1
再试试
2021.2.7f1c1 版本 MethodInfo  的结构体有变化
2022-7-21 09:47
0
雪    币: 226
活跃值: (2308)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
14
大佬,请教下frida要怎么读取和构造il2cpp里面的string类型的字符串哈
2022-8-30 16:34
0
雪    币: 1441
活跃值: (5640)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
15
wx_嗨 大佬,请教下frida要怎么读取和构造il2cpp里面的string类型的字符串哈

构造使用:il2cpp的导出函数 il2cpp_string_new
读取使用: mPtr.add(p_size * 2 + 4).readUtf16String()

参考:https://github.com/FateHack/MonoString


https://github.com/axhlzy/Il2CppHookScripts 中的 readU16() 以及 allocUStr()

最后于 2022-8-30 21:01 被唱过阡陌编辑 ,原因:
2022-8-30 20:58
0
雪    币: 226
活跃值: (2308)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
16
唱过阡陌 wx_嗨 大佬,请教下frida要怎么读取和构造il2cpp里面的string类型的字符串哈 构造使用:il2cpp的导出函数&nbsp ...
好嘞,谢谢,在代码里找到看明白了
2022-8-31 09:01
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
前来膜拜
2022-11-30 22:07
0
雪    币: 1
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
大佬,某个方法必须由实例去调用,我用 FindObjectsOfType  找到对象了,但是该如何让这个对象调用方法呢
2022-12-1 15:05
0
雪    币: 1441
活跃值: (5640)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
19
wx_水山 大佬,某个方法必须由实例去调用,我用 FindObjectsOfType 找到对象了,但是该如何让这个对象调用方法呢
callfunction
静态方法 第一个参数就是你的第一个参数
实例方法 第一个参数是对象实例,第二个参数是你的真实第一个参数
2022-12-2 14:47
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
大佬,加固后的游戏,findModuleByName("libil2cpp.so")  返回的是null,我看了mmaps里面,有多个libil2cpp.so。这种情况应该怎么办呢
2023-2-1 16:30
0
雪    币: 1441
活跃值: (5640)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
21
水丶山 大佬,加固后的游戏,findModuleByName("libil2cpp.so") 返回的是null,我看了mmaps里面,有多个libil2cpp.so。这种情况应该怎么办呢
demo 瞅瞅 ?
2023-2-1 17:43
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
22
https://ddchess-dl.dragonest.com/apks/2.16.2/autochess_ly_2162.apk    


运行之后,资源还要下载3个G,建议不要登录去下。就在的登录界面看看就行。
2023-2-1 18:39
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
对了   这个游戏还有反ptrace  调试,直接附加会失败,需要用这个评论里面的方式去启动 frida 
   
https://bbs.kanxue.com/thread-263701.htm
2023-2-1 18:42
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
唱过阡陌 demo 瞅瞅 ?
大佬,有戏么
2023-2-3 09:49
0
雪    币: 140
活跃值: (451)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
膜拜大佬   求大佬带我飞
2023-6-16 11:55
0
游客
登录 | 注册 方可回帖
返回
//