精华内容
下载资源
问答
  • Android 进阶解密

    2020-10-20 10:56:02
    链接:https://pan.baidu.com/s/1l_83TYbUNPVN6gj0AaLdQg 提取码:6w2s

    链接:https://pan.baidu.com/s/1l_83TYbUNPVN6gj0AaLdQg 
    提取码:6w2s 
     

    展开全文
  • Android进阶解密④—插件化原理

    千次阅读 2020-11-12 17:24:57
    Android进阶解密①—activity的启动过程 Android进阶解密②—Service的启动 Android进阶解密③—Hook 动态加载技术: 在程序运行时,动态加载一些程序中原本不存在的可执行文件并运行起来,,随着应用技术的发展,...

    在学习插件化之前需要看前面几篇文章:

    动态加载技术:

    在程序运行时,动态加载一些程序中原本不存在的可执行文件并运行起来,,随着应用技术的发展,动态加载技术逐渐派生出两个分支,热修复和插件化;

    • 热修复:用于修复bug
    • 插件化:解决应用庞大,功能模块解耦,复用其他apk的代码
    插件化思想:

    将复用的apk作为插件,插入另一个apk中,比如淘宝中会有咸鱼的页面,用淘宝为咸鱼引流,使用插件化技术,可以直接使用咸鱼apk中的dex文件,这样省去再次开发一套咸鱼页面的成本,并且有效的降低了淘宝apk的耦合度;

    Activity插件化原理:

    插件化activity的目的是直接使用另一个apk的activity,而activity的启动和生命周期的管理需要经过AMS的处理,另一个apk的activity没有在本项目的manifest注册,肯定是无法通过的,所以我们需要hook startActivity的流程,绕过ams的验证,可以在本项目使用一个占坑activity,在发送给ams前将插件activity换成占坑activity去通过ams的验证,验证好以后在真实的启动时再将插件activity换回来;

    步骤:
    • 事先在本项目准备好占坑activity
    • 使用占坑activity绕过ams验证
    • 还原插件activity

    1. 准备占坑activity

    直接在原项目准备一个空白的activity即可,记得必须在manifest注册,下文叫他SubActivity

    2. 使用插件activity替换占坑activity

    在交给ams进程验证之前,在用户进程会经过两个类的传递,Instrumentation, iActivityManager,者两个类都可以作为hook点,这里介绍hook iActivityManager的这种方法;

    2.1 创建hook点的代理类,iActivityManagerProxy
    public class IActivityManagerProxy implements InvocationHandler {
    
        private Object realActivityManager;
    
        public IActivityManagerProxy(Object realActivityManager) {
            this.realActivityManager = realActivityManager;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("startActivity".equals(method.getName())){
                //  首先找到,原本需要启动的插件activity的原始intent
                Intent originIntent = null;
                int index = 0;
                for (int i = 0;i<args.length;i++){
                    if (args[i] instanceof Intent){
                        originIntent = (Intent) args[i];
                        index = i;
                        break;
                    }
                }
                //  新建欺骗ams的占坑activity的intent
                Intent fakeIntent = new Intent();
                fakeIntent.setClass("xxx.xxx.xxx",SubActivity.class);
                //  将真实的intent保存在fakeIntent中用于第三步的还原操作
                fakeIntent.putExtra("real_intent",originIntent);
                //  将fakeIntent写回原来的arges数组中
                args[index] = fakeIntent;
            }
            return method.invoke(realActivityManager,args);
        }
    }
    

    这里使用的动态代理创建iActivityManager的代理,首先找到原本启动的插件Activity的Intent,然后新建一个启动SubActivity的intent替换它;

    2.2替换原本的iActivityManager:
        public void hookAMS() throws Exception {
            // 获取ActivityManager getService 返回的单例
            Class ActivityManagerClazz = ActivityManager.class;
            Field IActivityManagerSingletonField = ActivityManagerClazz.getDeclaredField("IActivityManagerSingleton");
            Object IActivityManagerSingleton = IActivityManagerSingletonField.get(ActivityManagerClazz);
    
            //  通过单例.get()获取iActivityManager, 这两步需要参考源码的iActivityManager的获取
            Class singleClazz = IActivityManagerSingleton.getClass();
            Method getMethod = singleClazz.getDeclaredMethod("get");
            Object iActivityManager = getMethod.invoke(IActivityManagerSingleton,null);
            
            // 生成动态代理对象
            Object proxyInstance = Proxy.newProxyInstance(
                    ActivityManagerClazz.getClassLoader(),
                    ActivityManagerClazz.getInterfaces(),
                    new IActivityManagerProxy(iActivityManager));
    
            // 将代理对象设置到单例上
            Field mInstanceField = singleClazz.getField("mInstance");
            mInstanceField.set(IActivityManagerSingleton,proxyInstance);
        }
    
    • 这个方法需要在startActivity前调用

    3. 还原插件Activity

    绕开ams验证后,我们还需要真实的启动TargetActivity,再学习了Handler机制后,我们知道message的处理顺序是首先会判断当前message.callback有没有逻辑,会首先执行callback;我们可以将Message作为Hook点

    3.1 创建自定义CallBack,在handleMessage处理前,将fakeIntent换成真实的intent
      class MCallBack implements android.os.Handler.Callback {
            @Override
            public boolean handleMessage(Message msg) {
                try {
                    Object activityClientRecord = msg.obj;
                    // 获取fakeIntent
                    Class acrClazz = activityClientRecord.getClass();
                    Field intentField = acrClazz.getDeclaredField("intent");
                    Intent intent = (Intent) intentField.get(activityClientRecord);
                    // 取出targetActivity的Intent
                    Intent realIntent = intent.getParcelableExtra("real_intent");
                    // 将realIntent的内容设置到fakeIntent
                    intent.setComponent(realIntent.getComponent());
                    
                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                msg.getTarget().handleMessage(msg);
                return true;
            }
        }
    
    3.2 hook ActivityThread, 修改主线程的H(Handler)的CallBack属性,原理参考dispatchMessage方法
        private void hookActivityThread() throws Exception {
            Class activityThreadClass = Class.forName("android.app.ActivityThread");
            Field singleInstanceField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
            Object activityThreadInstance = singleInstanceField.get(null);
            
            Field mHField = activityThreadClass.getDeclaredField("mH");
            Handler handler = (Handler) mHField.get(activityThreadInstance);
            
            // 修改handler 的callback
            Class handlerClazz = handler.getClass();
            Field callbackField = handlerClazz.getDeclaredField("mCallback");
            callbackField.set(handler,new MCallBack());
        }
    

    在Handler机制中有两个callback,一个是Handler.mCallback,一个是Message.callback

        public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    在loop中轮询处理message的时候会调用dispatchMessage;如果Message.callback不会null就处理Runnable的回调然后结束,如果msg.callback为null,则先执行Handler的mCallback,并根据Handler的mCallback.handleMessage的返回值判断是否执行Handler.handleMessage;
    根据上面的流程,我们可以在ActivityThread的H处理startActivity这个Message的handleMessage前,在H的Callback中插入修改intent的代码,做到真实的开启TargetActivity

    3.3 插件Activity的生命周期管理:

    上面的操作只做到了开启activity,插件activity的生命周期是如何管理的,AMS通过token来对activity进行识别管理,而插件activity token的绑定是不受影响的,所以插件activity是具有生命周期的;

    Service插件化原理

    代理分发实现:

    当启动插件Service时,就会先启动代理Service,当代理Service运行后,在其onStartCommand中启动插件Service;

    步骤:
    • 项目中准备好代理Service
    • hook iActivityManager 启动代理Service
    • 代理分发:
    1. ProxyService需要长时间对插件Service进行分发,所以需要return START_STICKY ProxyService重新创建
    2. 创建插件Service,attach,onCreate;

    1. 在项目中创建一个ProxyService,在manifest中注册;

    2. hook iActivityManager,将要启动的TargetService换成ProxyService

    2.1 创建自定义iActivityManagerProxy
    public class IActivityManagerProxy implements InvocationHandler {
    
        private Object realActivityManager;
    
        public IActivityManagerProxy(Object realActivityManager) {
            this.realActivityManager = realActivityManager;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("startService".equals(method.getName())){
                Intent targetIntent = null;
                int index = 0;
                for (int i = 0;i<args.length;i++){
                    if (args[i] instanceof Intent){
                        targetIntent = (Intent) args[i];
                        index = i;
                        break;
                    }
                }
                Intent proxyIntent = new Intent();
                proxyIntent.setClassName("com.xx.xx","com.xx.xx.ProxyService");
                proxyIntent.putExtra("target_intent",targetIntent);
                args[index] = proxyIntent;
            }
            return method.invoke(realActivityManager,args);
        }
    }
    
    2.2 hook AMS替换原来的IActivityManager 同上
     public void hookAMS() throws Exception {
            // 获取ActivityManager getService 返回的单例
            Class ActivityManagerClazz = ActivityManager.class;
            Field IActivityManagerSingletonField = ActivityManagerClazz.getDeclaredField("IActivityManagerSingleton");
            Object IActivityManagerSingleton = IActivityManagerSingletonField.get(ActivityManagerClazz);
    
            //  通过单例.get()获取iActivityManager, 这两步需要参考源码的iActivityManager的获取
            Class singleClazz = IActivityManagerSingleton.getClass();
            Method getMethod = singleClazz.getDeclaredMethod("get");
            Object iActivityManager = getMethod.invoke(IActivityManagerSingleton,null);
            
            // 生成动态代理对象
            Object proxyInstance = Proxy.newProxyInstance(
                    ActivityManagerClazz.getClassLoader(),
                    ActivityManagerClazz.getInterfaces(),
                    new IActivityManagerProxy(iActivityManager));
    
            // 将代理对象设置到单例上
            Field mInstanceField = singleClazz.getField("mInstance");
            mInstanceField.set(IActivityManagerSingleton,proxyInstance);
        }
    

    只要在startService前调用这段代码,就会启动proxyService,下面我们在proxyService中对targetService进行分发;

    2.3 在proxyService中启动TargetService:
    • 调用attach绑定Context
    • 调用onCreate
    public class ProxyService extends Service {
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            try {
                // 准备attach方法的参数
                Class activityThreadClazz = null;
                activityThreadClazz = Class.forName("android.app.ActivityThread");
                Method getApplicationMethod = activityThreadClazz.getDeclaredMethod("getApplicationMethod");
                Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
                sCurrentActivityThreadField.setAccessible(true);
                // activityThread
                Object activityThread = sCurrentActivityThreadField.get(null);
                // applicationThread
                Object applicationThread = getApplicationMethod.invoke(activityThread, null);
                Class iInterFaceClazz = Class.forName("android.os.IInterface");
                Method asBinderMethod = iInterFaceClazz.getDeclaredMethod("asBinder");
                asBinderMethod.setAccessible(true);
                // token
                Object token = asBinderMethod.invoke(applicationThread);
                // iActivityManager
                Class ActivityManagerClazz = ActivityManager.class;
                Field IActivityManagerSingletonField = ActivityManagerClazz.getDeclaredField("IActivityManagerSingleton");
                Object IActivityManagerSingleton = IActivityManagerSingletonField.get(ActivityManagerClazz);
                Class singleClazz = IActivityManagerSingleton.getClass();
                Method getMethod = singleClazz.getDeclaredMethod("get");
                Object iActivityManager = getMethod.invoke(IActivityManagerSingleton, null);
    
                // targetService
                Class serviceClazz = Class.forName("android.app.Service");
                Service targetService = (Service) serviceClazz.newInstance();
    
                // attach
                Method attachMethod = serviceClazz.getDeclaredMethod("attach");
                attachMethod.invoke(targetService, this,
                        activityThread, intent.getComponent().getClassName(),
                        token, getApplication(), iActivityManager);
                targetService.onCreate();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return START_STICKY;
        }
    }
    
    
    • 首先准备attach需要的参数,通过反射获取
    • 调用targetService的attach方法
    • 调用targetService的onCreate方法

    资源的插件化

    参考这边换肤文章:安卓换肤实现原理

    某东的插件化实践

    1. 插件化做的事情:

    插件化的目的就是在主工程使用插件工程的代码/类资源

    1.1插件中四大组件的处理:

    当使用插件的四大组件类时,比如插件activity的使用必须做特殊的处理,一般的处理方式有:

    • hook AMS,绕过AMS对四大组件的验证,手动管理生命周期
    • 直接在主工程的manifest注册插件的activity

    方式1的特点是实现难度高,灵活性高,但是随着谷歌对于系统非sdk调用的限制,这个方式可能会在未来失效;
    方式2的特点是实现简单,但是不够灵活,必须在manifest中写死

    某东采用的方式2,直接在manifest中写死插件的四大组件注册

    1.2插件中类的加载和使用:

    每一个插件都设置一个ClassLoader,目的是整个插件的类都是由一个加载器加载,所有的插件的ClassLoader都在双亲委派中继承了另一个ClassLoader,这个目的是便于主工程的统一管理;

    1.3DelegateClassLoader的替换:

    替换LoadedAPK的ClassLoader为DelegateClassLoader即可;

    1.4插件资源的引用

    添加一个path给AssetManager就行,详见上文

    2. 插件如何打包进主工程,即主工程如何集成插件包:

    • 将插件apk放入asset目录,通过assetManager去加载
    • 将插件apk文件修改后缀为.so 放入lib/armeabi目录,主工程apk在安装的时候会自动将这个目录的文件加载到data/data/< package_name >/lib/目录下,可以直接获取;

    某东使用的第二种以so的形式放入lib目录自动加载,因为在运行时去使用AssetManager加载asset资源会影响程序的运行时速度

    3. 插件和主工程如何通信

    主要借鉴的airbnb的DeepLinkDispatch

    DeepLinkDispatch类似Android原生的scheme协议,用于跳转到另一个APP的页面,比如在美团中打开高德地图,或者在微信中打开京东,

    DeepLinkDispatch的实现思路:首先在插件工程中需要被打开的Activity添加注解,然后在主工程调用DeepLinkDispatch.startActivityDirect()传入注解中设置好的参数,最后会通过系统的apiContext.startActivity()开启页面

    4. 插件化的未来

    随着谷歌对于系统API的限制越来越严格,并且现在已经分成黑名单,深灰名单,浅灰名单留时间开发者调整,插件化应该是没有未来的,我们想想插件化到底是为了什么:

    • 独立编译,提高开发效率
    • 模块解耦,复用代码
    东东的解决方案:

    组件化:
    传统的组件话是新建一个Android Library,在开发调试和实际引用的时候在Application和Library之间切换,东东的组件化是将每一个组件单独做成一个项目,然后在项目结构中保留Application和Android Library,library用于实现组件的功能,app用于开发调试,在主工程使用时直接通过依赖从云端的maven sync;

    展开全文
  • Android进阶二部曲第一部《Android进阶之光》介绍 :点击这里 ...Android进阶二部曲包括《Android进阶之光》和本书,因此写这本书的原因和《Android进阶之光》有些关联,主要有以下几点: 《Android进阶之...

    独立博客版本请点击这里
    勘误贴请点击这里
    本书源码地址:https://github.com/henrymorgen/android-advanced-decode
    Android进阶三部曲第一部《Android进阶之光》介绍 :点击这里

    为什么写这本书

    Android进阶三部曲包括《Android进阶之光》和本书,因此写这本书的原因和《Android进阶之光》有些关联,主要有以下几点:

    1. 《Android进阶之光》适合初、中级工程师阅读,因此我需要写一本适合中、高级工程师阅读的进阶书。
    2. 目前市场上的系统源码分析的书大部分不是专门为应用开发编写的,因此我要专门为Android应用开发编写一本系统源码分析的书,不仅如此,我还要将系统源码和应用开发相结合并融会贯通。
    3. 目前市面上的源码分析类书籍大多是基于Android 6.0以前版本的,需要有一本书籍来对更新版本的系统源码进行分析。

    内容简介

    本书主要针对Android 8.0系统源码并结合应用开发相关知识进行介绍。《Android进阶解密》共分为17章,从3个方面来组织内容。 一方面介绍Android应用开发所需要掌握的系统源码知识,第二方面介绍JNI、ClassLoader、Java虚拟机、DVM&ART虚拟机和Hook等技术,第三方面介绍热修复原理、插件化原理、绘制优化和内存优化等与应用开发相关的知识点。3个方面有所关联并形成一个知识体系,从而使Android开发者能通过阅读本书达到融会贯通的目的。

    《Android进阶解密》适合有一定基础的Android应用开发工程师、Android系统开发工程师和对Android系统源码感兴趣的读者阅读。

    目录

    V1YYVg.jpg
    V1YtaQ.jpg
    V1YGqS.jpg
    V1Y8r8.jpg
    V1Y3Kf.jpg
    V1Yw2q.jpg
    V1Y0x0.jpg
    V1YN5j.jpg
    V1YaPs.jpg

    封面

    封面是我在游戏中认识的设计师设计的,有些解密的感觉。
    V1YdGn.jpg

    购买

    目前各大商城基本已经全面开售。
    京东
    当当
    天猫
    亚马逊

    后记

    由于本书的篇幅有限,我还有很多想要讲的技术知识无法在书中写出,这些技术知识会继续在我的博客和微信公众号上分享出来。如果感兴趣,你可以在我的博客和微信公众号中继续学习。


    这里不仅分享大前端、Android、Java等技术,还有程序员成长类文章。
    展开全文
  • Android进阶三部曲第一部《Android进阶之光》介绍 :点击这里 本书源码地址:github.... 为什么写这本书 Android进阶三部曲包括《Android进阶之光》和本书,因此写这本书的原因和《Android进阶之光》有些...

    Android进阶三部曲第一部《Android进阶之光》介绍 :点击这里
    本书源码地址:github.com/henrymorgen…

    为什么写这本书

    Android进阶三部曲包括《Android进阶之光》和本书,因此写这本书的原因和《Android进阶之光》有些关联,主要有以下几点:

    1. 《Android进阶之光》适合初、中级工程师阅读,因此我需要写一本适合中、高级工程师阅读的进阶书。
    2. 目前市场上的系统源码分析的书大部分不是专门为应用开发编写的,因此我要专门为Android应用开发编写一本系统源码分析的书,不仅如此,我还要将系统源码和应用开发相结合并融会贯通。
    3. 目前市面上的源码分析类书籍大多是基于Android 6.0以前版本的,需要有一本书籍来对更新版本的系统源码进行分析。

    内容简介

    本书主要针对Android 8.0系统源码并结合应用开发相关知识进行介绍。《Android进阶解密》共分为17章,从3个方面来组织内容。 一方面介绍Android应用开发所需要掌握的系统源码知识,第二方面介绍JNI、ClassLoader、Java虚拟机、DVM&ART虚拟机和Hook等技术,第三方面介绍热修复原理、插件化原理、绘制优化和内存优化等与应用开发相关的知识点。3个方面有所关联并形成一个知识体系,从而使Android开发者能通过阅读本书达到融会贯通的目的。

    《Android进阶解密》适合有一定基础的Android应用开发工程师、Android系统开发工程师和对Android系统源码感兴趣的读者阅读。

    目录

    封面

    封面是我在游戏中认识的设计师设计的,有些解密的感觉。

    感谢为这本书写推荐语的朋友们,谢谢你们的支持。

    购买

    目前各大商城基本已经全面开售。
    京东
    天猫
    亚马逊

    后记

    由于本书的篇幅有限,我还有很多想要讲的技术知识无法在书中写出,这些技术知识会继续在我的博客和微信公众号上分享出来。如果感兴趣,你可以在我的博客和微信公众号中继续学习。


    分享Android、Java和大前端相关技术。

    展开全文
  • Android 进阶解密 - 系统启动 一、init 进程启动过程 init 进程是 Android 系统中用户空间的第一个进程,进程号为1, 是 Android 系统启动流程中一个关键的步骤,作为第一个进程, 它被赋予了很多极其重要的工作...
  • 源码的执行是按照一定流程思路进行的,hook就是在源码的执行流程之间插入一步操作,起...首先需要知道startactivity的流程:Android进阶解密①——activity的启动过程 我们知道startActivity会通过mInstrumentation这个
  • 第二十二期 AMA 掘金团队请来了《Android进阶解密》作者-- 刘望舒做了为期三天的 Ask Me Anything (AMA) 活动(活动已结束)。 我们在此精选了一些来自用户的提问及刘望舒的回答。 提醒:本期分布式、微服务主题的 ...
  • 点击上方“何俊林”,马上关注,每天早上8:50准时推送真爱,请置顶或星标今天给大家送点技术书,《Android音视频开发》和《Android进阶解密》,先说下这两本书背景...
  • Android进阶解密》电子书基于Android8.0剖析系统源码,需要必须有一定的Android开发积累,如果能有C语言基础就更好了,层层紧扣逐步深入,可谓良心之作。 。。。。。。。。。。。。。。。。 花了一整天时间...
  • 完整简略流程图如下:
  • 文章目录第1章 Android系统架构 1第2章 Android系统启动 13第3章 应用程序进程启动过程 61第4章 四大组件的工作过程 81第5章 理解上下文Context 149第6章 理解ActivityManagerService 164第7章 理解WindowManager ...
  • Android进阶解密读书笔记5——第3章:应用程序进程启动过程——第1、2、3、4小节
  • 在此之前已经总结过ClassLoader的原理,以及通过ClassLoader方式实现的热修复思路,实现热修复的方法有很多,大致有三种...关于Instant Run的了解可以参考这篇文章: Android Studio新功能解析,你真的了解Instant R
  • 文章目录一 StartServiceContextImpl到AMSContextImpl.startServiceCommon()AMS到ActivityThreadActivityThread.scheduleCreateService()H的处理二 BindService:AMS的bindService逻辑都交给了`ActiveServices`...

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 237
精华内容 94
关键字:

android进阶解密