篇幅有限 完整内容及源码关注公众号:ReverseCode,发送 冲
逆向基本流程 使用自动化检测工具检测apk是否加壳,或者借助一些反编译工具依靠经验推断是否加壳 GDA3.85 或者 常见so文件
《娜迦》企业版本加固 libedog.so 《娜迦》免费版本加固 libddog.so 《梆梆》企业版本加固 libDexHelper.so 《梆梆》免费版本加固 libsecexe.so 《爱加密》企业版本加固 ijiami.ajm 《爱加密》免费版本加固 libexec.so 《通付盾》加固 libegis.so 《360》加固 libprotectClass.so,libjiagu.so 《百度》加固 libbaiduprotect.so 《阿里》加固 libmobisec.so 《腾讯》加固 libtup.so 《盛大》加固 libapssec.so 《瑞星》加固 librsprotect.so 《网秦》加固 nqdata 《国信灵通》加固 libnqshield.so 《apkprotect》加固 apkprotect 《几维安全》加固 libkwscmm.so,libkwscr.so,libkwslinker.so 《UU安全》加固 libuusafe.jar.so,libuusafe.so,libuusafeempty.so 几维安全 lib/armeabi-v7a/libkwscmm.so,lib/armeabi-v7a/libkwscr.so,lib/armeabi-v7a/libkwslinker.so UU安全 assets/libuusafe.jar.so,assets/libuusafe.so,lib/armeabi/libuusafeempty.so
如果apk加壳,则需要首先对apk进行脱壳
使用jeb,jadx,apktool等反编译工具对apk进行反编译
如果反编译工具打不开,使用010Editor把文件魔术字修复前8个字节
64 65 78 0A 30 33 35 00 dex.035. 再使用jadx反编译
grep -ril “MainAcitvity” ./*.txt 找到对应dex前缀名
先依据静态分析中得到的关键字字符串,关键api调用等方法快速定位需要分析的关键函数和流程
如果依据简单的字符串,关键api无法快速定位,则apk可能使用了字符串加密,反射调用等手段,此时可以结合hook,动态调试等
定位到关键函数后,再根据是java实现还是jni实现进一步分析
类加载基本原理 JVM类加载器 Bootstrap ClassLoader(引导类加载器)
C/C++代码实现的加载器,用于加载指定的JDK的核心类库,比如java. lang、java.utI等这些系统类。Java虚拟机的启动就是通过 Bootstrap,该 Classloader在java里无法获取,负责加载/lib下的类。
Extensions Classloader(拓展类加载器)
Java中的实现类为 Extclassloader,提供了除了系统类之外的额外功能,可以在java里获取,负责加载/lib/ext下的类
Application ClassLoader(应用程序类加载器)
Java中的实现类为 AppClassLoader,是与我们接触对多的类加载器,开发人员写的代码默认就是由它来加载, ClassLoader.getSystemClassLoader返回的就是它。
可以自定义类加载器,只需要通过java.lang.ClassLoader来实现自己的类加载器。
加载顺序:Bootstrap ClassLoader->Extensions Classloader->Application ClassLoader
双亲委派
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载。
1)避免重复加载,如果已经加载过一次class,可以直接读取已经加载的class
2)更加安全,无法自定义类来替代系统的类,可以防止核心API库被随意篡改
Android类加载器 加载时机 :
隐式加载:创建类的实例,访问类的静态变量,或者为静态变量赋值,调用类的静态方法,使用反射方式来强制创建某个类或接口对应的java.lang.Class对象,初始化某个类的子类 显式加载:使用LoadClass()加载,使用forName()加载 加载过程 :
装载:查找和导入Class文件 链接:其中解析步骤是可以选择的 (a)检查:检查载入的class文件数据的正确性 (b)准备:给类的静态变量分配存储空间 (c)解析:将符号引用转成直接引用 初始化:即调用<clinit>函数,对静态变量,静态代码块执行初始化工作 ClassLoader继承关系
ClassLoader :抽象类;BootClassLoader :预加载常用类,单例模式。与Java中的BootClassLoader不同,它并不是由C/C++代码实现,而是由Java实现的;BaseDexClassLoader 是PathClassLoader、DexClassLoader、InMemoryDexClassLoader的父类,类加载的主要逻辑都是在BaseDexClassLoader完成的。SecureClassLoader 继承了抽象类ClassLoader,拓展了ClassLoader类加入了权限方面的功能,加强了安全性,其子类URLClassLoader是用URL路径从jar文件中加载类和资源。 其中重点关注的是PathClassLoader 和DexClassLoader 。PathClassLoader是Android默认使用的类加载器,一个apk中的Activity等类便是在其中加载。 DexClassLoader可以加载任意目录下的dex/jar/apk/zip文件,比PathClassLoader更灵活,是实现插件化、热修复以及dex加壳的重点。
DexClassLoader方法参数
dexPath:目标所在的apk或者jar文件的路径,装载器将从路径中寻找指定的目标类。 dexOutputDir:由于dex 文件在APK或者 jar文件中,所以在装载前面前先要从里面解压出dex文件,这个路径就是dex文件存放的路径,在 android系统中,一个应用程序对应一个linux用户id ,应用程序只对自己的数据目录有写的权限,所以我们存放在这个路径中。 libPath :目标类中使用的C/C++库。 parent:该装载器的父装载器,一般为当前执行类的装载器。
Android8.0新引入InMemoryDexClassLoader,用于直接从内存中加载dex。
http://androidxref.com/8.0.0_r4/ 搜索位于libcore中的Definition的DexClassLoader 、PathClassLoader、 InmemoryDexClassLoader查看源码。
ClassLoaderTest 新建项目ClassLoaderTest验证类加载器的加载顺序
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 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); testClassLoader(); } public void testClassLoader () { ClassLoader thisClassLoader=MainActivity.class .getClassLoader () ; Log.i("kanxue" ,"thisClassLoader:" +thisClassLoader); ClassLoader tmpClassLoader = null ; ClassLoader parentClassLoader=thisClassLoader.getParent(); while (parentClassLoader!=null ){ Log.i("kanxue" ,"this:" +thisClassLoader+"===" +parentClassLoader); tmpClassLoader=parentClassLoader.getParent(); thisClassLoader=parentClassLoader; parentClassLoader=tmpClassLoader; } Log.i("kanxue" ,"root:" +thisClassLoader); } }
LoadDex 通过DexClassLoader实现一个动态加载 的dex插件
在ClassLoaderTest项目中打印log
1 2 3 4 5 public class TestDexClass { public void testFunc () { Log.i("kanxue" ,"I'm from com.onejane.classloadertest.TestDexClass.testFunc" ); } }
通过build生成ClassLoaderTest\app\build\outputs\apk\debug\app-debug.apk, apktool d -s app-debug.apk
保留并抽取出classes.dex
adb push classes.dex /sdcard
创建LoadDex空白Android项目,加载调用位于dex下com.onejane.classloadertest.TestDexClass
在AndroidManifest.xml中添加读写权限
1 2 <uses-permission android:name ="android.permission.WRITE_EXTERNAL_STORAGE" > </uses-permission > <uses-permission android:name ="android.permission.READ_EXTERNAL_STORAGE" > </uses-permission >
新增application配置android:requestLegacyExternalStorage=”true”
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 65 66 67 68 69 70 71 72 73 74 public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Context context = this .getApplicationContext(); testDexClassLoader(context,"/sdcard/classes.dex" ); } private void requestMyPermissions () { if (ContextCompat.checkSelfPermission(this , Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100 ); } else { Log.d("kanxue" , "requestMyPermissions: 有写SD权限" ); } if (ContextCompat.checkSelfPermission(this , Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this , new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100 ); } else { Log.d("kanxue" , "requestMyPermissions: 有读SD权限" ); } } public void testDexClassLoader (Context context, String dexfilepath) { File optFile = context.getDir("opt_dex" ,0 ); File libFile = context.getDir("lib_path" ,0 ); ClassLoader parentClassLoader=MainActivity.class .getClassLoader () ; Log.i("kanxue" ,parentClassLoader.toString()); ClassLoader tmpClassLoader=context.getClassLoader(); Log.i("kanxue" ,tmpClassLoader.toString()); requestMyPermissions(); DexClassLoader dexClassLoader = new DexClassLoader(dexfilepath,optFile.getAbsolutePath(),libFile.getAbsolutePath(),MainActivity.class .getClassLoader ()) ; Class<?> clazz=null ; try { clazz=dexClassLoader.loadClass("com.onejane.classloadertest.TestDexClass" ); }catch (ClassNotFoundException e){ e.printStackTrace(); } if (clazz!=null ){ try { Method testFuncMethod=clazz.getDeclaredMethod("testFunc" ); Object obj = clazz.newInstance(); testFuncMethod.invoke(obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } }
利用外部dex下发热修复bug,用户无感知更新。
APP启动流程 由发起进程通过binder进程间通信告知system_server进程启动指定的app,system_server通过socket方式与安卓孵化器进程进行通信,告诉孵化器进程启动指定app,由进程fork产生新的进程真正进入ActivityThread.main(),在此之前一直处于安卓的framework中。
ActivityThread是单例模式,在app整个进程的生命周期中,只存在一个实例,http://androidxref.com/8.0.0_r4/ 搜索位于frameworks中的ActivityThread
调用静态函数currentActivityThread获取当前进程中的ActivityThread实例,进而可以获取ActivityThread的重要变量,如mPackages
1 2 3 4 public static ActivityThread currentActivityThread() { return sCurrentActivityThread; } final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
其中LoadedApk中有加载app组件的PathClassLoader,即mClassLoader
通过反射获取app进程中单例ActivityThread ,其中的sCurrentActivityThread静态变量 用于全局保存创建的ActivityThread实例,同时还提供了currentActivityThread()静态函数 用于获取当前虚拟机创建的ActivityThread实例。
反射拿到ActivityThread中的 mPackages 的ArrayMap,通过当前app包名获取LoadedApk 。
最后通过LoadedApk获取mClassLoader->PathClassLoader 。
PathClassLoader: app运行过程中用于加载四大组件类的ClassLoader
**ActivityThread.main()**函数是java中的入口main函数,这里会启动主消息循环,并创建ActivityThread实例,之后调用thread.attach(false)完成一系列初始化准备工作,并完成全局静态变量sCurrentActivityThread的初始化。之后主线程进入消息循环,等待接收来自系统的消息。当收到系统发送来的bindapplication的进程间调用时,调用函数handleBindApplication来处理该请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private void handleBindApplication(AppBindData data) { //step 1: 创建LoadedApk对象 data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo); ... //step 2: 创建ContextImpl对象; final ContextImpl appContext = ContextImpl.createAppContext(this, data.info); //step 3: 创建Instrumentation mInstrumentation = new Instrumentation(); //step 4: 创建Application对象;在makeApplication函数中调用了newApplication,真正执行app代码。在该函数中又调用了app.attach(context),在attach函数中调用了Application.attachBaseContext函数 Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; //step 5: 安装providers List<ProviderInfo> providers = data.providers; installContentProviders(app, providers); //step 6: 执行Application.Create回调 mInstrumentation.callApplicationOnCreate(app); ... }
在 handleBindApplication 函数中第一次进入了app的代码世界,该函数功能是启动一个application ,并把系统收集的apk组件等相关信息绑定到application里,在创建完application对象后,接着调用了application的attachBaseContext 方法,之后调用了application的onCreate 函数。由此可以发现,app的Application类中的attachBaseContext 和onCreate 这两个函数是最先获取执行权进行代码执行的。这也是为什么各家的加固工具的主要逻辑都是通过替换app入口Application,并自实现这两个函数,在这两个函数中进行加密dex释放以及执行权交付的原因。
APP运行流程 无壳:PathClassLoader加载自身app自身dex,包括app声明的Application及所有其他类信息
加壳:PathClassLoader只加载壳自身代码,不包含app自身真正代码。首先进入壳的application的attachBaseContext,解密原始dex,再完成执行权的交付。
DexClassLoader加载的类是没有组件生命周期的 ,也就是说即使DexClassLoader通过对APK的动态加载完成了对组件类的加载,当系统启动该组件时,依然会出现加载类失败的异常,因为插件没有组件相关的类,如一些activity或service,若只用DexClassLoader进行动态加载,系统PathClassLoader无法找到相关组件信息,app将直接崩溃。所以如何解决动态加载dex中的生命周期成为加壳厂商首先需要解决的问题!!!
在项目ClassLoaderTest 中新建TestActivity ,并将build的apk中的classes.dex取出,adb push classes.dex /sdcard
1 2 3 4 5 6 7 8 public class TestActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // adb push classes.dex /sdcard Log.i("kanxue","I'm from TestActivity.onCreate"); } }
在项目LoadDex 中,配置AndroidManifest.xml<activity android:name="com.onejane.classloadertest.TestActivity"></activity>
,在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 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Context context = this.getApplicationContext(); startTestActivity(context,"/sdcard/classes.dex"); } public void startTestActivity(Context context,String dexfilepath){ // 存放dex文件 File optFile = context.getDir("opt_dex",0); // 存放依赖的so文件 File libFile = context.getDir("lib_path",0); ClassLoader parentClassLoader=MainActivity.class.getClassLoader(); Log.i("kanxue",parentClassLoader.toString()); //PathClassLoader ClassLoader tmpClassLoader=context.getClassLoader(); Log.i("kanxue",tmpClassLoader.toString()); // PathClassLoader requestMyPermissions(); DexClassLoader dexClassLoader = new DexClassLoader(dexfilepath,optFile.getAbsolutePath(),libFile.getAbsolutePath(),MainActivity.class.getClassLoader()); Class<?> clazz=null; try{ clazz=dexClassLoader.loadClass("com.onejane.classloadertest.TestActivity"); }catch (ClassNotFoundException e){ e.printStackTrace(); } if(clazz!=null){ context.startActivity(new Intent(context,clazz)); } }
启动app将报错ClassNotFoundException: Didn’t find class “com.onejane.classloadertest.TestActivity”
说明组件相关的Activity由mClassLoader->PathClassLoader加载了,虽然获取到非空TestActivity,却无法找到启动的目标Activity 。
方案一 反射替换,替换系统组件类加载器mClassLoader为我们的DexClassLoader,同时设置DexClassLoader的parent为系统组件类加载器PathClassLoader;
修改LoadDex项目的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 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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Context context = this.getApplicationContext(); startTestActivityReplaceMethod(this,"/sdcard/classes.dex"); } private void requestMyPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //没有授权,编写申请权限代码 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); } else { Log.d("kanxue", "requestMyPermissions: 有写SD权限"); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //没有授权,编写申请权限代码 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100); } else { Log.d("kanxue", "requestMyPermissions: 有读SD权限"); } } public void replaceClassloader(ClassLoader classloader){ try { Class<?> ActivityThreadClazz=classloader.loadClass("android.app.ActivityThread"); // 获取静态函数currentActivityThread Method currentActivityThreadMethod= ActivityThreadClazz.getDeclaredMethod("currentActivityThread"); currentActivityThreadMethod.setAccessible(true); Object activityThreadObj=currentActivityThreadMethod.invoke(null); // 获取ActivityThread对象 //final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>(); Field mPackagesField=ActivityThreadClazz.getDeclaredField("mPackages"); // 获取ActivityThread对象的ArrayMap的Field mPackagesField.setAccessible(true); ArrayMap mPackagesObj= (ArrayMap) mPackagesField.get(activityThreadObj); // 通过Field获取mPackages实例 WeakReference wr= (WeakReference) mPackagesObj.get(this.getPackageName()); // 从ArrayMap中获取LoadApk Object loadedApkObj=wr.get(); Class LoadedApkClazz=classloader.loadClass("android.app.LoadedApk"); //private ClassLoader mClassLoader; Field mClassLoaderField=LoadedApkClazz.getDeclaredField("mClassLoader"); mClassLoaderField.setAccessible(true); mClassLoaderField.set(loadedApkObj,classloader); // 将LoadApk(mClassLoader)替换为我们的DexClassLoader } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } public void startTestActivityReplaceMethod(Context context,String dexfilepath){ // 存放dex文件 File optFile = context.getDir("opt_dex",0); // 存放依赖的so文件 File libFile = context.getDir("lib_path",0); requestMyPermissions(); DexClassLoader dexClassLoader = new DexClassLoader(dexfilepath,optFile.getAbsolutePath(),libFile.getAbsolutePath(),MainActivity.class.getClassLoader()); replaceClassloader(dexClassLoader); Class<?> clazz=null; try{ clazz=dexClassLoader.loadClass("com.onejane.classloadertest.TestActivity"); }catch (ClassNotFoundException e){ e.printStackTrace(); } if(clazz!=null){ context.startActivity(new Intent(context,clazz)); } } }
方案二 打破原有的双亲关系,在系统组件类加载器和BootClassLoader的中间插入我们自己的DexClassLoader即可
修改ClassLoaderTest中的TestActivity类继承自Activity
1 2 3 4 5 6 7 8 9 public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_main); // adb push classes.dex /sdcard Log.i("kanxue","I'm from TestActivity.onCreate"); } }
修改LoadDex中的MainActivity继承Activity
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 65 66 67 68 69 70 71 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Context context = this.getApplicationContext(); // 若sdk30需要sdcard,通过requestMyPermissions动态获取读写权限 // testDexClassLoader(context,"/sdcard/classes.dex"); // startTestActivityReplaceMethod(this,"/sdcard/classes.dex"); startTestActivityInsertMethod(this,"/sdcard/classes.dex"); } private void requestMyPermissions() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //没有授权,编写申请权限代码 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 100); } else { Log.d("kanxue", "requestMyPermissions: 有写SD权限"); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //没有授权,编写申请权限代码 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 100); } else { Log.d("kanxue", "requestMyPermissions: 有读SD权限"); } } public void startTestActivityInsertMethod(Context context,String dexfilepath){ File optfile=context.getDir("opt_dex",0); File libfile=context.getDir("lib_path",0); requestMyPermissions(); ClassLoader pathClassloader=MainActivity.class.getClassLoader(); ClassLoader bootClassloader=MainActivity.class.getClassLoader().getParent(); // 设置自定义dexClassLoader父ClassLoader为bootClassloader DexClassLoader dexClassLoader=new DexClassLoader(dexfilepath,optfile.getAbsolutePath(),libfile.getAbsolutePath(),bootClassloader); try { Field parentField=ClassLoader.class.getDeclaredField("parent"); parentField.setAccessible(true); // 设置pathClassLoader父ClassLoader为自定义dexClassLoader parentField.set(pathClassloader,dexClassLoader); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } ClassLoader tmpClassloader=pathClassloader; ClassLoader parentClassloader=pathClassloader.getParent(); while(parentClassloader!=null){ Log.i("kanxue","this:"+tmpClassloader+"--parent:"+parentClassloader); tmpClassloader=parentClassloader; parentClassloader=parentClassloader.getParent(); } Log.i("kanxue","root:"+tmpClassloader); Class<?> clazz=null; try { clazz = dexClassLoader.loadClass("com.onejane.classloadertest.TestActivity"); } catch (ClassNotFoundException e) { e.printStackTrace(); } context.startActivity(new Intent(context,clazz)); } }
以上都是基于文件形式的外部加载,Android 8.0引入InMemoryDexClassLoader实现从内存中直接加载字节流,更加安全,不能通过文件监控形式达到脱壳目的,需要从进程内存中扣出这块完整的dex实现脱壳。
部分app在AndroidManifest.xml中没有声明application,加固厂商只需要添加一个application,在自己的application中完成classloader的替换。 部分app在AndroidManifest.xml中已经声明application,加固需要一个代理的application,壳的application不仅需要完成解密dex以及classloader相关修复,还需要完成解密dex后原app的application的attachBaseContext和onCreate函数调用。 逆向分析和脱壳的意义 ClassLoader和动态加载 加壳APP运行流程和ClassLoader修正