Xposed是一個(gè)很強(qiáng)大的Android平臺上的HOOK工具腾它,而且作者為了方便開發(fā)者使用開發(fā)了一個(gè)APP(Xposed Installer,下文稱為Installer) 來使用開發(fā)者自己開發(fā)的模塊异旧。開發(fā)者安裝自己的模塊后需要在Installer中勾選自己的模塊然后重啟手機(jī)自己的模塊才會(huì)起作用莽使。但是這樣有點(diǎn)不利于開發(fā)者測試凑懂,每次都要點(diǎn)開Installer操作幾下尤其是還要重啟就顯得有點(diǎn)麻煩了腥椒。
讀過Xposed的源碼后會(huì)發(fā)現(xiàn)僅通過更改XposedBridge.jar的源碼就可以更簡便一些:
1. 不需重啟手機(jī)
2. 不需操作Installer這個(gè)App,且不用安裝hook模塊敌蚜,只需push到手機(jī)即可
首先需要下載源碼桥滨,rovo89鏈接里面有Xposed所有源碼,我們只需要下載XposedBridge就好弛车。原版的Xposed并不適用于三星手機(jī)该园,wanam改過的Xposed可以用,所以如果是三星手機(jī)的話就下載wanam的帅韧。
1. Xposed原理簡介
現(xiàn)在安裝Xposed比較方便里初,因?yàn)閄posed作者開發(fā)了一個(gè)Xposed Installer App,下載后按照提示傻瓜式安裝(前提是root手機(jī))忽舟。其實(shí)它的安裝過程是這個(gè)樣子的:首先探測手機(jī)型號双妨,然后按照手機(jī)版本下載不同的刷機(jī)包,最后把Xposed刷機(jī)包刷入手機(jī)重啟就好叮阅。刷機(jī)包下載 里面有所有版本的刷機(jī)包刁品。
刷機(jī)包解壓打開里面的問件構(gòu)成是這個(gè)樣子的:
META-INF/ 里面有文件配置腳本 flash-script.sh 配置各個(gè)文件安裝位置。
system/bin/ 替換zygote進(jìn)程等文件
system/framework/XposedBridge.jar jar包位置
system/lib system/lib64 一些so文件所在位置
xposed.prop xposed版本說明文件
所以安裝Xposed的過程就上把上面這些文件放到手機(jī)里相同文件路徑下浩姥。
通過查看文件安裝腳本發(fā)現(xiàn):
system/bin/下面的文件替換了app_process等文件挑随,app_process就是zygote進(jìn)程文件。所以Xposed通過替換zygote進(jìn)程實(shí)現(xiàn)了控制手機(jī)上所有app進(jìn)程勒叠。因?yàn)樗衋pp進(jìn)程都是由Zygote fork出來的兜挨。
Xposed的基本原理是修改了ART/Davilk虛擬機(jī),將需要hook的函數(shù)注冊為Native層函數(shù)眯分。當(dāng)執(zhí)行到這一函數(shù)是虛擬機(jī)會(huì)優(yōu)先執(zhí)行Native層函數(shù)拌汇,然后再去執(zhí)行Java層函數(shù),這樣完成函數(shù)的hook弊决。如下圖:
通過讀Xposed源碼發(fā)現(xiàn)其啟動(dòng)過程:
- 手機(jī)啟動(dòng)時(shí)init進(jìn)程會(huì)啟動(dòng)zygote這個(gè)進(jìn)程噪舀。由于zygote進(jìn)程文件app_process已被替換,所以啟動(dòng)的時(shí)Xposed版的zygote進(jìn)程飘诗。
- Xposed_zygote進(jìn)程啟動(dòng)后會(huì)初始化一些so文件(system/lib system/lib64)与倡,然后進(jìn)入XposedBridge.jar中的XposedBridge.main中初始化jar包完成對一些關(guān)鍵Android系統(tǒng)函數(shù)的hook。
- Hook則是利用修改過的虛擬機(jī)將函數(shù)注冊為native函數(shù)昆稿。
- 然后再返回zygote中完成原本zygote需要做的工作纺座。
這只是在宏觀層面稍微介紹了下Xposed,要想詳細(xì)了解需要讀它的源碼了貌嫡。下面兩篇寫的挺好比驻,要想深入理解的可以看看。
Android Hook框架Xposed原理與源代碼分析
深入理解Android之Xposed詳解
2. Xposed精簡化
上面稍微介紹了下它的原理岛抄,下面就介紹如何精簡化Xposed别惦。下面只修改了XposedBridge.jar包中的XposedBridge.java這個(gè)文件,修改完重新Build apk然后把a(bǔ)pk重命名為XposedBridge.jar然后替換刷機(jī)包中的jar包夫椭,刷入手機(jī)即可掸掸。
2.1 取消重啟手機(jī)
看下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();
}
//修改時(shí)需注釋下面這行代碼
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()這個(gè)函數(shù),這個(gè)函數(shù)的作用就是load hook模塊到進(jìn)程中蹭秋。
因?yàn)閦ygote啟動(dòng)時(shí)先跑到j(luò)ava層XposeBridge.main中扰付,在main里面有一步操作是將hook模塊load進(jìn)來,模塊加載到zygote進(jìn)程中仁讨,zygote fork所有的app進(jìn)程里面也有這個(gè)hook模塊羽莺,所以這個(gè)模塊可以hook任意app。(編寫hook模塊的第一步就是判斷當(dāng)前的進(jìn)程名字洞豁,如果是要hook的進(jìn)程就hook盐固,不是則返回)。
所以修改模塊后丈挟,要將模塊重新load zygote里面必須重啟zygote刁卜,要想zygote重啟就要重啟手機(jī)了。
所以修改的邏輯是不把模塊load到zygote里面曙咽,而是load到自己想要hook的進(jìn)程里面蛔趴,這樣修改模塊后只需重啟該進(jìn)程即可。
在上面代碼的代碼修改位置添加如下代碼,并將上面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 = "指定進(jìn)程名稱如: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的源碼發(fā)現(xiàn)其實(shí)勾選hook模塊其實(shí)app就是把模塊的apk位置寫到一個(gè)文件里孝情,等load模塊時(shí)會(huì)讀取這個(gè)文件,從這個(gè)文件中的apk路徑下把a(bǔ)pk load到進(jìn)程中洒嗤。
看下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這個(gè)文件data/data/de.robv.android.xposed.installer/conf/modules.list
或者data/user_de/0/de.robv.android.xposed.installer/conf/modules.list(不同手機(jī)版本位置不同)
所以我們改下咧叭,直接給他個(gè)確定的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 = "指定進(jìn)程名稱如: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);
}
最后, 同學(xué)點(diǎn)個(gè)贊吧E伤骸M竦! 加個(gè)關(guān)注好么
所以每次修改模塊后直接修改名字為module.apk然后push到/data/local/tmp/下终吼,然后重啟app就好镀赌。