Android APK reinforcement - memory load dex

Android APK reinforcement - memory load dex

Analysis of construction method DexClassLoader

To view the source code can AndroidXref website

http://androidxref.com/

1570587480978

1570587566974

1570587581706

1570587625848

Look at the code found, DexClassLoader calls the parent class constructor BaseDexClassLoader

Click on the name of the parent class, continue to observe the parent source

1570587718294

Found constructed with a core function DexPathList, continue to view

1570587827805

Observed, DexPathList structure, there is a method makeDexElements like to create a set of elements,

With the

1570587922853

Function to determine whether the suffix is ​​dex, if the method is invoked LoadDexFile

This is another definition of its name suffix

1570588002168

Continue to observe LoadDexFile

1570588290827

OptimizedPathFor method parameters are found optimizedDirectory into a path method to load dex files is loadDex method DexFile class, continue with the

1570588549179

The loadDex found DexFile returned DexFile object parameters are the source and output files

Continue to view the structure DexFile

1570589042448

We found constructor DexFile class have a method openDexFile and load files associated Dex. Continue to see

1570589663881

Found openDexFIle returned to a native method. Which portion is implemented in C ++.

Continue with the

1570589848047


in conclusion:

1.DexClassLoader calls the parent class constructor BaseDexClassLoader found the parent class constructor has a core function DexPathList,

2. The core functionality DexPathList structure, there is a method makeDexElements like to create a set of elements,

3.makeDexElements this function to determine whether the suffix is ​​dex, if the method is invoked LoadDexFile,

4. In the method optimizedDirectory LoadDexFile optimizedPathFor parameters into a single path method, and the method of loading the file is loadDex dex method DexFile class,

The loadDex 5.DexFile returned DexFile object, the object parameters are the source and output files,

6.跟入发现DexFile类的构造方法中又有个方法openDexFile和加载Dex文件有关联。

7.发现openDexFIle返回了个native方法。其实现部分是在C++中。

简述:

makDexELements判断了4种文件类型,dex/jar/zip/apk,所以android中能够动态加载的构造方法中,就这四种。

DexClassLoader跟到最后发现最核心功能是openDexFile,native层的,传递文件字节码,返回值是一个虚拟机的cookie值(java层)(C++层是pDexOrJar的指针)

分析DexClassLoader的loadClass方法

由于我们使用的是DexClassLoader,继承自BaseDexClassLoader,而查看findClass方法在ClassLoader的源码是必须要实现的,所以应该看BaseDexClassLoader的重写方法。

1570592255439

发现其中有查找类的方法。调用findClass的pathList对象是在BaseDexClassLoader构造中创建的。

继续分析DexPathList的findClass方法

1570592397390

发现返回类的对象代码,是DexFile中的loadClassBinaryName

1570592638711

loadClassBinaryName方法中调用了defineClass,其中参数是名称,类加载器,cookie值,cookie值是DexFile中的openDexFile方法的返回值。

defineClass方法是个native方法

流程:

1.ClassLoader.loadClass 方法

2.BaseDexClassLoader.findClass方法

3.DexPathList.findClass方法

4.DexFile.loadClassBinaryName方法

5.DexFile.defineClass,Native方法

与DexClassLoad的构造结合起来,可以看到,DexFile这个类是加载类的关键,在DexClassLoader的构造方法中,最后调用的openDexFIle方法,返回dalvik虚拟机中的一个cookie值,这个值正是loadClasss方法最后调用的defineClass的参数。

编写自己的DexClassLoader

思路:

0.创建一个DexClassLoader的子类

1.创建构造方法,加入参数byte[]

2.使用反射调用openDexFile,获取mCookie

3.重写loadClass,使用反射调用defineClass

1.因为想要一个带有传入参数byte[]的DexClassLoader,所以新创建一个DexClassLoader子类MyDexClassLoader,创建构造方法加入参数byte[]。

 public MyDexClassLoader(byte bytes[],
                            String dexPath,
                            String optimizedDirectory,
                            String librarySearchPath,
                            ClassLoader parent) {
        super(dexPath, optimizedDirectory, librarySearchPath, parent);

        createDexClassLoader(bytes,parent);

   }

2.使用反射调用openDexFile,获取mCookie。

为了方便,封装了一个方法,实现调用openDexFile的逻辑,这个逻辑就是创建自己的DexClassLoader的逻辑。另外定义了两个变量,,存放cookie和parentclassLoadr。

因为openDexFile方法是在DexFile类里面,所以代码的逻辑应该是先获取DexFile类,再获取openDexFile,然后调用。

private ClassLoader mClassLoader;
    private int mCookie;
    private void createDexClassLoader(byte[] bytes, ClassLoader parent) {
        // android 4.1 DexFile.openDexFile(byte[])
        mClassLoader = parent;
        try {
            // 1. 获取 DexFile 类类型
            Class clz = Class.forName("dalvik.system.DexFile");
            // 2. 获取 openDexFile 方法对象
            Method method = clz.getDeclaredMethod("openDexFile",byte[].class);
            // 3. 调用方法,返回 cookie
            method.setAccessible(true);
            mCookie = (int) method.invoke(null,new Object[]{bytes});
       } catch (Exception e) {
            e.printStackTrace();
       }
   }

3.重写loadClass

@Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {

        // android 4.1 DexFile.defineClass(String name, ClassLoader loader, int cookie)
        Class c = null;
        try {
            // 获取加载的类信息
            Class dexFile = Class.forName("dalvik.system.DexFile");
            // 获取静态方法
            Method method = dexFile.getDeclaredMethod("defineClass", String.class, ClassLoader.class, int.class);
            method.setAccessible(true);
            // 调用
            c = (Class)method.invoke(null,name, mClassLoader, mCookie);
            return c;
       } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
       }
        return super.loadClass(name);
   }

完整的MyDexClassLoader.java

package com.bluelesson.mydexclassloader;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;
import dalvik.system.DexFile;

public class MyDexClassLoader extends DexClassLoader {
    public MyDexClassLoader(byte bytes[],
                            String dexPath,
                            String optimizedDirectory,
                            String librarySearchPath,
                            ClassLoader parent) {
        super(dexPath, optimizedDirectory, librarySearchPath, parent);

        createDexClassLoader(bytes,parent);

   }
    private ClassLoader mClassLoader;
    private int mCookie;
    private void createDexClassLoader(byte[] bytes, ClassLoader parent) {
        // android 4.1 DexFile.openDexFile(byte[])
        mClassLoader = parent;
        try {
            // 1. 获取 DexFile 类类型
            Class clz = Class.forName("dalvik.system.DexFile");
            // 2. 获取 openDexFile 方法对象
            Method method = clz.getDeclaredMethod("openDexFile",byte[].class);
            // 3. 调用方法,返回 cookie
            method.setAccessible(true);
            mCookie = (int) method.invoke(null,new Object[]{bytes});
       } catch (Exception e) {
            e.printStackTrace();
       }
   }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {

        // android 4.1 DexFile.defineClass(String name, ClassLoader loader, int cookie)
        Class c = null;
        try {
            // 获取加载的类信息
            Class dexFile = Class.forName("dalvik.system.DexFile");
            // 获取静态方法
            Method method = dexFile.getDeclaredMethod("defineClass", String.class, ClassLoader.class, int.class);
            method.setAccessible(true);
            // 调用
            c = (Class)method.invoke(null,name, mClassLoader, mCookie);
            return c;
       } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
       }
        return super.loadClass(name);
   }
}

使用MyDexClassLoader

为了测试方便,回顾之前的例子,动态加载Activity是比较简单,我们可以把原先动态加载activity例子中,创建ClassLoader改为创建自己的MyDexClassLoader,加载Activity时,调用自己的loadClass。

首先新建一个简单的activity,然后编译代码,将Main2Activity生成的calss文件转为dex文件。然后将文件复制到项目中的assets目录中,名为m2a.dex

先整理下思路,因为是内存中加载dex,所以把assets目录中的dex文件读取到byte数组中即可,然后创建自己的MyDexClassLoader,在获取类型。步骤如下:

1.读取文件,返回数组地址

2.创建dex文件的类加载器,返回DexClassLoader对象

3.使用loadClass获取加载的类信息

4.创建Intent,启动Activity

根据思路开始写代码。

封装asset目录读取文件的方法

byte[] getdexFromAssets(String dexName){
        // 获取assets目录管理器
        AssetManager as = getAssets();
        // 合成路径
        String path = getFilesDir() + File.separator + dexName;
        Log.i(TAG, path);
        try {
            // 创建文件流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            // 打开文件
            InputStream is = as.open(dexName);
            // 循环读取文件,拷贝到对应路径
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                out.write(buffer, 0, len);
           }
            return out.toByteArray();
       } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
       }
        return null;
   }

然后就可以用我们的MyDexLoader载入读取到的数据,并运行它。

1.读取文件到内存

2.用自己的MyDexLoader加载

3.加载载入的m2a .dex里面的类

4.替换ClassLoader

5.启动

 public void btnClick(View view) {

        // 1. 获取dex字节数组
        byte bytes[] = getdexFromAssets("m2a.dex");
        // 2. 加载dex,返回dexClassLoader对象
        MyDexClassLoader dex = new MyDexClassLoader(bytes,getPackageCodePath(),
                getCacheDir().toString(),null,getClassLoader()
               );
        // 3. 加载类
        Class clz = null;
        try {
            clz = dex.loadClass("com.bluelesson.mydexclassloader.Main2Activity");
       } catch (ClassNotFoundException e) {
            e.printStackTrace();
       }
        // 4. 替换ClassLoader
        replaceClassLoader1(dex);
        // 5. 启动activity
        startActivity(new Intent(this,clz));
   }

完整的MainActivity.java

package com.bluelesson.mydexclassloader;

import android.content.Intent;
import android.content.res.AssetManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "15pb-log";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
   }

    public void btnClick(View view) {

        // 1. 获取dex字节数组
        byte bytes[] = getdexFromAssets("m2a.dex");
        // 2. 加载dex,返回dexClassLoader对象
        MyDexClassLoader dex = new MyDexClassLoader(bytes,getPackageCodePath(),
                getCacheDir().toString(),null,getClassLoader()
               );
        // 3. 加载类
        Class clz = null;
        try {
            clz = dex.loadClass("com.bluelesson.mydexclassloader.Main2Activity");
       } catch (ClassNotFoundException e) {
            e.printStackTrace();
       }
        // 4. 替换ClassLoader
        replaceClassLoader1(dex);
        // 5. 启动activity
        startActivity(new Intent(this,clz));
   }

    byte[] getdexFromAssets(String dexName){
        // 获取assets目录管理器
        AssetManager as = getAssets();
        // 合成路径
        String path = getFilesDir() + File.separator + dexName;
        Log.i(TAG, path);
        try {
            // 创建文件流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            // 打开文件
            InputStream is = as.open(dexName);
            // 循环读取文件,拷贝到对应路径
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                out.write(buffer, 0, len);
           }
            return out.toByteArray();
       } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
       }
        return null;
   }

    public void replaceClassLoader1(DexClassLoader dexClassLoader){
        try {
            // 1. 获取ActivityThead类对象
            // android.app.ActivityThread
            // 1.1 获取类类型
            Class clzActivityThead = Class.forName("android.app.ActivityThread");
            // 1.2 获取类方法
            Method currentActivityThread = clzActivityThead.getMethod("currentActivityThread",new Class[]{});
            // 1.3 调用方法
            currentActivityThread.setAccessible(true);
            Object objActivityThread = currentActivityThread.invoke(null);

            // 2. 通过类对象获取成员变量mBoundApplication
            //clzActivityThead.getDeclaredField()
            Field field = clzActivityThead.getDeclaredField("mBoundApplication");
            // AppBindData
            field.setAccessible(true);
            Object data = field.get(objActivityThread);
            // 3. 获取mBoundApplication对象中的成员变量info
            // 3.1 获取 AppBindData 类类型
            Class clzAppBindData = Class.forName("android.app.ActivityThread$AppBindData");
            // 3.2 获取成员变量info
            Field field1 = clzAppBindData.getDeclaredField("info");
            // 3.3 获取对应的值
            //LoadedApk
            field1.setAccessible(true);
            Object info = field1.get(data);
            // 4. 获取info对象中的mClassLoader
            // 4.1 获取 LoadedApk 类型
            Class clzLoadedApk = Class.forName("android.app.LoadedApk");
            // 4.2 获取成员变量 mClassLoader
            Field field2 = clzLoadedApk.getDeclaredField("mClassLoader");
            field2.setAccessible(true);

            // 5. 替换ClassLoader
            field2.set(info,dexClassLoader);

       } catch (Exception e) {
            e.printStackTrace();
       }
   }

}

After running version 4.4 of an error: there is no such method, the test can be replaced by 4.1.

summary

This experiment on the surface like there still exist m2a.dex file folder in assets, but, this whole process is to first m2a.dex loaded into memory (loaded m2a. Dex dex data only exist to make memory) then use MyDexClassLoader read out. That is, as long as there dex data memory by using other methods, can be used to read data directly MyDexClassLoader, Assets folder which can be used without dex file. To achieve the purpose of dynamically loaded dex APK reinforcement from memory.

Guess you like

Origin www.cnblogs.com/ltyandy/p/11642108.html