首页
社区
课程
招聘
[原创]内存解析libmono函数地址
2021-2-2 18:12 9860

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

2021-2-2 18:12
9860
  • 简述

    前面文章搞了搞libil2cpp.so用来批量hook函数确实方便
    https://bbs.pediy.com/thread-264813.htm
    https://github.com/axhlzy/Il2CppHookScripts/tree/master/MonoHook
    这里又想着搞搞libmono.so,和上文相同的思路完成对dll function的hook及其参数获取,返回值修改等等操作

  • 大概思路就分为四部分

  1. 找到MonoImage,找到MonoClass(mono_image_loaded , mono_class_from_name)
  2. 找到函数Method的对象指针(mono_class_get_method_from_name)
  3. 能去主动调用函数(mono_runtime_invoke)
  4. 能去拦截函数的调用并获取和修改参数及其返回值(mono_compile_method)

  • 找到MonoImage

    启动时候HookDlopen,并在加载libmono.so的时候对导出函数 mono_image_open_from_data_with_name 进行Hook 拿到加载进来的dll的image对象,返回值就是一个_MonoImage结构体指针


    关注上图中的
    *raw_data dll在内存中的起始地址
    raw_data_len dll的长度
    即可写出,记录下arr_imgs_addr 和 arr_imgs_name

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /**
    * 启动时候Hook mono_image_open_from_data_with_name 记录dll的起始位置,名称
    */
    function HookMono() {
      //MonoImage *mono_image_open_from_data_with_name (char *data, uint32_t data_len, mono_bool need_copy,MonoImageOpenStatus *status, mono_bool refonly, const char *name);
      Interceptor.attach(Module.findExportByName(soName,"mono_image_open_from_data_with_name"), {
          onEnter: function (args) {
              this.name = args[5].readCString()
              this.size = args[1].toInt32()
          },
          onLeave: function (ret) {
              var t_ret = ret
              LOG("[*] "+t_ret + "\t"+this.name,LogColor.C36)
              var t_arr = this.name.split("/")
              var t_name = t_arr[(t_arr.length)-1].split(".dll")[0]
              arr_imgs_addr.push(String(t_ret))
              arr_imgs_name.push(t_name)
          }
      });
    }

    以上为启动的时候就进行Hook可以拿到Image结构体,不在启动的时候Hook同样也可以拿到
    使用导出函数中的 mono_image_loaded 即可

    1
    MonoImage    *mono_image_loaded   (const char *name);

    对于如何拿到method对象的话,我们还是冲导出函数入手,使用IDA查看一下导出函数,看看关键字method,可以看到这些函数,选几个长得像那么回事的函数去源码搜搜


    搜了一下源码得到一下声明

    1
    2
    3
    4
    MonoMethod *mono_get_method (MonoImage *image, uint32_t token, MonoClass *klass);
    MonoMethod *mono_class_get_method_from_name (MonoClass *klass, const char *name, int param_count);
    MonoMethod *mono_class_get_method_from_name_flags (MonoClass *klass, const char *name, int param_count, int flags);
    MonoMethod *mono_class_get_methods       (MonoClass* klass, void **iter);

    第一个token我们不好找,放弃
    第二个flags也不好找,放弃
    第四个显然就是一个通过class来遍历method的方法,可用但是没必要


  • 找到 MonoClass

    所以看参数我们要去找 MonoClass,那继续去找导出函数


    于是找到了 mono_class_from_nam() 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//函数声明
MonoClass *mono_class_from_name       (MonoImage *image, const char* name_space, const char *name);
 
struct _MonoMethod {
    guint16 flags;  /* method flags */
    guint16 iflags; /* method implementation flags */
    guint32 token;
    MonoClass *klass; /* To what class does this method belong */
    MonoMethodSignature *signature;
    /* name is useful mostly for debugging */
    const char *name;
    .....
}
 
struct _MonoClass {
    MonoClass *element_class;
    MonoClass *cast_class;
    MonoClass **supertypes;
    guint16     idepth;
    guint8     rank;         
    int        instance_size;
    ......
    MonoImage *image;
    const char *name;
    const char *name_space;
    ......
}
 
struct _MonoMethodSignature {
    MonoType     *ret;
#ifdef MONO_SMALL_CONFIG
    guint8        param_count;
    gint8         sentinelpos;
    unsigned int  generic_param_count : 5;
#else
    guint16       param_count;
    gint16        sentinelpos;
    unsigned int  generic_param_count : 16;
#endif
    unsigned int  call_convention     : 6;
    unsigned int  hasthis             : 1;
    unsigned int  explicit_this       : 1;
    unsigned int  pinvoke             : 1;
    unsigned int  is_inflated         : 1;
    unsigned int  has_type_parameters : 1;
    MonoType     *params [MONO_ZERO_LEN_ARRAY];
};

  • 找到函数Method

    由此我们需要的参数也都筹齐了,写出一下hook代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    /**
    * 获得 MonoMethod
    * getMethod("UnityEngine","UnityEngine","Application","get_identifier",0)
    * getMethod("UnityEngine",'UnityEngine','Debug',"LogError",1,true)
    * @param {String}  imageName
    * @param {String}  NameSpace
    * @param {String}  className
    * @param {String}  FunctName
    * @param {Int}     argsCount
    * @param {Boolean} showDetail
    */
    function getMethod(imageName,NameSpace,className,FunctName,argsCount,showDetail){
      if (imageName==undefined ||NameSpace==undefined||className==undefined||FunctName==undefined||argsCount==undefined) return ptr(0)
      if (showDetail == undefined) showDetail = false
      // var mono_thread_attach = new NativeFunction(Module.findExportByName(soName,"mono_thread_attach"),'pointer',['pointer'])
      // var mono_get_root_domain = new NativeFunction(Module.findExportByName(soName,"mono_get_root_domain"),'pointer',[])
      var mono_class_from_name = new NativeFunction(Module.findExportByName(soName,"mono_class_from_name"),'pointer',['pointer','pointer','pointer'])
      var mono_class_get_method_from_name = new NativeFunction(Module.findExportByName(soName,"mono_class_get_method_from_name"),'pointer',['pointer','pointer','int'])
      var mono_image_loaded = new NativeFunction(Module.findExportByName(soName,"mono_image_loaded"),'pointer',['pointer'])
     
      // mono_thread_attach(mono_get_root_domain())
     
      var p_image = getImageByName(imageName) == 0 ? mono_image_loaded(allcStr(imageName)) : getImageByName(imageName)
      var p_class = mono_class_from_name(ptr(p_image),allcStr(NameSpace),allcStr(className))
      var p_method = mono_class_get_method_from_name(p_class,allcStr(FunctName),argsCount)
     
      if (!showDetail) return ptr(p_method)
     
      LOG(getLine(85),LogColor.C33)
      LOG("MonoImage\t---->\t"+p_image,LogColor.C36)
      LOG("MonoClass\t---->\t"+p_class,LogColor.C36)
      LOG("MonoMethod\t---->\t"+p_method + " ("+ReadMethodName(p_method)+")",LogColor.C36)
      LOG(getLine(85),LogColor.C33)
    }

    运行见效果:


  • 主动调用函数

    简单的看了一下导出函数,发现有一个 mono_runtime_invoke 传参就是 MonoMethod
    于是可以尝试主动调用

    1
    2
    3
    4
    5
    6
    7
    8
    //MonoObject* mono_runtime_invoke (MonoMethod *method, void *obj, void **params,MonoObject **exc);
    var mono_runtime_invoke = new NativeFunction(Module.findExportByName(soName,"mono_runtime_invoke"),'pointer',['pointer','pointer','pointer','pointer'])
    function invoke(monoMethod, obj, params,monoObject){
      obj = obj == undefined ? NULL : obj
      params = params == undefined ? NULL : params
      monoObject = monoObject == undefined ? NULL : monoObject
      return mono_runtime_invoke(ptr(monoMethod),obj,params,monoObject)
    }

    这里选了一个get_identifier 来主动调用,可以发现没问题
    (但是这里仅限于不带参数的Static方法)


    看着这个mono_runtime_invoke(MonoMethod method, void obj, void **params,MonoObject **exc)的声明参数就不想看它了,有更简单的方法,这里就简述一下这个简单的方法
    我们换一个思路,总所周知,dll是被加载进去内存在动态编译的,这里我们手动调用mono_compile_method来编译这个MonoMethod即可得到类似于il2cpp中的MethodInfo的函数指针,详见下图


  • 能去拦截函数的调用并获取和修改参数及其返回值

    最后这部分也没啥好说的了,函数地址都有了,剩下的就是交给各位大佬为所欲为了
1
2
3
4
5
6
7
8
9
10
11
12
13
function TestAttach(){
    HookMonoMethod("UnityEngine",'UnityEngine','Debug',"LogError",1,{
        onEnter:function(args){
            LOG(readU16(args[0]))
        },
        onLeave:function(ret){}
    })
}
 
function HookMonoMethod(imageName,NameSpace,className,FunctName,argsCount,callbacks) {
    var mPtr = mono_compile_method(getMethod(imageName,NameSpace,className,FunctName,argsCount))
    Interceptor.attach(ptr(mPtr),callbacks)
}

补充:

https://github.com/axhlzy/Il2CppHookScripts/tree/master/MonoHook


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-5-31 17:24 被唱过阡陌编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (3)
雪    币: 107
活跃值: (3580)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Imxz 2021-2-2 23:37
2
0
nice 兄弟
雪    币: 3964
活跃值: (2043)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
Umiade 2021-2-3 15:34
3
0
思路很清晰,支持一下
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_vnkkwevd 2022-10-25 16:14
4
0
如果是x86模拟器,运行arm架构,会找不到libil2cpp.so库,怎么解决
游客
登录 | 注册 方可回帖
返回