插件化原理之hook系統函數

 

插件化原理之hook系統函數

插件化主要問題之一是如startActivity一個未在註冊表裏面註冊的acitivity

我們都知道開啓一個activity是涉及到app進程和系統服務進程的交互過程,其中驗證要打開的acitivity是否在清單文件中也是在系統服務進程進行的,那麼”如何”欺騙系統服務進程?

方案一是設置一個代理ProxyActivity,這個ProxyActivity在清單文件中註冊過,然後在該ProxyActivity裏面注入一個真實的Activity,ProxyActivity所有的生命週期方法裏面都回調真實的Activity生命週期方法。關於這個方案有個框架實現的挺好的,可以看動態代理框架。這個方案調用startyActivity是要特別處理,首先要把真實的activity信息隱藏在intent裏面,然後在代理ProxyActivity裏面在解析出真正的activity信息並且實例化,然後回調真正的activity生命週期方法。

github地址:https://github.com/singwhatiwanna/dynamic-load-apk

方案二 是直接hook appstartActivity方法,這裏先梳理一下startActivity的邏輯。startActivity雖然只有一行代碼,但裏面涉及的調用卻很複雜,app持有一個AMSBinder引用,在app端最終調用ActivityManagerNative.getDefault().startActivity後,系統服務進程開始做一些準備處理,而ActivityManagerNative.getDefault()是個單例模式,我們可以在這裏傳遞一個註冊表裏面註冊過的activity過去,這樣系統服務進程驗證的時候就會通過,然後回調給ApplicationThreadNative,ApplicationThreadNative再通過一個內部類H發送消息到ActivityThread,這樣消息發送過來的時候我們再換回真實的activity,如此一來app就會認爲這個activity已經被系統驗證過了,生命週期的調用和其他acitivity的生命週期方法調用過程一模一樣。查看startActivity調用流程時,發現ActivityManagerNative.getDefault()是個單例,我們可以反射重新設置,ActivityThread裏面所有AMS發送過來的消息都會通過內部類H發送到主線程,查看Handler的源碼我們發現,Hander在處理消息的方法是會先查看內部一個 變量Callback是否存在,如果存在則先處理Callback的方法,

HandlerdispatchMessage方法源碼,我們通常是複寫handleMessage,如果我們要攔截哪個方法,我們看一看設置HandlerCallback 並且設置handleMessage返回true,這樣就總不會走HandlerhandleMessage方法了。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

 

我們可以在ActivityThreadH裏面的mCallback裏面攔截startActivity發出來的消息,然後在這裏把真實的activity替換。

 

本文講解的是第二種方案,當然這種方案要對startActivity具體流程有個清楚的認識,不認識的可以查看我的另一篇博客

startActivity說起 https://blog.csdn.net/mr_lu_/article/details/80137617

主要方法如

public void hookSystem() throws Exception{

// 1 加載ActivityManagerNative類信息
        Class<?>

activityManagerNative=Class.forName("android.app.ActivityManagerNative");
        //2 獲取gDefault字段屬性
       Field field= getField(activityManagerNative,"gDefault");
        //3 獲取一個對象。。。。Singleton
       Object o= field.get(null);
        DebugLog.d(o.toString());

//4獲取Singleton類信息
        Class<?> singleton=Class.forName("android.util.Singleton");
        //5 獲取mInstance 字段信息
        Field field2= getField(singleton,"mInstance");

//6 獲取該第三步對象裏面的變量對象
        Object mInstance=field2.get(o);
        DebugLog.d(mInstance.toString());
        MyInvocationHandler myInvocationHandler=new MyInvocationHandler(mInstance);
        //7 生成代理類
        Object proxy= Proxy.newProxyInstance(mInstance.getClass().getClassLoader(),mInstance.getClass().getInterfaces(),myInvocationHandler);
//        //8 替換Singleton類裏面的mInstance屬性
        field2.set(o,proxy);

 

//9獲取ActivityThread類信息
       activityThreadClass=Class.forName("android.app.ActivityThread");

//10獲取mH字段信息,該變量是個Handler對象
        Field mHField=getField(activityThreadClass,"mH");

//11 ActivityThread類裏面有個唯一對象,就是sCurrentActivityThread屬性
        Field

sCurrentActivityThread=getField(activityThreadClass,"sCurrentActivityThread");

        //12 獲取到ActivityThread對象
        activityThread=sCurrentActivityThread.get(null);
        //13 獲取ActivityThread對象 mH對象
        Object mH= mHField.get(activityThread);
//14 獲取Handler類裏面的mCallback字段信息
        Field field1=getField(Handler.class,"mCallback");

        //15 設置mH對面裏面的callback爲我們自己寫的callback
        field1.set(mH,myCallback);
    }

 

第二步我們就獲取到ActivityManagerNative類裏面的gDefault屬性了

 

由於該變量是個static 所以第三步我們調用get傳入null參數就可以獲取該對象了,然後通過動態代理生成一個代理對象,由於代理對象的方法都會調用我們設置的MyInvocationHandler對象的invoke方法,故我們可以在這裏攔截startActivity方法,我們把真實的activity隨便替換一個在註冊表裏面註冊過的activity信息傳輸到AMS那裏。

 

查看Singleton源代碼我們可以知道,該get方法返回的就是create方法創建的對象,也是內部的一個變量,我們只要把這個對象替換成我們的對象就可以了。

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

到第八步的時候我們已經把ActivityManagerNative裏面的gDefault變量裏面的mInstance變量換成我們自己的代理對象,這樣AcitivityManagerNative.getDefault對象也就是我們的代理對象了。

 

12步的時候我們已經獲取到當前ActivityThread類的唯一對象,這個和我們在自定義Application返回Application實例一樣。

 

13步的我們已經獲取到ActivityThread裏面mH這個變量

 

 

15步的時候我們已經把ActivityThread裏面的mH對象裏面的mCallback設置成我們自己的callback,這樣AMS發送給APP的消息,我們這裏都能進行攔截。


MyInvocationHandler類代碼如下

這裏我們攔截了APPstartActivity方法,把要開啓的Activity信息保存起來,替換一個新註冊過的Activity信息放在裏面

class MyInvocationHandler implements InvocationHandler {
        Object target;
        public MyInvocationHandler(Object o){
            this.target=o;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("startActivity")){
                int index=-1;
                DebugLog.d("invoke:"+method.getName());
                for (Object o:args){
                    index++;
                    if (o instanceof Intent){
//                        intent.setComponent(componentName);
                        Intent oldIntent= (Intent) o;
                        //把原來的intent的跳轉類信息保存起來
//                        Intent intent=new Intent();
                        DebugLog.d("原來要跳轉的getAction:"+oldIntent.getAction());
                        DebugLog.d("原來要跳轉的getPackage:"+oldIntent.getPackage());
                        DebugLog.d("原來要跳轉的Component():"+(oldIntent.getComponent()==null?"null":oldIntent.getComponent().toString()));
                        //替換
                        ComponentName componentName=new ComponentName(context,TestAidlActivity.class);
                        oldIntent.putExtra("realComponentName",oldIntent.getComponent());
                        oldIntent.setComponent(componentName);
                        break;
                    }
                }
//                if (index!=-1){
//                    args[index]=
//                }
            }
            return method.invoke(target,args);
        }
    }

 

 

 

我們自己的callback

Handler.Callback myCallback=new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what==100){
            DebugLog.d("handleMessage");
            handleStartActivity(msg);
        }
        return false;
    };
};

 

查看H源碼我們知道開啓一個activity消息是100.所以我們這裏處理msg.what==100的情況,看下面知道所有的生命週期回調都有對應的消息,我們都能進行攔截處理。目前我們暫時處理開啓activity的消息

 

這個方法就好理解了,獲取msg裏面的intent對象,然後把裏面的Component設置成我們保持的那個Component信息

private void handleStartActivity(Message msg){
    Object activityClientRecord= msg.obj;
    try {

        isReplaceOncreate=false;
        Field field=getField(activityClientRecord.getClass(),"intent");

        //拿到intent對象。
        Intent intent= (Intent) field.get(activityClientRecord);
        ComponentName componentName=intent.getParcelableExtra("realComponentName");

       String className=componentName.getClassName();

        Class<?> clazz=Class.forName(className);
        if (clazz.newInstance() instanceof AppCompatActivity){
            DebugLog.d("AppCompatActivity............");
            isReplaceOncreate=true;
        }
        //重新替換過來
        intent.setComponent(componentName);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Manifest.xml信息如下

 

TestAidi2Activity我並沒有在清單文件中註冊。運行後打印信息如下

 

所有代碼上次到github:https://github.com/helloworld777/hello-jni

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章