i春秋首发,未经允许,禁止转载
0x00 前言
本文提供三种打开方式,娱乐模式,入门模式,以及研究模式。
1.娱乐模式 >>> 0x02
如何你想秀一波操作,那么激动人心的一秒就满金币,实现自我不是梦。
可以学习到的知识:
如果你想从基础学习,那么从这里开始是你不二的选择。
2.入门模式 >>> 0x01
从HelloWorld开始学习就从HelloWorld开始破解
可以学到的知识
1.用unity3d开发Helloworld,并且生成apk。
2.破解Helloworld
3.研究模式 >>> 0x03
初学者请避开这一部门。
从mono源码出手,研究运行过程,当然这个很复杂只能一点一点来。能研究多少,就看个人努力了。
可以学到的知识
自豪的说你看过mono源码,但是没看懂。
0x01 入门模式

1.开发你的Helloworld
简单说一下开发环境,unity3d,全自动安装,vs,也是一直next。
要是说你懒的配环境了,没关系,我直接提供demo。demo在最后面。
简单说一下,不啰嗦
1.1 新建一个工程

1.2 新建一个脚本
Assets -> Create ->C# Script
起个名字叫HelloWorld。双击
然后输入脚本内容:

1.3 脚本交给摄影机
选中摄影机,Component->Script->HelloWorld就ok了。
运行一下游戏。

1.4 导成APK文件
File->Build Settings->Android->Player Setting->设置报名->run,就OK了。
1.5 运行结果

2.逆向分析你的HelloWorld
2.1 准备工具
1.Reflector
2.签名工具,随便找一个就ok了。
2.2 提取重要文件
apk就是zip包,在assets->bin->Data->Managed这个目录下有一个叫做Assembly-CSharp.dll的文件,这个文件就是我们的脚本文件的集合,最后所有的脚本都会集合在这里。所以我们的目标就是和这个文件了。
2.3 提取文件拖到工具中

这里可以看到我们自己写的脚本HelloWorld。
点开看看。然后在点开我们自己脚本看看。
哦豁。

这不是和我们之前的源代码一模一样嘛。这哪有安全性可言,就是裸的噻。
2.4 修改
好了,我们最后的目的就是要修改,这里要用到插件Reflexil,这个包里有,怎么安装,请百度一下。
插件使用之后是这个样子。

找到这里改成。hello,ichunqiu.

然后右键使用插件另存为,然后替换就ok了。
2.5 来看下效果

大功告成
0x02 娱乐模式

我们来简答的进行一个实例应用吧,showtime。
同样demo会提供下载链接。
1.安装游戏观察一下

现在开局游戏是0。秀操作就要开始了。
2.找到这个关键key
同样的方法,提取关键文件,拖到工具里。
修改金币自然就是要对coin这个关键字进行过滤了,程序员的好认识,搞破解的也好过滤,你好我好大家好。
在GameStart下面找到了一个getcoins

好,我们就修改这里,不过这里我们要返回的就是int类型的了。
3.改

我们现在改成这样。简单说一下就是给coins赋值为9999,类型就是int类型

4.保存签名查看效果

0x03 研究模式,小白止步

主要是为了对mono源码进行一个分析入手,不知道能分析多少,但是希望在分析的时候学到一些东西,明白一些东西,这样就足够了。

这里主要的函数就是
probe_embedded (argv [0], &argc, &argv);
传入了三个参数,第一个参数就是路径以及程序全名称,第二个参数是传入参数的个数,这里传的是赋值,第三个参数就是传进去了一个参数列表。
所以猜测这个函数主要就是对输入的参数进行一个处理的。
找到这个函数的位置。

这里函数的返回值是gboolean。这里相当于是boolean就可以了。
MonoBundledAssembly last = { NULL, 0, 0 };
这里很明显是一个结构体
找到这个结构体。
typedef struct {
const char *name;
const unsigned char *data;
const unsigned int size;
} MonoBundledAssembly;
第一个就是name,名字,第二个是data,数据,第三个是size,大小。
其实就是Mono,Bundled,Assembly。
MonoBundledAssembly last = { NULL, 0, 0 };
相当于创建了一个MonoBundledAssembly 结构体类型的 last。
last.name=NULL
last.data=0
last.size=0
char sigbuffer [16+sizeof (uint64_t)];
计算sizeof(uint64_t)的大小再加上16,这里实际就是16+8,因为unit64_t的大小是8个字节。
这里的uint64——t 就是unsigned long int 。
然后到
off_t sigstart,baseline=0
off_t类型默认是32位的long int。
GArray *assemblies;
glib库中的数组GArray类型很像C++标准容器库中的vector容器。
int fd = open (program, O_RDONLY);
以只读模式打开program,这个program就是传入的arg[0],其实这里好奇,为什么要打开arg[0],arg[0]不就是自己本身嘛。
继续向下

sigstart = lseek (fd, -24, SEEK_END)) == -1
这里lseek将偏移放在离文件结尾处-24的位置,并且返回举例文件的首部长度,相当于整个文件的大小减去了24,然后把这个值给了sigstart
read (fd, sigbuffer, sizeof (sigbuffer)) == -1
如果顺利read()会返回实际读到的字节数,这里的作用是将fd里sigbuffer大小的内容给sigbuffer。这里也就是24.
memcmp (sigbuffer+sizeof(uint64_t), "xmonkeysloveplay", 16) != 0
memcmp比较前16个字符,x monkeys loveplay???这个是一个什么鬼。不知道我没有找到,是不是恶搞???这个签名有点意思哦。
directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0]));
获取一个偏移
directory_location = GUINT64_FROM_LE ((*(uint64_t *) &sigbuffer [0]));
获得一个偏移值
lseek (fd, directory_location, SEEK_SET) == -1
将指针移向这个偏移的位置
directory_size = sigstart-directory_location;
用总长度-24-这个偏移量,得到一个区间,这个区间猜测就是对一些设置项进行操作。
directory = g_malloc (directory_size);
开辟空间
read (fd, directory, directory_size) == -1
读取directory_size大小的内容。
接下来的就要靠各位想要继续研究下去的继续探索了,这篇只是一个引导,之后可能会进行一个持续的分析。可以跟进也一起分析也可以分析好了来带带我。都是可以的。
有疑问也可以加 2517238082 询问
以上