Xposed原理简介及其精简化

Xposed是一个很强大的Android平台上的HOOK工具,而且作者为了方便开发者使用开发了一个APP(Xposed Installer,下文称为Installer) 来使用开发者自己开发的模块。开发者安装自己的模块后需要在Installer中勾选自己的模块然后重启手机自己的模块才会起作用。但是这样有点不利于开发者测试,每次都要点开Installer操作几下尤其是还要重启就显得有点麻烦了。

读过Xposed的源码后会发现仅通过更改XposedBridge.jar的源码就可以更简便一些:
1. 不需重启手机
2. 不需操作Installer这个App,且不用安装hook模块,只需push到手机即可

首先需要下载源码,rovo89链接里面有Xposed所有源码,我们只需要下载XposedBridge就好。原版的Xposed并不适用于三星手机,wanam改过的Xposed可以用,所以如果是三星手机的话就下载wanam的。

1. Xposed原理简介

现在安装Xposed比较方便,因为Xposed作者开发了一个Xposed Installer App,下载后按照提示傻瓜式安装(前提是root手机)。其实它的安装过程是这个样子的:首先探测手机型号,然后按照手机版本下载不同的刷机包,最后把Xposed刷机包刷入手机重启就好。刷机包下载 里面有所有版本的刷机包。
刷机包解压打开里面的问件构成是这个样子的:

META-INF/    里面有文件配置脚本 flash-script.sh 配置各个文件安装位置。
system/bin/   替换zygote进程等文件
system/framework/XposedBridge.jar jar包位置
system/lib system/lib64 一些so文件所在位置
xposed.prop xposed版本说明文件

所以安装Xposed的过程就上把上面这些文件放到手机里相同文件路径下。
通过查看文件安装脚本发现:
system/bin/下面的文件替换了app_process等文件,app_process就是zygote进程文件。所以Xposed通过替换zygote进程实现了控制手机上所有app进程。因为所有app进程都是由Zygote fork出来的。
Xposed的基本原理是修改了ART/Davilk虚拟机,将需要hook的函数注册为Native层函数。当执行到这一函数是虚拟机会优先执行Native层函数,然后再去执行Java层函数,这样完成函数的hook。如下图:

Xposed HOOK 原理

通过读Xposed源码发现其启动过程:

  1. 手机启动时init进程会启动zygote这个进程。由于zygote进程文件app_process已被替换,所以启动的时Xposed版的zygote进程。
  2. Xposed_zygote进程启动后会初始化一些so文件(system/lib system/lib64),然后进入XposedBridge.jar中的XposedBridge.main中初始化jar包完成对一些关键Android系统函数的hook。
  3. Hook则是利用修改过的虚拟机将函数注册为native函数。
  4. 然后再返回zygote中完成原本zygote需要做的工作。
    这只是在宏观层面稍微介绍了下Xposed,要想详细了解需要读它的源码了。下面两篇写的挺好,要想深入理解的可以看看。
    Android Hook框架Xposed原理与源代码分析
    深入理解Android之Xposed详解

2. Xposed精简化

上面稍微介绍了下它的原理,下面就介绍如何精简化Xposed。下面只修改了XposedBridge.jar包中的XposedBridge.java这个文件,修改完重新Build apk然后把apk重命名为XposedBridge.jar然后替换刷机包中的jar包,刷入手机即可。

2.1 取消重启手机

看下XposedBridge.jar的源码
代码文件de.robv.android.xposed.XposedBridge.java

    protected static void main(String[] args) {
        // Initialize the Xposed framework and modules
        try {
            if (!hadInitErrors()) {
                initXResources();

                SELinuxHelper.initOnce();
                SELinuxHelper.initForProcess(null);

                runtime = getRuntime();
                XPOSED_BRIDGE_VERSION = getXposedVersion();

                if (isZygote) {
                    XposedInit.hookResources();
                    XposedInit.initForZygote();
                }
               //修改时需注释下面这行代码
                XposedInit.loadModules();//*********load hook 模块*******************
            } else {
                Log.e(TAG, "Not initializing Xposed because of previous errors");
            }
        } catch (Throwable t) {
            Log.e(TAG, "Errors during Xposed initialization", t);
            disableHooks = true;
        }

        // Call the original startup code
        if (isZygote) {  //****代码修改位置****           
            ZygoteInit.main(args);
        } else {
            RuntimeInit.main(args);
        }
    }

注意上面的XposedInit.loadModules()这个函数,这个函数的作用就是load hook模块到进程中。
因为zygote启动时先跑到java层XposeBridge.main中,在main里面有一步操作是将hook模块load进来,模块加载到zygote进程中,zygote fork所有的app进程里面也有这个hook模块,所以这个模块可以hook任意app。(编写hook模块的第一步就是判断当前的进程名字,如果是要hook的进程就hook,不是则返回)。
所以修改模块后,要将模块重新load zygote里面必须重启zygote,要想zygote重启就要重启手机了。
所以修改的逻辑是不把模块load到zygote里面,而是load到自己想要hook的进程里面,这样修改模块后只需重启该进程即可。
在上面代码的代码修改位置添加如下代码,并将上面XposedInit.loadModules()注释掉即可

      if (isZygote) {
            XposedHelpers.findAndHookMethod("com.android.internal.os.ZygoteConnection", BOOTCLASSLOADER, "handleChildProc",
                    "com.android.internal.os.ZygoteConnection.Arguments",FileDescriptor[].class,FileDescriptor.class,
                    PrintStream.class,new XC_MethodHook() {

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            // TODO Auto-generated method stub
                            super.afterHookedMethod(param);
                            String processName = (String) XposedHelpers.getObjectField(param.args[0], "niceName");
                            String coperationAppName = "指定进程名称如:com.android.settings";
                            if(processName != null){
                                if(processName.startsWith(coperationAppName)){
                                    log("--------Begin Load Module-------");
                                    XposedInit.loadModules()
                                }
                            }
                        }

                    });
            ZygoteInit.main(args);
        } else {
            RuntimeInit.main(args);
        }

2.2 取消操作Installer APP

通过读Install App的源码发现其实勾选hook模块其实app就是把模块的apk位置写到一个文件里,等load模块时会读取这个文件,从这个文件中的apk路径下把apk load到进程中。
看下loadmodules的源码

XposedInit.java
/**
     * Try to load all modules defined in <code>BASE_DIR/conf/modules.list</code>
     */
    /*package*/ static void loadModules() throws IOException {
        final String filename = BASE_DIR + "conf/modules.list";
        BaseService service = SELinuxHelper.getAppDataFileService();
        if (!service.checkFileExists(filename)) {
            Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
            return;
        }

        ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
        ClassLoader parent;
        while ((parent = topClassLoader.getParent()) != null) {
            topClassLoader = parent;
        }

        InputStream stream = service.getFileInputStream(filename);
        BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
        String apk;
        while ((apk = apks.readLine()) != null) {
            loadModule(apk, topClassLoader);
        }
        apks.close();
    }

apk配置文件就是installer app文件路径下的conf/modules.list这个文件data/data/de.robv.android.xposed.installer/conf/modules.list
或者data/user_de/0/de.robv.android.xposed.installer/conf/modules.list(不同手机版本位置不同)
所以我们改下,直接给他个确定的apk路径及名称。就定为/data/local/tmp/module.apk。
所以再把之前的代码改成如下的样子就好了

 if (isZygote) {
            XposedHelpers.findAndHookMethod("com.android.internal.os.ZygoteConnection", BOOTCLASSLOADER, "handleChildProc",
                    "com.android.internal.os.ZygoteConnection.Arguments",FileDescriptor[].class,FileDescriptor.class,
                    PrintStream.class,new XC_MethodHook() {

                        @Override
                        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                            // TODO Auto-generated method stub
                            super.afterHookedMethod(param);
                            String processName = (String) XposedHelpers.getObjectField(param.args[0], "niceName");
                            String coperationAppName = "指定进程名称如:com.android.settings";
                            if(processName != null){
                                if(processName.startsWith(coperationAppName)){
                                    log("--------Begin Load Module-------");
                                    String path = "/data/local/tmp/module.apk";
                                    //注意由loadModules换成了loadModule,记得改
                                    XposedInit.loadModule(path, BOOTCLASSLOADER);

                                }
                            }
                        }

                    });
            ZygoteInit.main(args);
        } else {
            RuntimeInit.main(args);
        }

最后, 同学点个赞吧!!! 加个关注好么
所以每次修改模块后直接修改名字为module.apk然后push到/data/local/tmp/下,然后重启app就好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 前言 Xposed这位老兄大家可能不认识,微信自动抢红包大家听过吧、微信记录器作弊大家听过吧、地理位置模拟大家听过...
    王永迪阅读 29,001评论 15 62
  • 转载注明出处:简书-十个雨点 简介 我们模仿锤子制作的Bigbang应用,通过辅助服务基本上实现了在微信、QQ等聊...
    十个雨点阅读 13,347评论 2 16
  • 白牛皮,我的风景 20170402 “白牛皮,白牛皮,一半风一半雨,天上风雨海上流急...
    501林德荣阅读 278评论 0 0
  • 想你,是不安分的风 将清晨迷蒙的雾气搅动 是古刹厚重的钟声 穿过遥远的距离 我在身畔响起 屋后初旭 夜莺还在啼叫 ...
    空与窗阅读 190评论 0 0