首页
社区
课程
招聘
[原创]U3D逆向-Mono另类解密
发表于: 2021-5-26 17:24 22882

[原创]U3D逆向-Mono另类解密

2021-5-26 17:24
22882

        


        很久不来看雪了,这段时间过得真的是一言难尽。许久的发呆后决定干点有意义的事,于是乎就顺手来写一篇技术文章,算是本年的打卡。

        如题所示,U3D的Mono解密,这个玩意我昨晚在我的技术群里貌似看到说U3D是要弃用了Mono这个机制,也不知道是真是假,趁着还没弃用,我也就来说说这个机制的一个另类解密。

       关于Mono这东西我就不啰嗦了,我就直接进入正题。众所周知,Mono的加密主要是针对 Assembly-CSharp.dll,此DLL包含了游戏的所有功能性函数,并且可以通过工具dnSpy.exe加载后进行查看。


     

    

此DLL公开等于说源码公开,可以通过C#工程引入该DLL自写一个GameObject注入到游戏里调用游戏自带函数实现作弊。

    大多的加密手段就是对这DLL进行二进制处理,也就是把文件字节给进行处理。对于这种加密的处理方式很简单,Mono.dll是U3D用来初始化并加载dll的一个模块,他里面有一个函数mono_image_open_from_data_with_name这里放一下他的函数代码

        看不懂没关系,我们只需要关注他的data,data_len,name这三个参数,这三个分别表示当前被加载模块的二进制内容,二进制长度,模块名。大部分游戏厂商都会在这里判断模块名是否为Assembly-CSharp,然后进行二进制内容解密。那么只需要用调试工具在这个函数下段,然后在这里分析结束的位置,然后直接dump即可。这里放一下大概代码

        直接在解密完毕的位置下断,然后把rdi给dump出来就行,得到的就是解密后的dll。也可以自写脚本。

____________________________________________________________________________________________________________________________________________________________


上面介绍了Assembly-CSharp的一种加密和解密方式,虽说是加密,但是非常下饭,基本有手就行。今天就来说说另一种加密方式。小白鼠是国内某款游戏,现在应该应该不开放了,但是游戏依旧躺在我硬盘里。该游戏同样是加密Assembly-CSharp,但是有个不同点就是该文件只有1kb,这个就很可疑,因为这等于说这文件是空的,用十六进制打开文件也能看出里面无内容,那么游戏很可能是联网获取新的,或者是内存释放。那么除了去分析后解密,就没有办法拿到解密后的文件吗,答案是否定的。这个时候就需要上点硬技术了。

    回到Mono的源码,我们需要去分析一下他的一个流程。这里我就不去分析了,有一个帖子已经说得很详细。[原创]关于Unity游戏Mono注入型外挂的一个检测思路-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com

    大家可以看看这个帖子,主要是去熟悉一下mono的一个流程。我也直接请出主角。

这里可以清楚的看到,U3D直接把需要的加载的模块插入了一个HashTable,然后完成模块的一个装载。这里我们先不管这个模块进行了如何加密和解密,既然你要扔给U3D托管,那么你不可能扔了一个加密模块的给U3D托管,除非你的U3D引擎进行了一个大规模的魔改。所以说,我们只需要找到这个HashTable,然后去遍历一下里面的模块,我们是不是就能拿到解密后的Assembly-CSharp,答案是肯定的。那么现在重点对象从 如何解密Assembly-CSharp变成了 如何使用loaded_images

    我们应该如何去找到这个loaded_images?没办法,上分析。调试器打开游戏跳到mono_image_open_from_data_with_name这个函数然后对照源码进行分析。

         从源码得知,register_image是最后一个调用的函数,我们直接找到最后一个函数,进去后分析得知。


    拿到loaded_images后,我们下一步就是要去遍历这个HashTabel,两个办法,一个是自己去搭建GHashTable库,然后自己去跑一遍。第二个办法,自己去分析g_hash_table_insert函数的流程。我选择后者 =。=

        Ida打开mono.dll后搜索g_hash_table_insert,有一个

        

            进去F5后无脑暴力分析。



        分析了一个大概流程后,打开CE,用数据结构工具分析验证看看




[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 12
支持
分享
最新回复 (18)
雪    币: 401
活跃值: (4121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
牛逼 666
2021-5-26 17:26
0
雪    币: 889
活跃值: (4118)
能力值: ( LV6,RANK:98 )
在线值:
发帖
回帖
粉丝
3
棒。
2021-5-26 17:29
0
雪    币: 40
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
牛逼
2021-5-26 22:59
0
雪    币: 1195
活跃值: (1125)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
牛逼 学习了
2021-5-27 07:37
0
雪    币: 29182
活跃值: (63621)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
6
感谢分享啦~
2021-6-4 15:30
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
你那个塔科夫的游戏是咋弄的啊
2021-6-6 11:27
0
雪    币: 8787
活跃值: (5798)
能力值: ( LV13,RANK:296 )
在线值:
发帖
回帖
粉丝
8
感谢分享
2021-6-6 13:03
0
雪    币: 6221
活跃值: (5660)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
9
感谢分享,收藏了
2021-6-6 18:13
0
雪    币: 4997
活跃值: (2430)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark
2021-6-6 22:31
0
雪    币: 458
活跃值: (1907)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
活捉生死狙击2大手子
现在人家也有注入检测了,注入就封
可以研究下把ACE剥离了
2021-6-7 19:33
0
雪    币: 141
活跃值: (7521)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
12
renbohan [em_13]活捉生死狙击2大手子 现在人家也有注入检测了,注入就封 可以研究下把ACE剥离了
参见大哥
2021-6-7 21:00
0
雪    币: 458
活跃值: (1907)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
13
PlaneJun 参见大哥[em_48]
为什么你的帖子上看雪公众号了,我心里不平衡
2021-6-9 22:41
0
雪    币: 141
活跃值: (7521)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
14
renbohan 为什么你的帖子上看雪公众号了,我心里不平衡[em_11]
可能是我比较可爱吧
2021-6-10 00:31
0
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
大佬能否看看我这个dll
2021-8-25 03:41
0
雪    币: 228
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
能否给个联系方式交流一下 
2022-1-22 14:30
0
雪    币: 141
活跃值: (7521)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
17
2022-3-7 16:20
0
雪    币: 19
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18

看雪论坛-安全社区|安全招聘|bbs.pediy.com

    首页
    课程
    问答
    CTF
    论坛
    招聘
    看雪峰会
    发现

消息

    看雪论坛
    软件逆向

    [原创]U3D逆向-Mono另类解密

    2021-5-26 17:24 11520
        举报 

        


        很久不来看雪了,这段时间过得真的是一言难尽。许久的发呆后决定干点有意义的事,于是乎就顺手来写一篇技术文章,算是本年的打卡。

        如题所示,U3D的Mono解密,这个玩意我昨晚在我的技术群里貌似看到说U3D是要弃用了Mono这个机制,也不知道是真是假,趁着还没弃用,我也就来说说这个机制的一个另类解密。

       关于Mono这东西我就不啰嗦了,我就直接进入正题。众所周知,Mono的加密主要是针对 Assembly-CSharp.dll,此DLL包含了游戏的所有功能性函数,并且可以通过工具dnSpy.exe加载后进行查看。


     

    

此DLL公开等于说源码公开,可以通过C#工程引入该DLL自写一个GameObject注入到游戏里调用游戏自带函数实现作弊。

    大多的加密手段就是对这DLL进行二进制处理,也就是把文件字节给进行处理。对于这种加密的处理方式很简单,Mono.dll是U3D用来初始化并加载dll的一个模块,他里面有一个函数mono_image_open_from_data_with_name,这里放一下他的函数代码

MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)
{
       return mono_image_open_from_data_internal (data, data_len, need_copy, status, refonly, FALSE, name);
}

MonoImage *
mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only, 
                               const char *name)
{
       MonoCLIImageInfo *iinfo;
       MonoImage *image;
       char *datac;

       if (!data || !data_len) {
               if (status)
                       *status = MONO_IMAGE_IMAGE_INVALID;
               return NULL;
       }
       datac = data;
       if (need_copy) {
               datac = (char *)g_try_malloc (data_len);
               if (!datac) {
                       if (status)
                               *status = MONO_IMAGE_ERROR_ERRNO;
                       return NULL;
               }
               memcpy (datac, data, data_len);
       }

       image = g_new0 (MonoImage, 1);
       image->raw_data = datac;
       image->raw_data_len = data_len;
       image->raw_data_allocated = need_copy;
       image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);
       iinfo = g_new0 (MonoCLIImageInfo, 1);
       image->image_info = iinfo;
       image->ref_only = refonly;
       image->metadata_only = metadata_only;
       image->ref_count = 1;

       image = do_mono_image_load (image, status, TRUE, TRUE);
       if (image == NULL)
               return NULL;

       return register_image (image);
}

        看不懂没关系,我们只需要关注他的data,data_len,name这三个参数,这三个分别表示当前被加载模块的二进制内容,二进制长度,模块名。大部分游戏厂商都会在这里判断模块名是否为Assembly-CSharp,然后进行二进制内容解密。那么只需要用调试工具在这个函数下段,然后在这里分析结束的位置,然后直接dump即可。这里放一下大概代码

        直接在解密完毕的位置下断,然后把rdi给dump出来就行,得到的就是解密后的dll。也可以自写脚本。

____________________________________________________________________________________________________________________________________________________________


上面介绍了Assembly-CSharp的一种加密和解密方式,虽说是加密,但是非常下饭,基本有手就行。今天就来说说另一种加密方式。小白鼠是国内某款游戏,现在应该应该不开放了,但是游戏依旧躺在我硬盘里。该游戏同样是加密Assembly-CSharp,但是有个不同点就是该文件只有1kb,这个就很可疑,因为这等于说这文件是空的,用十六进制打开文件也能看出里面无内容,那么游戏很可能是联网获取新的,或者是内存释放。那么除了去分析后解密,就没有办法拿到解密后的文件吗,答案是否定的。这个时候就需要上点硬技术了。

    回到Mono的源码,我们需要去分析一下他的一个流程。这里我就不去分析了,有一个帖子已经说得很详细。[原创]关于Unity游戏Mono注入型外挂的一个检测思路-编程技术-看雪论坛-安全社区|安全招聘|bbs.pediy.com

    大家可以看看这个帖子,主要是去熟悉一下mono的一个流程。我也直接请出主角。

static MonoImage *
register_image (MonoImage *image)
{
       MonoImage *image2;
       GHashTable *loaded_images = get_loaded_images_hash (image->ref_only);           //       重点关注对象

       mono_images_lock ();
       image2 = (MonoImage *)g_hash_table_lookup (loaded_images, image->name);

       if (image2) {
               /* Somebody else beat us to it */
               mono_image_addref (image2);
               mono_images_unlock ();
               mono_image_close (image);
               return image2;
       }

       GHashTable *loaded_images_by_name = get_loaded_images_by_name_hash (image->ref_only);
       g_hash_table_insert (loaded_images, image->name, image);                       //       重点关注对象
       if (image->assembly_name && (g_hash_table_lookup (loaded_images_by_name, image->assembly_name) == NULL))
               g_hash_table_insert (loaded_images_by_name, (char *) image->assembly_name, image);
       mono_images_unlock ();

       return image;
}

这里可以清楚的看到,U3D直接把需要的加载的模块插入了一个HashTable,然后完成模块的一个装载。这里我们先不管这个模块进行了如何加密和解密,既然你要扔给U3D托管,那么你不可能扔了一个加密模块的给U3D托管,除非你的U3D引擎进行了一个大规模的魔改。所以说,我们只需要找到这个HashTable,然后去遍历一下里面的模块,我们是不是就能拿到解密后的Assembly-CSharp,答案是肯定的。那么现在重点对象从 如何解密Assembly-CSharp变成了 如何使用loaded_images。

    我们应该如何去找到这个loaded_images?没办法,上分析。调试器打开游戏跳到mono_image_open_from_data_with_name这个函数然后对照源码进行分析。

         从源码得知,register_image是最后一个调用的函数,我们直接找到最后一个函数,进去后分析得知。


    拿到loaded_images后,我们下一步就是要去遍历这个HashTabel,两个办法,一个是自己去搭建GHashTable库,然后自己去跑一遍。第二个办法,自己去分析g_hash_table_insert函数的流程。我选择后者 =。=

        Ida打开mono.dll后搜索g_hash_table_insert,有一个

        

            进去F5后无脑暴力分析。



        分析了一个大概流程后,打开CE,用数据结构工具分析验证看看



        很明显,这里应该就是模块列表,我们再随便进去一个去看看。通过刚刚IDA的分析,0x0是模块名,0x8是image。

            和我们刚刚分析的一致,现在我们还差一个东西,那就是image的结构,我们在源码查找一下。

struct _MonoImage {
           ………
           guint32 raw_data_len;
           char *raw_data;                   //模块二进制
           char *name;              //模块名
           …………
}

        这里就贴出重要的。问题来了,我们怎么知道他的偏移。别忘了,这里有

MonoImage *
mono_image_open_from_data_internal (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, gboolean metadata_only, 
                               const char *name)
{
       MonoCLIImageInfo *iinfo;
       MonoImage *image;
       char *datac;

       if (!data || !data_len) {
               if (status)
                       *status = MONO_IMAGE_IMAGE_INVALID;
               return NULL;
       }
       datac = data;
       if (need_copy) {
               datac = (char *)g_try_malloc (data_len);
               if (!datac) {
                       if (status)
                               *status = MONO_IMAGE_ERROR_ERRNO;
                       return NULL;
               }
               memcpy (datac, data, data_len);
       }

       image = g_new0 (MonoImage, 1);
       image->raw_data = datac;                                                                       <<<<<<<<<<<<<<<
       image->raw_data_len = data_len;                                                                <<<<<<<<<<<<<<<
       image->raw_data_allocated = need_copy;
       image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);            <<<<<<<<<<<<<<<
       iinfo = g_new0 (MonoCLIImageInfo, 1);
       image->image_info = iinfo;
       image->ref_only = refonly;
       image->metadata_only = metadata_only;
       image->ref_count = 1;

       image = do_mono_image_load (image, status, TRUE, TRUE);
       if (image == NULL)
               return NULL;

       return register_image (image);
}

    我们ida返回去分析

__int64 __fastcall sub_18006CA20(__int64 data, unsigned int data_len, int need_cpy, _DWORD *status, __int64 refonly, char metadata_only, __int64 name)
{
  __int64 len; // r14
  _DWORD *v8; // rdi
  char v9; // si
  __int64 data_; // rbx
  __int64 data__; // rbp
  __int64 v12; // rax
  __int64 result; // rax
  __int64 v14; // rbx
  __int64 m_name; // rax
  signed __int64 v16; // rax
  __int64 v18; // rax
  __int64 v19; // rax

  len = data_len;
  v8 = status;
  v9 = need_cpy;
  data_ = data;
  if ( data && data_len )
  {
    data__ = data;
    if ( need_cpy )
    {
      v12 = sub_180004B80(data_len);
      data__ = v12;
      if ( !v12 )
      {
        if ( v8 )
          *v8 = 1;
        return 0i64;
      }
      sub_180314D40(v12, data_, len);
    }
    v14 = sub_180004AE0(1856i64);
    *(_BYTE *)(v14 + 0x1C) &= 0xFDu;
    *(_BYTE *)(v14 + 0x1C) |= 2 * (v9 & 1);
    *(_QWORD *)(v14 + 0x10) = data__;   //data
    *(_DWORD *)(v14 + 0x18) = len;      //data_len
    if ( name )
    {
      v16 = -1i64;
      while ( *(_BYTE *)(name + v16++ + 1) != 0 )
        ;
      m_name = sub_180004A10(name, (unsigned int)(v16 + 1));
    }
    else
    {
      m_name = sub_180006230("data-%p", data__);
    }
    *(_QWORD *)(v14 + 0x20) = m_name;    //name
    v18 = sub_180004AE0(408i64);
    *(_BYTE *)(v14 + 0x1C) &= 0xBFu;
    *(_BYTE *)(v14 + 0x1D) &= 0xFEu;
    *(_QWORD *)(v14 + 0x50) = v18;
    *(_DWORD *)v14 = 1;
    *(_BYTE *)(v14 + 0x1C) |= (refonly & 1) << 6;
    *(_BYTE *)(v14 + 0x1D) |= metadata_only & 1;
    v19 = sub_1800699B0(v14, v8, 1i64);
    if ( !v19 )
      return 0i64;
    result = sub_18006D6D0(v19);
  }
  else
  {
    if ( status )
      *status = 3;
    result = 0i64;
  }
  return result;
}

    可知

0x10 raw_data
0x18 raw_data_len
0x20 name

        我们再用CE看看


        可以看到,直接是一个标准的PE文件,说明这里存的是二进制内容。

        

        到这里,我们就已经分析完了loaded_images的一个内存结构。接下来就是写一个遍历工具,然后打开游戏,运行工具,等待DLL生成。这里我用易语言写了一个。

        

                        


        运行。。。


            原文件

            

        dump文件

        

        Dnspy打开

   

    到这里,就已经完美提取了。这里的话,个人觉得,也可以提取一些第三方挂钩mono的DLL,但是本人没去试过。有兴趣的可以去试试。输入法和键盘同时出了问题,打字很难受,如果文章中存在错别字,,,见谅。。。。

                                                                                                   

       

【公告】 讲师招募 | 全新“预付费”模式,不想来试试吗?
#调试逆向 #.NET平台
上传的附件:

    mono部分源码.zip (1.42MB,50次下载)

收藏
点赞・6
打赏
分享

最新回复 (16)

       

0x太上 活跃值 2021-5-26 17:26
    引用 举报
    2 楼 0

牛逼 666
       

还我六千雪币 活跃值 2021-5-26 17:29
    引用 举报
    3 楼 0

棒。
       

Xmix 活跃值 2021-5-26 22:59
    引用 举报
    4 楼 0

牛逼
       

初学小潘 活跃值 2021-5-27 07:37
    引用 举报
    5 楼 0

牛逼 学习了
       

Editor 活跃值 2021-6-4 15:30
    引用 举报
    6 楼 0

感谢分享啦~
       

mb_hapxpitd 活跃值 2021-6-6 11:27
    引用 举报
    7 楼 0

你那个塔科夫的游戏是咋弄的啊
       

sunfishi 活跃值 3 2021-6-6 13:03
    引用 举报
    8 楼 0

感谢分享
       

gamehack 活跃值 2021-6-6 18:13
    引用 举报
    9 楼 0

感谢分享,收藏了
       

hekes 活跃值 2021-6-6 22:31
    引用 举报
    10 楼 0

mark
       

renbohan 活跃值 2021-6-7 19:33
    引用 举报
    11 楼 0

活捉生死狙击2大手子
现在人家也有注入检测了,注入就封
可以研究下把ACE剥离了
       

PlaneJun 活跃值 6 2021-6-7 21:00
    引用 举报
    12 楼 0

    renbohan 活捉生死狙击2大手子 现在人家也有注入检测了,注入就封 可以研究下把ACE剥离了 

参见大哥        
       

renbohan 活跃值 2021-6-9 22:41
    引用 举报
    13 楼 0

    PlaneJun 参见大哥 

为什么你的帖子上看雪公众号了,我心里不平衡        
       

PlaneJun 活跃值 6 2021-6-10 00:31
    引用 举报
    14 楼 0

    renbohan 为什么你的帖子上看雪公众号了,我心里不平衡 

可能是我比较可爱吧        
       

mb_gkcldltc 活跃值 2021-8-25 03:41
    引用 举报
    15 楼 0

大佬能否看看我这个dll
       

肾虚公子 活跃值 2022-1-22 14:30
    引用 举报
    16 楼 0

能否给个联系方式交流一下 
       

PlaneJun 活跃值 6 2022-3-7 16:20
    引用 举报
    17 楼 0

       

我是唐榕爹

表情 雪币赚取及消费
    高级回复 

PlaneJun 6
活跃值
20
发帖
61
回帖
310
RANK
他的文章
[原创]2022腾讯游戏安全决赛wp 5735
[原创]2022腾讯游戏安全初赛wp 4274
[原创]记录自己调试windows服务的操作 4593
[原创]一个数据加密恶意样本分析 9663
[原创]记一次头铁的病毒样本分析过程 15464
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
©2000-2022 看雪 | Based on Xiuno BBS
域名:加速乐 | SSL证书:亚洲诚信 | 安全网易易盾| 同盾反欺诈
看雪APP | 公众号:ikanxue | 关于我们 | 联系我们 | 企业服务
Processed: 0.026s, SQL: 76 / 沪ICP备16048531号-3 / 沪公网安备31011502006611号
2022-5-29 19:15
0
雪    币: 403
活跃值: (241)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19

何不直接找到该版本unity mono的源代码[开源的],自己编译一份把它的mono.dll替换掉,然后想干啥都行~

最后于 2022-7-26 10:35 被鬼来啦编辑 ,原因:
2022-7-26 10:35
0
游客
登录 | 注册 方可回帖
返回
//