首页
社区
课程
招聘
某手游反外挂分析
发表于: 2017-11-1 13:57 26884

某手游反外挂分析

2017-11-1 13:57
26884

个人博客: http://www.cnblogs.com/2014asm/

工具与环境:

IDA7.0

JEB2.2.5

Nexus 5

Android 4.4

目录:

一:app简单分析与java层反编译

二: compatible.so反调试与反反调试

三: compatible.so注册jni函数分析

四: stub.so反调试与反反调试

五: stub.so注册jni函数分析

六: Assembly-CSharp.dll解密分析

七: libengine模块分析

八:总结

整体图:

1.最近在学习手游保护方面的技术,本文是学习过程中分析某反外挂的一点记录,高手莫要见笑,有不对的地方还请指教,首先简单通过资源目录中文件名做基本了解,

lib目录中有libmono.solibunity.so,资源目录中存在(assets\bin\Data\Managed\Assembly-CSharp.dll),应该是unity 3D编写,通过反编译发现该文件己被加密,在资源目录下armeabi文件夹中还存放着libengine.soxlibstub.sox文件,看名字猜测很可能这两个文件就是反外挂其中的一些模块了,在看看lib目录下只有libcompatible.so模块比较可疑。如下图所示:



当我们用调试器附加游戏程进时会有如下提示:


被发现有调试器附加,下节我们将分析它的反调试机制。

2.通过JEB反编译来看看大致流程,反编译后先找到application类,代码如下图:

主要是加载so模块,so名称字符串被加密了,解密出来后so名称"compatible",compatible.so放到IDA中反编译发现函数名被混淆了,字符串己加密,如下图:

通过以上简单分析,我们主要关注的重点关注的模块主要有lib目录下的libcompatible.so与资源目录中的libengine.soxlibstub.sox,还有就是发现java层的字符串与函数名都被混淆,so模块中的字符串也函数名也被混淆。

3.拷贝资源,解密libstub.sox并加载 。

Lcom/inca/security/Core/AppGuardEngine初始函数<init>(Landroid/content/Context;Lcom/inca/security/AppGuard/AppGuardEventListener;Z)V中将判断X86ARM平台并将对应的\assets\appguard中的libengine.soxlibstub.soxupdate.dat拷贝到程序安装目录。JEB未能正常反编译出java代码,smali代码。

4. 解密libstub.sox模块。

解密函数在类com/inca/security/qbiiIIIiiiIi函数,代码如下:

@SuppressLint(value={"SdCardPath"}) public boolean iiIIIiiiIi(String arg25, String arg26, byte[] arg27) throws IOException, InvalidKeyException {

        Object v18;

        Object v5_2;

        long v16_1;

        Method v8_3;

        Class v11_2;

        Object v7_2;

        Object v4_7;

        Method v15;  // doFinal

        Method v14_1;  // init java.security.Key

        Object v13_1;  // RSA/ECB/PKCS1Padding

        int v8_1;

        int v7;

        FileInputStream v13;

        Method v4_6;

        byte[] v7_1;

        boolean v4_2;

        Method v5_1;  // read

        byte[] v12;

        byte[] v11;

        Object v10;  // /

        Class v9;  // java.io.FileInputStream

        try {

            v9 = Class.forName(vb.iiIIIiiiIi("&3:3b;#|\n; 7\u0005<<\'8\u00018 )3!"));  // java.io.FileInputStream

            Constructor v4_1 = v9.getConstructor(String.class);

            v10 = v4_1.newInstance(arg25.indexOf(yb.iiIIIiiiIi("I")) == 0 ? arg25 : new StringBuilder().insert(0, this.iiIiiiIIIi).append(arg25).toString());  // /

            v11 = new byte[16];

            v12 = new byte[4];

            v5_1 = v9.getMethod(vb.iiIIIiiiIi(" )3("), byte[].class, Integer.TYPE, Integer.TYPE);  // read

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(3));

            if(v11[0] == 83 && v11[1] == 79 && v11[2] == 88) {  // 判断开头是否为SOX

                goto label_82;

            }

            v4_2 = false;

            return v4_2;

        }

        catch(Exception v4) {

            goto label_78;

        }

    label_82:

        int v4_3 = 3;

        try {

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));

            if((((short)((((short)v11[0])) | (((short)v11[1])) << 8))) != 1) {

                return false;

            }

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(1));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));

            v5_1.invoke(v10, v12, Integer.valueOf(0), Integer.valueOf(4));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(12));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(16));

            v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(16));

            byte[] v4_5 = null;

            if(v11[0] == 0 || v11[1] == 0 || v11[14] == 0 || v11[15] == 0) {

                v7_1 = v4_5;

                v4_6 = v5_1;

                goto label_291;

            label_276:

                while(v7 < 64) {

                    v13.read(v11);

                    if(v8_1 == v14) {

                        v4_5 = new byte[16];

                        System.arraycopy(v11, 0, v4_5, 0, 16);

                    }

                    v7 = v8_1 + 1;

                    v8_1 = v7;

                }

                v13.close();

                v7_1 = v4_5;

                v4_6 = v5_1;

            }

            else {

                File v8 = new File(String.format(yb.iiIIIiiiIi("CuI#\u0015(\u0012v\r"), arg25.substring(0, arg25.lastIndexOf(47)), qb.iiIIIiiiIi(v11)));  // %s/%s.tpk

                if(v8.exists()) {

                    v13 = new FileInputStream(v8);

                    v13.read(v11);

                    int v14 = (Math.abs(v11[0] << 24 | v11[4] << 16 | v11[8] << 8 | v11[12]) + 1) % 64;

                    v7 = 1;

                    v8_1 = 1;

                    goto label_276;

                }

                else {

                    return false;

                }

            }

        label_291:

            v4_6.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));

            v4_5 = new byte[(v11[3] & 255) << 24 | 0 | (v11[2] & 255) << 16 | (v11[1] & 255) << 8 | v11[0] & 255];

            v5_1 = v9.getMethod(vb.iiIIIiiiIi(" )3("), byte[].class);  // read

            v5_1.invoke(v10, v4_5);

            Class v8_2 = Class.forName(yb.iiIIIiiiIi("\fg\u0010g\u001E(\u0005t\u001Fv\u0012iHE\u000Fv\u000Ec\u0014"));  // javax.crypto.Cipher

            Method v11_1 = v8_2.getMethod(vb.iiIIIiiiIi("+78\u001B\"!83\"1)"), String.class);  // getInstance

            v13_1 = v11_1.invoke(null, yb.iiIIIiiiIi("T5GIC%DIV-E576g\u0002b\u000Fh\u0001"));  // RSA/ECB/PKCS1Padding

            v14_1 = v8_2.getMethod(vb.iiIIIiiiIi(";\";8"), Integer.TYPE, Class.forName(yb.iiIIIiiiIi("\fg\u0010gHu\u0003e\u0013t\u000Fr\u001F(-c\u001F")));  // init java.security.Key

            v14_1.invoke(v13_1, Integer.valueOf(2), this.iIiIIiIiIi);

            v15 = v8_2.getMethod(vb.iiIIIiiiIi("(=\n;\"3 "), byte[].class);  // doFinal

            v4_7 = v15.invoke(v13_1, v4_5);

            if(v7_1 != null) {

                v13_1 = v11_1.invoke(null, yb.iiIIIiiiIi("\'C5"));  // AES

                v14_1.invoke(v13_1, Integer.valueOf(2), Class.forName(vb.iiIIIiiiIi("&3:34|/ 5\"8=b!<7/|\u001F7/ )&\u000775\u0001<7/")).getConstructor(byte[].class, String.class).newInstance(v7_1, yb.iiIIIiiiIi("\'C5")));  // javax.crypto.spec.SecretKeySpec AES

                v4_7 = v15.invoke(v13_1, v4_7);

            }

            v7_2 = null;

            v7_2 = v11_1.invoke(v7_2, vb.iiIIIiiiIi("\r\u0017\u001F"));

            v14_1.invoke(v7_2, Integer.valueOf(2), Class.forName(yb.iiIIIiiiIi("\fg\u0010g\u001E(\u0005t\u001Fv\u0012iHu\u0016c\u0005(5c\u0005t\u0003r-c\u001FU\u0016c\u0005")).getConstructor(byte[].class, String.class).newInstance(v4_7, vb.iiIIIiiiIi("\r\u0017\u001F")));

            v11_2 = Class.forName(yb.iiIIIiiiIi("\fg\u0010gHo\t($\u007F\u0012c\'t\u0014g\u001FI\u0013r\u0016s\u0012U\u0012t\u0003g\u000B"));

            v13_1 = v11_2.getConstructor(null).newInstance(null);

            byte[] v14_2 = new byte[1024];

            v15 = v8_2.getMethod(vb.iiIIIiiiIi("\'<6-&)"), byte[].class, Integer.TYPE, Integer.TYPE);

            Method v16 = v11_2.getMethod(yb.iiIIIiiiIi("\u0011t\u000Fr\u0003"), byte[].class);

            for(v4_6 = v5_1; true; v4_6 = v5_1) {

                v4_3 = v4_6.invoke(v10, v14_2).intValue();

                if(v4_3 == -1) {

                    break;

                }

                v16.invoke(v13_1, v15.invoke(v7_2, v14_2, Integer.valueOf(0), Integer.valueOf(v4_3)));

            }

            v16.invoke(v13_1, v8_2.getMethod(vb.iiIIIiiiIi("(=\n;\"3 "), null).invoke(v7_2, null));

            v4_7 = v11_2.getMethod(yb.iiIIIiiiIi("\u0012i$\u007F\u0012c\'t\u0014g\u001F"), null).invoke(v13_1, null);

            if(arg25.indexOf(vb.iiIIIiiiIi("c")) != 0) {

                arg26 = new StringBuilder().insert(0, this.iiIiiiIIIi).append(arg26).toString();

            }

            Class v7_3 = Class.forName(yb.iiIIIiiiIi("l\u0007p\u0007(\u000FiH@\u000Fj\u0003I\u0013r\u0016s\u0012U\u0012t\u0003g\u000B"));

            v8_3 = v7_3.getMethod(vb.iiIIIiiiIi("; %&)"), byte[].class);

            v14_1 = v7_3.getMethod(yb.iiIIIiiiIi("\u0005j\tu\u0003"), null);

            v15 = v7_3.getMethod(vb.iiIIIiiiIi("*>9!$"), null);

            v7_2 = v7_3.getConstructor(String.class).newInstance(arg26);

            v16_1 = na.iIIIiiiIII(((byte[])v4_7), 5);

            v5_2 = null;

        }

        catch(Exception v4) {

            goto label_78;

        }

        try {

            v18 = Binder.getReserved1();

            if(v18 == null) {

                goto label_761;

            }

        }

        catch(Exception v4) {

            goto label_760;

        }


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (27)
雪    币: 5303
活跃值: (1625)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错
2017-11-1 15:39
0
雪    币: 233
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
1
最后于 2019-3-8 22:21 被烟雨破解编辑 ,原因:
2017-11-1 17:21
0
雪    币: 7012
活跃值: (4222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2017-11-1 18:17
0
雪    币: 210
活跃值: (641)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
5
先mark,再继续看
2017-11-1 21:27
0
雪    币: 4055
活跃值: (2817)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
三哥威武
2017-11-2 14:42
0
雪    币: 9479
活跃值: (757)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
烟雨破解 这个游戏叫少女前线的游戏,加固做的不行,以前试过做了秒杀无敌的自己玩,没点意思,直接绕过加密hook关键函数即可得到解密后的dll文件,有点好奇这些游戏公司为什么那么看重算法上!而不是游戏本身加固上面 ...
加固会让流畅性成问题,你觉得没人愿玩的游戏还有加固的必要吗
2017-11-2 15:38
0
雪    币: 233
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8



无边

加固会让流畅性成问题,你觉得没人愿玩的游戏还有加固的必要吗
说的有道理
2017-11-2 19:33
0
雪    币: 1395
活跃值: (195)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
支持一下,
2017-11-3 18:09
0
雪    币: 3712
活跃值: (1401)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
10
  三哥V5
2017-11-3 20:47
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
11
现在不流行技术反外挂了,都是法律反外挂
2017-11-3 21:52
1
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
人家美国人做的中国挂  你法律反的到?
2017-11-5 20:27
0
雪    币: 14855
活跃值: (6083)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
IDA  Pro代码分析做得越来越强了
2017-11-13 16:05
0
雪    币: 10017
活跃值: (3457)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
烟雨破解 这个游戏叫少女前线的游戏,加固做的不行,以前试过做了秒杀无敌的自己玩,没点意思,直接绕过加密hook关键函数即可得到解密后的dll文件,有点好奇这些游戏公司为什么那么看重算法上!而不是游戏本身加固上面 ...
我刚才拆包了少女前线的安装包,发现dll被加密了,网上说"在apk压缩包找到lib\armeabi-v7a\libmono.so,用IDA打开,定位到mono_image_open_from_data_width_name方法",但是我看了一下,这个方法并没有对dll做解密,大佬可以指点一下,少女前线的Assembly-CSharp.dll到底在哪里解密的吗
2018-5-18 17:54
0
雪    币: 341
活跃值: (1171)
能力值: ( LV3,RANK:24 )
在线值:
发帖
回帖
粉丝
15
经典文章,马克一下
2018-7-17 22:03
0
雪    币: 45
活跃值: (339)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
马克一下
2018-7-18 11:39
0
雪    币: 20
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
不明觉厉,学习了
2018-7-18 13:45
0
雪    币: 205
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
支持一下
2018-7-18 15:30
0
雪    币: 2
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
19
马克一下
2018-7-21 09:19
0
雪    币: 256
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
有人可以帮助我绕过这个游戏吗? https://play.google.com/store/apps/details?id=com.megaxus.ayodance
2019-4-9 22:38
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
mark
2019-4-10 00:23
0
雪    币: 5
活跃值: (26)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mmm
2019-4-10 22:38
0
雪    币: 15
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
mark
2019-5-15 16:40
0
雪    币: 137
活跃值: (514)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
厉害
2019-7-23 10:43
0
雪    币: 8216
活跃值: (3887)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
25
大神太牛了
2019-7-23 10:58
0
游客
登录 | 注册 方可回帖
返回
//