Android Hook技術小實踐 [復制鏈接]

2019-11-13 09:58
愛碼仕 閱讀:451 評論:0 贊:0

概述

在學習Android插件化的過程中有用到Hook相關技術,本篇文章對Hook相關技術做也給簡單的介紹,并寫兩個小Demo,當你了解了Hook之后可能會對你以后的碰到問題時多了一個解題思路

定義

Hook單詞的意思就是鉤子,那我們在什么時候用到這個鉤子呢,如上圖所示,在一個事件或者動作執行的過程中,截獲相關事件或者動作,加入自己的代碼或者替換裝自己的代理對象,這就叫Hook

Hook的原理

本文主要是采用java反射機制拿到要執行的對象或者方法就行修改或者替換

關注點:在hook的時候我們首先需要找到要Hook的對象,什么樣的對象比較好Hook呢,那就是單例和靜態變量,單例和靜態變量在進程中不容易發生變化,相對容易被定位到,二普通象則比價容易發生變化(隨時有可能被銷毀),。我們根據這個原則找到所謂的Hook點

以上就是我對Hook的理解,且是還挺簡單的,但實踐是檢驗真理的唯一標準,下面我會寫兩個小Demo

Demo1

本例子Hook的是一個工具類

/**
 * 打印機工具類,提供黑白打印和彩色打印
 */
public class PrintUtil {
    private static IPrint colorPrint = new ColorPrint(); //彩色打印機
    private static IPrint blackWhitePrint = new BlackWhitePrint(); //黑白打印機

    public static void colorPrint(String content){
        colorPrint.print(content);
    }

    public static void blackWhitePrint(String content){
        blackWhitePrint.print(content);
    }

}

工具類如上

    private void operate4(){
//        HookHelper.hookPrint();
        PrintUtil.blackWhitePrint("黑白內容");
        PrintUtil.colorPrint("彩色內容");
    }

正常結果如上 ,下面我們對PrintUtil進行hook ,首先我們先找Hook點,在PrintUtil中有兩個靜態變量,這就是我們要找的Hook點 具體代碼如下

/**
     * 對printUtil進行hook處理
     */
    public static void hookPrint(){
        try {
            Class<?> printClass = Class.forName("com.example.shiyagang.myapplication.util.PrintUtil");
            Field colorPrintField= printClass.getDeclaredField("colorPrint");
            Field blackWhitePrintField = printClass.getDeclaredField("blackWhitePrint");
            colorPrintField.setAccessible(true);
            blackWhitePrintField.setAccessible(true);
            colorPrintField.set(null,new BlackWhitePrint());
            blackWhitePrintField.set(null,new ColorPrint());
        }catch (Exception e){
            e.printStackTrace();
        }
    }

我們通過反射對PrintUtil的兩個靜態變量進行替換

替換完執行結果如下







彩色打印機打出了黑白內容,我們成功了,嘿嘿

Demo2

這個例子我們在context.startActivity的調用鏈,找到相關的hook點進行替換。我們首先分下context.startActivity的流程,Context.startActivity其實走到了ContextImpl的startActivity

如上圖所示最終調用了ActivityThread類的mInstrumentation成員的execStartActivity方法;注意到,ActivityThread 實際上是主線程,而主線程一個進程只有一個,因此這里是一個良好的Hook點
  • 我們要拿到ActivityThread的mInstrumentation ,首先得拿到ActivityThread的實例
  • ActivityThread類里面有一個靜態方法currentActivityThread可以幫助我們拿到ActivityThread的實例

通過以上步驟我們就能進行相關hook了

 /**
     * 對activityThread進行Hook
     * 
     */
    public static void attachContext() throws Exception{
        // 先獲取到當前的ActivityThread對象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);
        // 拿到mInstrumentation  字段
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
        // 創建代理對象
        Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
        // 偷梁換柱
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);
    }

EvilInstrumentation的代理對象如下:

/**
 * Instrumentation 的靜態代理類
 */
public class EvilInstrumentation extends Instrumentation {
    private static final String TAG = EvilInstrumentation.class.getSimpleName();

    // ActivityThread中原始的對象, 保存起來
    Instrumentation mBase;

    public EvilInstrumentation(Instrumentation base) {
        mBase = base;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Log.e(TAG, "我們Hook了 Activity的啟動流程");
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mBase, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            throw new RuntimeException("出問題了,去適配吧");
        }
    }
}

下面我們看下Activity的代碼 ,我們在attachBaseContext中進行Hook

private void operate3(){
        Intent intent = new Intent(getApplicationContext(),SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        getApplicationContext().startActivity(intent);
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
            // 在這里進行Hook
            HookHelper.attachContext();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

入上圖所示我們已經成功了,我們在這只是打印了一個日志,當然你可以干任何事情

總結

到此已經對Hook做了簡單的介紹

  • 我們需要先找到Hook點 ,靜態變量和單例比較好Hook
  • 植入我們的代碼,可以采用代理的方式進行植入
  • 進行偷梁換柱

今年年初我花一個月的時間收錄整理了一套知識體系,如果有想法深入的系統化的去學習的,可以點擊傳送門,我會把我收錄整理的資料都送給大家,幫助大家更快的進階。



我來說兩句
您需要登錄后才可以評論 登錄 | 立即注冊
facelist
所有評論(0)
領先的中文移動開發者社區
18620764416
7*24全天服務
意見反饋:[email protected]

掃一掃關注我們

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粵ICP備15117877號 )

龙江福彩p62开奖 开个养鸡场赚钱吗 2018年学纹绣能赚钱吗 卖紫薯面条赚钱吗 单刷什么差事赚钱 四川成都麻将玩法 psv学战都市赚钱 美剧是按什么赚钱的 广东麻将十三幺怎么胡 猪肉供应商赚钱吗 手机赚钱随时可以提现 无网四人单机麻将 不用投资如何赚钱方法 微信捕鱼游戏送分 2019年商业赚钱模式图 迅雷玩客云 如何赚钱 1+2捕鱼游戏中心