Unity下的注入与修改

2018-09-02 4,949 ℃

已经有很长一段时间没写过游戏修改的文章了,一个原因是现在越来越多的手游厂商都开始给游戏上各种各样的保护,以前简单的直接修改dll或者直接修改so早已经不适合这个版本了,所以打算介绍一下现在主流,或者说是我现在常用的一类修改方案。但是拖延症晚期,虽然很早就想写这篇文章,却一直拖到现在才开始动手。。。

这篇文章其实主要还是水,请不要抱着能学到东西的心态往下看(

0x1 注入

Unity下注入的话其实还是注入一个so或者一个dll来进行修改,注入dll一般也要先注入一个so作为loader,所以大前提还是怎么注入一个so,这里就先介绍一些常用的注入方法,具体的就请善用百度啦~

1.ptrace游戏进程

作为最古老的注入方式,已经过时了,现在是个保护都有ptrace反调试,与其考虑如何过掉反调试不如选择换个方式

2.修改classes.dex

修改classes.dex在入口点的onCreate里添加代码载入自己的so,但是大多数保护都有自校验

3.Xposed

handleLoadPackage里过滤包名后载入自己的so即可,但是Xposed作为一款出了名的框架太容易被检测了,常见的利用java反射Xposed的相关类和函数或者读取进程maps检测

4.VirtualApp类程序

VirtualApp相关或者类似的程序(VirtualXposed),修改源码NativeEngine.java载入自己的so,有个最关键的优点就是无需root

5.Zygote注入

安卓下进程都是从Zygote fork的,当注入so到Zygote后,之后启动的进程就都会带有这个so。目前我自己用的也是这个方式,从原理上讲跟Xposed没有太大差别,但是比起Xposed更不容易被检测

0x2 修改

当我们注入一个so进游戏后,接下来就要考虑如何修改了

1.修改libil2cpp.so

如果只是单纯的修改libil2cpp.so,目前没有太多的花样,只需要操作指针修改相应的代码就行,其实就跟直接用编辑器修改libil2cpp.so是一样,但是能绕过对libil2cpp.so的校验

2.hook

hook能干的事就非常多了,比如hook mono_image_open_from_data_with_name在它载入dll时替换成自己修改好的,有些时候为了绕过保护需要hook更底层的函数或者其它函数,比如mono_metadata_parse_mh_full,在method执行的时候将method body替换成修改好的。依托于mono这方面复杂的处理机制,可以在很多函数上做手脚,保护也很难面面俱到。

除了通过hook dll相关的函数,还可以hook比如Unity assets读取的相关函数来修改游戏读取的数据,大多数游戏的人物之类的数据都会存放在assets里。

还有如果游戏逻辑是用lua运行的,也可以hook lua的相关函数实现lua的替换或者lua注入。

3.dll注入

这个修改方式的优点就是可以在游戏运行中的时候进行修改,缺点就是只能对付还没使用il2cpp的游戏,手头刚好有现成的代码就顺带贴一下

typedef enum {
	MONO_IMAGE_OK,
	MONO_IMAGE_ERROR_ERRNO,
	MONO_IMAGE_MISSING_ASSEMBLYREF,
	MONO_IMAGE_IMAGE_INVALID
} MonoImageOpenStatus;

long module_start_addr = get_module_base("libmono.so");

typedef void* (*mono_get_root_domain_t)();
mono_get_root_domain_t mono_get_root_domain = (mono_get_root_domain_t)(module_start_addr + 0x0);

typedef void* (*mono_thread_attach_t)(void* mDomain);
mono_thread_attach_t mono_thread_attach = (mono_thread_attach_t)(module_start_addr + 0x0);

typedef void* (*mono_image_open_from_data_t) (char *data, uint32_t data_len, int32_t need_copy, MonoImageOpenStatus *status);
mono_image_open_from_data_t mono_image_open_from_data = (mono_image_open_from_data_t)(module_start_addr + 0x0);

typedef void* (*mono_assembly_load_from_full_t)(void *image, const char *fname, MonoImageOpenStatus *status, int32_t refonly);
mono_assembly_load_from_full_t mono_assembly_load_from_full = (mono_assembly_load_from_full_t)(module_start_addr + 0x0);

typedef void* (*mono_assembly_get_image_t)(void *assembly);
mono_assembly_get_image_t mono_assembly_get_image = (mono_assembly_get_image_t)(module_start_addr + 0x0);

typedef void* (*mono_class_from_name_t)(void* image, const char* name_space, const char* name);
mono_class_from_name_t mono_class_from_name = (mono_class_from_name_t)(module_start_addr + 0x0);

typedef void* (*mono_class_get_method_from_name_t)(void* mclass, const char* name, int param_count);
mono_class_get_method_from_name_t mono_class_get_method_from_name = (mono_class_get_method_from_name_t)(module_start_addr + 0x0);

typedef void* (*mono_runtime_invoke_t)(void* method, void* obj, void** params, void** exc);
mono_runtime_invoke_t mono_runtime_invoke = (mono_runtime_invoke_t)(module_start_addr + 0x0);

mono_thread_attach(mono_get_root_domain());

FILE * pFile = fopen("UnityHack.dll", "rb");
fseek(pFile, 0, SEEK_END);
long lSize = ftell(pFile);
rewind(pFile);
char * buffer = (char *)malloc(sizeof(char)*lSize);
fread(buffer, 1, lSize, pFile);
fclose(pFile);

MonoImageOpenStatus status;
void* image = mono_image_open_from_data(buffer, lSize, 1, &status);
void* assembly = mono_assembly_load_from_full(image, "UNUSED", &status, 0);
image = mono_assembly_get_image(assembly);
void* pClass = mono_class_from_name(image, "UnityHack", "HackLoad");
void* method = mono_class_get_method_from_name(pClass, "Load", 0);
mono_runtime_invoke(method, NULL, NULL, NULL);

这里就实现了注入一个自己写的UnityHack.dll,并调用其中UnityHack namespace下HackLoad类的Load函数
Load函数中就可以进行需要的修改了,比如游戏在运行时都会用变量来存储人物或者怪物的数据,如果是公开的就可以直接修改它们,如果是非公开的也可以使用C#万能的反射来修改,或者直接修改函数IL代码或者修改指针把关键函数替换成自己的,这方面具体的修改可以参考Harmony这个框架

0x3

介绍就到这里了啦,可能之后还会编辑文章进行补充,至于详细的东西就请善用百度啦~

使用VirtualXposed修改手游

作为这篇文章的后续,原本是打算很快就写完的,但是不知怎么一转眼就已经12月了,眼看今年都要过了,还是赶紧把这篇文章水出来吧。 在上篇文章说道的修改的核...

阅读全文

【2018-10-27】碧蓝航线Live2D提取

2018-10-27 v1.2 完善4种Segment 2018-10-23 v1.1 修复易拉罐等SteppedSegment问题 正文 自从看到舰B的live2d后就决定要提取一发来射爆,经过简单的分析后发...

阅读全文

少女前线Live2D解密

上周在CP22看到这个游戏两周年了,打算回坑玩一阵,不过上一个号是买的初始号账号密码都忘记了,所以只好开了个新号。然后新皮肤倒是抽到了,不过枪都没有怎...

阅读全文

16 条评论

  1. 用VirtualXposed把包含这段代码的so按照自己的libmono地址偏移注进去了,但是在void* image = mono_image_open_from_data(buffer, lSize, 1, status);这句话发生了错误

    pid: 22419, tid: 22438, name: queued-work-loo >>> app.game <<<
    r0 00000000 r1 00004000 r2 00000003 r3 cdb7ee44
    r4 c3ad2000 r5 f1984108 r6 00001000 r7 cf37f934
    r8 cd9e8000 r9 000057a3 sl cf5165b1 fp cf37f8bc
    ip cdd95cd0 sp cf37f8a0 lr cdc55118 pc cdb7e1d0 cpsr 312f3030
    backtrace:
    #00 pc 001961d0 /data/data/io.virtualapp.ex/virtual/data/app/app.game/lib/libmono.so
    #01 pc 00196dbc /data/data/io.virtualapp.ex/virtual/data/app/app.game/lib/libmono.so (mono_image_open_from_data_with_name+388)
    #02 pc 00196e30 /data/data/io.virtualapp.ex/virtual/data/app/app.game/lib/libmono.so (mono_image_open_from_data_full+60)
    #03 pc 00196e78 /data/data/io.virtualapp.ex/virtual/data/app/app.game/lib/libmono.so (mono_image_open_from_data+52)
    #04 pc 0000188f /data/app/io.virtualapp.ex-1/lib/arm/libhack.so (dll_inject(long)+126)

    还请大佬指教一下

  2. 最近在看一个android改端游的游戏,安卓上是il2cpp,看不到逻辑,pc上加密了,感觉是直接xor的,就想看看主程序有没有解密。主程序用了mono_image_open_from_data_with_name,但是只有一个xref,而且data也没看到加密。这样还有什么思路吗…

    1. 开始的时候加载了个库hook_mono的mono_image_open_from_data_with_name。库加了壳。这样的话还有什么思路…

        1. 风之大陆
          Assembly-CSharp能dump出来但是0x408开始是错的,应该是mono.dll或者游戏主进程还有另一个加密
          最近在尝试直接从内存里扣

  3. 我用这种方式把dll注入进去了,但是却不能用反射,好像是缺少dll,但是我用同样的方式把mscrolib.dll也注入进去了,还是不能引用反射的函数,是为什么呢?android平台

  4. 求問大大 日版 chain chronicle 3.8.5 的Loader 是哪個方面的保護…
    希望指引一點點方向該如何研究

  5. 会想用Unity Studio其实就是因为自己不会这么进阶的东西,所以还真是一点都看不懂。偏偏自己常玩的手游最近又开始有一些图档保护措施

欢迎留言

2 + 3 =