目录
一、加固后的APK包
1)为什么要加固?
- 一定程度保护源代码
加壳又称加固技术为了加强Android保护强度,随着安全技术的发展,又出现了新型的“加固技术”。Dex加固是对Dex文件进行加壳防护,防止被静态反编译工具破解而泄露源码,
整体加固技术的原理如上所示,包括替换application/classes.dex、解密/动态加载原classes.dex、调用原application相关方法、将原application对象/名称设置到系统内部相关变量四大环节。其中最为关键的一步就是解密/动态加载原classes.dex,通过加密编译好的最终Dex源码文件,然后在一个新项目中用新项目的application启动来解密原项目代码并加载到内存中,再把当前进程替换为解密后的代码,能够很好地隐藏源码并防止直接性的反编译。
2)加固方式?
.dex
加固.so
加固
一般在哪家应用市场发布,就要用哪家的加固方案。
APP常用加固厂商特征:
娜迦: libchaosvmp.so, libddog.so,libfdog.so
爱加密:libexec.so,libexecmain.so,ijiami.dat
梆梆: libsecexe.so,libsecmain.so , libDexHelper.so
360:libprotectClass.so,libjiagu.so, libjiagu_art.so,libjiagu_x86.so
通付盾:libegis.so,libNSaferOnly.so
网秦:libnqshield.so
百度:libbaiduprotect.so
腾讯:libshellx-2.10.6.0.so,libBugly.so,libtup.so, libexec.so,libshell.so
阿里聚安全:aliprotect.dat,libsgmain.so,libsgsecuritybody.so
腾讯御安全:libtosprotection.armeabi.so,libtosprotection.armeabi-v7a.so,libtosprotection.x86.so
网易易盾:libnesec.so
APKProtect:libAPKProtect.so
几维安全:libkwscmm.so, libkwscr.so, libkwslinker.so
3)查看app是否被加固
使用反编译工具,比如:apktool或者Android Killer 反编译一下代码就知道了,能否看到源代码或者看到的源代码是不是有意义,没意义就是代码被混淆过了,比如全是a,b,c,d这样的代码或者方法名,你看不懂就是被混淆过了,没有源代码就是被加固了。
加壳还会加入特定的so文件进入。所以可以根据包名或者SO名来判断是否加壳以及加了什么壳(但不是说apk中有so就是加壳的,很多apk为了提升性能,增强安全性,也会用so来实现一些重要的功能)。用一些工具比如GDA(自己写的)、apk查壳PKiDApk、Detecter查壳工具等等可以直接识别出壳,甚至还能识别版本号。
例如我们将apk文件后缀改为zip后解压缩:
如果该目录下没有显示加固特征可以进入\lib\armeabi
目录下查看.so
文件
借助搜索引擎直接搜索
二、了解加固后的app启动流程
app启动流程:https://www.jianshu.com/p/910df9643d8c
加固后APK运行
- 先加载壳APP
- 壳APP读取Dex文件末尾的源APK大小
- 在内存中壳APP解密出源APP
- 运行源APP
壳有自己的Application对象
源APK有自己的Application对象
没有加固的app在程序启动后首先会进入自己的application
然后找到入口函数android:name="com.xxx.xxx.xxx
经过加固后的app首先会进入壳的application
然后找到壳的入口函数进行相关的解密(脱壳)操作,解密后在进入apk自己的application
然后找到入口函数
解密源码
三、FDex2脱壳工具使用
实验环境
- 夜神模拟器6.2.1.1
- xposed2.7
- FDex2(Hook ClassLoader loadClass方法 通用脱壳 )
1、打开xposed模块下面的FDex2
2、选择要脱壳的app
注意:点击ok后需要打开目标app后脱壳后的dex才会保存在对应的路径下。
3、找到对应路径下脱壳后的dex文件
4、利用adb工具将脱壳后的dex文件导出到pc
adb pull /data/data/com.iCitySuzhou.suzhou001/com.iCitySuzhou.suzhou001191572.dex D:\工具\安卓反编译
5、利用jadx反编译脱壳后的dex查看源码
可以看到在导出的5个dex文件中,该文件是这个app的源码,且代码进行了混淆。
四、分析加密参数signature
1、全局搜索参数signature
2、定位关键代码
我们通过抓包工具获取的数据包
之所以定位到如下代码,因为代码中的参数正是我们通过抓包工具获取的其中包含了signature
3、跟进查看代码
注意:反混淆并不是将代码完全还原,只是将原有相同的方法名(a/b)进行重命名
import android.os.Build.VERSION;
import com.hualong.framework.C2407a;
import com.hualong.framework.LibApplication;
import com.hualong.framework.p170b.C2408a;
import com.hualong.framework.p172d.C2415a;
import java.io.IOException;
import p001a.C0002u;
import p001a.C0002u.C0003a;
import p001a.C0121aa;
import p001a.C0127ac;
/* renamed from: com.iCitySuzhou.suzhou001.d.d */
public class C2651d implements C0002u {
/* renamed from: a */
public C0127ac mo4a(C0003a c0003a) throws IOException {
C0121aa a = c0003a.mo5a();
String udid = LibApplication.getInstance().getUDID();
String valueOf = String.valueOf(System.currentTimeMillis() / 1000);
C0127ac a2 = c0003a.mo6a(a.mo317e().mo311b("sys", "Android").mo311b("sysVersion", VERSION.RELEASE).mo311b("appVersion", LibApplication.getInstance().getAppVersion()).mo311b("appVersionCode", String.valueOf(LibApplication.getInstance().getAppVersionCode())).mo311b("udid", udid).mo311b("clientType", "android").mo311b("timestamp", valueOf).mo311b("signature", C2651d.m12099a(udid, valueOf)).mo309a());
if (C2407a.m11253a()) {
float l = ((float) (a2.mo352l() - a2.mo351k())) / 1000.0f;
C2415a.m11307c("LoggingInterceptor", " ");
C2415a.m11307c("LoggingInterceptor", " ");
C2415a.m11307c("LoggingInterceptor", String.format("[%s] %s, %d in %.2f ms", new Object[]{a.mo314b(), a.mo312a(), Integer.valueOf(a2.mo341b()), Float.valueOf(l)}));
C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
C2415a.m11307c("LoggingInterceptor", " Request Headers ");
C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
C2415a.m11307c("LoggingInterceptor", String.format("%s", new Object[]{a.mo315c()}));
C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
C2415a.m11307c("LoggingInterceptor", " Response Headers ");
C2415a.m11307c("LoggingInterceptor", "-----------------------------------");
C2415a.m11307c("LoggingInterceptor", String.format("%s", new Object[]{a2.mo346f()}));
C2415a.m11307c("LoggingInterceptor", " ");
C2415a.m11307c("LoggingInterceptor", " ");
}
return a2;
}
/* renamed from: a */
public static String m12099a(String str, String str2) {
return C2408a.m11256a(String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}));
}
}
分析如下代码:
C0127ac a2 = c0003a.mo6a(a.mo317e().mo311b("sys", "Android").mo311b("sysVersion", VERSION.RELEASE).mo311b("appVersion", LibApplication.getInstance().getAppVersion()).mo311b("appVersionCode", String.valueOf(LibApplication.getInstance().getAppVersionCode())).mo311b("udid", udid).mo311b("clientType", "android").mo311b("timestamp", valueOf).mo311b("signature", C2651d.m12099a(udid, valueOf)).mo309a());
结合抓包情况进行分析,大概猜出上述代码实际上就是给参数进行赋值操作。
例如:
sys=Android
sysVersion=VERSION.RELEASE(获取版本)
关键参数signature:
ignature=C2651d.m12099a(udid, valueOf)
该参数调用了m12099a
方法并传入了两个参数udid
和valueOf
由上面代码可查看到:
String udid = LibApplication.getInstance().getUDID(); // 抓包中可以获取(不变的值)
String valueOf = String.valueOf(System.currentTimeMillis() / 1000); // 时间戳
两个参数已经明确,接下来需要找到m12099a
方法:
public static String m12099a(String str, String str2) {
return C2408a.m11256a(String.format("%s&&%s&&%s", new Object[]{str, str2, "f1190aca-d08e-4041-8666-29931cd89dde"}));
}
分析上述代码可知是将传入的两个参数(udid
和valueOf
)与"f1190aca-d08e-4041-8666-29931cd89dde"
进行格式化拼接后传给m11256a
方法进行处理,处理后的结果返回。
IMEI8661740 10682363-IMSI4600 768236 19756 && 当前时间戳 && f1190aca-d08e-4041-8666-29931cd89dde
查找m11256a
方法
import java.security.MessageDigest;
/* renamed from: com.hualong.framework.b.a */
public class C2408a {
/* renamed from: a */
public static String m11256a(String str) {
if (str == null) {
return null;
}
StringBuffer stringBuffer = new StringBuffer();
try {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(str.getBytes());
for (byte b : instance.digest()) {
stringBuffer.append(Integer.toString((b >>> 4) & 15, 16)).append(Integer.toString(b & 15, 16));
}
} catch (Exception e) {
}
return stringBuffer.toString();
}
}
分析可以发现上面代码就是最后将格式化字符串加密的地方,也就是signature生成的地方。
如果代码实现原理简单我们完全可以用python实现,如果过于复杂我们可以将上面的代码编译成jar包使用python进行调用。
五、将Java代码编译成jar包
import java.security.MessageDigest;
/* renamed from: com.hualong.framework.b.a */
public class MySig {
/* renamed from: a */
public static String m11256a(String str) {
if (str == null) {
return null;
}
StringBuffer stringBuffer = new StringBuffer();
try {
MessageDigest instance = MessageDigest.getInstance("MD5");
instance.update(str.getBytes());
for (byte b : instance.digest()) {
stringBuffer.append(Integer.toString((b >>> 4) & 15, 16)).append(Integer.toString(b & 15, 16));
}
} catch (Exception e) {
}
return stringBuffer.toString();
}
}
/*javac MySig.java编译时类名和文件名要一致*/
上面代码复制到文本文件重命名为MySig.java
注意:类名要和java文件名相同
1、编译Java成class
javac MySig.java
2、把class做成jar
jar cvf MySig.jar 打包目录
六、python调用jar包
参考文章:
https://blog.csdn.net/xqtesting/article/details/79895829
https://blog.csdn.net/zhusongziye/article/details/92066686
参考文章: