新建项目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);
}
}
加载壳
先在PackerApp的MainActivity
里加载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类。