runtime/native/dalvik_system_DexFile.cc
在这里实现DexFile类的函数private static native void dumpMethodCode(Object methodid);
一、背景
二、如何逆向system.img呢?
1、simg2img system.img system.img.ext4
2、sudo mkdir sysmain
3、sudo mount -t ext4 -o loop system.img.ext4 sysmain
进入到sysmain中,找到framework.jar、core-libart.jar、libart.so,主要涉及修改的是
framework.jar中的ActivityThread.java、core-libart.jar中的DexFile.java、libart.so中的libdexfile/dex/standard_dex_file.h、runtime/art_method-inl.h、runtime/art_method.h、runtime/native/dalvik_system_DexFile.cc。
将framework.jar改为
framework.zip解压后得到classes.dex,使用dex2jar,jd-gui转换为java代码查看,但jd-guid无法将核心的smali代码转为java代码。
所以只能通过阅读smali来了解脱壳思路,使用dex2smali将dex转换为smali,我们主要看ActivityThread.smali;
同理core-libart.jar也是同样的思路,最终我们得到DexFile.java,在这里只是加了一个函数,这个函数时个native方法,我们会在libart.so里面实现,在
ActivityThread.smali 里面调用,如何调用呢,我们看接下来的分析。
public classs DexFile {
+private static native void dumpMethodCode(Object methodid);
}
public classs DexFile {
+private static native void dumpMethodCode(Object methodid);
}
三、分析Java层脱壳代码
我们先看
ActivityThread.smali里面核心的脱壳代码:
.method public static fart()V
.catch Ljava/lang/Exception; { :L0 .. :L1 } :L11
.catch Ljava/lang/Exception; { :L3 .. :L4 } :L12
.catch Ljava/lang/IllegalAccessException; { :L15 .. :L16 } :L19
.catch Ljava/lang/IllegalAccessException; { :L21 .. :L22 } :L25
.catch Ljava/lang/reflect/InvocationTargetException; { :L21 .. :L22 } :L24
.registers 30
.prologue
.line 701
invoke-static { }, Landroid/app/ActivityThread;->getClassloader()Ljava/lang/ClassLoader;
move-result-object v5
.line 702
.local v5, appClassloader:Ljava/lang/ClassLoader;
new-instance v9, Ljava/util/ArrayList;
invoke-direct { v9 }, Ljava/util/ArrayList;-><init>()V
.line 703
.local v9, dexFilesArray:Ljava/util/List;, "Ljava/util/List<Ljava/lang/Object;>;"
const-string/jumbo v25, "dalvik.system.BaseDexClassLoader"
const-string/jumbo v26, "pathList"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
move-result-object v23
.line 705
.local v23, pathList_Field:Ljava/lang/reflect/Field;
const-string/jumbo v25, "dalvik.system.BaseDexClassLoader"
const-string/jumbo v26, "pathList"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v0, v5, v1 }, Landroid/app/ActivityThread;->getFieldOjbect(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v24
.line 706
.local v24, pathList_object:Ljava/lang/Object;
const-string/jumbo v25, "dalvik.system.DexPathList"
const-string/jumbo v26, "dexElements"
move-object/from16 v0, v25
move-object/from16 v1, v24
move-object/from16 v2, v26
invoke-static { v0, v1, v2 }, Landroid/app/ActivityThread;->getFieldOjbect(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v4
check-cast v4, [Ljava/lang/Object;
.line 707
.local v4, ElementsArray:[Ljava/lang/Object;
const/4 v8, 0
:L0
.line 709
.local v8, dexFile_fileField:Ljava/lang/reflect/Field;
const-string/jumbo v25, "dalvik.system.DexPathList$Element"
const-string/jumbo v26, "dexFile"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
:L1
move-result-object v8
:L2
.line 713
.end local v8
const/4 v3, 0
:L3
.line 715
.local v3, DexFileClazz:Ljava/lang/Class;
const-string/jumbo v25, "dalvik.system.DexFile"
move-object/from16 v0, v25
invoke-virtual { v5, v0 }, Ljava/lang/ClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;
:L4
move-result-object v3
:L5
.line 719
.end local v3
const/16 v19, 0
.line 720
.local v19, getClassNameList_method:Ljava/lang/reflect/Method;
const/4 v7, 0
.line 721
.local v7, defineClass_method:Ljava/lang/reflect/Method;
const/4 v11, 0
.line 722
.local v11, dumpDexFile_method:Ljava/lang/reflect/Method;
const/4 v12, 0
.line 724
.local v12, dumpMethodCode_method:Ljava/lang/reflect/Method;
invoke-virtual { v3 }, Ljava/lang/Class;->getDeclaredMethods()[Ljava/lang/reflect/Method;
move-result-object v26
const/16 v25, 0
move-object/from16 v0, v26
array-length v0, v0
move/from16 v27, v0
:L6
.end local v7
.end local v11
.end local v12
.end local v19
move/from16 v0, v25
move/from16 v1, v27
if-ge v0, v1, :L13
aget-object v18, v26, v25
.line 725
.local v18, field:Ljava/lang/reflect/Method;
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "getClassNameList"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L7
.line 726
move-object/from16 v19, v18
.line 727
.local v19, getClassNameList_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move-object/from16 v0, v19
move/from16 v1, v28
invoke-virtual { v0, v1 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L7
.line 729
.end local v19
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "defineClassNative"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L8
.line 730
move-object/from16 v7, v18
.line 731
.local v7, defineClass_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v7, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L8
.line 733
.end local v7
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "dumpDexFile"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L9
.line 734
move-object/from16 v11, v18
.line 735
.local v11, dumpDexFile_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v11, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L9
.line 737
.end local v11
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "dumpMethodCode"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L10
.line 738
move-object/from16 v12, v18
.line 739
.local v12, dumpMethodCode_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v12, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L10
.line 724
.end local v12
add-int/lit8 v25, v25, 1
goto :L6
:L11
.line 710
.end local v18
.restart local v8
move-exception v13
.line 711
.local v13, e:Ljava/lang/Exception;
const-string/jumbo v25, "ActivityThread->err"
invoke-static { v13 }, Landroid/util/Log;->getStackTraceString(Ljava/lang/Throwable;)Ljava/lang/String;
move-result-object v26
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
goto/16 :L2
:L12
.line 716
.end local v8
.end local v13
.restart local v3
move-exception v13
.line 717
.restart local v13
const-string/jumbo v25, "ActivityThread->err"
invoke-static { v13 }, Landroid/util/Log;->getStackTraceString(Ljava/lang/Throwable;)Ljava/lang/String;
move-result-object v26
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
goto/16 :L5
:L13
.line 742
.end local v3
.end local v13
const-string/jumbo v25, "dalvik.system.DexFile"
const-string/jumbo v26, "mCookie"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
move-result-object v21
.line 743
.local v21, mCookiefield:Ljava/lang/reflect/Field;
const/16 v20, 0
:L14
.local v20, j:I
array-length v0, v4
move/from16 v25, v0
move/from16 v0, v20
move/from16 v1, v25
if-ge v0, v1, :L26
.line 744
aget-object v17, v4, v20
.line 745
.local v17, element:Ljava/lang/Object;
const/4 v10, 0
:L15
.line 747
.local v10, dexfile:Ljava/lang/Object;
move-object/from16 v0, v17
invoke-virtual { v8, v0 }, Ljava/lang/reflect/Field;->get(Ljava/lang/Object;)Ljava/lang/Object;
:L16
move-result-object v10
:L17
.line 751
.end local v10
if-nez v10, :L20
.line 752
const-string/jumbo v25, "ActivityThread"
const-string/jumbo v26, "dexfile is null"
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
:L18
.line 743
add-int/lit8 v20, v20, 1
goto :L14
:L19
.line 748
.restart local v10
move-exception v14
.line 749
.local v14, e:Ljava/lang/IllegalAccessException;
invoke-virtual { v14 }, Ljava/lang/IllegalAccessException;->printStackTrace()V
goto :L17
:L20
.line 755
.end local v10
.end local v14
if-eqz v10, :L18
.line 756
invoke-interface { v9, v10 }, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 757
const-string/jumbo v25, "dalvik.system.DexFile"
const-string/jumbo v26, "mCookie"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v10, v1 }, Landroid/app/ActivityThread;->getClassFieldObject(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v22
.line 758
.local v22, mcookie:Ljava/lang/Object;
if-eqz v22, :L18
.line 761
const/4 v6, 0
.line 763
.local v6, classnames:[Ljava/lang/String;
const/16 v25, 1
:L21
move/from16 v0, v25
new-array v0, v0, [Ljava/lang/Object;
move-object/from16 v25, v0
const/16 v26, 0
aput-object v22, v25, v26
move-object/from16 v0, v19
move-object/from16 v1, v25
invoke-virtual { v0, v10, v1 }, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
move-result-object v6
.end local v6
check-cast v6, [Ljava/lang/String;
:L22
.line 771
.local v6, classnames:[Ljava/lang/String;
if-eqz v6, :L18
.line 772
const/16 v25, 0
array-length v0, v6
move/from16 v26, v0
:L23
move/from16 v0, v25
move/from16 v1, v26
if-ge v0, v1, :L18
aget-object v16, v6, v25
.line 773
.local v16, eachclassname:Ljava/lang/String;
move-object/from16 v0, v16
invoke-static { v5, v0, v12 }, Landroid/app/ActivityThread;->loadClassAndInvoke(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/reflect/Method;)V
.line 772
add-int/lit8 v25, v25, 1
goto :L23
:L24
.line 767
.end local v6
.end local v16
move-exception v15
.line 768
.local v15, e:Ljava/lang/reflect/InvocationTargetException;
invoke-virtual { v15 }, Ljava/lang/reflect/InvocationTargetException;->printStackTrace()V
goto :L18
:L25
.line 764
.end local v15
move-exception v14
.line 765
.restart local v14
invoke-virtual { v14 }, Ljava/lang/IllegalAccessException;->printStackTrace()V
goto :L18
:L26
.line 779
.end local v14
.end local v17
.end local v22
return-void
.end method
.method public static fartthread()V
.registers 2
.prologue
.line 783
new-instance v0, Ljava/lang/Thread;
new-instance v1, Landroid/app/ActivityThread$1;
invoke-direct { v1 }, Landroid/app/ActivityThread$1;-><init>()V
invoke-direct { v0, v1 }, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
invoke-virtual { v0 }, Ljava/lang/Thread;->start()V
.line 782
return-void
.end method
转换为java代码如下:
public static void fart() {
try {
ClassLoader class_loader = getClassloader();
Method[] md = class_loader.loadClass("dalvik.system.DexFile").getDeclaredMethods();
Method getClassNameListMethod = null;
Method dumpMethodCodeMethod = null;
int mdCount = md.length;
for (int i = 0; i < mdCount; i++) {
if (md[i].getName().equals("getClassNameList")) {
getClassNameListMethod = md[i];//获取DexFile类的getClassNameList方法
md[i].setAccessible(true);
} else if (md[i].getName().equals("dumpMethodCode")) {
dumpMethodCodeMethod = md[i];//获取DexFile类的dumpMethodCode方法
md[i].setAccessible(true);
}
}
Object[] dexElementsObjs = (Object[]) getFieldOjbect("dalvik.system.DexPathList", getFieldOjbect("dalvik.system.BaseDexClassLoader", class_loader, "pathList"), "dexElements");
Field dexFileField = getClassField(class_loader, "dalvik.system.DexPathList$Element", "dexFile");
for (int i = 0; i < dexElementsObjs.length; i++) {
Object dexFileObj = dexFileField.get(dexElementsObjs[i]);
Object cookObj = getClassFieldObject(class_loader, "dalvik.system.DexFile", dexFileObj, "mCookie");//获取mCookie
String[] classNames = (String[]) getClassNameListMethod.invoke(dexFileObj, new Object[]{cookObj});//调用DexFile类的getClassNameList获取dex中所有类名
for (int j = 0; j < classNames.length; j++) {
Log.e(TAG, "fart classNames:" + classNames[j]);
loadClassAndInvoke(class_loader, classNames[j], dumpMethodCodeMethod);
}
}
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
}
public static void loadClassAndInvoke(ClassLoader class_loader, String className, Method dumpMethodCodeMethod) {
try {
Class class1 = class_loader.loadClass(className);//主动加载dex中的所有类,此时Method数据已解密
Constructor[] constructors = class1.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{constructors[i]});//调用DexFile中dumpMethodCode方法,参数为Constructor对象
}
Method[] methods = class1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{methods[i]});//调用DexFile中dumpMethodCode方法,参数为Method对象
}
Log.e(TAG, "className:" + className + ",constructors length:" + constructors.length + ",method length:" + methods.length);
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
return;
}
public static void fartthread() {
(new Thread(new Runnable() {
public void run() {
try {
Log.e("ActivityThread", "start sleep......");
Thread.sleep(10000L);//睡眠10s钟
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
Log.e("ActivityThread", "sleep over and start fart");
ActivityThread.fart();//调用脱壳程序
Log.e("ActivityThread", "fart run over");
}
})).start();
}
主要是开启了一个线程,睡眠10s后开始干活,fart方法执行流程如下:
1、通过反射获取了DexFile类的getClassNameList方法和dumpMethodCode方法,dumpMethodCode我们刚刚在DexFile里面填加上的native方法。
2、通过当前进程的classloader,一步一步根据如下类结构pathList->dexElements->dexFile->mCookie进一步获取到当前classloader所加载的dexfile的mCookie,这个
mCookie 是native层所加载dex文件结构的标识。
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
......
}
package*/ final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
private Element[] dexElements;
......
}
/*package*/ static class Element {
private final DexFile dexFile;
......
}
public final class DexFile {
private Object mCookie;
private static native void dumpMethodCode(Object methodid);
private static native String[] getClassNameList(Object cookie);
....
}
那么当前进程的classlodaer如何获取的呢?
public static ClassLoader getClassloader() {
Object currentActivityThread = invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class[]{}, new Object[]{});
Object mBoundApplication = getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mBoundApplication");
return ((Application) getFieldOjbect("android.app.LoadedApk", getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "info"), "mApplication")).getClassLoader();
}
通过反射调用
ActivityThread 类的静态函数
currentActivityThread
获取当前的
ActivityThread对象,然后获取
ActivityThread对象的mBoundApplication成员变量,t之后获取mBoundApplication对象的info成员变量,他是个LoadedApk类型;最终获取
info对象的mApplication成员变量,他的类型是Application,最后通过调用
Application.getClassLoader得到当前进程的classloader。理解整个流程请参考下面的类关系:
public final class ActivityThread extends ClientTransactionHandler {
AppBindData mBoundApplication;
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
static final class AppBindData {
LoadedApk info;
......
}
......
}
public final class LoadedApk {
private Application mApplication;
......
} .method public static fart()V
.catch Ljava/lang/Exception; { :L0 .. :L1 } :L11
.catch Ljava/lang/Exception; { :L3 .. :L4 } :L12
.catch Ljava/lang/IllegalAccessException; { :L15 .. :L16 } :L19
.catch Ljava/lang/IllegalAccessException; { :L21 .. :L22 } :L25
.catch Ljava/lang/reflect/InvocationTargetException; { :L21 .. :L22 } :L24
.registers 30
.prologue
.line 701
invoke-static { }, Landroid/app/ActivityThread;->getClassloader()Ljava/lang/ClassLoader;
move-result-object v5
.line 702
.local v5, appClassloader:Ljava/lang/ClassLoader;
new-instance v9, Ljava/util/ArrayList;
invoke-direct { v9 }, Ljava/util/ArrayList;-><init>()V
.line 703
.local v9, dexFilesArray:Ljava/util/List;, "Ljava/util/List<Ljava/lang/Object;>;"
const-string/jumbo v25, "dalvik.system.BaseDexClassLoader"
const-string/jumbo v26, "pathList"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
move-result-object v23
.line 705
.local v23, pathList_Field:Ljava/lang/reflect/Field;
const-string/jumbo v25, "dalvik.system.BaseDexClassLoader"
const-string/jumbo v26, "pathList"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v0, v5, v1 }, Landroid/app/ActivityThread;->getFieldOjbect(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v24
.line 706
.local v24, pathList_object:Ljava/lang/Object;
const-string/jumbo v25, "dalvik.system.DexPathList"
const-string/jumbo v26, "dexElements"
move-object/from16 v0, v25
move-object/from16 v1, v24
move-object/from16 v2, v26
invoke-static { v0, v1, v2 }, Landroid/app/ActivityThread;->getFieldOjbect(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v4
check-cast v4, [Ljava/lang/Object;
.line 707
.local v4, ElementsArray:[Ljava/lang/Object;
const/4 v8, 0
:L0
.line 709
.local v8, dexFile_fileField:Ljava/lang/reflect/Field;
const-string/jumbo v25, "dalvik.system.DexPathList$Element"
const-string/jumbo v26, "dexFile"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
:L1
move-result-object v8
:L2
.line 713
.end local v8
const/4 v3, 0
:L3
.line 715
.local v3, DexFileClazz:Ljava/lang/Class;
const-string/jumbo v25, "dalvik.system.DexFile"
move-object/from16 v0, v25
invoke-virtual { v5, v0 }, Ljava/lang/ClassLoader;->loadClass(Ljava/lang/String;)Ljava/lang/Class;
:L4
move-result-object v3
:L5
.line 719
.end local v3
const/16 v19, 0
.line 720
.local v19, getClassNameList_method:Ljava/lang/reflect/Method;
const/4 v7, 0
.line 721
.local v7, defineClass_method:Ljava/lang/reflect/Method;
const/4 v11, 0
.line 722
.local v11, dumpDexFile_method:Ljava/lang/reflect/Method;
const/4 v12, 0
.line 724
.local v12, dumpMethodCode_method:Ljava/lang/reflect/Method;
invoke-virtual { v3 }, Ljava/lang/Class;->getDeclaredMethods()[Ljava/lang/reflect/Method;
move-result-object v26
const/16 v25, 0
move-object/from16 v0, v26
array-length v0, v0
move/from16 v27, v0
:L6
.end local v7
.end local v11
.end local v12
.end local v19
move/from16 v0, v25
move/from16 v1, v27
if-ge v0, v1, :L13
aget-object v18, v26, v25
.line 725
.local v18, field:Ljava/lang/reflect/Method;
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "getClassNameList"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L7
.line 726
move-object/from16 v19, v18
.line 727
.local v19, getClassNameList_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move-object/from16 v0, v19
move/from16 v1, v28
invoke-virtual { v0, v1 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L7
.line 729
.end local v19
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "defineClassNative"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L8
.line 730
move-object/from16 v7, v18
.line 731
.local v7, defineClass_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v7, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L8
.line 733
.end local v7
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "dumpDexFile"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L9
.line 734
move-object/from16 v11, v18
.line 735
.local v11, dumpDexFile_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v11, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L9
.line 737
.end local v11
invoke-virtual/range { v18 .. v18 }, Ljava/lang/reflect/Method;->getName()Ljava/lang/String;
move-result-object v28
const-string/jumbo v29, "dumpMethodCode"
invoke-virtual/range { v28 .. v29 }, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v28
if-eqz v28, :L10
.line 738
move-object/from16 v12, v18
.line 739
.local v12, dumpMethodCode_method:Ljava/lang/reflect/Method;
const/16 v28, 1
move/from16 v0, v28
invoke-virtual { v12, v0 }, Ljava/lang/reflect/Method;->setAccessible(Z)V
:L10
.line 724
.end local v12
add-int/lit8 v25, v25, 1
goto :L6
:L11
.line 710
.end local v18
.restart local v8
move-exception v13
.line 711
.local v13, e:Ljava/lang/Exception;
const-string/jumbo v25, "ActivityThread->err"
invoke-static { v13 }, Landroid/util/Log;->getStackTraceString(Ljava/lang/Throwable;)Ljava/lang/String;
move-result-object v26
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
goto/16 :L2
:L12
.line 716
.end local v8
.end local v13
.restart local v3
move-exception v13
.line 717
.restart local v13
const-string/jumbo v25, "ActivityThread->err"
invoke-static { v13 }, Landroid/util/Log;->getStackTraceString(Ljava/lang/Throwable;)Ljava/lang/String;
move-result-object v26
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
goto/16 :L5
:L13
.line 742
.end local v3
.end local v13
const-string/jumbo v25, "dalvik.system.DexFile"
const-string/jumbo v26, "mCookie"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v1 }, Landroid/app/ActivityThread;->getClassField(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/reflect/Field;
move-result-object v21
.line 743
.local v21, mCookiefield:Ljava/lang/reflect/Field;
const/16 v20, 0
:L14
.local v20, j:I
array-length v0, v4
move/from16 v25, v0
move/from16 v0, v20
move/from16 v1, v25
if-ge v0, v1, :L26
.line 744
aget-object v17, v4, v20
.line 745
.local v17, element:Ljava/lang/Object;
const/4 v10, 0
:L15
.line 747
.local v10, dexfile:Ljava/lang/Object;
move-object/from16 v0, v17
invoke-virtual { v8, v0 }, Ljava/lang/reflect/Field;->get(Ljava/lang/Object;)Ljava/lang/Object;
:L16
move-result-object v10
:L17
.line 751
.end local v10
if-nez v10, :L20
.line 752
const-string/jumbo v25, "ActivityThread"
const-string/jumbo v26, "dexfile is null"
invoke-static/range { v25 .. v26 }, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
:L18
.line 743
add-int/lit8 v20, v20, 1
goto :L14
:L19
.line 748
.restart local v10
move-exception v14
.line 749
.local v14, e:Ljava/lang/IllegalAccessException;
invoke-virtual { v14 }, Ljava/lang/IllegalAccessException;->printStackTrace()V
goto :L17
:L20
.line 755
.end local v10
.end local v14
if-eqz v10, :L18
.line 756
invoke-interface { v9, v10 }, Ljava/util/List;->add(Ljava/lang/Object;)Z
.line 757
const-string/jumbo v25, "dalvik.system.DexFile"
const-string/jumbo v26, "mCookie"
move-object/from16 v0, v25
move-object/from16 v1, v26
invoke-static { v5, v0, v10, v1 }, Landroid/app/ActivityThread;->getClassFieldObject(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;
move-result-object v22
.line 758
.local v22, mcookie:Ljava/lang/Object;
if-eqz v22, :L18
.line 761
const/4 v6, 0
.line 763
.local v6, classnames:[Ljava/lang/String;
const/16 v25, 1
:L21
move/from16 v0, v25
new-array v0, v0, [Ljava/lang/Object;
move-object/from16 v25, v0
const/16 v26, 0
aput-object v22, v25, v26
move-object/from16 v0, v19
move-object/from16 v1, v25
invoke-virtual { v0, v10, v1 }, Ljava/lang/reflect/Method;->invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
move-result-object v6
.end local v6
check-cast v6, [Ljava/lang/String;
:L22
.line 771
.local v6, classnames:[Ljava/lang/String;
if-eqz v6, :L18
.line 772
const/16 v25, 0
array-length v0, v6
move/from16 v26, v0
:L23
move/from16 v0, v25
move/from16 v1, v26
if-ge v0, v1, :L18
aget-object v16, v6, v25
.line 773
.local v16, eachclassname:Ljava/lang/String;
move-object/from16 v0, v16
invoke-static { v5, v0, v12 }, Landroid/app/ActivityThread;->loadClassAndInvoke(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/reflect/Method;)V
.line 772
add-int/lit8 v25, v25, 1
goto :L23
:L24
.line 767
.end local v6
.end local v16
move-exception v15
.line 768
.local v15, e:Ljava/lang/reflect/InvocationTargetException;
invoke-virtual { v15 }, Ljava/lang/reflect/InvocationTargetException;->printStackTrace()V
goto :L18
:L25
.line 764
.end local v15
move-exception v14
.line 765
.restart local v14
invoke-virtual { v14 }, Ljava/lang/IllegalAccessException;->printStackTrace()V
goto :L18
:L26
.line 779
.end local v14
.end local v17
.end local v22
return-void
.end method
.method public static fartthread()V
.registers 2
.prologue
.line 783
new-instance v0, Ljava/lang/Thread;
new-instance v1, Landroid/app/ActivityThread$1;
invoke-direct { v1 }, Landroid/app/ActivityThread$1;-><init>()V
invoke-direct { v0, v1 }, Ljava/lang/Thread;-><init>(Ljava/lang/Runnable;)V
invoke-virtual { v0 }, Ljava/lang/Thread;->start()V
.line 782
return-void
.end method
转换为java代码如下:
public static void fart() {
try {
ClassLoader class_loader = getClassloader();
Method[] md = class_loader.loadClass("dalvik.system.DexFile").getDeclaredMethods();
Method getClassNameListMethod = null;
Method dumpMethodCodeMethod = null;
int mdCount = md.length;
for (int i = 0; i < mdCount; i++) {
if (md[i].getName().equals("getClassNameList")) {
getClassNameListMethod = md[i];//获取DexFile类的getClassNameList方法
md[i].setAccessible(true);
} else if (md[i].getName().equals("dumpMethodCode")) {
dumpMethodCodeMethod = md[i];//获取DexFile类的dumpMethodCode方法
md[i].setAccessible(true);
}
}
Object[] dexElementsObjs = (Object[]) getFieldOjbect("dalvik.system.DexPathList", getFieldOjbect("dalvik.system.BaseDexClassLoader", class_loader, "pathList"), "dexElements");
Field dexFileField = getClassField(class_loader, "dalvik.system.DexPathList$Element", "dexFile");
for (int i = 0; i < dexElementsObjs.length; i++) {
Object dexFileObj = dexFileField.get(dexElementsObjs[i]);
Object cookObj = getClassFieldObject(class_loader, "dalvik.system.DexFile", dexFileObj, "mCookie");//获取mCookie
String[] classNames = (String[]) getClassNameListMethod.invoke(dexFileObj, new Object[]{cookObj});//调用DexFile类的getClassNameList获取dex中所有类名
for (int j = 0; j < classNames.length; j++) {
Log.e(TAG, "fart classNames:" + classNames[j]);
loadClassAndInvoke(class_loader, classNames[j], dumpMethodCodeMethod);
}
}
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
}
public static void loadClassAndInvoke(ClassLoader class_loader, String className, Method dumpMethodCodeMethod) {
try {
Class class1 = class_loader.loadClass(className);//主动加载dex中的所有类,此时Method数据已解密
Constructor[] constructors = class1.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{constructors[i]});//调用DexFile中dumpMethodCode方法,参数为Constructor对象
}
Method[] methods = class1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{methods[i]});//调用DexFile中dumpMethodCode方法,参数为Method对象
}
Log.e(TAG, "className:" + className + ",constructors length:" + constructors.length + ",method length:" + methods.length);
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
return;
}
public static void fartthread() {
(new Thread(new Runnable() {
public void run() {
try {
Log.e("ActivityThread", "start sleep......");
Thread.sleep(10000L);//睡眠10s钟
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
Log.e("ActivityThread", "sleep over and start fart");
ActivityThread.fart();//调用脱壳程序
Log.e("ActivityThread", "fart run over");
}
})).start();
}
主要是开启了一个线程,睡眠10s后开始干活,fart方法执行流程如下:
1、通过反射获取了DexFile类的getClassNameList方法和dumpMethodCode方法,dumpMethodCode我们刚刚在DexFile里面填加上的native方法。
public static void fart() {
try {
ClassLoader class_loader = getClassloader();
Method[] md = class_loader.loadClass("dalvik.system.DexFile").getDeclaredMethods();
Method getClassNameListMethod = null;
Method dumpMethodCodeMethod = null;
int mdCount = md.length;
for (int i = 0; i < mdCount; i++) {
if (md[i].getName().equals("getClassNameList")) {
getClassNameListMethod = md[i];//获取DexFile类的getClassNameList方法
md[i].setAccessible(true);
} else if (md[i].getName().equals("dumpMethodCode")) {
dumpMethodCodeMethod = md[i];//获取DexFile类的dumpMethodCode方法
md[i].setAccessible(true);
}
}
Object[] dexElementsObjs = (Object[]) getFieldOjbect("dalvik.system.DexPathList", getFieldOjbect("dalvik.system.BaseDexClassLoader", class_loader, "pathList"), "dexElements");
Field dexFileField = getClassField(class_loader, "dalvik.system.DexPathList$Element", "dexFile");
for (int i = 0; i < dexElementsObjs.length; i++) {
Object dexFileObj = dexFileField.get(dexElementsObjs[i]);
Object cookObj = getClassFieldObject(class_loader, "dalvik.system.DexFile", dexFileObj, "mCookie");//获取mCookie
String[] classNames = (String[]) getClassNameListMethod.invoke(dexFileObj, new Object[]{cookObj});//调用DexFile类的getClassNameList获取dex中所有类名
for (int j = 0; j < classNames.length; j++) {
Log.e(TAG, "fart classNames:" + classNames[j]);
loadClassAndInvoke(class_loader, classNames[j], dumpMethodCodeMethod);
}
}
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
}
public static void loadClassAndInvoke(ClassLoader class_loader, String className, Method dumpMethodCodeMethod) {
try {
Class class1 = class_loader.loadClass(className);//主动加载dex中的所有类,此时Method数据已解密
Constructor[] constructors = class1.getDeclaredConstructors();
for (int i = 0; i < constructors.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{constructors[i]});//调用DexFile中dumpMethodCode方法,参数为Constructor对象
}
Method[] methods = class1.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
dumpMethodCodeMethod.invoke(null, new Object[]{methods[i]});//调用DexFile中dumpMethodCode方法,参数为Method对象
}
Log.e(TAG, "className:" + className + ",constructors length:" + constructors.length + ",method length:" + methods.length);
} catch (ClassNotFoundException e) {
Log.e(TAG, "fart ClassNotFoundException" + e.getMessage());
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(TAG, "fart IllegalAccessException" + e.getMessage());
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(TAG, "fart InvocationTargetException" + e.getMessage());
e.printStackTrace();
}
return;
}
public static void fartthread() {
(new Thread(new Runnable() {
public void run() {
try {
Log.e("ActivityThread", "start sleep......");
Thread.sleep(10000L);//睡眠10s钟
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
Log.e("ActivityThread", "sleep over and start fart");
ActivityThread.fart();//调用脱壳程序
Log.e("ActivityThread", "fart run over");
}
})).start();
}
主要是开启了一个线程,睡眠10s后开始干活,fart方法执行流程如下:
1、通过反射获取了DexFile类的getClassNameList方法和dumpMethodCode方法,dumpMethodCode我们刚刚在DexFile里面填加上的native方法。
2、通过当前进程的classloader,一步一步根据如下类结构pathList->dexElements->dexFile->mCookie进一步获取到当前classloader所加载的dexfile的mCookie,这个
mCookie 是native层所加载dex文件结构的标识。
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
......
}
package*/ final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
private Element[] dexElements;
......
}
/*package*/ static class Element {
private final DexFile dexFile;
......
}
public final class DexFile {
private Object mCookie;
private static native void dumpMethodCode(Object methodid);
private static native String[] getClassNameList(Object cookie);
....
}
那么当前进程的classlodaer如何获取的呢?
public class BaseDexClassLoader extends ClassLoader {
private final DexPathList pathList;
......
}
package*/ final class DexPathList {
private static final String DEX_SUFFIX = ".dex";
private static final String zipSeparator = "!/";
private Element[] dexElements;
......
}
/*package*/ static class Element {
private final DexFile dexFile;
......
}
public final class DexFile {
private Object mCookie;
private static native void dumpMethodCode(Object methodid);
private static native String[] getClassNameList(Object cookie);
....
}
那么当前进程的classlodaer如何获取的呢?
public static ClassLoader getClassloader() {
Object currentActivityThread = invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class[]{}, new Object[]{});
Object mBoundApplication = getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mBoundApplication");
return ((Application) getFieldOjbect("android.app.LoadedApk", getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "info"), "mApplication")).getClassLoader();
}
通过反射调用
ActivityThread 类的静态函数
currentActivityThread
获取当前的
ActivityThread对象,然后获取
ActivityThread对象的mBoundApplication成员变量,t之后获取mBoundApplication对象的info成员变量,他是个LoadedApk类型;最终获取
info对象的mApplication成员变量,他的类型是Application,最后通过调用
Application.getClassLoader得到当前进程的classloader。理解整个流程请参考下面的类关系:
public static ClassLoader getClassloader() {
Object currentActivityThread = invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class[]{}, new Object[]{});
Object mBoundApplication = getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mBoundApplication");
return ((Application) getFieldOjbect("android.app.LoadedApk", getFieldOjbect("android.app.ActivityThread$AppBindData", mBoundApplication, "info"), "mApplication")).getClassLoader();
}
通过反射调用
ActivityThread 类的静态函数
currentActivityThread
获取当前的
ActivityThread对象,然后获取
ActivityThread对象的mBoundApplication成员变量,t之后获取mBoundApplication对象的info成员变量,他是个LoadedApk类型;最终获取
info对象的mApplication成员变量,他的类型是Application,最后通过调用
Application.getClassLoader得到当前进程的classloader。理解整个流程请参考下面的类关系:
public final class ActivityThread extends ClientTransactionHandler {
AppBindData mBoundApplication;
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
static final class AppBindData {
LoadedApk info;
......
}
......
}
public final class LoadedApk {
private Application mApplication;
......
} public final class ActivityThread extends ClientTransactionHandler {
AppBindData mBoundApplication;
public static ActivityThread currentActivityThread() {
return sCurrentActivityThread;
}
static final class AppBindData {
LoadedApk info;
......
}
......
}
public final class LoadedApk {
private Application mApplication;
......
}
3、我们已经获取了DexFile类的
getClassNameList方法和dumpMethodCode方法,和
getClassNameList所需要的cookie参数如下:
public final class DexFile {
private Object mCookie;
private static native void dumpMethodCode(Object methodid);
private static native String[] getClassNameList(Object cookie);
....
}
调用getClassNameList(mCookie)来获取当前dex中的所有类名。
4、loadClassAndInvoke,首先通过loadClass来主动加载所有类,然后调用dumpMethodCode来进行脱壳,参数为Method或者Constructor对象。
这个有个重点我们的标题是《ART环境下基于主动调用的自动化脱壳方案》,这里主动调用就体现到这里了,loadClass 。加壳程序hook了加载类的方法,当真正执行时加载类的时候会进行还原,这个加载类相当于隐式加载。我们这里loadClass是显示加载所有的类,这时候类的方法已经被还原。
其他关于反射所使用的方法,请参考文末github。
那么什么时候调用fartthread呢?
public final class ActivityThread extends ClientTransactionHandler {
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
+ Log.e(TAG, "go into performLaunchActivity");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
@@ -2951,10 +2956,161 @@ public final class ActivityThread extends ClientTransactionHandler {
+ ": " + e.toString(), e);
}
}
-
+ Log.e(TAG, "app name:" + r.packageInfo.getPackageName());
+ if (r.packageInfo.getPackageName().equals("com.example.jltxgcy.arttest")) { //在这里判断进程
+ ActivityThread.fartthread();
+ }
return activity;
}
}
原作者是在native层判断的进程,这里采用在ActivityThread的performLaunchActivity判断。
public final class DexFile {
private Object mCookie;
private static native void dumpMethodCode(Object methodid);
private static native String[] getClassNameList(Object cookie);
....
}
调用getClassNameList(mCookie)来获取当前dex中的所有类名。
4、loadClassAndInvoke,首先通过loadClass来主动加载所有类,然后调用dumpMethodCode来进行脱壳,参数为Method或者Constructor对象。
这个有个重点我们的标题是《ART环境下基于主动调用的自动化脱壳方案》,这里主动调用就体现到这里了,loadClass 。加壳程序hook了加载类的方法,当真正执行时加载类的时候会进行还原,这个加载类相当于隐式加载。我们这里loadClass是显示加载所有的类,这时候类的方法已经被还原。
其他关于反射所使用的方法,请参考文末github。
那么什么时候调用fartthread呢?
public final class ActivityThread extends ClientTransactionHandler {
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
+ Log.e(TAG, "go into performLaunchActivity");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
@@ -2951,10 +2956,161 @@ public final class ActivityThread extends ClientTransactionHandler {
+ ": " + e.toString(), e);
}
}
-
+ Log.e(TAG, "app name:" + r.packageInfo.getPackageName());
+ if (r.packageInfo.getPackageName().equals("com.example.jltxgcy.arttest")) { //在这里判断进程
+ ActivityThread.fartthread();
+ }
return activity;
}
}
原作者是在native层判断的进程,这里采用在ActivityThread的performLaunchActivity判断。
public final class ActivityThread extends ClientTransactionHandler {
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
+ Log.e(TAG, "go into performLaunchActivity");
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
@@ -2951,10 +2956,161 @@ public final class ActivityThread extends ClientTransactionHandler {
+ ": " + e.toString(), e);
}
}
-
+ Log.e(TAG, "app name:" + r.packageInfo.getPackageName());
+ if (r.packageInfo.getPackageName().equals("com.example.jltxgcy.arttest")) { //在这里判断进程
+ ActivityThread.fartthread();
+ }
return activity;
}
}
原作者是在native层判断的进程,这里采用在ActivityThread的performLaunchActivity判断。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2020-1-7 18:28
被jltxgcy编辑
,原因: