android-类加载器 grand 2025-04-10 2025-04-28 参考资料 [原创]Android加壳脱壳学习(1)——动态加载和类加载机制详解-Android安全-看雪-安全社区|安全招聘|kanxue.com 
类加载器 Android中的类加载器机制与JVM一样遵循双亲委派模式
双亲委派模式 1.加载.class文件的时候,以递归的的形式逐级向上委托给父加载器ParentClassLoader去加载,如果加载过了,就不用在加载一遍
2.如果父加载器也没加载过,则继续委托给父加载器去加载,一直到这条链路的顶级,顶级classLoader判断如果没加载过,则尝试加载,加载失败,则逐级向下交还调用者来加载.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{            //1.先检查是否已经加载过--findLoaded            Class<?> c = findLoadedClass(name);            if (c == null) {                try {                    //2.如果自己没加载过,存在父类,则委托父类                    if (parent != null) {                        c = parent.loadClass(name, false);                    } else {                        c = findBootstrapClassOrNull(name);                    }                } catch (ClassNotFoundException e) {                }                  if (c == null) {                    //3.如果父类也没加载过,则尝试本级classLoader加载                    c = findClass(name);                }            }           return c;    } 
 
加载流程 
1 2 3 4 5 6 7 我们要加载一个class文件,我们定义了一个CustomerClassLoader类加载器: (1)首先会判断自己的CustomerClassLoader否加载过,如果加载过直接返回, (2)如果没有加载过则会调用父类PathClassLoader去加载,该父类同样会判断自己是否加载过,如果没有加载过则委托给父类BootClassLoader去加载, (3)这个BootClassLoader是顶级classLoader,同样会去判断自己有没有加载过,如果也没有加载过则会调用自己的findClass(name)去加载, (4)如果顶级BootClassLoader加载失败了,则会把加载这个动作向下交还给PathClassLoader, (5)这个PathClassLoader也会尝试去调用findClass(name);去加载,如果加载失败了,则会继续向下交还给CustomClassLoader来完成加载,这整个过程感觉是一个递归的过程,逐渐往上然后有逐渐往下,直到加载成功 其实这个String.class在系统启动的时候已经被加载了,我们自己定义一个CustomerClassLoader去加载,其实也是父类加载的 
 
Android中的主要类加载器 1.PathClassLoader复杂的加载系统类和英勇程序的类,通常用来加载已安装apk的dex文件,实际上外部存储的dex文件也能加载
2.DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk,dex,jar,zip)
3.BaseDexClassLoader全名是dalvik/system.PathClassLoader,可以加载已经安装的Apk,也就是/data/app/package 下的apk文件,也可以加载/vendor/lib, /system/lib下的nativeLibrarypathList来完成
4.BootClassLoaderAndroid平台上所有ClassLoader的最终parent,Android系统启动时会使用BootClassLoader来预加载常用类
andriod8以后引入了InMemoryDexClassLoader,从内存加载
几个类之间的关系
查看已经当前的class类和父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package com.eeee.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity {     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         ClassLoader thisclassloader = MainActivity.class.getClassLoader();         Log.i("test","testim:"+thisclassloader);         ClassLoader secondclassloader =null;         ClassLoader parentclassloader = thisclassloader.getParent();         while (parentclassloader!=null){             Log.i("test","testim:"+thisclassloader+"parent"+parentclassloader);             thisclassloader=parentclassloader;             parentclassloader=thisclassloader.getParent();         }         Log.i("root","final"+thisclassloader);     } } 
 
1 2 3 2025-04-08 19:39:58.591 9327-9327/com.eeee.myapplication I/test: testim:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/~~Nm6s9RY_9WyoaS0gsxNIog==/com.eeee.myapplication-gDnm4BKglPQGA3i4PsNz4g==/base.apk"],nativeLibraryDirectories=[/data/app/~~Nm6s9RY_9WyoaS0gsxNIog==/com.eeee.myapplication-gDnm4BKglPQGA3i4PsNz4g==/lib/x86_64, /system/lib64, /system/system_ext/lib64, /system/product/lib64, /system/vendor/lib64]]] 2025-04-08 19:39:58.591 9327-9327/com.eeee.myapplication I/test: testim:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/~~Nm6s9RY_9WyoaS0gsxNIog==/com.eeee.myapplication-gDnm4BKglPQGA3i4PsNz4g==/base.apk"],nativeLibraryDirectories=[/data/app/~~Nm6s9RY_9WyoaS0gsxNIog==/com.eeee.myapplication-gDnm4BKglPQGA3i4PsNz4g==/lib/x86_64, /system/lib64, /system/system_ext/lib64, /system/product/lib64, /system/vendor/lib64]]]parentjava.lang.BootClassLoader@7d15f50 2025-04-08 19:39:58.591 9327-9327/com.eeee.myapplication I/root: finaljava.lang.BootClassLoader@7d15f50 
 
类加载时机 隐式加载 创建类的实例 访问类的静态变量,或者为静态变量赋值 调用类的静态方法 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象 初始化某个类的子类
显示加载 使用LoadClass()加载 使用forName()加载
显示父类加载器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package com.eeee.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity {     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         ClassLoader thisclassloader = MainActivity.class.getClassLoader();         Log.i("test","testim:"+thisclassloader);         ClassLoader secondclassloader =null;         ClassLoader parentclassloader = thisclassloader.getParent();         while (parentclassloader!=null){             Log.i("test","testim:"+thisclassloader+"parent"+parentclassloader);             parentclassloader=parentclassloader.getParent();         }         Log.i("root","final"+thisclassloader);     } } 
 
尝试使用bootclassloader加载mainactivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package com.eeee.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; public class MainActivity extends AppCompatActivity {     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         ClassLoader classLoader=MainActivity.class.getClassLoader();         Log.i("test","this"+classLoader.toString());         ClassLoader parent=classLoader.getParent();         Log.i("test","this"+parent.toString());         try {             Class main=classLoader.loadClass("com.eeee.myapplication.MainActivity");             Log.i("test","pathclassloader main"+main.toString());         }catch (ClassNotFoundException e){             Log.i("fail","pathclassloader main fail");             e.printStackTrace();         }         try {             Class main=parent.loadClass("com.eeee.myapplication.MainActivity");             Log.i("test","bootclassloader main"+main.toString());         }catch (ClassNotFoundException e){             Log.i("fail","bootclassloader main fail");             e.printStackTrace();         }     } } 
 
接下来使用DexClassLoader类实现一个最简单的动态加载插件dex,并 验证此时的ClassLoader|间的继承关系; DexClassLoader方法参数: dexPath:目标所在的apk或者jar文件的路径,装载器将从路径中寻找指 定的目标类。 dexOutputDir:由于dex文件在APK或者jar文件中,所以在装载前面前 先要从里面解压出dex文件,这个路径就是dex文件存放的路径,在 android系统中,一个应用程序对应一个inux用户id,应用程序只对自 己的数据目录有写的权限,所以我们需要存放在一个app可写的径中 libPath:目标类中使用的c/c++库。 最后一个参数是该装载器的父装载器,一般为当前执行类的装载器
编写插件dex 新建工程
1 2 3 4 5 6 7 8 9 10 11 12 package com.eeee.plugindex; import android.util.Log; public class dex {         public static void test(){             Log.i("dextest","suessfull");         } } 
 
直接编译为apk 提取dex 
再次新建 这里我们由于把dex塞sdcard文件夹里面了 要增加一下sdcard的读写权限
修改androidmainifest.xml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="com.eeee.loaddex">     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>     <application         android:allowBackup="true"         android:icon="@mipmap/ic_launcher"         android:label="@string/app_name"         android:roundIcon="@mipmap/ic_launcher_round"         android:supportsRtl="true"         android:theme="@style/Theme.Loaddex">         <activity             android:name=".MainActivity"             android:exported="true">             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                 <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>     </application> </manifest> 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.eeee.loaddex; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.widget.TextView; import com.eeee.loaddex.databinding.ActivityMainBinding; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import dalvik.system.DexClassLoader; public class MainActivity extends AppCompatActivity {     // Used to load the 'loaddex' library on application startup.     static {         System.loadLibrary("loaddex");     }     private ActivityMainBinding binding;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         binding = ActivityMainBinding.inflate(getLayoutInflater());         setContentView(binding.getRoot());         // Example of a call to a native method         TextView tv = binding.sampleText;         tv.setText(stringFromJNI());         String path ="/sdcard/pdex.dex";         DexClassLoader dexClassLoader =new DexClassLoader(path,this.getApplication().getCacheDir().getAbsolutePath(),null,MainActivity.class.getClassLoader());         try {             Class pdex= dexClassLoader.loadClass("com.eeee.plugindex.dex");             Log.i("mainactivity", dexClassLoader.toString()+"---"+pdex);             Method test=pdex.getDeclaredMethod("test");             test.invoke(null);         }catch (ClassNotFoundException e){             e.printStackTrace();         }catch (NoSuchMethodException e) {             e.printStackTrace();         } catch (InvocationTargetException e) {             e.printStackTrace();         } catch (IllegalAccessException e) {             e.printStackTrace();         }     }     /**      * A native method that is implemented by the 'loaddex' native library,      * which is packaged with this application.      */     public native String stringFromJNI(); } 
 
1 2 2025-04-10 00:41:33.620 13630-13630/com.eeee.loaddex I/mainactivity: dalvik.system.DexClassLoader[DexPathList[[dex file "/sdcard/pdex.dex"],nativeLibraryDirectories=[/system/lib64, /system/system_ext/lib64, /system/product/lib64, /system/vendor/lib64]]]---class com.eeee.plugindex.dex 
 
可以看到我们成功加载了sdcard中的dex文件 
1 2025-04-10 00:41:33.621 13630-13630/com.eeee.loaddex I/dextest: suessfull 
 
同时dex文件中的方法也成功调用