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

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

第二篇讀書筆記

梳理了下思路,解決了疑惑

我們使用剪下板服務的時候是呼叫了ContextImplgetSystemService方法

ContextImplgetSystemService方法

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

該方法將返回Object型別物件,我們將它強制轉換為一個ClipboardManager,也就是說它返回了一個ClipboardManager

供我們使用。而這個方法最終將構造一個ClipboardManager,在ClipboardManager構造的過程中,將獲取遠端Binder並呼叫IClipboard.StubasInterface方法轉化為本地代理物件儲存在其中,

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需要呼叫StubasInterface方法轉化為本地代理物件才能使用(上面說到在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方法,下面是

BinderProxyqueryLocalInterface的實現

    public IInterface queryLocalInterface(String descriptor) {
        return null;
    }

可以看見它直接返回了null,而這個方法是在哪裡被呼叫的呢,反編譯framework.jar發現,是在IClipboard.Stub中,這裡IClipboard就是用aidl生成的,和我們自己生成的差不多
看看

IClipboard.StubasInterface方法

    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