一、前言

在上篇文章源码编译(2)——Xopsed源码编译详解中具体介绍了Xposed源码编译的残缺过程,本文将从Android编译过程到Xposed运行机制,最初进行Xposed框架的具体定制。其中Xposed的定制次要参考世界美景大佬的定制Xposed框架和肉丝大佬的来自高纬的反抗:魔改XPOSED过框架检测(下)。

致谢:

首先感激世界美景大佬的定制Xposed框架,从外面学习到对Xposed框架特色的批改,然而因为集体程度无限,大佬的贴子不够具体,不能残缺复现,通过搜寻发现肉丝大佬的基于此的两篇具体的贴子解说:来自高纬的反抗:魔改XPOSED过框架检测(上)和来自高纬的反抗:魔改XPOSED过框架检测(下),本文的Android零碎运行参考老罗的博客

二、Android运行机制

咱们在理解Xposed的运行机制前,不得不须要理解Android零碎的根本构造和运行机制,这样咱们能力进一步学习如何进行Xposed定制,能力缩小更多的谬误

1. Android平台架构

Android的平台架构如下图所示:

上面咱们顺次介绍各层之间的性能和作用:

(1)Linux内核

Android平台的根底是linux内核,Android Runtime(ART)依附Linux内核来执行底层性能,应用Linux内核可让Android利用次要平安性能,并且运行设施制造商为驰名的内核开发硬件驱动程序,能够了解基于linux内核让Android更平安并且能够领有很多设施驱动

(2)硬件形象层(HAL)

HAL提供规范界面,向更高级别Java API框架显示设施硬件性能,HAL蕴含多个模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如相机和蓝牙模块,当框架API要拜访设施硬件时,Android零碎为该硬件组件加载库模块。

(3)Android Runtime

Android 5.0之前Android Runtime为Dalvik,Android 5.0之后Android Runtime为ART

首先咱们先理解一些文件的含意:

(1)dex文件:Android将所有的class文件打包造成一个dex文件,是Dalvik运行的程序(2)odex文件:优化过的dex文件,Apk在装置时会进行验证和优化,通过dexopt生成odex文件,放慢Apk的响应工夫(3)oat文件:Android公有ELF文件格式,有dex2oat解决生成,蕴含(原dex文件+dex翻译的本地机器指令),是ART虚拟机应用的文件,能够间接加载(4)vdex文件:Android 8.0引入,蕴含APK的未压缩DEX代码,以及一些旨在放慢验证速度的元数据

上面咱们从Android零碎的倒退过程中具体介绍二者的区别:

版本虚拟机类型个性
2.1-4.4DalvikJIT+解释器
5.0-7.0ARTAOT
7.0-11ARTAOT+JIT+解释器

上面局部参考博客:博客地址

Android 2.2

Dalvik

    反对已转换成dex格局的android利用,基于寄存器,指令执行更快,加载的是odex文件,采纳JIT运行时编译

JIT:

    JIT即运行时编译策略,能够了解成一种运行时编译器,此时Android的虚拟机应用的是Dalvik,为了放慢Dalvik虚拟机解释dex速度,运行时动静地将执行频率很高的dex字节码翻译老本地机器码毛病:    (1)每次启动利用都须要从新编译    (2)运行时比拟耗电,造成电池额定的开销

    基于Dalvik的虚拟机,在APK装置时会对dex文件进行优化,产生odex文件,而后在启动APP后,运行时会利用JIT即时编译,解决执行频率高的一部分dex,将其翻译成机器码,这样在再次调用的时候就能够间接运行机器码,从而进步了Dalvik翻译的速率,进步运行速度毛病:    (1)因为在Dex加载时会触发dexopt , 导致Multidex加载的时候会十分慢    (2)因为热点代码的Monitor始终在运行 , 解释器解释的字节码会带来CPU和工夫的耗费, 会带来电量的损耗

Android 4.4——ART和AOT

此时引入全新的虚拟机运行环境ART和全新的编译策略AOT,此时ART和Dalvik是共存的,用户能够在两者之间抉择

Android 5.0——ART全面取代Dalvik

AOT:

AOT是一种运行前编译的策略毛病:(1)利用装置和系统升级之后的利用优化比拟耗时(2)优化后的文件会占用额定的存储空间

AOT与JIT区别:

JIT 是在运行时进行编译,是动静编译,并且每次运行程序的时候都须要对 odex 从新进行编译AOT 是动态编译,利用在装置的时候会启动 dex2oat 过程把 dex 预编译成 ELF 文件,每次运行程序的时候不必从新编译,是真正意义上的本地利用

JVM、Dalvik和ART区别:

JVM:传统的Java虚拟机、基于栈、运行class文件Dalvik:反对已转换成dex格局的android利用,基于寄存器,指令执行更快,加载的是odex(优化的dex)ART:第一次装置时,将dex进行Aot(预编译),字节码事后编译成机器码,生成可执行oat文件(ELF文件)

    基于ART的虚拟机,会在APK第一次装置时,将dex进行AOT(预编译),通过dex2oat生成oat文件,即Android可执行ELF文件,包含原dex文件和翻译后的机器码,而后启动程序后,间接运行毛病:    (1)因为装置APK时触发dex2oat , 须要编译成native code , 导致安装时间过长    (2)因为dex2oat生成的文件较大 , 会占用较多的空间

Android 7.0——JIT回归

思考下面AOT的毛病,dex2oat过程比拟耗时且会占用额定的存储空间,Android 7.0 再次退出JIT造成AOT+JIT+解释器模式

特点:

(1)利用在装置的时候 dex 不会被编译(2)利用在运行时 dex 文件先通过解析器(Interpreter)后会被间接执行,与此同时,热点函数(Hot Code)会被辨认并被 JIT 编译后存储在 jit code cache 中并生成 profile 文件以记录热点函数的信息(3)手机进入 IDLE(闲暇) 或者 Charging(充电) 状态的时候,零碎会扫描 App 目录下的 profile 文件并执行 AOT 过程进行编译

混合编译模式综合了 AOT 和 JIT 的各种长处,使得利用在装置速度放慢的同时,运行速度、存储空间和耗电量等指标都失去了优化

最初咱们能够看下Android各版本ClassLoader加载dex时的dexopt过程:

(4)原生C/C++库

许多外围 Android 零碎组件和服务(例如 ART 和 HAL)构建自原生代码,须要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向利用显示其中局部原生库的性能,咱们能够通过NDK开发Android中的C/C++库

(5)Java API框架

通过以 Java 语言编写的 API 应用 Android OS 的整个功能集。这些 API 造成创立 Android 利用所需的构建块,它们可简化外围模块化零碎组件和服务的重复使用,包含以下组件和服务

2. Android启动流程

Android启动流程如下图所示:

(1)Loader

Boot ROM: 当手机处于关机状态时,长按Power键开机,疏导芯片开始从固化在ROM里的预设代码开始执行,而后加载疏导程序到RAMBoot Loader:这是启动Android零碎之前的疏导程序,次要是查看RAM,初始化硬件参数,拉起Android OS

咱们长按电源键后,手机就会在Loader层加载疏导程序,并启动疏导程序,初始化参数

(2)Linux内核

(1)启动Kernel的swapper过程(pid=0):该过程又称为idle过程, 零碎初始化过程Kernel由无到有创始的第一个过程, 用于初始化过程治理、内存治理,加载Display,Camera Driver,Binder Driver等相干工作,这些模块驱动都会封装到对应的HAL层中(2)启动 init 过程(用户过程的祖宗)。pid = 1,用来孵化用户空间的守护过程、HAL、开机动画等(3)启动kthreadd过程(pid=2):是Linux零碎的内核过程,会创立内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护过程。kthreadd过程是所有内核过程的鼻祖    

(3)执行init过程

init 过程是Linux零碎中用户空间的第一个过程,过程号为1,是所以用户过程的先人

Linux Kernel实现零碎设置后,会首先在零碎中寻找init.rc文件,并启动init过程,init.rc脚本寄存门路: /system/core/rootdir/init.rc ,init过程:/system/core/init

init过程的启动能够分为三个局部:

(1)init过程会孵化出ueventd、logd、healthd、installd、adbd、lm这里写代码片kd等用户守护过程(2)init过程还会启动ServiceManager(Binder服务管家)、bootanim(开机动画)等重要服务(3)解析init.rc配置文件并孵化zygote过程,Zygote过程是Android零碎的第一个java过程(虚拟机过程),zygote过程是所以Java过程的父过程

创立Zygote过程:

(1)解析 init.zygote.rc //parse_service() (2)启动 main 类型服务 //do_class_start() (3)启动 zygote 服务 //service_start() (4)创立 Zygote 过程 //fork() (5)创立 Zygote Socket //create_socket()

(4)Zygote

Zygote为孵化器,即所有Android利用的先人,Zygote 让 VM 共享代码、低内存占用以及最小的启动工夫成为可能, Zygote 是一个虚拟机过程,Zygote是由init过程通过解析init.zygote.rc文件而创立的,zygote所对应的可执行程序app_process,所对应的源文件是App_main.cpp,过程名为zygote

Zygote作用过程:

(1)解析init.zygote.rc中的参数,创立AppRuntime并调用AppRuntime.start()办法;(2)调用AndroidRuntime的startVM()办法创立虚拟机,再调用startReg()注册JNI函数;(3)通过JNI形式调用ZygoteInit.main(),第一次进入Java世界;(4)registerZygoteSocket()建设socket通道,zygote作为通信的服务端,用于响应客户端申请;(5)preload()预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于进步app启动效率;(6)zygote结束大部分工作,接下来再通过startSystemServer(),fork得力帮手system_server过程,也是下层framework的运行载体。(7)zygote功成身退,调用runSelectLoop()(死循环),随时待命,当接管到申请创立新过程申请时立刻唤醒并执行相应工作。

Android零碎流程总结:

(1) 手机开机后,疏导芯片启动,疏导芯片开始从固化在ROM里的预设代码执行,加载疏导程序到到RAM,BootLoader查看RAM,初始化硬件参数等性能;(2) 硬件等参数初始化实现后,进入到Kernel层,Kernel层次要加载一些硬件设施驱动,初始化过程治理等操作。在Kernel中首先启动swapper过程(pid=0),用于初始化过程治理、内管治理、加载Driver等操作,再启动kthread过程(pid=2),这些linux零碎的内核过程,kthread是所有内核过程的鼻祖;(3) Kernel层加载结束后,硬件设施驱动与HAL层进行交互。初始化过程治理等操作会启动init过程 ,这些在Native层中;(4) init过程(pid=1,init过程是所有过程的鼻祖,第一个启动)启动后,会启动adbd,logd等用户守护过程,并且会启动servicemanager(binder服务管家)等重要服务,同时孵化出zygote过程,这里属于C++ Framework,代码为C++程序;(5) zygote过程是由init过程解析init.rc文件后fork生成,它会加载虚拟机,启动System Server(zygote孵化的第一个过程);SystemServer负责启动和治理整个Java Framework,蕴含ActivityManager,WindowManager,PackageManager,PowerManager等服务;(6) zygote同时会启动相干的APP过程,它启动的第一个APP过程为Launcher,而后启动Email,SMS等过程,所有的APP过程都有zygote fork生成。

三、Xposed框架运行机制

咱们从上文中曾经具体介绍了Android的启动流程,如下图所示:

上面咱们来具体介绍Xposed框架的实现原理:

1. Xposed实现原理

咱们先进入Xposed官网,能够发现Xposed工程的5个文件夹:

具体的模块性能和作用,咱们在上文源码编译(2)——Xopsed源码编译详解曾经介绍了,大家能够去参考

上文中咱们晓得,Xposed集成到Android源码中次要是:

(1)替换了Android中的虚拟机art(2)替换了app_process以及生成的一些lib,bin文件等

具体如下图所示

咱们从上文中能够得悉Android所有的用户过程都是通过Zygote孵化进去的,而Zygote的执行程序是app_process,装置Xposed,将app_process替换,而后替换对应的虚拟机,这样Zygote孵化器就变成了Xposed孵化器

咱们先来剖析一下替换后的app_process

ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))  LOCAL_SRC_FILES := app_main2.cpp  LOCAL_MULTILIB := both  LOCAL_MODULE_STEM_32 := app_process32_xposed  LOCAL_MODULE_STEM_64 := app_process64_xposedelse  LOCAL_SRC_FILES := app_main.cpp  LOCAL_MODULE_STEM := app_process_xposedendif...ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))  include frameworks/base/cmds/xposed/ART.mkelse  include frameworks/base/cmds/xposed/Dalvik.mkendif

程序通过判断SDK的版本抉择加载文件,这里咱们是在Android 6.0上运行,SDK版本为23,因而app_process会执行app_main2.cppART.mk

为了不便咱们剖析,这里我画了一个思维导图不便大家联合前面源码剖析:

咱们再剖析app_main2.cpp中main函数:

int main(int argc, char* const argv[]){    if (xposed::handleOptions(argc, argv))        return 0;    //代码省略...    runtime.mParentDir = parentDir;    // 初始化xposed,次要是将jar包增加至Classpath中    isXposedLoaded = xposed::initialize(zygote, startSystemServer, className, argc, argv);    if (zygote) {        // 如果xposed初始化胜利,将zygoteInit 替换为 de.robv.android.xposed.XposedBridge,而后创立虚拟机        runtime.start(isXposedLoaded ? XPOSED_CLASS_DOTS_ZYGOTE : "com.android.internal.os.ZygoteInit",                startSystemServer ? "start-system-server" : "");    }    ...}
main函数次要做了两件事:(1)初始化xposed(2)创立虚拟机

初始化xposed:

bool initialize(bool zygote, bool startSystemServer, const char* className, int argc, char* const argv[]) {    ...    // 初始化xposed的相干变量    xposed->zygote = zygote;    xposed->startSystemServer = startSystemServer;    xposed->startClassName = className;    xposed->xposedVersionInt = xposedVersionInt;    ...    // 打印 release、sdk、manufacturer、model、rom、fingerprint、platform相干数据    printRomInfo();    // 次要在于将jar包退出Classpath    return addJarToClasspath();}
(1)初始化xposed内相干变量(2)调用addJarToClasspath将XposedBridge.jar增加至系统目录

创立虚拟机:

void AndroidRuntime::start(const char* className, const Vector<String8>& options){    /* start the virtual machine */    JniInvocation jni_invocation;    jni_invocation.Init(NULL);    JNIEnv* env;    //创立虚拟机    if (startVm(&mJavaVM, &env) != 0) {        return;    }    // 初始化虚拟机,xposed对虚拟机进行批改    onVmCreated(env);    // 虚拟机初始化实现后,会调用传入的de.robv.android.xposed.XposedBridge类,初始化java层XposedBridge.jar    char* slashClassName = toSlashClassName(className);    jclass startClass = env->FindClass(slashClassName);    if (startClass == NULL) {        ...    } else {        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",        ...    }}
(1)创立虚拟机(2)初始化虚拟机xposed对虚拟机进行批改, onVmCreated(env)(3)传入调用类de.robv.android.xposed.XposedBridge(4)初始化XposedBridge

这里咱们能够参考老罗的源码剖析:

咱们能够发现,咱们在初始化虚拟机后,xposed会对虚拟机批改,函数onVmCreated(env)

onVmCreated(env):

void onVmCreated(JNIEnv* env) {    // Determine the currently active runtime    if (!determineRuntime(&xposedLibPath))     ...    // Load the suitable libxposed_*.so for it 通过dlopen加载libxposed_art.so    void* xposedLibHandle = dlopen(xposedLibPath, RTLD_NOW);    ...    // Initialize the library  初始化xposed相干库    bool (*xposedInitLib)(XposedShared* shared) = NULL;    *(void **) (&xposedInitLib) = dlsym(xposedLibHandle, "xposedInitLib");    if (!xposedInitLib)  {        ALOGE("Could not find function xposedInitLib");        return;    }    ...    // xposedInitLib -> onVmCreatedCommon -> initXposedBridge -> 注册Xposed相干Native办法    if (xposedInitLib(xposed)) {        xposed->onVmCreated(env);    }}

咱们来剖析xposedInitLib

libxposed_art.cpp#xposedInitLib

libxposed_common.cpp#onVmCreatedCommon

libxposed_common.cpp#initXposedBridge

libxposed_art.cpp#onVmCreated

onVmCreated总结:

(1)通过dlopen加载libxposed_art.so(2)初始化xposed相干库 xposedInitLib(3)xposedInitLib->onVmCreatedCommon->initXposedBridge,初始化XposedBridge,将register_natives_XposedBridge中的函数注册为Native办法(4)xposedInitLib->onVmCreatedCommon->onVmCreated,为xposed_callback_class与xposed_callback_method赋值

de.robv.android.xposed.XposedBridge#main

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();            } else {                Log.e(TAG, "Not initializing Xposed because of previous errors");            }        }         // Call the original startup code        if (isZygote) {            ZygoteInit.main(args);        } else {            RuntimeInit.main(args);        }    }

源码剖析:

虚拟机初始化实现后,会调用传入的de.robv.android.xposed.XposedBridge类,初始化java层XposedBridge.jar,调用main函数(1)hook 系统资源相干的办法   XposedInit.hookResources()(2)hook zygote 的相干办法   XposedInit.initForZygote()(3)加载零碎中曾经装置的xposed 模块   XposedInit.loadModules()

到此Xposed初始化完结

2. Xposed hook原理

通过上文的详细分析,咱们能够得出Xposed的hook原理:

    Xposed的基本原理是批改了ART/Davilk虚拟机,将须要hook的函数注册为Native层函数。当执行到这一函数是虚构机会优先执行Native层函数,而后再去执行Java层函数,这样实现函数的hook

启动过程总结:

(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的基本原理和hook原理就根本把握了,大家都晓得咱们这应用Xposed时,须要一直的去重启手机和勾选咱们装置的模块,为了方便使用,这里补充两个技巧,咱们理解Xposed源码后,就能够很不便实现了

(1)勾销重启手机

咱们先察看上文XposedBridge中main

下面的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的过程外面,这样批改模块后只需重启该过程即可

步骤:

(1)将下面XposedInit.loadModules()正文掉即可(2)在2处批改代码
      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)勾销操作Installer APP

通过浏览源码,咱们发现读Install App的源码发现其实勾选hook模块其实app就是把模块的apk地位写到一个文件里,等load模块时会读取这个文件,从这个文件中的apk门路下把apk load到过程中

loadmodules的源码:

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

所以咱们勾选一个文件,理论是将其写到conf/modules.list文件下,此时咱们发现Xposed中还有一个办法loadModule

这个办法能够依据具体的门路和类加载器,间接导入模块,所以只有咱们在下面代码中批改一些,就能够间接导入,不须要勾选那,咱们确定apk门路:pathclass = "/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 pathclass = "/data/local/tmp/module.apk";                                    //留神这里是loadModule办法,类加载器是咱们的根类加载器                                    XposedInit.loadModule(pathclass,BOOTCLASSLOADER);                                }                            }                        }                    });            ZygoteInit.main(args);        } else {            RuntimeInit.main(args);        }

四、Xposed框架特色

本节参考世界美景大佬定制Xposed框架和肉丝大佬来自高纬的反抗:魔改XPOSED过框架检测(下),通过咱们上文的Android运行机制和Xposed框架运行机制解说,置信大家对Xposed的框架曾经有了进一步的意识,这样咱们再来看这篇帖子里的批改的特色,就显得非常清晰了

五、Xposed特色批改

1. XposedInstaller

咱们下载XposedInstaller的工程代码加载到AndroidStudio中,让XposedInstaller配置环境,谬误提醒解决见上文源码编译(2)——Xopsed源码编译详解

批改点:

(1)批改整体包名(2)批改xposed.prop

(1)批改整体包名

先来改下整体的包名,首先将目录折叠给勾销掉

而后咱们在包名门路中,将xposed改成xppsed,这样能够保障包名长度是一样,同时xposed特色隐没不见,抉择Refactor→Rename

咱们间接点击Refactor,就能够替换胜利,点击Preview,则须要再点击 Do Refactor

这时候咱们能够发现程序下的包名都扭转了

接下来就是在整个我的项目的根文件夹下,进行整体的包名替换,因为还有很多编译配置、或者门路配置等等,须要进行包名的更换

在app文件夹右击,抉择Replace in Path

把所有的de.robv.android.xposed.installe都改成de.robv.android.xpsed.installer

搜进去匹配的中央只有5个文件中共计的7处中央,并不多,间接replace All替换即可

(2)xposed.prop改成xpsed.prop

就是把如下图处的xposed.prop改成xppsed.prop即可

接下来就是编译了。编译时先Build→Clean一下,而后再Build→Make Project,这样就间接编译通过了。能够连贯到手机,刷到手机下来,App会被装在手机上,然而无奈主动启动,得手动点开

2. XposedBridge

(1)批改整体包名

首先是改包名,办法与上文截然不同,也是首先将xposed进行重构,改成xppsed

注:咱们发现很多类型须要手动批改包名,咱们顺次将包名批改,这里咱们发现重构后没反馈,很可能是Android Studio的问题,换一个版本或重启

而后也是一样的在我的项目根目录下,执行Replace in Path,将所有的de.robv.android.xposed.installer都改成de.robv.android.xppsed.installer

这里就批改实现

(2)生成文件

XposedBridge.jar:

先Make Clean一下,而后编译,将编译进去的文件复制一份,命名为XppsedBridge.jar即可

api.jar:

而后咱们在Gradle->others->generate API中生成api.jar,保留在build/api下

3. Xposed

Xposed中的文件须要批改的中央不少:

(1)libxposed_common.h

批改之前:

批改之后:

(2)Xposed.h

批改之前:

#define XPOSED_PROP_FILE "/system/xposed.prop"#define XPOSED_LIB_ART XPOSED_LIB_DIR "libxposed_art.so"#define XPOSED_JAR "/system/framework/XposedBridge.jar"#define XPOSED_CLASS_DOTS_ZYGOTE "de.robv.android.xposed.XposedBridge"#define XPOSED_CLASS_DOTS_TOOLS "de.robv.android.xposed.XposedBridge$ToolEntryPoint"

批改之后:

#define XPOSED_PROP_FILE "/system/xppsed.prop"#define XPOSED_LIB_ART XPOSED_LIB_DIR "libxppsed_art.so"#define XPOSED_JAR "/system/framework/XppsedBridge.jar“#define XPOSED_CLASS_DOTS_ZYGOTE "de.robv.android.xppsed.XposedBridge"#define XPOSED_CLASS_DOTS_TOOLS "de.robv.android.xppsed.XposedBridge$ToolEntryPoint"

(3)xposed_service.cpp

批改之前:

IMPLEMENT_META_INTERFACE(XposedService, "de.robv.android.xposed.IXposedService");

批改之后:

IMPLEMENT_META_INTERFACE(XposedService, "de.robv.android.xppsed.IXposedService");

(4)xposed_shared.h

批改之前:

#define XPOSED_DIR "/data/user_de/0/de.robv.android.xposed.installer/"#define XPOSED_DIR "/data/data/de.robv.android.xposed.installer/"

批改之后:

#define XPOSED_DIR "/data/user_de/0/de.robv.android.xppsed.installer/"#define XPOSED_DIR "/data/data/de.robv.android.xppsed.installer/"

(5)ART.mk

批改之前:

libxposed_art.cppLOCAL_MODULE := libxposed_art

批改之后:

libxppsed_art.cppLOCAL_MODULE := libxppsed_art

(6)libxposed_art.cpp

将文件夹下的libxposed_art.cpp文件,重命名为libxppsed_art.cpp

4. XposedTools

咱们在XposedTools中将build.plzipstatic/_all/META-INF/com/google/android/flash-script.sh的字符替换就能够了

xposed.prop--->xppsed.propXposedBridge.jar--->XppsedBridge.jarlibxposed_art--->libxppsed_art

记得不要有脱漏,能够在批改完之后,到根目录下运行下述grep命令试试看,找不到相应的字符串即为全副替换实现

grep -ril xposed.propgrep -ril "xposed.prop" . ##过滤当前目录下含该字符串的文件

可是明明这里我是替换了的,我进入文件中也查找不到

通过剖析,咱们发现这里会将xposed_prop辨认为xposed.prop,阐明咱们是替换实现了

5.源码编译

源码编译流程,具体的参考上文

(1)这里咱们曾经替换了art(2)记得将批改的xposed替换/SourceCode/Android-6.0.1_r1/frameworks/base/cmds/`文件夹下的xposed文件夹(3)还记得把编译进去的XppsedBridge.jar放到$AOSP/out/java/目录中去噢,替换旧的XposedBridge.jar

咱们再次输出编译指令:

./build.pl -t arm:23

编译胜利,生成文件:

咱们从新挪动生成文件到源码文件夹下,具体参考上文:

cp /home/tom/SourceCode/XposedBridge/sdk23/arm/files/system/bin/* .cp /home/tom/SourceCode/XposedBridge/sdk23/arm/files/system/lib/* .cp /home/tom/SourceCode/XposedBridge/sdk23/arm/files/system/xppsed.prop  .

记得将xposed编译生成的app_process32_xposed替换system/bin文件夹下的app_process32

而后咱们进入源码目录下,再次编译镜像:

source build/envsetup.shlunch 19make snod  //make snod命令的作用是从新生成镜像文件system.img

6.后果与验证

(1)后果

咱们将镜像刷入手机:

fastboot flash system system.img

而后重启,发现Xposed装置胜利

(2)验证

咱们下载XposedCheck,这里咱们从官网下载源码,用Android Studio关上,而后编译装置,发现咱们定制Xposed框架胜利

7.谬误

(1)谬误1

问题剖析:

这是咱们没有替换xposed导致的

问题解决:

咱们将批改的Xposed替换原来/SourceCode/Android-6.0.1_r1/frameworks/base/cmds/文件夹下的xposed文件夹

(2)谬误2

问题剖析:

这是咱们的Xposed文件夹首字母为大写导致

问题解决:

咱们将其首字母改为小写

(3)谬误3

谬误剖析:

这是xposedinstaller和XposedBridge版本不统一导致

问题解决:

匹配具体参考上文

(4)谬误4

谬误剖析:

谬误剖析:

通过重复的查看,最初原来是XposedBridge.jar编译的问题

问题解决:

因为咱们编译过程中,将这里给正文掉了 所以导致并没导入Android6.0的环境反对,咱们须要退出反对,具体见上文

六、试验总结

通过几天的学习,解决完许许多多的bug,从源码剖析到源码定制,花了一周终于将这几篇文章写完了,从中播种了很多,这两头参考了很多大佬的文章,在文中和参考文献中会一一列进去,如果其中还存在一些问题,就请各位大佬斧正了。

七、参考文献

Android源码剖析:

https://juejin.cn/post/6844903748058218509、https://www.jianshu.com/p/2c0b76d0f4f2https://www.jianshu.com/p/8bb770ec4c48https://www.javatt.com/p/42078https://blog.csdn.net/luoshengyang/article/details/8852432https://blog.csdn.net/luoshengyang/article/details/8885792https://www.jianshu.com/p/89d06f626540https://www.wuyifei.cc/dex-vdex-odex-art/https://skytoby.github.io/2019/Android%20dex%EF%BC%8Codex%EF%BC%8Coat%EF%BC%8Cvdex%EF%BC%8Cart%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84/https://cloud.tencent.com/developer/article/1755790https://www.kancloud.cn/alex_wsc/androids/473620

Xposed:

https://zhuanlan.zhihu.com/p/389889716https://www.bbsmax.com/A/MAzAq2Ynz9/https://www.cnblogs.com/baiqiantao/p/10699552.htmlhttps://www.jianshu.com/p/6b4a80654d4ehttp://www.uml.org.cn/mobiledev/201903052.asphttps://bbs.pediy.com/thread-255836.htmhttps://mp.weixin.qq.com/s/YAMCrQSi0LFJGNIwB9qHDA
本文由平安后厨团队分享,转载请注明起源,违者必究!