Android Hook AMS

Hook概念

Hook翻譯過來是鉤子的意思铜异,我們都知道無論是手機還是電腦運行的時候都依賴系統(tǒng)各種各樣的API黔州,當(dāng)某些API不能滿足我們的要求時键思,我們就得去修改某些api衩藤,使之能滿足我們的要求谦屑。這樣api hook就自然而然的出現(xiàn)了驳糯。我們可以通過api hook,改變一個系統(tǒng)api的原有功能氢橙≡褪啵基本的方法就是通過hook“接觸”到需要修改的api函數(shù)入口點,改變它的地址指向新的自定義的函數(shù)悍手。當(dāng)然這種技術(shù)同樣適用于Android系統(tǒng)帘睦,在Android開發(fā)中,我們同樣能利用Hook的原理讓系統(tǒng)某些方法運行時調(diào)用的是我們定義的方法坦康,從而滿足我們的要求

Hook原則

Android中主要是依靠分析系統(tǒng)源碼類來做到的竣付,首先我們得找到被Hook的對象,我稱之為Hook點滞欠;什么樣的對象比較好Hook呢卑笨?自然是容易找到的對象。什么樣的對象容易找到仑撞?靜態(tài)變量和單例赤兴;在一個進(jìn)程之內(nèi)妖滔,靜態(tài)變量和單例變量是相對不容易發(fā)生變化的,因此非常容易定位桶良,而普通的對象則要么無法標(biāo)志座舍,要么容易改變。我們根據(jù)這個原則找到所謂的Hook點

在安卓中實現(xiàn)hook主要通過兩種方式

1.反射技術(shù)和代理實現(xiàn)陨帆,當(dāng)然代理不管是動態(tài)還是靜態(tài)的都是可以實現(xiàn)的曲秉,但是只能hook自己應(yīng)用內(nèi)存中的對象;

2.在root的情況下疲牵,Xposed通過替換/system/bin/app_process程序控制zygote進(jìn)程承二,使得app_process在啟動過程中會加載XposedBridge.jar這個jar包,從而完成對Zygote進(jìn)程及其創(chuàng)建的Dalvik 虛擬機的劫持纲爸,可以達(dá)到hook整個系統(tǒng)中所有進(jìn)程內(nèi)存里面的對象的目的亥鸠;

HookAms實踐

  • 插件技術(shù)中很重要的一項就是宿主啟動插件APK中的Activity,因為插件都是后面業(yè)務(wù)迭代加進(jìn)來的识啦,所以Activity不可能提前注冊在宿主Activity的清單文件中的负蚊,所以正常的情況下是不可能啟動插件里的Activity的,因為啟動Activity的過程是需要在清單文件中尋找是否注冊颓哮,若沒有家妆,則直接crash。

  • 所以想實現(xiàn)跳過系統(tǒng)檢查冕茅,做法就是先在宿主里注冊一個ProxyActivity伤极,在啟動插件的Activity的時候,把我們這個真實意圖Intent替換為可以通過檢查的啟動ProxyActivity的代理意圖Intent姨伤,然后讓真實意圖作為Extra添加進(jìn)代理意圖里塑荒,在通過檢查后再取出來替換回來,而這樣的功能姜挺,是通過hook實現(xiàn)的齿税。

  • 我們知道Activity的啟動過程是通過AIDL Binder的方式跟AMS進(jìn)行一系列的交互,最終通過反射newInstance創(chuàng)建出來的炊豪,由于AMS處于系統(tǒng)進(jìn)程中凌箕,所以我們是沒法從它里面尋找hook點的。所以這里所說的hookAms词渤,其實是hook位于我們自己的應(yīng)用這邊的與AMS交互的AIDL的接口IActivityManeger牵舱,那怎么才能找到這個對象并且進(jìn)行替換呢,需要看FrameWork源碼

Instrumentation execStartActivity

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

    try {
        // 通過ActivityManagerNative.getDefault()的startActivity來啟動activity
        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        // 根據(jù)返回的result結(jié)果缺虐,給用戶對應(yīng)的提示
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

ActivityManagerNative

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{

static public IActivityManager getDefault() {
    return gDefault.get();
}



private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
    protected IActivityManager create() {
        IBinder b = ServiceManager.getService("activity");
        if (false) {
            Log.v("ActivityManager", "default service binder = " + b);
        }
        IActivityManager am = asInterface(b);
        if (false) {
            Log.v("ActivityManager", "default service = " + am);
        }
        return am;
    }
};
}

這里可以看到ActivityManagerNative.getDefault()內(nèi)部直接返回gDefault.get()芜壁,其中g(shù)Default是Singleton泛型類

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

protected abstract T create();

// get方法返回對應(yīng)的泛型類,這里是IActivityManager
public final T get() {
    synchronized (this) {
        if (mInstance == null) {
            mInstance = create();
        }
        return mInstance;
    }
}
}

接下來要揭開gDefault.get()的面紗,我們知道其中在Singleton中維護(hù)了一個mInstance變量慧妄,并且gDefault.get()方法最終返回的就是mInstance對應(yīng)的泛型類實例顷牌,也就是說通過ContextImpl#startActivity方法啟動activity的時候,最終是通過mInstance也就是IActivityManager來啟動的塞淹,其實就是ActivityManagerService窟蓝,所以我們要做的就是通過反射對mInstance重新賦值,將我們自己的代理類賦值給mInstance饱普,然后在代理類中根據(jù)系統(tǒng)獲取的IActivityManager在執(zhí)行操作运挫,所以這里我們就可以在對應(yīng)的操作前后,進(jìn)行一些記錄或者關(guān)鍵log的打印

創(chuàng)建一個代理類套耕,需要實現(xiàn)InvocationHandler接口谁帕,用來代理IActivityManager的操作

public class AMSProxy implements InvocationHandler {

private static final String TAG = "HookAMS";

private Object iActivityManager;

public AMSProxy(Object iActivityManager) {
    this.iActivityManager = iActivityManager;
}

@Override
public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
    Log.d(TAG, "method name is :" + method.getName() + " args length is :" + args.length + "   args is :" + args);
    if ("startActivity".equals(method.getName())) {
        // 第三個參數(shù)是intent
        Intent intent = (Intent)args[2];
        Log.d(TAG, "method name is :"+ method.getName()+"   intent is :"+intent+"   extradata is :"+intent.getStringExtra("DATA"));
    }
    return method.invoke(iActivityManager, args);
}
}

接下來開始Hook AMS

Class activityManagerNativeClazz = Class.forName("android.app.ActivityManagerNative");
Field gDefaultField = activityManagerNativeClazz.getDeclaredField("gDefault");
gDefaultField.setAccessible(true);
Object gDefault = gDefaultField.get(null); // 獲取gDefault實例,由于是靜態(tài)類型的屬性冯袍,所以這里直接傳遞null參數(shù)

// 下面通過反射執(zhí)行g(shù)Default.get();操作匈挖,最終返回IActivityManager,也就是ActivityManagerService的實例
Class singleTonClazz = Class.forName("android.util.Singleton");
Field mInstanceField = singleTonClazz.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object iActivityManager = mInstanceField.get(gDefault);

Class iActivityManagerClazz = Class.forName("android.app.IActivityManager");
// 指定被代理對象的類加載器
// 指定被代理對象所實現(xiàn)的接口颠猴,這里就是代理IActivityManager
// 表示這個動態(tài)代理對象在調(diào)用方法的時候关划,會關(guān)聯(lián)到哪一個InvocationHandler對象上
Object myProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>    
[]{iActivityManagerClazz}, new AMSProxy(iActivityManager));
mInstanceField.set(gDefault,myProxy);

此時運行我們的程序小染,啟動activity翘瓮,就可以看到,每次在啟動activity之前都會打印相關(guān)的參數(shù)裤翩,其實不只是啟動activity资盅,啟動service也可以,只要最終是通過ActivityManagerNative來操作的都可以攔截踊赠。

總結(jié)

Hook是個蠻有樂趣的調(diào)用方式呵扛。

Hook 的選擇點:靜態(tài)變量和單例,因為一旦創(chuàng)建對象筐带,它們不容易變化今穿,非常容易定位。
Hook 過程:
尋找 Hook 點伦籍,原則是靜態(tài)變量或者單例對象蓝晒,盡量 Hook public 的對象和方法。
選擇合適的代理方式帖鸦,如果是接口可以用動態(tài)代理芝薇。
偷梁換柱——用代理對象替換原始對象。

Hook 的這個本領(lǐng)作儿,使它能夠?qū)⒆陨淼拇a「融入」被勾茁宥(Hook)的程序的進(jìn)程中,成為目標(biāo)進(jìn)程的一個部分。API Hook 技術(shù)是一種用于改變 API 執(zhí)行結(jié)果的技術(shù)晾嘶,能夠?qū)⑾到y(tǒng)的 API 函數(shù)執(zhí)行重定向妓雾。在 Android 系統(tǒng)中使用了沙箱機制,普通用戶程序的進(jìn)程空間都是獨立的变擒,程序的運行互不干擾君珠。這就使我們希望通過一個程序改變其他程序的某些行為的想法不能直接實現(xiàn),但是 Hook 的出現(xiàn)給我們開拓了解決此類問題的道路娇斑。當(dāng)然策添,根據(jù) Hook 對象與 Hook 后處理的事件方式不同,Hook 還分為不同的種類毫缆,比如消息 Hook唯竹、API Hook 等。


點贊加關(guān)注是給我最大的鼓勵!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苦丁,一起剝皮案震驚了整個濱河市浸颓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌旺拉,老刑警劉巖产上,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛾狗,居然都是意外死亡晋涣,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進(jìn)店門沉桌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谢鹊,“玉大人,你說我怎么就攤上這事留凭〉瓒螅” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵蔼夜,是天一觀的道長兼耀。 經(jīng)常有香客問我,道長求冷,這世上最難降的妖魔是什么瘤运? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮遵倦,結(jié)果婚禮上尽超,老公的妹妹穿的比我還像新娘。我一直安慰自己梧躺,他們只是感情好似谁,可當(dāng)我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布傲绣。 她就那樣靜靜地躺著,像睡著了一般巩踏。 火紅的嫁衣襯著肌膚如雪秃诵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天塞琼,我揣著相機與錄音菠净,去河邊找鬼。 笑死彪杉,一個胖子當(dāng)著我的面吹牛毅往,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播派近,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼攀唯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了渴丸?” 一聲冷哼從身側(cè)響起侯嘀,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谱轨,沒想到半個月后戒幔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡土童,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年诗茎,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娜扇。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡错沃,死狀恐怖栅组,靈堂內(nèi)的尸體忽然破棺而出雀瓢,到底是詐尸還是另有隱情,我是刑警寧澤玉掸,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布刃麸,位于F島的核電站,受9級特大地震影響司浪,放射性物質(zhì)發(fā)生泄漏泊业。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一啊易、第九天 我趴在偏房一處隱蔽的房頂上張望吁伺。 院中可真熱鬧,春花似錦租谈、人聲如沸篮奄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窟却。三九已至昼丑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間夸赫,已是汗流浹背菩帝。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留茬腿,地道東北人呼奢。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像切平,于是被迫代替她去往敵國和親控妻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,612評論 2 350

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