利用DexClassLoader动态加载dex文件

Posted weiers

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用DexClassLoader动态加载dex文件相关的知识,希望对你有一定的参考价值。

Java中也有类加载器ClassLoader,其作用是动态装载Class文件,当我们从网络下载Class文件,或者在编译时不参与而在运行时动态调用时就需要用类加载器。由于android对class文件进行了重新打包和优化,最终APK文件中包含的是dex文件,加载这种文件就需要用到DexClassLoader。

DexClassLoader(dexPath, optimizedDirectory, libraryPath, parent)

dexPath:目标类所在的APK或者jar包,/.../xxx.jar

optimizedDirectory:从APK或者jar解压出来的dex文件存放路径

libraryPath:native库路径,可以为null

parent:父类装载器,一般为当前类的装载器、


插件类Plugin用于在运行时由宿主程序调用

public class Plugin {
	public int add(int a, int b) {
		return a+b;
	}
}
使用jar命令将其打包成jar文件

jar -cvf plugin.jar com/dl/plugin/Plugin.class
使用dx命令将其转化为android中的类格式dex文件
dx --dex --output=f:\\dynamic.jar f:\\Plugin.jar
将其放到手机目录中,比如放到根目录
adb push F:\\dynamic.jar /

在宿主程序中动态加载调用插件类Plugin的add函数

DexClassLoader loader = new DexClassLoader("/dynamic.jar", getApplicationInfo().dataDir, null, this.getClass().getClassLoader());
clazz = loader.loadClass("com.dl.plugin.Plugin");
Method add = clazz.getMethod("add", Integer.TYPE,Integer.TYPE);
int result = (Integer) add.invoke(clazz.newInstance(), 1,1);

首先动态加载Plugin类,然后通过反射调用add方法,完整代码如下
private Button btn;
	private Class<?> clazz;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		btn = (Button) findViewById(R.id.btn);
		btn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				try {
					if (clazz == null) {
						DexClassLoader loader = new DexClassLoader("/dynamic.jar", getApplicationInfo().dataDir, null, this.getClass().getClassLoader());
						clazz = loader.loadClass("com.dl.plugin.Plugin");
					}
					Method add = clazz.getMethod("add", Integer.TYPE,Integer.TYPE);
					int result = (Integer) add.invoke(clazz.newInstance(), 1,1);
					Toast.makeText(MainActivity.this, result+"", 0).show();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}


上面使用反射进行函数调用有些复杂,可以使用接口进行更方便的调用还能保持动态加载的灵活性。项目结构如下,保证接口IPlugin类名一致,包括包名。也可以通过引入jar包的形式来做。

于是就可以通过如下方式调用插件类中的代码了。

DexClassLoader loader = new DexClassLoader("/dynamic.jar", getApplicationInfo().dataDir, null, this.getClass().getClassLoader());
clazz = loader.loadClass("com.dl.plugin.Plugin");
IPlugin obj = (IPlugin) clazz.newInstance();
int result = obj.add(1, 1);

效果:



以上是关于利用DexClassLoader动态加载dex文件的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向类加载器 ClassLoader ( 使用 DexClassLoader 动态加载字节码文件 | 拷贝 DEX 文件到内置存储 | 加载并执行 DEX 字节码文件 )

Android 逆向整体加固脱壳 ( 脱壳起点 : 整体加固脱壳 | Dalvik 脱壳机制 : 利用 DexClassLoader 加载过程进行脱壳 | 相关源码分析 )

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | RawDexFile.cpp 分析 | dvmRawDexFileOpen函数读取 DEX 文件 )(代

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | 类加载器构造函数分析 | DexPathList 引入 )

Android 逆向启动 DEX 字节码中的 Activity 组件 ( 使用 DexClassLoader 获取组件类失败 | 失败原因分析 | 自定义类加载器没有加载组件类的权限 )

Android 逆向整体加固脱壳 ( DexClassLoader 加载 dex 流程分析 | DexPathList 构造函数分析 | makeDexElements 函数分析 )