萬物皆可Hook!重新撿起Hook神器-Xposed框架
- 2019 年 11 月 5 日
- 筆記
作者: Lateautumn4lin
來源:雲爬蟲技術研究筆記
首先注意!!這個Hook
不是鄧紫棋要給你唱的Hook
哦!
而是在程序界流傳的強大秘技-Hook
函數,Hook
原意是指鉤子
,它表示的就是在某個函數的上下文做自定義的處理來實現我們想要的黑科技
。 在很多技術領域都存在的這種Hook
技術,比如下面這些:
- 在
Python
的Web框架
中,如Django
,Flask
都存在這種Hook
技術,可以在請求的上下文
,應用的上下文
做自定義操作。 - 在
Scrapy
框架中,可以自定義MiddlerWare
,在請求
,解析
的時候做自定操作。 - 在
K8S
編排框架中,我們也可以在執行某些函數的上下文中插入Hook
函數,這也是和Web
框架同理
而今天我們講解的是關於Android
的Hook
技術,而有一款神器能夠幫助我們快速地開發Hook
模塊,也就是Xposed
框架。
其實網上的關於Xposed
模塊編寫的教程可謂是一抓一大把。但由於時間的推移,很多工具和方法都發生了變化(如Eclipse
退出安卓編程舞台,AndroidStudio
不斷升級導致其一些設置也隨之變化等)也正因此,網上的教程往往有一些時限性,比如現如今 provide
這個關鍵字已經被捨棄了卻仍有人在用,還有些說要把jar
包放到lib
文件夾而非libs
文件夾……種種錯誤或者落伍的教程對新手產生了很大的誤導。
之前也搞過一陣子Xposed
框架,而今天在重新部署環境的時候參考某些教程的時候也遇到了很多的坑,所以想重新結合最新的配套工具寫個小教程,主要講解的以下兩個方面:
Xposed
框架介紹以及原理Xposed
框架實戰




Xposed框架介紹以及原理
Xposed
是Github
上rovo89
大佬設計的一個針對Android平台
的動態劫持項目,通過替換/system/bin/app_process
程序控制Zygote
進程,使得app_process
在啟動過程中會加載XposedBridge.jar
這個jar
包,從而完成對Zygote
進程及其創建的Dalvik虛擬機
的劫持。
因為Xposed
工作原理是在/system/bin
目錄下替換文件,在install
的時候需要root
權限,但是運行時不需要root
權限。
看到這裡很多人會很懵,什麼是Zygote
?簡單來說在Android
系統中,應用程序進程都是由Zygote
進程孵化出來的,而Zygote
進程是由Init
進程啟動的。Zygote
進程在啟動時會創建一個Dalvik
虛擬機實例,每當它孵化一個新的應用程序進程時,都會將這個Dalvik
虛擬機實例複製到新的應用程序進程裏面去,而一個應用程序進程被Zygote
進程孵化出來的時候,不僅會獲得Zygote
進程中的Dalvik
虛擬機實例拷貝,還會與Zygote
一起共享Java運行時
庫。這也就是可以將XposedBridge
這個jar
包加載到每一個Android
應用程序中的原因。XposedBridge
有一個私有的Native(JNI)
方法hookMethodNative
,這個方法也在app_process
中使用。這個函數提供一個方法對象利用Java
的Reflection
機制來對內置方法覆寫。。。。等等這些都會借鑒各路大神的思路和分析,總而言之,就是從底層替換方法,可以讓我們在不修改APK
源碼的情況下,通過自己編寫的模塊來影響程序運行的框架服務,實現類似於自動搶紅包、微信消息自動回復等功能。
其實,從本質上來講,Xposed
模塊也是一個Android
程序。但與普通程序不同的是,想要讓寫出的Android
程序成為一個“Xposed 模塊,要額外多完成以下四個硬性任務:
硬性任務清單
1、讓手機上的xposed框架知道我們安裝的這個程序是個xposed模塊。
2、模塊里要包含有xposed的API的jar包,以實現下一步的hook操作
3、這個模塊裏面要有對目標程序進行hook操作的方法。
4、要讓手機上的xposed框架知道,我們編寫的xposed模塊中,哪一個方法是實現hook操作的。
這就引出我即將要介紹的四大件(與前四步一一對照):
四大件
1、AndroidManifest.xml
2、XposedBridgeApi-xx.jar 與 build.gradle
3、實現hook操作的具體代碼
4、xposed_Init
牢記以上四大件,按照順序一個一個實現,就能完成我們的第一個Xposed模塊編寫。以上的原理我們大致就介紹這麼多,下面我們實戰開始吧!
Xposed框架實戰
1. 邁開第一步,新建項目並編輯AndroidManifest.xml

我們使用的IDE
是Android Studio
,首先打開AndroidStudio
(以版本3.4.2
為例,還在用老版本的請升級),建立一個工程,提示我們選擇「Activity
」,那就選一個Empty Activity
吧。(這個是模塊的界面,隨意選擇即可)。

2、快速運行模板的Xposed模塊
我們可以把項目查看方式設置為Project
模式,以方便查看。然後在 「項目名稱/app/src/main/
」目錄下找到AndroidManifest.xml
,打開這個文件,並在指定位置插入以下三段代碼:
<meta-data android:name="xposedmodule" android:value="true" /> <meta-data android:name="xposeddescription" android:value="微信hook" /> <meta-data android:name="xposedminversion" android:value="53" />
效果如圖:


插入代碼之後,我們可以點擊Run
運行App
。

不過,此時會出現如圖提示,也就是缺少Device
設備來運行這個App

下一步我們要把手機連接Android Studio
,連接的辦法很多,包括通過USB連接(物理連接)
和Wifi連接(也就是網絡連接)
,我們為了節省方法,就採用物理連接,Ps: 有關於遠程連接可以參考這篇文章,連接好我們的實體機之後我們點擊這裡

我們等待Android Studio
連接手機,連接好我們就可以看到在Logcat
選項裏面看到我們的手機運行的日誌報告。

有如圖所示的日誌打印之後我們就會發現我們就可以運行了,點擊Run
之後會提示我們的手機安裝我們剛才剛寫的Apk
,不過我的手機提示安裝時驗證超時
,不能直接安裝,苦惱,以後選手機也要選個正常的。關於Android Studio
安裝Apk
失敗的原因可以參考這篇文章,既然我們不能直接安裝Apk
,我們就使用adb
直接來安裝

安裝好應用之後我們在Xposed
框架中勾選我們剛才的模塊,然後我們重啟一下Xposed
框架,就可以啦

這一步只是說明Xposed
框架已經認出了我們寫的程序。但先別高興太早——雖然框架已經覺得他是一個Xposed
模塊了,但我們自己心裏清楚,這個模塊還啥都不會幹呢。下一步,我們讓這個模塊長點本事。
3、搞定XposedBridgeApi-xx.jar 與 build.gradle
我們知道,Xposed
模塊主要功能是用來Hook
其他程序的各種函數。但是,如何讓前一步中的那個「一窮二白」的模塊長本事呢?那就要引入 XposedBridgeApi.jar
這個包,你可以理解為一把兵器,模塊有了這把寶刀才能施展出Hook
本領。很多以前的老教程都需要手動下載諸如XposedBridgeApi-54.jar
、 XposedBridgeApi-82.jar
等jar
包,然後手工導入到libs
目錄里,才能走下一步道路,而這些jar
沒有官方的渠道來安裝,通常只是一個傳一個的,都不知道變成了什麼版本。其實在最新的AndroidStudio 3.1
以後,我們完全不用這麼麻煩,只需要多寫一行代碼,就讓AndroidStuido
自動給我們配置XposedBridgeApi.jar
!下面操作開始:
在 「項目名稱/app/src/main/」
目錄下找到build.gradle
,在圖示位置加上:

repositories { jcenter() } compileOnly 'de.robv.android.xposed:api:82' compileOnly 'de.robv.android.xposed:api:82:sources'
這句代碼是告訴AndroidStuido
使用jcenter
作為代碼倉庫,從這個倉庫里遠程尋找 de.robv.android.xposed:api:82
這個API
。這個網上很少有Xposed
教程介紹它的!(我們不用自己找XposedBridgeApi.jar
了。注意!此處要用compileOnly
這個修飾符!網上有些寫的是provide
,但是現在已經停用了!坑人啊!)
寫完之後, build.gradle
會提示文件已經修改,是否同步。點擊 「sync now」
,同步即可:

等待依賴構建完成

【Ps:如果網絡不通,或者同步不暢,就不要進行第三步的repositories { jcenter()}
這個步驟了,改做這個步驟:手動下載XposedBridgeApi-82.jar
,拖放到「項目名稱/app/libs/」
裏面(不是網上說的單獨建立lib
文件夾,那是很久以前的故事了!),然後右鍵「Add As Library」
自行添加這個jar
包。而compileOnly 『de.robv.android.xposed:api:82′
和compileOnly 『de.robv.android.xposed:api:82:sources』
這兩句仍然照常添加。】
好了,我們現在已經搞好了所有的準備工作。下一步,就要開始「施展刀法」(編寫hook
代碼)了。
4、實現hook操作的具體代碼
在「施展刀法」(編寫hook
代碼)之前,我們先要立一個靶子。在界面上畫一個按鈕,並在MainAcitiviy
里寫代碼如下:
package com.example.wx; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Toast.makeText(MainActivity.this, toastMessage(), Toast.LENGTH_SHORT).show(); } }); } public String toastMessage() { return "我未被劫持"; } }
而在頁面布置的文件中,也就是activity_main.xml
中增加如下紅框的代碼

這個靶子很簡單:MainActivity
界面有個按鈕,點擊按鈕後會彈出一個toast
提示,該提示的內容由toastMessage()
方法提供,而toastMessage()
的返回值為「我未被劫持」
。
現在,我們已經做好了我們的App
了,下面我們正式開始「施展刀法」(編寫hook
代碼) 來hook
我們的MainActivity
並修改這個類的toastMessage()
方法,讓它的返回值為「你已被劫持」
:
5、在MainActivity的同級路徑下新建一個類「HookTest.java」
package com.example.wx; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; public class HookTest implements IXposedHookLoadPackage { public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable { if (loadPackageParam.packageName.equals("com.example.root.xposd_hook_new")) { XposedBridge.log(" has Hooked!"); Class clazz = loadPackageParam.classLoader.loadClass( "com.example.root.xposd_hook_new.MainActivity"); XposedHelpers.findAndHookMethod(clazz, "toastMessage", new XC_MethodHook() { protected void beforeHookedMethod(MethodHookParam param) throws Throwable { super.beforeHookedMethod(param); //XposedBridge.log(" has Hooked!"); } protected void afterHookedMethod(MethodHookParam param) throws Throwable { param.setResult("你已被劫持"); } }); } } }

由代碼可知,我們是通過IXposedHookLoadPackage
接口中的handleLoadPackage
方法來實現Hook
並篡改程序的輸出結果的。代碼中「com.example.wx」
是目標程序的包名,」com.example.wx.MainActivity」
是想要Hook的類,「toastMessage」
是想要Hook
的方法。我們在afterHookedMethod
方法(用來定義Hook了目標方法之後的操作)中,修改了toastMessage()
方法的返回值為「你已被劫持」。在完成代碼編寫之前,說一下為什麼要Hook toastMessage
這個方法,我們先用Jadx
查看一下我們Apk
的源代碼

這個源碼是沒有經過混淆的,所以我們可以看到這個源碼和我們之前寫的一樣,我們根據項目結構可以判斷出我們需要Hook
的函數。
OK,以上用來hook
的代碼編寫完畢,讓我們進行下一步操作。
6、最後一步,添加入口點
右鍵點擊 「main 」
文件夾 , 選擇new –> Folder –>Assets Folder
,新建assets
文件夾:

然後右鍵點擊 assets
文件夾, new–> file
,文件名為xposed_init
(文件類型選text
),並在其中寫上入口類的完整路徑(就是自己編寫的那一個Hook
類),這樣,Xposed
框架就能夠從這個xposed_init
讀取信息來找到模塊的入口,然後進行Hook
操作了:

好了,曙光就在前面!最後選擇禁用Instant Run
:單擊 File -> Settings -> Build, Execution, Deployment -> Instant Run
,把勾全部去掉。 我們重新之前的安裝Xposed
模塊的方法,運行模塊,點擊,奇蹟出現~

大功告成!!!
結語
從上面的實戰中我們可以發現Hook
的基本原理以及步驟,重新看看我們之前說的四大步,Hook
的關鍵其實我們需要知道針對哪個模塊的哪個方法進行Hook
像我們這個例子很簡單,沒有特意的進行代碼混淆
以及程序入口改寫
等等,我們尋找還是很簡單的,一般市面上的App
都是有很多反Xposed
的行為,我們其實要學習的還有很多,這個小教程就當做個小入門吧。
下一篇文章和最近工作上的需求有關係,針對的是2019.10.28
之後搜狗微信關閉了在某些公眾號內搜索的功能,所以我們想要獲取最新的公眾號文章就不能採取搜狗微信這個渠道了,網上有很多教程都在談論其他的方法,相比較來說,還是Hook
這個渠道是最實際的,我們將會在之後的文章里詳細談論,大家可以期待一下~
注意:項目已經完成,想要獲得源碼可以關注下面的微信號,回復「hook入門」
即可獲得項目地址以及現成的Apk