首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android旁门左道之动态替换应用程序

Android旁门左道之动态替换应用程序

作者头像
MelonTeam
发布于 2018-01-04 03:00:47
发布于 2018-01-04 03:00:47
2.3K20
代码可运行
举报
文章被收录于专栏:MelonTeam专栏MelonTeam专栏
运行总次数:0
代码可运行

导语: 本文讲述如何通过替换应用程序类的方法,可以协助开发调试甚至应用于项目中。

作者: yarkeyzhang  2017.8.31

一,引子

继上一篇文章( Android旁门左道之动态替换系统View类 )中我们讨论的,动态替换布局中的View,从而实现不需要修改xml布局文件的情况下控制View对象的创建。同事表示因吹斯听,思路轻奇;后来发现这个功能也可以应用于某些开发场景,比如日迹业务接入手Q基础拍摄框架,不需要修改到框架代码以及布局文件,通过动态替换View方案便可以实现业务特殊功能;以及用于定位并规避一些系统View类的Bug。然而自始至终我们一直局限在View的层次,有没有办法实现动态替换任意类?我们来继续讨论这个因吹斯听的话题吧!

二,安卓平台机制

Android App进程通过应用程序唯一的包名(package name)可以获取到Apk包的信息(apk路径),然后通过dalvik.system.PathClassLoader来加载对应的应用程序类。而用户自定义的Application派生类(比如MyAppApplication)是一个应用程序中第一个被加载的用户类,我们查看它的ClassLoader如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
|- dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.xxx.xxx-1/base.apk"], nativeLibraryDirectories=[/data/app/com.xxx.xxx-1/lib/arm, /vendor/lib, /system/lib]]]
|- java.lang.BootClassLoader

这里的BootClassLoader是PathClassLoader的“parent”。BootClassLoader可以加载各种基础的类(比如:List,String,Activity),PathClassLoader则完成从Apk中加载用户类。加载顺序:先BootClassLoader尝试加载,如果找不到类则由PathClassLoader加载。ClassA类加载ClassB类,默认使用ClassA类的ClassLoader。(包括new关键字,以及Class.forName(String)等等)

三,修改应用程序ClassLoader

我们的想法很简单,想要替换类,也就是能够控制类的加载,那么需要能够自行创建应用程序的ClassLoader。我们一旦成功地修改了应用程序ClassLoader,那么便可以动态控制用户类的加载。比如动态修改某个Activity(比如MyMainActivity)。只需要重写ClassLoader中的public Class> loadClass(String className)方法。比如:(示意代码)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyClassLoader extends ClassLoader {
    @Override
    public Class> loadClass(String className) throws ClassNotFoundException {
        if (className.equals("com.xxx.xxx.OldActivity")) {
            className = “com.xxx.xxx.NewActivity”;
        }
        return super.loadClass(className);
    }
}

如何修改应用程序ClassLoader?

1,插件框架方案:

有了解过插件框架原理的同学想必已经明白,比较彻底的做法是通过Android单进程多Application实例的特性:让假的FakeApplication先启动进程,然后构建一个NewClassLoader加载真正的MyAppApplication类。这样一来,我们整个App的用户代码都会被NewClassLoader加载,而不是默认的PathClassLoader。在NewClassLoader的实现中做手脚,我们可以动态替换类。插件框架的改动会比较大,我们不想把事情搞太大,看看能否在应用内自身完成替换。对插件框架有兴趣的我们可以私下一起讨论。

2,应用自身替换

应用自身替换,也就是需要在Application类以及启动之后开始做手脚。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private static boolean hookPackageClassLoader(Context context, ClassLoader appClassLoaderNew) {
    try {
        Field packageInfoField = Class.forName("android.app.ContextImpl").getDeclaredField("mPackageInfo");
        packageInfoField.setAccessible(true);
        Object loadedApkObject = packageInfoField.get(context);
        Class> LoadedApkClass = Class.forName("android.app.LoadedApk");
        Method getClassLoaderMethod = LoadedApkClass.getDeclaredMethod("getClassLoader");
        ClassLoader appClassLoaderOld = (ClassLoader) getClassLoaderMethod.invoke(loadedApkObject);
        Field appClassLoaderField = LoadedApkClass.getDeclaredField("mClassLoader");
        appClassLoaderField.setAccessible(true);
        appClassLoaderField.set(loadedApkObject, appClassLoaderNew);
        return true;
    } catch (Throwable ignored) {
    }
    return false;
}

public class MyAppApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        hookPackageClassLoader(base, new NewClassLoader());
    }
}

替换一个应用程序的ClassLoader核心代码如上所示。加上了以上的代码之后,启动一下我们的首页MyMainActivity。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyMainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.e("MyMainActivity", "getClass().getClassLoader() : " + getClass().getClassLoader());
        Log.e("MyMainActivity", "getClassLoader() : " + getClassLoader());
        Log.e("MyMainActivity", "MyView classLoader : " + MyView.class.getClassLoader());
    }
}

非常成功!我们发现MyMainActivity.class,以及context.getClassLoader都会是我们自定义的NewClassLoader。而且!如上第三句日志,自定义MyView类,它的ClassLoader也是NewClassLoader,也证明了ClassLoader的“传承”。自此,MyMainActivity类中加载的类都会经过NewClassLoader,于是我们可以控制它们的加载。

但是!这里比插件框架方案迟了一步。细心的你发现了问题,也就是说无论如何,我们的MyAppApplication类需要先被系统加载起来,它的ClassLoader是系统创建的PathClassLoader,而不是我们想要的NewClassLoader。根据以上我们说的“传承”,那么MyAppApplication类中创建出来的对象,都会跟随MyAppApplication类的ClassLoader。这些对象之后创建的对象,也会是如此!这条线我们无法控制。

四,类加载器终极修改

待续,记得再来看我 … ( ⊙ o ⊙ )

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
2 条评论
热度
最新
没后续?
没后续?
回复回复点赞举报
第二种方案只能让activity,view等组件的加载走到你的自定义类加载器。因为其他类即使一开始走到你的自定义类加载器,也会因为super.loadClass导致该类由pathClassLoader加载,在该类中触发的其他类加载就直接绕过了自定义类加载器了。
第二种方案只能让activity,view等组件的加载走到你的自定义类加载器。因为其他类即使一开始走到你的自定义类加载器,也会因为super.loadClass导致该类由pathClassLoader加载,在该类中触发的其他类加载就直接绕过了自定义类加载器了。
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
【胖虎的逆向之路】01——动态加载和类加载机制详解
之前一直了解到加壳脱壳,直接使用Fart等脱壳工具进行的,停留在知其然不知其所以然的层次,所以以此准备进行Android 基础理论的学习中,首先要深入理解类加载器和动态加载二者之间的关系,本文记录了类加载器和动态加载之间的关系和原理,由于作者能力有限,会尽力的详细讲解两者之间的关系,如本文中有任何错误,烦请指正,感谢~
胖虎哥
2023/05/10
8220
【胖虎的逆向之路】01——动态加载和类加载机制详解
Android 插件化实现原理解析
组件化开发就是将一个app分成多个模块,每个模块都是一个个组件,开发的过程中我们可以让这些组件相互依赖或者单独调试组件,但是最终发布的时候是将这些组件并成一个apk发布,而插件话 是分为一个宿主 和多个插件apk ,插件话成本高就是 适配 android版本,每个android版本的源码实现都不同,每个新版本出来,你就得去看源码然后 对这个源码做适配。
李林LiLin
2023/04/27
6530
ClassLoader解析(二):Android中的ClassLoader
main方法时ZygoteInit入口方法,其中调用了ZygoteInit的preload方法,preload方法中又调用了ZygoteInit的preloadClasses方法。
用户1205080
2019/01/23
1.6K0
ClassLoader解析(二):Android中的ClassLoader
深入理解Android Instant Run运行机制
Instant Run Instant Run,是android studio2.0新增的一个运行机制,在你编码开发、测试或debug的时候,它都能显著减少你对当前应用的构建和部署的时间。通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果。而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果。 传统的代码修改及编译部署流程 传统的代码修改及编译流程如下:构建整个apk → 部署app →
xiangzhihong
2018/02/06
5.1K0
深入理解Android Instant Run运行机制
【胖虎的逆向之路】02——Android整体加壳原理详解&实现
为了深入了解Android 逆向相关的内容中加壳的原理,前面已经完成了关于Android中的动态加载和动态加载类关系的详解,那么接下来是对Android的整体加壳进行实现,并对原理进行讲解,由于作者能力有限,会尽力的详细描述整体加壳的流程及原理,如本文中有任何错误,烦请指正,感谢~
胖虎哥
2023/05/10
8490
【胖虎的逆向之路】02——Android整体加壳原理详解&实现
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化】插件化原理 ( 类加载器 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 ) 【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
韩曙亮
2023/03/29
7170
【Android 插件化】插件化原理 ( 类加载器 )
【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 在 PathClassLoader 和 BootClassLoader 之间插入 DexClassLoader )
在 上一篇博客 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 ) 中 , 通过 替换 LoadedApk 中的类加载器可以成功加载 DEX 字节码文件中的 Activity 类 , 并成功启动 Activity ;
韩曙亮
2023/03/30
1.5K0
【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 在 PathClassLoader 和 BootClassLoader 之间插入 DexClassLoader )
Android热补丁动态更新实践
前言 好几个月之前关于Android App热补丁修复火了一把,源于QQ空间团队的一篇文章安卓App热补丁动态修复技术介绍,然后各大厂的开源项目都出来了,本文的实践基于HotFix,也就是QQ空间技术
巫山老妖
2018/07/20
1.4K0
Instant run动态加载机制
关于动态加载,实际上Instant run提供了两种动态加载的机制: 1.修改java代码需要重启应用加载补丁dex,而在Application初始化时替换了Application,新建了一个自定义的ClassLoader去加载所有的dex文件。我们称为重启更新机制 2.修改代码不需要重启,新建一个ClassLoader去加载修改部分。我们称为热更新机制
老马的编程之旅
2022/06/22
8040
Android插件化学习之路(二)之ClassLoader完全解析
Java代码都是写在Class里面的,程序运行在虚拟机上时,虚拟机需要把需要的Class加载进来才能创建实例对象并工作,而完成这一个加载工作的角色就是ClassLoader。
老马的编程之旅
2022/06/22
6280
android加载dex方法,android Dex文件的加载
上篇文章讲到了apk的分包,通过multidex构建出包含多个dex文件的apk,从而解决65536的方法数限制问题《Android Dex分包》。
全栈程序员站长
2022/09/07
2.4K0
class, classloder, dex 详解
例如记住了当前类的引用this、父类super等等。class文件记录的信息往往比java文件多。
全栈程序员站长
2022/07/20
4830
class, classloder, dex 详解
InstantRun从2.0到3.0,历史解毒
Instant Run已经出来3年了,为什么现在会想写这篇文章。从Instant Run 发布就已经有文章做了详细的介绍,但主要分为两类:一类是讲其主要实现原理或是讲 Instant Run2.0中的 Application 和 ClassLoader 的替换,另一类就是两者结合。但是在Instant Run2.0 以后包括(2.3和3.0)虽然主要的实现原理没做改变,但都不再有 Application 和 ClassLoader 的替换了。
静默加载
2020/05/29
1.5K0
Android热修复学习之旅——HotFix完全解析
在上一篇博客 Android热修复学习之旅开篇——热修复概述中,简单介绍了各个热修复框架的原理,本篇博客我将详细分析QQ空间热修复方案。
老马的编程之旅
2022/06/22
1.2K0
Android热修复学习之旅——HotFix完全解析
Android插件化原理解析
在解决插件中组件的生命周期,通常的做法是通过 Hook相应的系统对象,实现欺上瞒下,后面将通过Activity的插件化来进行讲解。
用户1205080
2019/02/26
9410
【Android 逆向】类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )
打印 Activity 组件类 的 类加载器 及 该 类加载器 的 父类类加载器 :
韩曙亮
2023/03/30
3980
【Android 逆向】类加载器 ClassLoader ( 加载 Android 组件的类加载器 | 双亲委派机制实例分析 )
【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 )
在 上一篇博客 【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( DEX 文件准备 | 拷贝资源目录下的文件到内置存储区 | 配置清单文件 | 启动 DEX 文件中的组件 | 执行结果 ) 的代码基础上 , 使用类加载器加载 com.example.dex_demo.MainActivity2 组件前 , 先替换 LoadedApk 的类加载器 , 就可以成功加载 DEX 文件了 , 该操作类似于热修复 ;
韩曙亮
2023/03/30
2K0
【Android 逆向】启动 DEX 字节码中的 Activity 组件 ( 替换 LoadedApk 中的类加载器 | 加载 DEX 文件中的 Activity 类并启动成功 )
Android中实现热补丁动态修复详析
热修复已经不是什么新的话题,目前仍然对它的讨论很火,本文是一篇动态修复的实践篇,以腾讯HotFix为蓝本,带你体验热修复之旅。
开发者技术前线
2020/11/23
1.1K0
Android中实现热补丁动态修复详析
Activity插件化解决方案
2.最简单的插件化方案就是在宿主的androidmanifest.xml中申明插件中的四大组件
用户3112896
2019/09/26
6220
实习杂记(31):android多dex方案一
内部复杂创造的方法是:return new PathClassLoader(classPath, BootClassLoader.getInstance());
wust小吴
2019/07/08
4870
推荐阅读
相关推荐
【胖虎的逆向之路】01——动态加载和类加载机制详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档