在Android平台上,程序员编写的Java代码最终将被编译成字节码在Android虚拟机上运行。自从Android进入大众的视野后,apktool,jadx等反编译工具也层出不穷,功能也越来越强大,由Java编译成的字节码在这些反编译工具面前变得不堪一击,这相当于一个人裸奔在茫茫人海,身体的各个部位被众人一览无余。一种事物的出现,也会有与之对立的事物出现。有反编译工具的出现,当然也会出现反反编译工具的出现,这种技术一般我们加固技术。APP经过加固,就相当于给那个裸奔的人穿了衣服,“衣服”在一定程度上保护了APP,使APP没那么容易被反编译。当然,有加固技术的出现,也会有反加固技术的出现,即本文要分析的脱壳技术。
Android经过多个版本的更迭,它无论在外观还是内在都有许多改变,早期的Android使用的是dalvik虚拟机,Android4.4开始加入ART虚拟机,但不默认启用。从Android5.0开始,ART取代dalvik,成为默认虚拟机。由于dalvik和ART运行机制的不同,在它们内部脱壳原理也不太相同,本文分析的是ART下的脱壳方案:FART。它的整体思路是通过主动调用的方式来实现脱壳,项目地址:https://github.com/hanbinglengyue/FART
2. 流程分析FART的入口在frameworks\base\core\java\android\app\ActivityThread.java
的performLaunchActivity函数中,即APP的Activity启动的时候执行fartthread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Log.e("ActivityThread","go into performLaunchActivity"); ActivityInfo aInfo = r.activityInfo; if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo, Context.CONTEXT_INCLUDE_CODE); } ...... //开启fart线程 fartthread(); ......}
fartthread函数开启一个线程,休眠一分钟后调用fart函数
public static void fartthread() {
new Thread(new Runnable() {
@Override public void run() {
try {
Log.e("ActivityThread", "start sleep,wait for fartthread start......"); Thread.sleep(1 * 60 * 1000); } catch (InterruptedException e) {
e.printStackTrace(); } Log.e("ActivityThread", "sleep over and start fartthread"); fart(); Log.e("ActivityThread", "fart run over"); } }).start();}
fart函数中,获取Classloader,反射获取一些类。反射调用dalvik.system.DexPathList
的dexElements字段得到dalvik.system.DexPathList$Element
类对象数组,Element类存储着dex的路径等信息。接下来通过遍历dexElements
,得到每一个Element对象中的DexFile对象,再获取DexFile对象中的mCookie字段值,调用DexFile类中的String[] getClassNameList(Object cookie)
函数并传入获取到mCookie,以得到dex文件中所有的类名。随后,遍历dex中的所有类名,传入loadClassAndInvoke
函数。
public static void fart() {
ClassLoader appClassloader = getClassloader(); List<Object> dexFilesArray = new ArrayList<Object>(); Field pathList_Field = (Field) getClassField(appClassloader, "dalvik.system.BaseDexClassLoader", "pathList"); Object pathList_object = getFieldOjbect("dalvik.system.BaseDexClassLoader", appClassloader, "pathList"); Object[] ElementsArray = (Object[]) getFieldOjbect("dalvik.system.DexPathList", pathList_object, "dexElements"); Field dexFile_fileField = null; try {
dexFile_fileField = (Field) getClassField(appClassloader, "dalvik.system.DexPathList$Element", "dexFile"); } catch (Exception e) {
e.printStackTrace(); } Class DexFileClazz = null; try {
DexFileClazz = appClassloader.loadClass("dalvik.system.DexFile"); } catch (Exception e) {
e.printStackTrace(); } Method getClassNameList_method = null; Method defineClass_method = null; Method dumpDexFile_method = null; Method dumpMethodCode_method = null; for (Method field : DexFileClazz.getDeclaredMethods()) {
if (field.getName().equals("getClassNameList")) {
getClassNameList_method = field; getClassNameList_method.setAccessible(true); } if (field.getName().equals("defineClassNative")) {
defineClass_method = field; defineClass_method.setAccessible(true); } if (field.getName().equals("dumpMethodCode")) {
dumpMethodCode_method = field; dumpMethodCode_method.setAccessible(true); } } Field mCookiefield = getClassField(appClassloader, "d