Hook實踐

一直據說hook,以前也瞭解了一個hook在Android中的應用,可是也一直沒搞懂是什麼,此次決定使用一個場景,用例子理解什麼是hook,這個場景就是hook免註冊跳轉Activity,因爲該例子中直接反射Activity啓動流程中的源碼和Handler機制中的源碼,所以對Activity啓動流程和Handler機制不太熟悉的小夥伴能夠參考我上兩篇文章哦。html

juejin.im/post/5caf05…java

juejin.im/post/5cd7b8…android

Hook實踐

免註冊跳轉Activity:直接上代碼bash

package com.android.hookjumpactivity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HookUtil {
    private Context context;
    private Class<?> proxyActivity;

    public HookUtil(Context context, Class<?> proxyActivity) {
        this.context = context;
        this.proxyActivity = proxyActivity;
    }

    /**
     * 在AMS檢測前設置鉤子
     * 
     * 根據Activity跳轉流程源碼分析可知,Activity跳轉須要使用到IActivityManager,
     * 而IActivityManager可經過反射獲取,而可經過動態代理替換掉原有的IActivityManager,
     * 隨後經過替換過的iActivityManager調用startActivity時便可觸發代理類中真正執行方法先後的操做,
     * 所以只須要在代理類中將真實想跳轉的Intent替換成在清單文件中註冊的ProxyActivity
     * ,而且將真實的Intent看成參數傳遞過去,經過ProxyActivity的意圖繞過AMS檢測,並在後面將真實的Intent替換回來便可。
     */
    public void hookAms() {
        try {
            Class<?> ActivityManagerNativecls = Class.forName("android.app.ActivityManagerNative");
            Field gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
            gDefault.setAccessible(true);
            //由於是靜態變量  因此獲取的到的是系統值  hook   僞hook
            Object defaltValue = gDefault.get(null);
            //mInstance對象
            Class<?> SingletonClass = Class.forName("android.util.Singleton");
            Field mInstance = SingletonClass.getDeclaredField("mInstance");
            //還原 IActivityManager對象  系統對象
            mInstance.setAccessible(true);
            Object iActivityManagerObject = mInstance.get(defaltValue);
            Class<?> iActivityManagerIntercept = Class.forName("android.app.IActivityManager");

            //第二參數  是即將返回的對象 須要實現那些接口,其中這些接口包含OnClickListener,和IActivityManagerIntercept所實現的接口。
            //也就是說IActivityManager和OnClickListener所實現的接口都動態替換成startActivtyMethod了
            Object oldIactivityManager = Proxy
                    .newProxyInstance(Thread.currentThread().getContextClassLoader()
                            , new Class[]{iActivityManagerIntercept}, new AmsInvocationHandler
                                    (iActivityManagerObject));

            //將系統的iActivityManager  替換成   本身經過動態代理實現的對象
            //oldIactivityManager對象 實現了 IActivityManager這個接口的全部方法
            mInstance.set(defaltValue, oldIactivityManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class AmsInvocationHandler implements InvocationHandler {
        private Object iActivityManagerObject;

        public AmsInvocationHandler(Object iActivityManagerObject) {
            this.iActivityManagerObject = iActivityManagerObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Log.i("INFO", "invoke " + method.getName());
            if ("startActivity".equals(method.getName())) {
                Log.i("INFO", "-----------------startActivity--------------------------");
                //瞞天過海
                //尋找傳進來的原intent(要跳轉的意圖)
                Intent intent = null;
                int index = 0;
                for (int i = 0; i < args.length; i++) {
                    //intent
                    Object arg = args[i];
                    if (arg instanceof Intent) {
                        // 此Intent爲原意圖,未註冊清單文件的意圖
                        intent = (Intent) args[i];
                        index = i;
                    }
                }
                Intent proxyIntent = new Intent();
                //ProxyActivity是合法意圖(註冊清單文件的意圖),這裏用它經過AMS檢測
                ComponentName componentName = new ComponentName(context, proxyActivity);
                proxyIntent.setComponent(componentName);
                //真實的意圖 被我隱藏到了  鍵值對中,等待待會繞過AMS後再經過ActivityMH取出來。
                proxyIntent.putExtra("realIntent", intent);
                args[index] = proxyIntent;
            }

            return method.invoke(iActivityManagerObject, args);
        }
    }

    /**
     * 在AMS檢測後設置鉤子
     * 
     * 根據Activity跳轉流程源碼分析可知,Activity的建立必不可少的是須要走ActivityThread類中的
     * scheduleLaunchActivity方法,此方法中調用sendMessage(H.LAUNCH_ACTIVITY, r)發送此消息執行
     * handleLaunchActivity(r, null, "LAUNCH_ACTIVITY")方法
     * ,此方法調用performLaunchActivity(r, customIntent)來真正建立Activity
     * 主要關注點在sendMessage(H.LAUNCH_ACTIVITY, r)方法中,此方法經過Handler發送消息,
     * 而經過Handler機制可知,消息的分發方法dispatchMessage中,首先判斷msg.callback此回調是否爲空,
     * 若不爲空則直接執行handleCallback(msg)方法,而後執行handleCallback方法message.callback.run(),
     * 所以只須要在 AmsInvocationHandler 繞過AMS檢測後,可經過反射msg.callback,在callback將真實要跳轉的
     * Intent意圖替換回來,便可在後面執行handleLaunchActivity時建立出咱們真實須要跳轉的Activity
     */
    public void hookSysHandler() {
        try {
            Class<?> forName = Class.forName("android.app.ActivityThread");
            Field currentActivityThreadField = forName.getDeclaredField("sCurrentActivityThread");
            currentActivityThreadField.setAccessible(true);
            //還原系統的ActivityTread   mH
            Object activityThreadObj = currentActivityThreadField.get(null);

            Field handlerField = forName.getDeclaredField("mH");
            handlerField.setAccessible(true);
            //hook點找到了
            Handler mH = (Handler) handlerField.get(activityThreadObj);
            Field callbackField = Handler.class.getDeclaredField("mCallback");

            callbackField.setAccessible(true);

            callbackField.set(mH, new ActivityThreadHandlerCallback(mH));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class ActivityThreadHandlerCallback implements Handler.Callback {
        private Handler mH;

        public ActivityThreadHandlerCallback(Handler mH) {
            this.mH = mH;
        }

        @Override
        public boolean handleMessage(Message msg) {
            //LAUNCH_ACTIVITY ==100 即將要加載一個activity了,這裏是系統的規範定義的
            if (msg.what == 100) {
                //替換回真實的Intent
                handleLuachActivity(msg);
            }
            //作了真正的跳轉
            mH.handleMessage(msg);
            return true;
        }

        private void handleLuachActivity(Message msg) {
            //還原
            Object obj = msg.obj;
            try {
                Field intentField = obj.getClass().getDeclaredField("intent");
                intentField.setAccessible(true);
                //  ProxyActivity   2
                Intent proxyIntent = (Intent) intentField.get(obj);
                // 到這裏後,其實已經經過AMS檢測了,
                // 這裏將咱們看成參數的realIntent取出來,並將真實用來跳轉的Component設置回去。
                Intent realIntent = proxyIntent.getParcelableExtra("realIntent");
                if (realIntent != null) {
                    proxyIntent.setComponent(realIntent.getComponent());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

複製代碼

使用方法:app

package com.android.hookjumpactivity;

import android.app.Application;

public class HookApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        HookUtil hookUtil = new HookUtil(this, ProxyActivity.class);
        hookUtil.hookAms();
        hookUtil.hookSysHandler();
    }
}


AndroidManifest.xml文件中:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.hookjumpactivity">

    <application
        android:name=".HookApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ProxyActivity"/>
    </application>

</manifest>
複製代碼

具體講解已在代碼中。ide

參考文章:源碼分析

www.jianshu.com/p/1a10703e2…post

...ui

hook防範可參考:this

tech.meituan.com/2018/02/02/…

注:如有什麼地方闡述有誤,敬請指正。

相關文章
相關標籤/搜索