实现最简单的壳

新建项目PackerApp

  Android Studio会为我们默认创建MainActivity,对应于这个工程,入口函数会从MainActivity里的onCreate开始执行,在onCreate里加一行代码Log.e("MainActivity","go into onCreate!")
在这里插入图片描述
运行程序,会正常打印信息。
在这里插入图片描述

接下来如果我想在onCreate函数之前去执行一些其他操作呢?

声明一个Application类的子类
  新建一个MyApplication类,重写onCreate和attachBaseContext方法,同时在AndroidMainfest.xml里的<application>里声明我们的入口类。

public class MyApplication extends Application {
    static {
        Log.e("MyApplication","go into MyApplication.<init>!");
    }

    public MyApplication() {
        Log.e("MyApplication","go into MyApplication!");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("MyApplication","go into MyAplication.onCreaete!");
    }

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        Log.e("MyApplication","go into MyApplication.attachBaseContext");
    }
}

在这里插入图片描述

如上,我们在MainActivity的onCreate函数调用之前执行了一些其他操作。

实现一个简单的壳

壳的类

package com.example.plugindex01;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

加载壳
先在PackerAppMainActivity里加载plugin.dex的MainActivity

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("packerapp");
    }
    private ActivityMainBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("MainActivity","go into onCreate!");
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(stringFromJNI());
        String dexpath = "sdcard/plugin.dex";
        ClassLoader pathClassLoader = MainActivity.class.getClassLoader();
        DexClassLoader dexClassLoader = new DexClassLoader(dexpath,this.getApplication().getCacheDir().getAbsolutePath(),null,pathClassLoader);
        try {
            Class SecondActivityClass = dexClassLoader.loadClass("com.example.plugindex01.PluginClass");
            this.startActivity(new Intent(this,SecondActivityClass));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    public native String stringFromJNI();
}

  在AndroidManifest.xml文件里添加用户权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
  在AndroidManifest.xml文件里声明插件Activity
<activity android:name="com.example.plugindex01.MainActivity"></activity>
  完了之后App还是运行不起来的,这是由于dexClassLoader不能直接加载Android系统组件类,DexClassLoader加载的类是没有组件生命周期的,如果想让它加载组件类,那么就需要对ClassLoader进行修正。

ClassLoader修正

第一种方式

  修改默认的系统组件类加载器,把它替换为DexClassLoader,首先要知道默认系统类加载器的位置,它是LoadedApk目录下的mClassLoader字段。
在这里插入图片描述
通过反射拿到mClassLoader
  对于每个App进程,都有且只有一个ActivityThread实例,可以通过currentActivityThread()静态方法获取ActivityThread实例。
在这里插入图片描述
有了ActivityThread实例,就可以通过反射拿到mClassLoader

public static void replaceClassLoader(Context context,DexClassLoader dexClassLoader) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
        ClassLoader pathClassLoader = MainActivity.class.getClassLoader();
        Class ActivityThread = pathClassLoader.loadClass("android.app.ActivityThread");
        Method currentActivityThread = ActivityThread.getDeclaredMethod("currentActivityThread");
        Object activityThreadObj = currentActivityThread.invoke(null);
        Field mPackagesFiled = ActivityThread.getDeclaredField("mPackages");
        mPackagesFiled.setAccessible(true);
        ArrayMap mPackageObj =(ArrayMap) mPackagesFiled.get(activityThreadObj);
        String packageName = context.getPackageName();
        WeakReference wr = (WeakReference) mPackageObj.get(packageName);
        Object loadedapkObj = wr.get();

        Class LoadedApkClass = pathClassLoader.loadClass("android.app.LoadedApk");
        Field mClassLoaderField = LoadedApkClass.getDeclaredField("mClassLoader");
        mClassLoaderField.setAccessible(true);
        ClassLoader mClassLoaderObj = (ClassLoader)mClassLoaderField.get(loadedapkObj);
        Log.e("mClassLoader",mClassLoaderObj.toString());
        mClassLoaderField.set(loadedapkObj,dexClassLoader);
    }

replaceClassLoader(this.getApplicationContext(),dexClassLoader)

第二种方式

  合并dexElements,将PathClassLoader当中的dexElements中的每一项(dex文件)和dexClassLoader动态加载的插件dex合并到当前的mClassLoader也就是PathClassLoader中。

public static Object getDexElementsInClassLoader(ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class BaseDexClassLoaderClass = classLoader.loadClass("dalvik.system.BaseDexClassLoader");
        Field pathListField = BaseDexClassLoaderClass.getDeclaredField("pathList");
        pathListField.setAccessible(true);
        Object pathListObj = pathListField.get(classLoader);

        Class DexPathListClass=classLoader.loadClass("dalvik.system.DexPathList");
        Field dexElementsField = DexPathListClass.getDeclaredField("dexElements");
        dexElementsField.setAccessible(true);
        Object dexElementsObj = dexElementsField.get(pathListObj);
        return dexElementsObj;
    }

    public static Object setDexElementsInClassLoader(ClassLoader classLoader,Object newdexElements) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class BaseDexClassLoaderClass = classLoader.loadClass("dalvik.system.BaseDexClassLoader");
        Field pathListField = BaseDexClassLoaderClass.getDeclaredField("pathList");
        pathListField.setAccessible(true);
        Object pathListObj = pathListField.get(classLoader);

        Class DexPathListClass=classLoader.loadClass("dalvik.system.DexPathList");
        Field dexElementsField = DexPathListClass.getDeclaredField("dexElements");
        dexElementsField.setAccessible(true);
        dexElementsField.set(pathListObj,newdexElements);
        return null;
    }

    public static Object combineDexElements(Object dexElements1,Object dexElements2){
        int length1 = Array.getLength(dexElements1);
        int length2 = Array.getLength(dexElements2);
        int length = length1+length2;
        Object newDexElements = Array.newInstance(dexElements1.getClass().getComponentType(),length);
        for(int i=0;i<length;i++){
            if(i<length1){
                Array.set(newDexElements,i,Array.get(dexElements1,i));
            }else {
                Array.set(newDexElements,i,Array.get(dexElements2,i-length1));
            }
        }
        return newDexElements;
    }

    public static void replacedexElements(Context context,ClassLoader dexclassloader) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        ClassLoader pathclassLoader = context.getClassLoader();
        Object dexElementsfrompathclassloader = getDexElementsInClassLoader(pathclassLoader);
        Object dexElementsfromdexclassloader = getDexElementsInClassLoader(dexclassloader);
        Object newdexElements = combineDexElements(dexElementsfrompathclassloader,dexElementsfromdexclassloader);
        setDexElementsInClassLoader(pathclassLoader,newdexElements);
    }

replacedexElements(this.getApplicationContext(),dexClassLoader);

这样在启动app的时候就会先调用执行dex的MainActivity类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值