天天看點

Android hook技術淺析與小實踐

一、什麼是沙箱機制?

沙箱是一個虛拟系統程式,沙箱提供的環境相對于每一個運作的程式的程序空間都是獨立的,程式的運作互不幹擾,而且不會對現有的系統産生影響。

二、什麼是hook?

hook的意思是勾住。比如A發送消息給B,在消息過去之前,可以先把消息勾住,不讓其傳遞,你可以先執行你的操作(或者說先執行你的鈎子函數)。

專業的說法就是:hook技術,能夠改變API執行的結果,将系統的API函數執行重定向。

三、為什麼需要hook?

android系統的沙箱機制使我們不能通過一個程式改變其他程式的某些行為,但是hook技術正好可以解決此類問題。

四、代理是什麼?

舉例生活中,比如我經常需要買一些香港的東西,又不太現實每次自己親自去,隻能找一些代購幫我們去買。那代購就比較坑了,不僅坑你的錢,還可能坑你的貨品,給你帶回了假貨回來。

五、代理分類?

1.靜态代理

2.動态代理

六、反射機制?

Java反射機制主要提供了以下功能: 在運作時判斷任意一個對象所屬的類;在運作時構造任意一個類的對象;在運作時判斷任意一個類所具有的成員變量和方法;在運作時調用任意一個對象的方法;生成動态代理。

在運作狀态中,對于任意一個類,都能夠擷取到這個類的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動态擷取的資訊以及動态調用對象的方法的功能就稱為java語言的反射機制。通俗點講,通過反射,該類對我們來說是完全透明的,想要擷取任何東西都可以。

七、hook技術分類?

分類1 —— API hook:

分類2 —— 系統消息hook:

八、小實踐

以下我們對API hook舉一個詳細的例子:

步驟

1.尋找hook點。一般是靜态變量或單例對象,盡量hook public的對象和方法,減輕因非public而導緻的各版本不一緻帶來的工作。

2.選擇合适的代理方式。動态代理适用于接口。

3.偷梁換柱。使用反射機制用代理對象替換掉被hook的對象

舉例 hook startActivity:

1.尋找hook點。

》》對于Context.startActivity,由于Context的實作類為ContextImpl,是以直接分析ContextImpl類的startActivity()。

@Override
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
                && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
           

》》這裡,實際上使用了ActivityThread類的mInstrumentation成員的execStartActivity方法;而ActivityThread 實際上是主線程,因為主線程一個程序隻有一個,是以這裡是一個良好的Hook點。

》》Hook主線程對象:要将這個主線程對象裡面的mInstrumentation替換成修改過的代理對象;要替換主線程對象裡面的字段,得先拿到主線程對象的引用,如何擷取呢? 》》ActivityThread類裡面有一個靜态方法currentActivityThread,通過它可以拿到這個對象類;但ActivityThread是一個隐藏類,需用反射去擷取。

2.選擇合适代理方式

》》拿到currentActivityThread後,要修改它的mInstrumentation字段為修改後的代理對象。是以需要先實作這個代理對象,由于JDK動态代理隻支援接口,而這個Instrumentation是一個類,是以隻能手寫一個靜态代理類,用來覆寫掉原始的方法。

3.偷梁換柱

》》采用反射直接修改即可。

完整代碼如下:

HookUtil

public class HookUtil {
    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

public class EvilInstrumentation extends Instrumentation {
    private static final String TAG = "EvilInstrumentation";

    Instrumentation mBase;

    public EvilInstrumentation(Instrumentation mBase) {
        this.mBase = mBase;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        Log.d(TAG, " Hook之前 列印 execStartActivity");

        // 開始調用原始的方法, 調不調用随你,但是不調用的話, 所有的startActivity都失效了.
        // 由于這個方法是隐藏的,是以需要使用反射調用;首先找到這個方法
        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("do not support!");
        }
    }
}
           

AActivity

public class AActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Button tv = new Button(this);
        tv.setText("Test");

        setContentView(tv);
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(AActivity.this, BActivity.class);
                i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );//必須添加
                getApplication().startActivity(i);
            }
        });
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
            HookUtil.attachContext();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
           

BActivity

public class BActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }
}
           

運作程式可以看到會列印資訊:Hook之前 列印 execStartActivity

注意:

不添加setFlags(Intent.FLAG_ACTIVITY_NEW_TASK )會報錯噢。

Android hook技術淺析與小實踐

九、總結

hook技術涉及到的知識點主要有反射、代理及android的一些底層知識,需要花更多的時間去學習和總結才能較好地掌握好hook相關的内容。

繼續閱讀