Hook 技術(shù)(一)

一、什么是 Hook 技術(shù)

??Hook 技術(shù)又叫做鉤子函數(shù)迁客,在系統(tǒng)沒有調(diào)用該函數(shù)之前檀何,鉤子程序就先捕獲該消息,鉤子函數(shù)先得到控制權(quán)践付,這時(shí)鉤子函數(shù)既可以加工處理(改變)該函數(shù)的執(zhí)行行為秦士,還可以強(qiáng)制結(jié)束消息的傳遞。簡(jiǎn)單來說永高,就是把系統(tǒng)的程序拉出來變成我們自己執(zhí)行代碼片段伍宦。
??要實(shí)現(xiàn)鉤子函數(shù),有兩個(gè)步驟:
??1. 利用系統(tǒng)內(nèi)部提供的接口乏梁,通過實(shí)現(xiàn)該接口,然后注入進(jìn)系統(tǒng)(特定場(chǎng)景下使用)
??2.動(dòng)態(tài)代理(使用所有場(chǎng)景)

二关贵、Hook 技術(shù)實(shí)現(xiàn)的步驟

??Hook 技術(shù)實(shí)現(xiàn)的步驟也分為兩步
??1.找到 hook 點(diǎn)(Java 層)遇骑,該 hook 點(diǎn)必須滿足以下的條件:需要 hook 的方法,所屬的對(duì)象必須是靜態(tài)的揖曾,因?yàn)槲覀兪峭ㄟ^反射來獲取對(duì)象的落萎,我們獲取的是系統(tǒng)的對(duì)象,所以不能夠 new 一個(gè)新的對(duì)象炭剪,必須用系統(tǒng)創(chuàng)建的那個(gè)對(duì)象练链,所以只有靜態(tài)的才能保證和系統(tǒng)的對(duì)象一致。
??2.將 hook 方法放到系統(tǒng)之外執(zhí)行(放入我們自己的邏輯)

三奴拦、使用 hook 技術(shù)實(shí)現(xiàn)免注冊(cè)式跳轉(zhuǎn)

hook.gif

??解釋一下上面的步驟媒鼓,我們有一個(gè) MainActivity,四個(gè)按鈕,前三個(gè)是打開不同的 Activity绿鸣,最后一個(gè)是退出登錄疚沐,這三個(gè) Activity 其中界面2是不需要登錄的,界面3和界面4都是需要登錄才能看到的潮模。
??那么既然要在打開 Activity 之前就判斷是否登錄了亮蛔,而且要使用 hook 技術(shù),那么我們下看一下 startActivity 的源碼擎厢,因?yàn)槲覀冎牢覀冃枰?hook 的就是 startActivity 方法究流。

找 Hook 點(diǎn)

image.png

image.png

image.png

注意:上述源碼只有部分源碼,為了方便截屏动遭,并不是全部的源碼芬探,如果想看全部源碼,請(qǐng)自行查看沽损。

??看到這灯节,我們明白了,其實(shí)是 ActivityManager.getService() 最終調(diào)用了 startActivity() 方法绵估,我們看 ActivityManager.getService() 方法炎疆。


image.png

image.png

image.png

??解釋一下上面的源碼,ActivityManager.getService()方法調(diào)用的是 IActivityManagerSingleton.get()方法国裳,而這個(gè)IActivityManagerSingleton是 Singleton(android.util)形入,所以 IActivityManagerSingleton.get()就是調(diào)用了 Singleton 里面的 get 方法,進(jìn)到 Singleton 類缝左,發(fā)現(xiàn) get() 方法里面會(huì)通過 create() 抽象方法方法給 mInstance 屬性賦值亿遂,回到剛才的地方,我們發(fā)現(xiàn)渺杉,create() 方法返回了一個(gè) IAcivityManager 對(duì)象蛇数。最終結(jié)果:其實(shí)最終是 IActivityManager 調(diào)用了 startActivity() 方法。
??所以我們真正想 hook 的點(diǎn)是 IActivityManager 對(duì)象是越,那么如何拿到這個(gè)靜態(tài)對(duì)象呢耳舅?其實(shí)聰明的帥哥和美女肯定都發(fā)現(xiàn)了,這個(gè) IActivityManagerSingleton 其實(shí)就是一個(gè)靜態(tài)的倚评,而且我們拿到該系統(tǒng)對(duì)象后就獲取到該對(duì)象的 mInstance 屬性浦徊,即 IActivityManager,那么我們就把 IActivityManagerSingleton 當(dāng)做一個(gè)偽 hook 點(diǎn)天梧。
??hook 點(diǎn)已經(jīng)找到了盔性,第一步已經(jīng)完成,接下來就該第二步了呢岗,那么如何將系統(tǒng)執(zhí)行的 startActivity() 拉到系統(tǒng)外執(zhí)行冕香,給其添加一些自己的邏輯呢蛹尝?這里我們使用動(dòng)態(tài)代理來實(shí)現(xiàn)。
??這里大概說一下項(xiàng)目暂筝,肯定有五個(gè) Activity箩言,一個(gè) MainActivity 是用來展示四個(gè)按鈕的,一個(gè) LoginActivity焕襟,還有其他三個(gè)是測(cè)試的展示頁面陨收,其實(shí)還有一個(gè) ProxyActivity,并且鸵赖,在清單文件中务漩,我們除了 MainActivity 是啟動(dòng)頁,ProxyActivity 進(jìn)行了注冊(cè)它褪,其他的 Activity 都沒有在清單文件中注冊(cè)饵骨,沒錯(cuò),你沒有看錯(cuò)茫打,就是沒有注冊(cè)居触,那運(yùn)行會(huì)崩潰嗎?空口無憑老赤,我們先看一下代碼轮洋,然后看運(yùn)行結(jié)果。

package com.radish.android.hookframeworktest;

import android.content.Context;
import android.content.Intent;

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

public class HookUtils {

    private Context context;

    public void hookStartActivity(Context context) {
        this.context = context;
        try {
            /**
             * 這里注意一下抬旺,用我們分析的源碼運(yùn)行不了弊予,所以稍微改了一下,
             * 思路什么都一樣开财,只是源碼的屬性名做了修改
             */
//            Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
            Class<?> activityManagerClass = Class.forName("android.app.ActivityManagerNative");
            //拿到 IActivityManagerSingleton 屬性
//            Field field = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
            Field field = activityManagerClass.getDeclaredField("gDefault");
            field.setAccessible(true);
            //獲取到是 Singleton 對(duì)象汉柒,也就是 field 對(duì)應(yīng)的類型
            Object singletonObj = field.get(null);

            //然后獲取 Singletone 的 mInstance 屬性
            Class<?> singtonClass = Class.forName("android.util.Singleton");
            Field mInstanceField = singtonClass.getDeclaredField("mInstance");
            mInstanceField.setAccessible(true);
            //真正的 hook 點(diǎn)
            Object iActivityManagerObj = mInstanceField.get(singletonObj);

            //hook 第二步,動(dòng)態(tài)代理
            Class<?> iActivityManagerIntercept = Class.forName("android.app.IActivityManager");
            StartActivityHandler startActivityHandler = new StartActivityHandler(iActivityManagerObj);
            Object proxyIActivityManager = Proxy.newProxyInstance(getClass().getClassLoader(),
                    new Class[]{iActivityManagerIntercept}, startActivityHandler);
            //在這我們將系統(tǒng)的對(duì)象更換成我們生成的動(dòng)態(tài)代理對(duì)象责鳍,為了是調(diào)用動(dòng)態(tài)代理的 invoke 方法碾褂,不更換不執(zhí)行
            mInstanceField.set(singletonObj, proxyIActivityManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class StartActivityHandler implements InvocationHandler {

        //系統(tǒng)真正的對(duì)象
        private Object trueIActivityManager;

        public StartActivityHandler(Object trueIActivityManager) {
            this.trueIActivityManager = trueIActivityManager;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if ("startActivity".equals(method.getName())) {
                System.out.println("abc : --------------------- startActivity ---------------------");
                Intent intent = null;
                int index = -1;
                for (int i = 0; i < args.length; i++) {
                    Object obj = args[i];
                    if (obj instanceof Intent) {
                        //找到 startActivity 傳遞進(jìn)來的 Intent
                        intent = (Intent) obj;
                        index = i;
                    }
                }
                //瞞天過海,獲取想要跳轉(zhuǎn)的意圖历葛,進(jìn)行篡改
                Intent newIntent = new Intent(context, ProxyActivity.class);
                //我們將真實(shí)的意圖封裝在假意圖當(dāng)中
                newIntent.putExtra("oldIntent", intent);
                args[index] = newIntent;
            }
            return method.invoke(trueIActivityManager, args);
        }
    }
}
![ProxyActivity.gif](https://upload-images.jianshu.io/upload_images/2541965-db61e62b7a19b584.gif?imageMogr2/auto-orient/strip)

??然后就是如何使用了


image.png
image.png
ProxyActivity.gif

??總結(jié)下正塌,目前我們實(shí)現(xiàn)的功能是,不管你跳轉(zhuǎn)任何的 Activity啃洋,我們都跳轉(zhuǎn)到 ProxyActivity,所以我們只需要在清單文件中注冊(cè)一個(gè) ProxyActivity 而不用注冊(cè)其他的 Activity 也不會(huì)崩潰屎鳍,是如何實(shí)現(xiàn)的呢宏娄?我們是通過使用 hook 技術(shù)篡改 Intent,并將你真正的意圖存放到我們新的 Intent 中逮壁。這時(shí)候孵坚,應(yīng)該有些人要打我了,我明明想去東京和巴黎,你卻帶我去了浪漫的土耳其~~~~~
??別急卖宠,還沒完呢巍杈。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扛伍,隨后出現(xiàn)的幾起案子筷畦,更是在濱河造成了極大的恐慌,老刑警劉巖刺洒,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳖宾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡逆航,警方通過查閱死者的電腦和手機(jī)鼎文,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來因俐,“玉大人拇惋,你說我怎么就攤上這事∧ㄊ#” “怎么了撑帖?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吧兔。 經(jīng)常有香客問我磷仰,道長(zhǎng),這世上最難降的妖魔是什么境蔼? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任灶平,我火速辦了婚禮,結(jié)果婚禮上箍土,老公的妹妹穿的比我還像新娘逢享。我一直安慰自己,他們只是感情好吴藻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布瞒爬。 她就那樣靜靜地躺著,像睡著了一般沟堡。 火紅的嫁衣襯著肌膚如雪侧但。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天航罗,我揣著相機(jī)與錄音禀横,去河邊找鬼。 笑死粥血,一個(gè)胖子當(dāng)著我的面吹牛柏锄,可吹牛的內(nèi)容都是我干的酿箭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼趾娃,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缭嫡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起抬闷,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤妇蛀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后饶氏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體讥耗,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年疹启,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了古程。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喊崖,死狀恐怖挣磨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荤懂,我是刑警寧澤茁裙,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站节仿,受9級(jí)特大地震影響晤锥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜廊宪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一矾瘾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箭启,春花似錦壕翩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荐操,卻和暖如春芜抒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背托启。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工宅倒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人驾中。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓唉堪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親肩民。 傳聞我的和親對(duì)象是個(gè)殘疾皇子唠亚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容