1、前言
Android中hook框架已經很是多了,最優秀的當屬Xposed和Substrate了,這兩個框架我在以前的文章都詳細介紹過了,不瞭解的同窗,能夠轉戰這裏:http://www.wjdiankong.cn;可是這兩個框架用於破解逆向是很是有用的,惋惜他們最大的侷限性就是須要root權限,這個幾乎現階段可能阻礙了不少產品的商業化,畢竟國內人的安全意識愈來愈高了。因此這裏就須要藉助一個免root的hook框架了。而對於免root的框架最大的好處就在於無需root權限,可是弊端就是隻能hook自身,對於系統其它app無能無力,就有人好奇了,只能hook本身有個卵用,的確是沒什麼用,可是也並不是一點用都沒有。android
2、免root的hook功能用途
關於Android中的Hook機制,我在以前已經說得很是詳細了,大體就兩個方式:git
第一個方式:root權限,直接hook系統,能夠幹掉全部app。github
第二種方式:免root權限,可是隻能hook自身。web
對於免root權限只能hook自身,並不是一點用都沒有,好比我以前介紹到的自動破解簽名校驗工具kstools,就是藉助這個原理實現,不瞭解原理的同窗能夠看這裏:Android中自動破解簽名校驗工具kstools,咱們在破解逆向app的時候,須要反編譯,二次打包,那麼這時候其實咱們想針對於這個app作點手腳,就能夠藉助這個技術,好比如今想改一個app,使得他獲取系統的imei值是隨機的,有這種需求的,有的遊戲有的功能會用設備的imei值作惟一處理,隨機imei值的話就能夠跳過這個功能了。那麼這時候咱們能夠反編譯這遊戲,在遊戲入口處加上hook代碼,即hook遊戲自身的imei值便可,這就是需求,只須要hook這一款遊戲便可。這樣無需在用xposed來編寫外掛了。遊戲二次打包便可使用了。api
3、以往hook框架原理分析
那麼其實免root進行hook功能在以前我已經介紹過了一款阿里的開源框架Dexposed了,不過這個框架已經廢棄了,取而代之的是AndFix,原理很簡單在native層把Java層的方法強制改爲native類型,而後在將方法的native標誌指向自定義的native函數便可。這樣原來的方法就會走到自定義的native函數中,這個框架是很是優秀的AOP框架,惋惜兼容性問題不能繼續維護了。取而代之的是AndFix,關於它的原理就更簡單了,他沒有了AOP功能,直接將原始方法和自定義的新方法信息進行對換,這樣原來方法執行的邏輯就是新的方法了。這個框架主要用於熱修復,因此不必須要AOP功能了。而今天介紹的這款框架原理也是相似,只不過以爲比較好的是,他把大部分的功能都放到了Java層,這樣的兼容性就很是好了,並且自身的兼容也很優秀。緩存
4、Legend框架原理分析
下面就來介紹一下他的原理,代碼量不是不少,框架下載地址能夠去github上:https://github.com/asLody/legend,下載下來直接運行就ok了,咱們繼續來分析它的代碼吧:安全
![](http://static.javashuo.com/static/loading.gif)
進入這個方法查看:數據結構
![](http://static.javashuo.com/static/loading.gif)
到這裏咱們會發現,他是編譯傳入進來的class類,而後編譯他全部用到了Hook註解的方法,獲取其須要hook的方法信息,具體樣式爲:類名::方法名@參數1#參數2#...架構
而後解析完了註解獲取方法簽名信息以後,開始調用hookMethod進行hook操做:app
![](http://static.javashuo.com/static/loading.gif)
這裏看到,每一個hook方法必須是static類型的,而後會區分是art仍是dalvik的,分別處理,這裏直接分析dalvik方法,後面的代碼是有一個緩存池,用於緩存每次替換完以後的原始方法信息,爲了後面會調用原始方法功能。下面繼續來看dalvik方法的hook代碼:
![](http://static.javashuo.com/static/loading.gif)
這裏首先會獲取原始方法和hook新方法對應的dalivk的數據結構信息,這個就是框架的核心點了,關於dalivk對應的方法結構體信息大體以下:
- accessFlags: 各個不一樣標誌位表示此方法的多個屬性,其中標誌位0x00000100代表此方法是native的
- registersSize: 該方法總共用到的寄存器個數,包含輸入參數所用到的寄存器,還有方法內部另外使用到的寄存器,在調用方法時會爲其申請棧內存
- outsSize: 該方法調用其餘方法時使用到的寄存器個數,注意:只有此方法爲非native方法時,此值纔有效.
- insSize: 該方法輸入參數用到的寄存器個數(registersSize包含此值)
- insns: 若方法類型爲1,這裏指向實際的字節碼首地址;若方法類型爲2,這裏指向實際的JNI函數首地址;若方法類型爲3,這裏爲null
- jniArgInfo: 當方法類型爲2時有效,記錄了一些預先計算好的信息(具體信息格式與實際CPU架構有關,但老是包含返回值類型),從而不須要在調用的時候再經過方法的參數和返回值實時計算了,提升了JNI調用的速度。若是第一位爲1(即0x80000000),則Dalvik虛擬機會忽略後面的全部信息,強制在調用時實時計算
- nativeFunc: 若方法類型爲1,此值無效;若方法類型爲2,這裏指向dvmCallJNIMethod;若方法類型爲3,這裏指向實際的處理函數(DalvikBridgeFunc類型)
還記得最後一個參數嗎?當初介紹Dexposed框架就是利用這個參數進行操做的,他對應的就是一個方法的native函數,對於AndFix框架,其實也就是直接替換方法的這些結構體信息便可。
替換新舊方法的結構體信息,作到移花積木的功能:
而後就把替換以後額數據寫入到內存中,而這裏咱們要注意的是,操做內存,Java實在不靠譜的,因此這裏須要將工做移動到native層作,操做內存數據,指針是萬能的,因此就跑到C++中幹了:
這裏看到,其實不是寫入內存,而是對內存數據的一個替換,直接利用指針方便快捷。
5、案例用法
那麼到這裏,其實咱們就大體分析完了這個框架的功能,關於art類型的能夠自行分析代碼,原理都是同樣的,這裏的hook機制很是簡潔,直接構造出新舊方法對應的虛擬機數據結構,而後替換信息,會寫到內存中便可。和阿里的AndFix框架原理幾乎同樣。分析完了原理以後,接下來纔是正事,演示一個案例,這裏主要介紹兩個案例,首先來看看如何hook系統的imei值:
這裏須要用到註解,形式如:類名::方法名,由於這裏沒有參數信息,因此忽略,而後在定義一個新的hook方法,每一個hook方法第一個參數必須是hook方法所屬的類類型,這裏直接返回imei值爲110,而後在獲取imei值看看效果:
看到了,hook成功了,操做就是這麼簡單。
下面再來看一個例子,就是hook應用中全部的webview加載的url值,這個也是此次我要分析這個框架的緣由,由於項目須要,想攔截應用全部webview加載的url值:
這裏看到了,咱們通常用WebView加載url都會走shouldOverrideUrlLoading方法,因此直接hook這個方法便可,看到這裏的註解類型也好理解:類名::方法名@參數1#參數2...,這裏還有一個重要的知識就是調用callSuper方法能夠調用原來的方法,這樣咱們只作了一層攔截,不要破壞原始方法的執行流程,而這裏能夠調用原始方法其實就是利用以前提到的原始方法緩存池功能便可實現,這裏能夠看出有AOP的意思了。運行看一下效果:
這裏會把每一個頁面加載請求的url進行攔截了,這就是我項目想要的功能。
由於上面分析了框架的原理,大部分功能都是在Java層作的,這樣很大程度上可以作到兼容,由於這樣,我也想嘗試利用這個框架來操做項目,畢竟也算是一種嘗試。
注意:
一、框架能夠選擇不使用註解的方式進行hook,直接使用api方式,具體用法參見github上說明。
二、hook的時機最好要早,通常在程序的入口處便可。