讀Android系統篇之-免root實現Hook系統服務攔截方法
第二篇讀書筆記
梳理了下思路,解決了疑惑
我們使用剪下板服務的時候是呼叫了ContextImpl
的getSystemService
方法
ContextImpl
的getSystemService
方法
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
該方法將返回Object
型別物件,我們將它強制轉換為一個ClipboardManager
,也就是說它返回了一個ClipboardManager
ClipboardManager
,在ClipboardManager
構造的過程中,將獲取遠端Binder
並呼叫IClipboard.Stub
的asInterface
方法轉化為本地代理物件儲存在其中,
ClipboardManager
的一個建構函式
/** {@hide} */
public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
mService = IClipboard.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
}
獲取遠端Binder
的操作其實是呼叫了ServiceManager
中的getService
方法,它返回的是一個遠端Binder
,實際上也就是一個BinderProxy
(當然ServiceManager
會把這個遠端物件快取到sCache
中以應對頻繁呼叫),姜維的文章裡就是從這裡切入,第一步先動態代理了這個BinderProxy
。
這裡需要銘記一點,遠端
Binder
需要呼叫Stub
的asInterface
方法轉化為本地代理物件才能使用(上面說到在ClipboardManager
的建構函式中,這一步驟ClipboardManager
幫我們封裝了這一操作)
ServiceManager
中的getService
方法
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
下面繼續解析姜維的文章中hook的流程,在上一步的動態代理之後,攔截了被代理物件(BinderProxy
物件)的queryLocalInterface
方法,下面是
BinderProxy
中queryLocalInterface
的實現
public IInterface queryLocalInterface(String descriptor) {
return null;
}
可以看見它直接返回了null
,而這個方法是在哪裡被呼叫的呢,反編譯framework.jar
發現,是在IClipboard.Stub
中,這裡IClipboard
就是用aidl
生成的,和我們自己生成的差不多
看看
IClipboard.Stub
的asInterface
方法
public static IClipboard asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin == null || !(iin instanceof IClipboard)) {
return new Proxy(obj);
}
return (IClipboard) iin;
}
回到正題,姜維的文章裡攔截了queryLocalInterface
以後,一開始我以為它又動態代理了一個叫做base
的物件,因為這裡new了一個HookBinderInvocationHandler
,傳入的第一個引數就是base
突然有點蒙這個base
是哪裡冒出來的,看看上下文,發現是在第一個動態代理的Handler的建構函式裡,傳入了一個rawBinder
,賦值給了成員變數base
了,而這個rawBinder
,就是第一次代理中,被代理的那個遠端Binder
,我就有點納悶了,代理兩次幹啥?,仔細想,這只是個建構函式啊,我想傳進去什麼和我要動態代理什麼物件沒有關係呀。
於是翻回去看,動態代理的介面是this.iinterface
,看了下第一次動態代理的Handler的建構函式,看到
this.iinterface = Class.forName("android.content.IClipboard")
仔細想想,這是要搞出來一個IClipboard
啊,其實這個IClipboard
我們前文接觸過了,這裡貼上IClipboard
部分原始碼(主要看結構)
package android.content;
······
public interface IClipboard extends IInterface {
public static abstract class Stub extends Binder implements IClipboard {
······
private static class Proxy implements IClipboard {
······
梳理一遍,
第一次動態代理了遠端Binder
,Handler是IClipboardHookBinderHandler
,
在第一次代理的Handler裡面,攔截了queryLocalInterface
方法,
這個方法是在asInterface
裡面呼叫的,
攔截以後,開始第二次動態代理,
用IClipboard
這個介面合成了一個代理物件,Handler是HookBinderInvocationHandler
,
把這個合成的代理物件return
了!!!
沒錯,這裡是關鍵,它直接把它作為queryLocalInterface
方法的返回值return
了
在asInterface
方法裡,我們合成的代理物件,賦值給了iin,接下來
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin == null || !(iin instanceof IClipboard)) { //關鍵!!!!!
return new Proxy(obj); //沒走這!!!
}
return (IClipboard) iin; //走了這裡,我們合成的代理物件被強制轉換以後直接返回了,被用來之後進行剪下板的一些操作
哇,幾乎哭出來,看了那麼久終於懂了關鍵部分,為什麼作者不標記一下呢
/(ㄒoㄒ)/~~
我們比較一下:
hook前:
[呼叫 getSystemService ]
--> [ ClipboardManager 的建構函式]
--> [間接呼叫了 ServiceManager 中的 getService ]
--> [獲得遠端 Binder 物件]
--> [呼叫 IClipboard.Stub 的 asInterface 並把遠端物件傳入]
--> [獲得 IClipboard.Stub.Proxy 物件]
--> [後續使用]
hook後:
[Hook開始]
--> [主動反射呼叫 ServiceManager 中的 getService 並動態代理遠端物件]
--> [正常呼叫開始]
--> [呼叫 getSystemService ]
--> [ ClipboardManager 的建構函式]
--> [間接呼叫了 ServiceManager 中的 getService ]
--> [獲得第一次動態代理生成的物件]
--> [呼叫 IClipboard.Stub 的 asInterface 並把遠端物件傳入]
--> [攔截 queryLocalInterface 併合成 IClipboard 介面的代理物件]
--> [返回合成的代理物件]
--> [後續使用]
就這樣,兩次動態代理,第一次代理遠端物件,攔截queryLocalInterface
方法,第二次動態代理合成了一個實現了IClipboard
介面的物件,騙過了ClipboardManager
。