1. 程式人生 > >Android系統篇之----免root實現Hook系統服務攔截方法

Android系統篇之----免root實現Hook系統服務攔截方法

一、Binder機制回顧

在之前一篇文章中介紹了 Android中的Binder機制和系統遠端服務呼叫機制,本文將繼續借助上一篇的內容來實現Hook系統服務攔截指定方法的邏輯,瞭解了上一篇文章之後,知道系統的服務其實都是一個遠端Binder物件,而這個物件都是由ServiceManager大管家管理的,使用者在使用系統服務的時候,會通過指定服務的Stub方法的asInterface把遠端的Binder物件轉化成本地化物件即可使用,而在這個過程中,我們也知道因為系統服務是在system_server程序中的,所以這個系統服務使用過程中屬於跨程序呼叫,那麼返回的物件其實就是Proxy代理物件。

二、系統中服務使用流程

本文主要就是藉助這個知識點,通過Hook系統的服務來攔截服務方法,下面我們就通過系統剪下板服務案例作為分析


這裡看到了,使用系統服務的時候都是用了getSystemService方法,通過定義在Context中的服務描述符常量來獲取服務物件,而getSystemService方法定義在ComtextImpl.Java類中:


這裡維護了一個ServiceFetcher的Map結構,看看這個結構在哪裡填充資料的:


在registerService方法中新增一個服務名稱和一個ServiceFetcher物件,而這個方法在靜態程式碼塊中進行呼叫的:


我們找到了ClipboardManager這個服務:


這裡其實是一個ClipboardManager物件,其實這個物件是內部封裝了IClipboard.Stub功能,可以看看其他的服務:


比如這裡的聯網服務,直接呼叫了IConnectivityManager.Stub類的asInterface方法獲取Proxy物件。

下面就進去ClipboardManager.java中看看究竟:


看到這裡的設定剪下板內容的方法,其實內部是呼叫了getService方法獲取物件然後在呼叫指定方法,那麼可以大概知道了這個getService方法返回的應該就是IClipboard.Stub通過asInterface方法返回的Proxy物件:



好吧,果然是這樣,這裡通過ServiceManager獲取到Clipboard的遠端IBinder物件,然後通過asInterface方法返回一個Proxy物件即可。

到這裡我們就簡單的分析完了系統中的獲取剪下板的服務,其實系統中的服務都是這麼個邏輯,只是有的可能會在外面包裝一層罷了,下面總結一下流程:


現在只要記住一點:每次獲取系統服務的流程都是一樣的,先通過ServiceManager的getService方法獲取遠端服務的IBinder物件,然後在通過指定服務的Stub類的asInterface方法轉化成本地可使用物件,而這個物件其實就是一個Proxy物件,在這個過程中,Stub類繼承了Binder物件和實現了AIDL介面型別,Proxy物件實現了AIDL介面型別,而AIDL介面型別實現了IInterface介面型別。

三、Hook系統服務

上面分析完了Android中系統服務的使用流程以及原理解析,下面在來看一下Android中實現Hook機制的方法和原理解析,我們知道其實在很多系統中都存在這樣一個Hook技術,有的也叫作鉤子,但是不管任何系統,Hook技術的核心點都是一樣的,只有兩點即可完成Hook技術:

1、找到Hook點,即你想Hook哪個物件,那麼得先找到這個物件定義的地方,然後使用反射獲取到這個物件例項。所以這裡可以看到,一般Hook點都是一個類的單例方法或者是靜態變數,因為這樣的話Hook起來就非常方便,都是static型別,反射呼叫都比較方便無需具體的例項物件即可。而關於這個點也是整個Hook過程中最難的點,因為很難找到這個點。Android中主要是依靠分析系統原始碼類來做到的。

2、構造一個Hook原始物件的代理類,關於這個代理其實在Java中有兩種方式,一種是靜態代理,一種是動態代理。

靜態代理:代理類中維護一個原始物件的成員變數,每個方法呼叫之前呼叫原始物件的方法即可。無需任何條件限制

動態代理:比靜態代理複雜點就是有一個規則:就是原始物件必須要實現接口才可以操作,原理是因為動態代理其實是自動生成一個代理類的位元組碼,類名一般都是Proxy$0啥的,這個類會自動實現原始類實現的介面方法,然後在使用反射機制呼叫介面中的所有方法。


案例使用就很簡單了,下面我們通過原始碼分析這個原理:


主要呼叫了getProxyClass0方法生成代理類的位元組碼:


然後生成代理類:


看到這裡,其實這裡的生成位元組碼不是很複雜的,我們可以藉助asm等工具包就可以手動的生成一個位元組碼,然後在使用類載入器載入即可,所以會發現需要傳遞類載入器。

當上面生成代理類之後,我們可以把這個類反編譯看一下,看一下方法的呼叫,在代理類中的靜態程式碼塊:


會使用反射獲取到類的所有方法。



然後在指定方法中呼叫傳遞進來的InvocationHandler的回撥介面的invoke方法,就是這麼實現的。

從原始碼角度分析完動態代理之後,發現其實沒什麼複雜的,就是手動的生成一個代理類,然後用反射獲取類中所有的方法,在指定方法中用反射呼叫,同時回撥InvocationHandler的invoke方法即可,所以這裡看到InvocationHandler中的invoke方法的第一個引數物件是代理類物件。


注意:

在JavaWeb開發中我們會用到spring框架中的AOP程式設計,其實他就是利用了Java中動態代理技術,但是他依賴的是CGlib的功能,不是採用Java原生的這種自動產生代理類,從上面可以看到能夠做動態代理的類必須要實現一個介面,而這一個規則有時候要求非常高,很多類都沒有實現介面,導致無法實現代理功能,所以Spring採用了cglib,原理其實差不多,他採用asm工具包也是手動生成一個類,但是這個類是原始物件的子類,這樣也可以實現代理功能,但是這樣一來也有一個限制了,那就是代理物件不能是final型別,同時一些主要方法最好不能是final型別的,當然這個限制和上面的那個介面實現限制比起來還是好點的。

到此我們瞭解了Java中的Hook技術的核心知識點了,下面就用開始的剪下板服務來做實驗,我們Hook系統的剪下板服務功能,攔截其方法,上面也說道了,既然要Hook服務,首先得找到Hook點,通過開始對Android中系統服務的呼叫流程分析知道,其實這些服務都是一些儲存在ServiceManager中的遠端IBinder物件,這其實是一個Hook點:


其實ServiceManager中每次在獲取服務的時候,其實是先從一個快取池中查詢,如果有就直接返回了:


而這個快取池正好是全域性的static型別,所以就可以很好的使用反射機制獲取到他了,然後進行操作了。

接下來,我們就需要構造一個剪下板的服務IBinder物件了,然後在把這個物件放到上面得到的池子中即可。那麼按照上面的動態代理的流程,

第一、原始物件必須實現一個介面,這裡也正好符合這個規則,每個遠端服務其實是實現了IBinder介面的。

第二、其次是要有原始物件,這個也可以,通過上面的快取池即可獲取

有了這兩個條件那麼接下來就可以使用動態代理構造一個代理類了:



通過反射去獲取ServiceManager中的快取池Binder物件就不多說了,完全反射機制即可,我們先獲取到快取池,然後得到剪下板服務Binder物件,構造一個代理類,最後在設定回去即可。下面主要來看一下構造了代理類之後,如何攔截哪些方法?

這裡一定要注意了,有的同學可能想直接在這裡攔截setPrimaryClip這樣的剪下板方法不就可以了嗎?想想是肯定不可以的,為什麼呢?因為我們現在代理的是遠端服務的Binder物件,他還沒有轉化成本地物件呢?如何會有這些方法呢,而我們真正要攔截的方法是IClipboardManager,其實就是Proxy類,而這個物件也是Stub類的asInterface方法得到的,所以我們現在的思路是有了遠端服務的代理物件,攔截肯定是攔截這個代理物件Binder的一些方法,那麼這個遠端服務有哪些方法會在這個過程中被呼叫呢?我們再看看之前的一個簡單AIDL的例子:


在asInterface方法中可以看到,傳遞進來的就是一個遠端服務IBinder物件,這裡會先呼叫它的queryLocalInerface方法獲取本程序的本地化物件,那麼這個方法就是攔截的目標了。

然後在想,我們如果想攔截IClipboardManager的setPrimaryClip方法,其實就是要攔截ClipboardManager$Proxy的這些方法,那麼還需要做一次代理,代理ClipboardManager$Proxy類物件

第一、ClipboardManager$Proxy類實現了AIDL介面型別,符合規則。

第二、我們可以直接使用反射獲取到IClipboardManager$Stub類,然後反射呼叫它的asInterface方法就可以得到了IClipboardManager$Proxy物件了,符合規則。


到這裡,看來這個物件也符合了代理的條件,那麼就簡單了,繼續使用動態代理機制產生一個代理類即可:


這個代理類的InvocationHandler中,先需要通過反射獲取到Proxy原始物件:


最後才開始實現攔截操作。

下面來看一個實驗結果:


我們呼叫系統的剪下板服務,但是返回的結果卻是:


看到了,這裡的剪下板的內容已經被之前攔截,內容也被替換了。

上面就全部介紹瞭如何Hook系統的剪下板服務功能,流程如下:


1、我們的目的就是攔截系統的服務功能,那麼最開始的入口就是服務大管家ServiceManager物件,而在他內部也正好有一個遠端服務物件的IBinder快取池,那麼這個變數就是我們操作的物件了,可以先使用反射機制去獲取到他,然後在獲取到指定的剪下板服務IBinder物件例項。

2、下一步肯定是Hook這個剪下板服務的Binder物件,這裡採用動態代理方式產生一個Binder物件代理類,符合兩個規則:

1》這個Binder物件實現了IBinder介面型別

2》我們已經得到了原始的Binder物件例項

構造完代理類之後,我們攔截的方法是queryLocalInterface方法,為什麼是這個方法呢?因為在整個服務使用過程中之後在Stub類中使用到了這個方法,很多同學會認為為什麼不在這裡直接攔截系統方法呢?這是一個誤區,要想清楚,這裡的代理物件是遠端服務的Binder,還不是本地化物件,不能會有哪些系統方法的,所以得再做一次Hook,去Hook住系統的本地化物件。

3、在攔截了Binder物件的queryLocalInterface方法之後,再一次做一下本地化服務物件的代理生成操作,而這個本地化物件一般都是IClipboard$Proxy,那麼動態代理的規則:

1》本地化服務物件都會實現AIDL介面型別(這裡才有哪些我們想攔截的系統方法)

2》通過反射呼叫IClipboard$Stub類的asInterface方法得到IClipboard$Proxy類物件例項

符合這兩個規則那麼就可以產生代理物件了,然後開始攔截服務的指定方法即可。

上面總結的這個流程是可以完全用於其他系統服務的Hook工作的,因為系統服務的機制都是一致的,所以這個流程一定要理解清楚,後面的工作才好進行。

四、補充說明

有的同學可能會好奇詢問,這個Hook系統服務貌似只對本應用有效吧?哈哈,的確是這樣的,上面的攔截只會對本應用有效,那有的同學會問,只對本應用有效意義就不是很大的,其實這個要看個人需求了,後面會介紹一些開發中遇到的問題,就需要藉助這個技術去解決了,但是真正能夠攔截系統的服務對所有的應用有效,其實想想實現也不難,因為應用都會請求服務,而所有的服務都在system_server程序中,那麼就可以採用root之後,注入system_server程序,那時候在開始Hook工作即可完成真正意義上的攔截操作,這個用途就大了,比如我們可以修改系統的經緯度資訊,偽造假的當前位置資訊,篡改裝置的IMEI值,讓有的遊戲識別唯一裝置無效等。但是這個就需要root了之後才可以操作了,具體方案感興趣的同學可以去這裡看看:Android中注入系統程序實現攔截

五、總結

到這裡我們就介紹完了Android中Hook系統服務的流程,本文中主要介紹了Hook系統剪下板服務,攔截指定方法,其實後面還會繼續介紹攔截AMS和PMS服務,實現應用啟動的攔截操作,達到我們想要的效果。


轉自:http://blog.csdn.net/jiangwei0910410003/article/details/52523679