重点知识:
1. \assets\bin\Data\Managed\Assembly-CSharp.dll扔进Reflector+reflexil环境
2.libs\libmono.so的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);
转:https://bbs.pediy.com/thread-226135.htm
最近找乐子破解了点手游玩玩,顺便分享一下,目标APK--发条XX。
1、大致分析
拿到APK先看看libs和assets,发现有libmono.so以及\assets\bin\Data\Managed\Assembly-CSharp.dll,可以确定是unity3d手游,可以尝试修改 Assembly-CSharp.dll来破解游戏,不过发条英雄的这个dll加密了,所以需要先解密。
2、解密dll
把 libmono.so扔进ida,找到mono_image_open_from_data_with_name函数,F5查看发现了解密代码:
在这个函数先判断加载的dll是否包含ssem字符串,接着判断前两个字节是否等于82(0x52),77(0x4d),因此可以确定下面的for循环是解密代码,比较简单,抄下来自己写一个解密程序(附件有一份),然后对这个 Assembly-CSharp.dll进行解密。
3、修改dll
把 Assembly-CSharp.dll扔进Reflector+reflexil环境进行修改(不会的同学百度学习一下),找到关键函数get_DamageValue,直接改成返回100W,然后攻击就变成了100W。
不过这个时候怪物攻击也是100W,因此再找到Player的applyDamage函数,修改这个函数直接ret,然后玩家就无敌了。
最后保存为一个新的dll并替换掉之前APK里面的 Assembly-CSharp.dll,解密后的dll头部不等于82(0x52),77(0x4d),因此so可以直接加载。
4、运行
重新签名,打包成新的APK,安装运行,一刀100W。
5、结束语
服务器信赖客户端的计算结果就导致这种情况,不过服务器加强数据校验也能检测出来。有兴趣的同学可以去改改dll实现竞技场百战百胜。
6、附件
decrypt.c
#include
#include
#include
#include
#include
#include
#include
typedef char _BYTE;
int main(int argc, const char **argv) {
if(argc != 3) {
printf("usage:%s \n", argv[0]);
return -1;
}
FILE *fpDll;
fpDll = fopen(argv[1], "r");
if(fpDll == NULL)
{
printf("open fail\n");
}
fseek(fpDll, 0, SEEK_END);
int len = ftell(fpDll);
fseek(fpDll, 0, SEEK_SET);
char *data = (char *)malloc(len);
fread(data, 1, len, fpDll);
fclose(fpDll);
signed int v11; // [sp+20h] [bp-18h]@3
signed int v12; // [sp+24h] [bp-14h]@3
int v10;
size_t i;
v12 = 55;
v11 = 89;
char *src = data;
int n = len;
if ( *(_BYTE *)src == 82 && *((_BYTE *)src + 1) == 77 )
{
for ( i = 4; i < n; ++i )
{
v10 = v12 + v11;
v12 = v11;
v11 = v10;
if ( v10 > 0x10000000 )
{
v12 = 1;
v11 = 1;
}
*((_BYTE *)src + i) ^= v10;
}
*(_BYTE *)src = 77;
*((_BYTE *)src + 1) = 90;
*((_BYTE *)src + 2) = -112;
*((_BYTE *)src + 3) = 0;
}
FILE* pFile = fopen(argv[2],"wb");
fwrite(data,len,1,pFile);
fclose(pFile);
}
reflexil
https://github.com/sailro/Reflexil/releases
第一步:
下载Reflector 10和Reflexil 2.2,装好后将Reflexil的插件DLL文件加载到Reflector中
具体操作:Tools->Add-Ins->+->选择Reflexil的DLL文件Reflexil.Reflector.AIO.dll,点击确定加载到Reflector中
第二步:
2.1 Reflector加载需要编译的DLL或exe文件,找到对应的方法。
2.2 打开Tools->Reflexil 你将会看到对应方法的IL代码
选择Reflace all with code 对对应代码进行修改后,点击左下角的 Complie(编译),然后点击“确定”;
第三步:
确定操作完毕后,在左侧DLL或exe文件上右击
Save as 程序集编译成功!
Reflexil 它是Reflector的一个插件,结合Reflector,可以进行DLL代码注入等工作,实践证明完全可用,方便开发人员对.NET程序进行修改;可以作为一个Reflector插件修改程序集的IL并保存到磁盘文件,也可以在自己的.NET程序中调用进行更为灵活的.NET程序集修改。
下载好Reflexil后,在Add-ins 界面,点"+",选择Reflexil.Reflector.AIO.dll,然后Close
现在在Tool中会多一个Reflexil选项
它的界面
用法:
把我刚才写的Hello world拖进Reflector,我们选中程序集,命名空间,类名,方法, Reflexil的界面都是不一样的.
这里可以知道,我们能注入类,接口,枚举,方法,等,也可以重命名,删除.
现在我们来添加一个方法试试,选择Inject method 。 Item Name:Demo(此时只能声明无参,无返回值的方法,后面会讲什么添加这些)
点ok会给出警告
意思就是,做大改动(注入,删除,重命名)时你是看不见结果,让你保存一次在导入新的dll文件,那就保存一次
保存好后,把新dll拖到Reflector中,就会发现在 Programe中多了一个Dmeo方法.
现在来给Demo 写实现
选择Demo 后右边的界面为:
选择Main:
instructions 中是IL指令, 高级玩家可以直接修改添加指令,但不是高级玩家咋办?
我们选择Replace all with code... 替换所以代码.
不过在这之前 先在Parameters添加一个参数string name,并在Attributes 将Return type设为stirng
我再次点击Replace all with code... 代码已经更新为
修改完成后,点compile 如果没有错误,就会生成IL指令,点ok就行
现在在来修改一下Main方法,调用Demo
代码已经改完,现在保存看看执行效果:
原程序:
修改后程序:
https://www.52pojie.cn/thread-271347-1-1.html
潜水潜了那么久,上来换口气..
昨天和小伙伴一起玩一个CrackMe(C#),打算一起来写KeyGen,他用的是爆破的方法,我修复程序后发现里面常量各种溢出...无奈,只能用reflexil注入方法,然后写出KeyGen.
分享下使用reflexil常用的几个方法..
Reflector 之reflexil使用
先写个简单的控制台程序
工具,添加插件.
选中reflexil 1.6 (1.7从来没附加成功过.不知道为啥..)
一 直接修改操作数
可以直接编辑IL
Update 后
在程序集中右键
另存程序.
执行刚保存的程序
还可以直接添加IL 接着让后面继续输出
二 直接注入IL
右键,新建(new Create),填写对应的操作码,选择类型,
值得注意的是右边的按钮,append(添加),接着是插入在选择之前,然后是插入在选择之后;别选错了.
注意 注入的时候操作数的类型别选错了.
然后继续参照上面的,将程序另存一次.
三 替换代码
如果不想折腾IL,直接选择 替换所有代码.
不过这意味着你要重写所有代码(大多数时,我都会用在重写某个方法上)…然后提交一下,他会自动编译, 然后继续参照上面的,将程序另存一次.
提交完成后会自动编译,并且再右侧区域会生成对应的IL.
然后继续参照上面的,将程序另存一次.
执行下…
四 注入方法
别选错地方了.是你要将方法注入到某个类中,不是注入class所以,一定是在类上右键
暂时只能注入返回值为void的方法
对了,点击OK后会有个该死的提示,这提示的大致意思是:
当你在执行,增加,删除,重命名,等动作时,你不会直接看见相应的操作,他们是不同步的.
你必须要重新加载程序集才可以看见.
当然,你还需要将程序另存一次.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
现在方法已经注入进去了.
然后我需要给它添加点内容..
让方法输出个InjectedMethod字符串
在方法中添加IL
接着修改Main方法,并且调用刚才注入的方法.
另存下..
执行结果.
Ps: 如果是替换代码的话,先更改injectmethod时不用实现main方法,只需要修改injectmethod()方法,然后再去修改Main方法.调用时因为injectmethod方法不是static所以需要实例化Program
接下来说说添加有参数,有返回值的方法:
先在参数(parameter)标签,中添加一个参数.
然后在属性标签中修改返回值为string.
接着使用替换代码的功能
可以看见方法已经带有参数并且有返回值.
修改下这个方法
打印下传进来的名字,并且打印当前时间
修改主函数调用
编译,然后不要忘记另存….
Good Job .
Have Fun .
By McevilRock
9-7/2014