个人博客: 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.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代码。
4. 解密libstub.sox模块。
解密函数在类com/inca/security/qb中iiIIIiiiIi函数,代码如下:
@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;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!