Android中的HOOK技術(shù)是什么
1. 什么是 Hook
Hook 英文翻譯過來就是「鉤子」的意思,那我們在什么時候使用這個「鉤子」呢?在 Android 操作系統(tǒng)中系統(tǒng)維護著自己的一套事件分發(fā)機制。應(yīng)用程序,包括應(yīng)用觸發(fā)事件和后臺邏輯處理,也是根據(jù)事件流程一步步地向下執(zhí)行。而「鉤子」的意思,就是在事件傳送到終點前截獲并監(jiān)控事件的傳輸,像個鉤子鉤上事件一樣,并且能夠在鉤上事件時,處理一些自己特定的事件。
Hook 的這個本領(lǐng),使它能夠?qū)⒆陨淼拇a「融入」被勾住(Hook)的程序的進程中,成為目標進程的一個部分。API Hook 技術(shù)是一種用于改變 API 執(zhí)行結(jié)果的技術(shù),能夠?qū)⑾到y(tǒng)的 API 函數(shù)執(zhí)行重定向。在 Android 系統(tǒng)中使用了沙箱機制,普通用戶程序的進程空間都是獨立的,程序的運行互不干擾。這就使我們希望通過一個程序改變其他程序的某些行為的想法不能直接實現(xiàn),但是 Hook 的出現(xiàn)給我們開拓了解決此類問題的道路。當然,根據(jù) Hook 對象與 Hook 后處理的事件方式不同,Hook 還分為不同的種類,比如消息 Hook、API Hook 等。
2. Hook的應(yīng)用場景
Hook的應(yīng)用非常廣泛,不僅開發(fā)人員會用到,攻擊者也會用到。
開發(fā)有:對程序的執(zhí)行記錄日志、防止應(yīng)用重復(fù)啟動等。
攻擊有:使用hook攔截用戶輸入信息,獲取鍵盤數(shù)據(jù)等。
3. Hook的技術(shù)方式或框架
- inline hook方式:目標函數(shù)執(zhí)行指令中插入Jump跳轉(zhuǎn)指令實現(xiàn)重定向
- 動態(tài)代理方式:思路應(yīng)該是類似于設(shè)計模式中的代理模式,代理原本的函數(shù)的執(zhí)行
- Method Swizzle方式:動態(tài)改變SEL(方法編號)與IMP(方法實現(xiàn))的對應(yīng)關(guān)系
- Cydia Substrate方式:適用于iOS和andriod,定義了一系列的函數(shù)和宏,底層調(diào)用了objc的runtime和fishHook來替代目標函數(shù)或者系統(tǒng)方法
- fishHook方式:是Facebook提供一種動態(tài)修改鏈接Mach-O文件的工具。此利用Mach-O文件加載原理,通過修改非懶加載和懶加載兩個表的指針達到C函數(shù)的Hook的目的
- Xposed框架:目標函數(shù)為native,利用JNI hook重定向表中的函數(shù)指針
- Legend框架:Android 免 Root 環(huán)境下的一個 Apk Hook 框架,該框架代碼設(shè)計簡潔,通用性高,適合逆向工程時一些 Hook 場景。大部分的功能都放到了 Java 層,兼容性非常好。原理是直接構(gòu)造出新舊方法對應(yīng)的虛擬機數(shù)據(jù)結(jié)構(gòu),然后替換信息寫到內(nèi)存中即可
4. Hook的一般步驟和技巧
- 尋找 Hook 點。原則是盡可能是靜態(tài)變量或者單例對象,因為它們?nèi)菀锥ㄎ?,其次是盡量 Hook public 的對象和方法。
- 選擇適當?shù)膆ook方式或框架。
- 將hook代碼注入到目標程序的運行內(nèi)存中。
實戰(zhàn)
我們自己的代碼里面,給一個view設(shè)置了點擊事件,現(xiàn)在要求在不改動這個點擊事件的情況下,添加額外的點擊事件邏輯.
View v = findViewById(R.id.tv); v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "別點啦,再點我咬你了...", Toast.LENGTH_SHORT).show(); } });
這是view的點擊事件,toast了一段話,現(xiàn)在要求,不允許改動這個OnClickListener,要在toast之前添加日志打印 Log.d(…).
按照上面的思路來:
第一步:根據(jù)需求 確定要hook的對象;
我們的目的是在OnClickListener中,插入自己的邏輯.所以要hook的是v.setOnClickListener()方法的實參。
第二步:尋找要hook的對象的持有者,拿到要hook的對象
進入v.setOnClickListener源碼:發(fā)現(xiàn)我們創(chuàng)建的OnClickListener對象被賦值給了getListenerInfo().mOnClickListener
public void setOnClickListener(@Nullable OnClickListener l) { if (!isClickable()) { setClickable(true); } getListenerInfo().mOnClickListener = l; }
繼續(xù)索引:getListenerInfo() 是個什么玩意?繼續(xù)追查:
ListenerInfo getListenerInfo() { if (mListenerInfo != null) { return mListenerInfo; } mListenerInfo = new ListenerInfo(); return mListenerInfo; }
結(jié)果發(fā)現(xiàn)這個其實是一個偽單例,一個View對象中只存在一個ListenerInfo對象. 進入ListenerInfo內(nèi)部:發(fā)現(xiàn)OnClickListener對象 被ListenerInfo所持有.
static class ListenerInfo { ... public OnClickListener mOnClickListener; ... }
到這里為止,完成第二步,找到了點擊事件的實際持有者:ListenerInfo .
第三步:定義“要hook的對象”的代理類,并且創(chuàng)建該類的對象
我們要hook的是View.OnClickListener對象,所以,創(chuàng)建一個類 實現(xiàn)View.OnClickListener接口.
static class ProxyOnClickListener implements View.OnClickListener { View.OnClickListener oriLis; public ProxyOnClickListener(View.OnClickListener oriLis) { this.oriLis = oriLis; } @Override public void onClick(View v) { Log.d("HookSetOnClickListener", "點擊事件被hook到了"); if (oriLis != null) { oriLis.onClick(v); } } }
然后,創(chuàng)建出一個代理對象
ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance);
可以看到,這里傳入了一個View.OnClickListener對象,它存在的目的,是讓我們可以有選擇地使用到原先的點擊事件邏輯。一般hook,都會保留原有的源碼邏輯.
另外提一句:當我們要創(chuàng)建的代理類,是被接口所約束的時候,比如現(xiàn)在,我們創(chuàng)建的ProxyOnClickListener implements View.OnClickListener,只實現(xiàn)了一個接口,則可以使用JDK提供的Proxy類來創(chuàng)建代理對象
Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), new Class[]>>{View.OnClickListener.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Log.d("HookSetOnClickListener", "點擊事件被hook到了");//加入自己的邏輯 return method.invoke(onClickListenerInstance, args);//執(zhí)行被代理的對象的邏輯 } });
到這里為止,第三步:定義“要hook的對象”的代理類,并且創(chuàng)建該類的對象 完成。
第四步:使用上一步創(chuàng)建出來的對象,替換掉要hook的對象,達成 偷梁換柱的最終目的. 利用反射,將我們創(chuàng)建的代理點擊事件對象,傳給這個view field.set(mListenerInfo, proxyOnClickListener);
這里,貼出最終代碼:
輔助類
/** * hook的輔助類 * hook的動作放在這里 */ public class HookSetOnClickListenerHelper { /** * hook的核心代碼 * 這個方法的唯一目的:用自己的點擊事件,替換掉 View原來的點擊事件 * * @param v hook的范圍僅限于這個view */ public static void hook(Context context, final View v) {// try { // 反射執(zhí)行View類的getListenerInfo()方法,拿到v的mListenerInfo對象,這個對象就是點擊事件的持有者 Method method = View.class.getDeclaredMethod("getListenerInfo"); method.setAccessible(true);//由于getListenerInfo()方法并不是public的,所以要加這個代碼來保證訪問權(quán)限 Object mListenerInfo = method.invoke(v);//這里拿到的就是mListenerInfo對象,也就是點擊事件的持有者 //要從這里面拿到當前的點擊事件對象 Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");// 這是內(nèi)部類的表示方法 Field field = listenerInfoClz.getDeclaredField("mOnClickListener"); final View.OnClickListener onClickListenerInstance = (View.OnClickListener) field.get(mListenerInfo);//取得真實的mOnClickListener對象 //2. 創(chuàng)建我們自己的點擊事件代理類 // 方式1:自己創(chuàng)建代理類 // ProxyOnClickListener proxyOnClickListener = new ProxyOnClickListener(onClickListenerInstance); // 方式2:由于View.OnClickListener是一個接口,所以可以直接用動態(tài)代理模式 // Proxy.newProxyInstance的3個參數(shù)依次分別是: // 本地的類加載器; // 代理類的對象所繼承的接口(用Class數(shù)組表示,支持多個接口) // 代理類的實際邏輯,封裝在new出來的InvocationHandler內(nèi) Object proxyOnClickListener = Proxy.newProxyInstance(context.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Log.d("HookSetOnClickListener", "點擊事件被hook到了");//加入自己的邏輯 return method.invoke(onClickListenerInstance, args);//執(zhí)行被代理的對象的邏輯 } }); //3. 用我們自己的點擊事件代理類,設(shè)置到"持有者"中 field.set(mListenerInfo, proxyOnClickListener); //完成 } catch (Exception e) { e.printStackTrace(); } } // 還真是這樣,自定義代理類 static class ProxyOnClickListener implements View.OnClickListener { View.OnClickListener oriLis; public ProxyOnClickListener(View.OnClickListener oriLis) { this.oriLis = oriLis; } @Override public void onClick(View v) { Log.d("HookSetOnClickListener", "點擊事件被hook到了"); if (oriLis != null) { oriLis.onClick(v); } } } }
具體調(diào)用
v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "別點啦,再點我咬你了...", Toast.LENGTH_SHORT).show(); } }); HookSetOnClickListenerHelper.hook(this, v);//這個hook的作用,是 用我們自己創(chuàng)建的點擊事件代理對象,替換掉之前的點擊事件。
ok,目的達成v.setOnClickListener已經(jīng)被hook.
文末
關(guān)于 Android 中的 Hook 機制,大致有兩個方式:
- 要 root 權(quán)限,直接 Hook 系統(tǒng),可以干掉所有的 App。
- 免 root 權(quán)限,但是只能 Hook 自身,對系統(tǒng)其它 App 無能為力。
到此這篇關(guān)于Android中的HOOK技術(shù)是什么的文章就介紹到這了,更多相關(guān)Android HOOK內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android編程實現(xiàn)WebView添加進度條的方法
這篇文章主要介紹了Android編程實現(xiàn)WebView添加進度條的方法,涉及Android WebView界面及控件功能相關(guān)操作技巧,需要的朋友可以參考下2017-02-02android基礎(chǔ)教程之a(chǎn)ndroid的listview與edittext沖突解決方法
這篇文章主要介紹了android的listview與edittext沖突解決方法,需要的朋友可以參考下2014-02-02Kotlin協(xié)程Job生命周期結(jié)構(gòu)化并發(fā)詳解
這篇文章主要為大家介紹了Kotlin協(xié)程Job生命周期結(jié)構(gòu)化并發(fā)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12解決Error:All flavors must now belong to a named flavor dimens
這篇文章主要介紹了解決Error:All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com,需要的朋友可以參考下2017-11-11Android開發(fā)重寫Animation實現(xiàn)下拉圖片后彈射回去效果示例
這篇文章主要介紹了Android開發(fā)重寫Animation實現(xiàn)下拉圖片后彈射回去效果,結(jié)合實例形式分析了Android自定義類繼承Animation實現(xiàn)圖片彈射效果的相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Android RecyclerView實現(xiàn)點擊條目刪除
這篇文章主要為大家詳細介紹了Android RecyclerView實現(xiàn)點擊條目刪除,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-11-11Android Studio如何為Activity添加自定義注解信息
好久沒用寫文章了,今天給大家分享Android Studio如何為Activity添加自定義注解信息,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2021-06-06Android網(wǎng)絡(luò)編程之獲取網(wǎng)絡(luò)上的Json數(shù)據(jù)實例
這篇文章主要介紹了Android網(wǎng)絡(luò)編程之獲取網(wǎng)絡(luò)上的Json數(shù)據(jù)實例,本文用完整的代碼實例講解了在Android中讀取網(wǎng)絡(luò)中Json數(shù)據(jù)的方法,需要的朋友可以參考下2014-10-10