nProtect APPGuard安卓反外挂分析

Posted 逆向与安全

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了nProtect APPGuard安卓反外挂分析相关的知识,希望对你有一定的参考价值。

工具与环境:

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模块分析

八:总结

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

整体图:

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

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

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

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

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

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

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

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

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

 1 :1946
 2 00001946  const/4             v9, 3
 3 00001948  if-ge               v6, v9, :1C16
 4 :194C
 5 0000194C  invoke-static       Binder->getABI()I
 6 00001952  move-result         v6
 7 00001954  move-object/from16  v0, p0
 8 00001958  move-object/from16  v1, p1
 9 0000195C  invoke-virtual      AppGuardEngine->iiIIIiiiIi(Context, I)Z, v0, v1, v6  # 拷贝资源
10 00001962  move-result         v6
11 00001964  if-eqz              v6, :1BE0
12 :1968
13 00001968  new-instance        v6, qb
14 0000196C  invoke-static       JNISoxProxy->getContext()Context
15 00001972  move-result-object  v9
16 00001974  invoke-direct       qb-><init>(Context)V, v6, v9
17 0000197A  const-string        v9, "fChY~_h\\u0004yEr"  # libstub.sox
18 0000197E  invoke-virtual      qb->iiIIIiiiIi([B)V, v6, v8
19 00001984  invoke-static       b->iiIIIiiiIi(String)String, v9
20 0000198A  move-result-object  v9
21 0000198C  const-string        v10, "j\\u000Fd\\u0015r\\u0013dHu\\t"  # libstub.so
22 00001990  invoke-static       yb->iiIIIiiiIi(String)String, v10
23 00001996  move-result-object  v10
24 00001998  const/4             v11, 0
25 0000199A  invoke-virtual      qb->iiIIIiiiIi(String, String, [B)Z, v6, v9, v10, v11  # 解密libstub.sox((最终传入so层解密private static native byte[] iIiIIIiIiI(byte[] arg0, int arg1)))
26 000019A0  move-result         v6
27 000019A2  if-eqz              v6, :1BE0
28 :19A6
29 000019A6  new-instance        v6, File
30 000019AA  new-instance        v9, StringBuilder
31 000019AE  invoke-direct       StringBuilder-><init>()V, v9
32 000019B4  const/4             v10, 0
33 000019B6  invoke-static       JNISoxProxy->getContext()Context
34 000019BC  move-result-object  v11
35 000019BE  invoke-virtual      Context->getFilesDir()File, v11
36 000019C4  move-result-object  v11
37 000019C6  invoke-virtual      File->getAbsolutePath()String, v11
38 000019CC  move-result-object  v11
39 000019CE  invoke-virtual      StringBuilder->insert(I, String)StringBuilder, v9, v10, v11
40 000019D4  move-result-object  v9
41 000019D6  const-string        v10, "%FcHy^\\u007FH$Ye"  # /libstub.so
42 000019DA  invoke-static       b->iiIIIiiiIi(String)String, v10
43 000019E0  move-result-object  v10
44 000019E2  invoke-virtual      StringBuilder->append(String)StringBuilder, v9, v10
45 000019E8  move-result-object  v9
46 000019EA  invoke-virtual      StringBuilder->toString()String, v9
47 000019F0  move-result-object  v9
48 000019F2  invoke-direct       File-><init>(String)V, v6, v9
49 000019F8  invoke-virtual      File->exists()Z, v6
50 :19FE
51 000019FE  move-result         v9
52 00001A00  if-eqz              v9, :1BE0
53 :1A04
54 00001A04  invoke-virtual      File->getAbsolutePath()String, v6
55 00001A0A  move-result-object  v9
56 00001A0C  invoke-static       System->load(String)V, v9  # 加载指定路径的SO
57 00001A12  invoke-direct/range AppGuardEngine->lllIIIlllI(Context)V, p0 .. p1  # 调用Native
58 :1A18
59 00001A18  invoke-virtual      File->delete()Z, v6
60 00001A1E  new-instance        v6, File
61 00001A22  new-instance        v9, StringBuilder
62 00001A26  invoke-direct       StringBuilder-><init>()V, v9
63 00001A2C  const/4             v10, 0
64 00001A2E  invoke-static       JNISoxProxy->getContext()Context
65 00001A34  move-result-object  v11
66 00001A36  invoke-virtual      Context->getFilesDir()File, v11
67 00001A3C  move-result-object  v11
68 00001A3E  invoke-virtual      File->getAbsolutePath()String, v11
69 00001A44  move-result-object  v11
70 00001A46  invoke-virtual      StringBuilder->insert(I, String)StringBuilder, v9, v10, v11
71 00001A4C  move-result-object  v9
72 00001A4E  const-string        v10, ")\\no\\u0004u\\u0012s\\u0004(\\u0015i\\u001E"  # /libstub.sox
73 00001A52  invoke-static       yb->iiIIIiiiIi(String)String, v10
74 00001A58  move-result-object  v10
75 00001A5A  invoke-virtual      StringBuilder->append(String)StringBuilder, v9, v10
76 00001A60  move-result-object  v9
77 00001A62  invoke-virtual      StringBuilder->toString()String, v9
78 00001A68  move-result-object  v9
79 00001A6A  invoke-direct       File-><init>(String)V, v6, v9
80 00001A70  invoke-virtual      File->delete()Z, v6

4. 解密libstub.sox模块。

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

  1    @SuppressLint(value={"SdCardPath"}) public boolean iiIIIiiiIi(String arg25, String arg26, byte[] arg27) throws IOException, InvalidKeyException {
  2         Object v18;
  3         Object v5_2;
  4         long v16_1;
  5         Method v8_3;
  6         Class v11_2;
  7         Object v7_2;
  8         Object v4_7;
  9         Method v15;  // doFinal
 10         Method v14_1;  // init java.security.Key
 11         Object v13_1;  // RSA/ECB/PKCS1Padding
 12         int v8_1;
 13         int v7;
 14         FileInputStream v13;
 15         Method v4_6;
 16         byte[] v7_1;
 17         boolean v4_2;
 18         Method v5_1;  // read
 19         byte[] v12;
 20         byte[] v11;
 21         Object v10;  // /
 22         Class v9;  // java.io.FileInputStream
 23         try {
 24             v9 = Class.forName(vb.iiIIIiiiIi("&3:3b;#|\\n; 7\\u0005<<\\\'8\\u00018 )3!"));  // java.io.FileInputStream
 25             Constructor v4_1 = v9.getConstructor(String.class);
 26             v10 = v4_1.newInstance(arg25.indexOf(yb.iiIIIiiiIi("I")) == 0 ? arg25 : new StringBuilder().insert(0, this.iiIiiiIIIi).append(arg25).toString());  // /
 27             v11 = new byte[16];
 28             v12 = new byte[4];
 29             v5_1 = v9.getMethod(vb.iiIIIiiiIi(" )3("), byte[].class, Integer.TYPE, Integer.TYPE);  // read
 30             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(3));
 31             if(v11[0] == 83 && v11[1] == 79 && v11[2] == 88) {  // 判断开头是否为SOX
 32                 goto label_82;
 33             }
 34 
 35             v4_2 = false;
 36             return v4_2;
 37         }
 38         catch(Exception v4) {
 39             goto label_78;
 40         }
 41 
 42     label_82:
 43         int v4_3 = 3;
 44         try {
 45             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));
 46             if((((short)((((short)v11[0])) | (((short)v11[1])) << 8))) != 1) {
 47                 return false;
 48             }
 49 
 50             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(1));
 51             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));
 52             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(2));
 53             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));
 54             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));
 55             v5_1.invoke(v10, v12, Integer.valueOf(0), Integer.valueOf(4));
 56             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(12));
 57             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(16));
 58             v5_1.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(16));
 59             byte[] v4_5 = null;
 60             if(v11[0] == 0 || v11[1] == 0 || v11[14] == 0 || v11[15] == 0) {
 61                 v7_1 = v4_5;
 62                 v4_6 = v5_1;
 63                 goto label_291;
 64             label_276:
 65                 while(v7 < 64) {
 66                     v13.read(v11);
 67                     if(v8_1 == v14) {
 68                         v4_5 = new byte[16];
 69                         System.arraycopy(v11, 0, v4_5, 0, 16);
 70                     }
 71 
 72                     v7 = v8_1 + 1;
 73                     v8_1 = v7;
 74                 }
 75 
 76                 v13.close();
 77                 v7_1 = v4_5;
 78                 v4_6 = v5_1;
 79             }
 80             else {
 81                 File v8 = new File(String.format(yb.iiIIIiiiIi("CuI#\\u0015(\\u0012v\\r"), arg25.substring(0, arg25.lastIndexOf(47)), qb.iiIIIiiiIi(v11)));  // %s/%s.tpk
 82                 if(v8.exists()) {
 83                     v13 = new FileInputStream(v8);
 84                     v13.read(v11);
 85                     int v14 = (Math.abs(v11[0] << 24 | v11[4] << 16 | v11[8] << 8 | v11[12]) + 1) % 64;
 86                     v7 = 1;
 87                     v8_1 = 1;
 88                     goto label_276;
 89                 }
 90                 else {
 91                     return false;
 92                 }
 93             }
 94 
 95         label_291:
 96             v4_6.invoke(v10, v11, Integer.valueOf(0), Integer.valueOf(4));
 97             v4_5 = new byte[(v11[3] & 255) << 24 | 0 | (v11[2] & 255) << 16 | (v11[1] & 255) << 8 | v11[0] & 255];
 98             v5_1 = v9.getMethod(vb.iiIIIiiiIi(" )3("), byte[].class);  // read
 99             v5_1.invoke(v10, v4_5);
100             Class v8_2 = Class.forName(yb.iiIIIiiiIi("\\fg\\u0010g\\u001E(\\u0005t\\u001Fv\\u0012iHE\\u000Fv\\u000Ec\\u0014"));  // javax.crypto.Cipher
101             Method v11_1 = v8_2.getMethod(vb.iiIIIiiiIi("+78\\u001B\\"!83\\"1)"), String.class);  // getInstance
102             v13_1 = v11_1.invoke(null, yb.iiIIIiiiIi("T5GIC%DIV-E576g\\u0002b\\u000Fh\\u0001"));  // RSA/ECB/PKCS1Padding
103             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
104             v14_1.invoke(v13_1, Integer.valueOf(2), this.iIiIIiIiIi);
105             v15 = v8_2.getMethod(vb.iiIIIiiiIi("(=\\n;\\"3 "), byte[].class);  // doFinal
106             v4_7 = v15.invoke(v13_1, v4_5);
107             if(v7_1 != null) {
108                 v13_1 = v11_1.invoke(null, yb.iiIIIiiiIi("\\\'C5"));  // AES
109                 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
110                 v4_7 = v15.invoke(v13_1, v4_7);
111             }
112 
113             v7_2 = null;
114             v7_2 = v11_1.invoke(v7_2, vb.iiIIIiiiIi("\\r\\u0017\\u001F"));
115             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")));
116             v11_2 = Class.forName(yb.iiIIIiiiIi("\\fg\\u0010gHo\\t($\\u007F\\u0012c\\\'t\\u0014g\\u001FI\\u0013r\\u0016s\\u0012U\\u0012t\\u0003g\\u000B"));
117             v13_1 = v11_2.getConstructor(null).newInstance(null);
118             byte[] v14_2 = new byte[1024];
119             v15 = v8_2.getMethod(vb.iiIIIiiiIi("\\\'<6-&)"), byte[].class, Integer.TYPE, Integer.TYPE);
120             Method v16 = v11_2.getMethod(yb.iiIIIiiiIi("\\u0011t\\u000Fr\\u0003"), byte[].class);
121             for(v4_6 = v5_1; true; v4_6 = v5_1) {
122                 v4_3 = v4_6.invoke(v10, v14_2).intValue();
123                 if(v4_3 == -1) {
124                     break;
125                 }
126 
127                 v16.invoke(v13_1, v15.invoke(v7_2, v14_2, Integer.valueOf(0), Integer.valueOf(v4_3)));
128             }
129 
130             v16.invoke(v13_1, v8_2.getMethod(vb.iiIIIiiiIi("(=\\n;\\"3 "), null).invoke(v7_2, null));
131             v4_7 = v11_2.getMethod(yb.iiIIIiiiIi("\\u0012i$\\u007F\\u0012c\\\'t\\u0014g\\u001F"), null).invoke(v13_1, null);
132             if(arg25.indexOf(vb.iiIIIiiiIi("c")) != 0) {
133                 arg26 = new StringBuilder().insert(0, this.iiIiiiIIIi).append(arg26).toString();
134             }
135 
136             Class v7_3 = Class.forName(yb.iiIIIiiiIi("l\\u0007p\\u0007(\\u000FiH@\\u000Fj\\u0003I\\u0013r\\u0016s\\u0012U\\u0012t\\u0003g\\u000B"));
137             v8_3 = v7_3.getMethod(vb.iiIIIiiiIi("; %&)"), byte[].class);
138             v14_1 = v7_3.getMethod(yb.iiIIIiiiIi("\\u0005j\\tu\\u0003"), null);
139             v15 = v7_3.getMethod(vb.iiIIIiiiIi("*>9!$"), null);
140             v7_2 = v7_3.getConstructor(String.class).newInstance(arg26);
141             v16_1 = na.iIIIiiiIII(((byte[])v4_7), 5);
142             v5_2 = null;
143         }
144         catch(Exception v4) {
145             goto label_78;
146         }
147 
148         try {
149             v18 = Binder.getReserved1();
150             if(v18 == null) {
151                 goto label_761;
152             }
153         }
154         catch(Exception v4) {
155             goto label_760;
156         }
157 
158         try {
159             v5_2 = v18.getClass().getMethod(yb.iiIIIiiiIi("o/o/O\\u000Fo/o/"), byte[].class, Integer.TYPE).invoke(v18, v4_7, Integer.valueOf(((int)v16_1)));
160         }
161         catch(Exception v4) {
162             try {
163                 v4.printStackTrace();
164                 goto label_659;
165             label_761:
166                 byte[] v5_3 = AppGuardEngine.iiIIIiiiIi(((byte[])v4_7), ((int)v16_1));  // 传入SO层解密 (该Native函数在compatible.so进行动态注册)
167             label_760:
168             }
169             catch(Exception v4) {
170                 goto label_760;
171             }
172         }
173 
174     label_659:
175         v4_3 = 3;
176         try {
177             int v12_1 = v12[0] & 255 | ((v12[v4_3] & 255) << 24 | 0 | (v12[2] & 255) << 16 | (v12[1] & 255) << 8);
178             Class v16_2 = Class.forName(vb.iiIIIiiiIi("&3:3b\\\'8; |6;<|\\r6 7>a~"));
179             Object v17 = v16_2.getConstructor(null).newInstance(null);
180             v16_2.getMethod(yb.iiIIIiiiIi("s\\u0016b\\u0007r\\u0003"), byte[].class, Integer.TYPE, Integer.TYPE).invoke(v17, v5_2, Integer.valueOf(0), Integer.valueOf(v5_2.length));
181             v16_1 = v16_2.getMethod(vb.iiIIIiiiIi("5)&\\u001A3 \\\')"), null).invoke(v17, null).longValue();
182             if(v12_1 != 0 && v12_1 != (((int)v16_1))) {
183                 v14_1.invoke(v7_2, null);
184                 v4_2 = false;
185             }
186             else {
187                 v8_3.invoke(v7_2, v5_2);
188                 v15.invoke(v7_2, null);
189                 v14_1.invoke(v7_2, null);
190                 v9.getMethod(yb.iiIIIiiiIi("\\u0005j\\tu\\u0003"), null).invoke(v10, null);
191                 v11_2.getMethod(vb.iiIIIiiiIi("/>#!)"), null).invoke(v13_1, null);
192                 v9.getMethod(yb.iiIIIiiiIi("\\u0005j\\tu\\u0003"), null).invoke(v10, null);
193                 v4_2 = true;
194             }
195 
196             return v4_2;
197         }
198         catch(Exception v4) {
199         label_78:
200             Exception v5_4 = v4;
201             v4_2 = false;
202             v5_4.printStackTrace();
203             return v4_2;
204         }
205     }

java层AES解密再传入传入so层解密private static native byte[] iIiIIIiIiI(byte[] arg0, int arg1)。

5. 解密libengine.sox模块

在Lcom/inca/security/Core/AppGuardEngine;->iiIIIiiiIi([B)Z生成一个随机数后SHA1后字会串做为解密后的文件名存放在/data/data/包名/files/目录下,解密函数与上一步相同。

生成随机数代码:

 1 .method private synthetic iiIIIiiiIi([B)Z
 2           .registers 12
 3           .annotation build SuppressLint
 4               value = {
 5                   "TrulyRandom"
 6               }
 7           .end annotation
 8           .annotation system Throws
 9               value = {
10                   AppGuardException
11               }
12           .end annotation
13 00000000  const/4             v9, 2
14 00000002  const/16            v8, -0x007E
15 00000006  const/16            v7, 0x0030
16 0000000A  const/4             v2, 0
17 0000000C  const/4             v3, 1
18 :E
19 0000000E  const-string        v1, "U.GW"  # SHA1
20 00000012  invoke-static       yb->iiIIIiiiIi(String)String, v1
21 00000018  move-result-object  v1
22 0000001A  invoke-static       MessageDigest->getInstance(String)MessageDigest, v1
23 :20
24 00000020  move-result-object  v1
25 :22
26 00000022  new-instance        v4, SecureRandom
27 00000026  invoke-direct       SecureRandom-><init>()V, v4
28 0000002C  const/16            v5, 0x0100
29 00000030  new-array           v5, v5, [B
30 00000034  const/16            v6, 0x0126
31 00000038  invoke-virtual      SecureRandom->nextBytes([B)V, v4, v5
32 0000003E  invoke-virtual      MessageDigest->update([B)V, v1, v5
33 00000044  invoke-virtual      SecureRandom->nextBytes([B)V, v4, v5
34 0000004A  invoke-virtual      MessageDigest->update([B)V, v1, v5
35 00000050  new-instance        v4, BigInteger
36 00000054  invoke-virtual      MessageDigest->digest()[B, v1
37 0000005A  move-result-object  v1
38 0000005C  invoke-direct       BigInteger-><init>(I, [B)V, v4, v3, v1
39 00000062  const/16            v1, 0x0010
40 00000066  invoke-virtual      BigInteger->toString(I)String, v4, v1  # 随机数后SHA1值
41 0000006C  move-result-object  v1
42 0000006E  iput-object         v1, p0, AppGuardEngine->IiIIiiiiii_Random_SHA1:String  # 解密后文件名
43 //解密并调用
44 00000D7A  new-instance        v4, qb
45 00000D7E  invoke-static       JNISoxProxy->getContext()Context
46 00000D84  move-result-object  v5
47 00000D86  invoke-direct       qb-><init>(Context)V, v4, v5
48 00000D8C  const-string        v5, "fChOdMcDo\\u0004yEr"  # libengine.sox
49 00000D90  invoke-virtual      qb->iiIIIiiiIi([B)V, v4, v1
50 00000D96  invoke-static       b->iiIIIiiiIi(String)String, v5
51 00000D9C  move-result-object  v1
52 00000D9E  iget-object         v5, p0, AppGuardEngine->IiIIiiiiii_Random_SHA1:String
53 00000DA2  const/4             v6, 0
54 00000DA4  invoke-virtual      qb->iiIIIiiiIi(String, String, [B)Z, v4, v1, v5, v6  # 解密libengine.sox(最终传入so层解密private static native byte[] iIiIIIiIiI(byte[] arg0, int arg1))
55 :DAA
56 00000DAA  move-object         v0, p0
57 :DAC
58 00000DAC  invoke-static       JNISoxProxy->getContext()Context
59 00000DB2  move-result-object  v1
60 00000DB4  new-instance        v4, StringBuilder
61 00000DB8  invoke-direct       StringBuilder-><init>()V, v4
62 00000DBE  invoke-static       JNISoxProxy->getContext()Context
63 00000DC4  move-result-object  v5
64 00000DC6  invoke-virtual      Context->getFilesDir()File, v5
65 00000DCC  move-result-object  v5
66 00000DCE  invoke-virtual      File->getAbsolutePath()String, v5
67 00000DD4  move-result-object  v5
68 00000DD6  invoke-virtual      StringBuilder->insert(I, String)StringBuilder, v4, v2, v5
69 00000DDC  move-result-object  v4
70 00000DDE  const-string        v5, "%"  # /
71 00000DE2  invoke-static       b->iiIIIiiiIi(String)String, v5
72 00000DE8  move-result-object  v5
73 00000DEA  invoke-virtual      StringBuilder->append(String)StringBuilder, v4, v5
74 00000DF0  move-result-object  v4
75 00000DF2  iget-object         v5, p0, AppGuardEngine->IiIIiiiiii_Random_SHA1:String
76 00000DF6  invoke-virtual      StringBuilder->

以上是关于nProtect APPGuard安卓反外挂分析的主要内容,如果未能解决你的问题,请参考以下文章

帧同步(LockStep)该如何反外挂

合金弹头 逆向分析与外挂制作报告内联HOOK

独家开放反外挂能力 WeTest手游安全测试服务登陆腾讯云(内含《龙之谷手游》反外挂实战)

对反游戏外挂技术的思考及实现

iOS 反外挂产品对外开放试用,八大核心功能,为游戏保驾护航

反模拟类游戏外挂 转