專案地址:https://github.com/miqt/android-plugin/blob/master/plugin/hook-method-plugin/readme.md
這是一個 android 方法位元組碼插樁hook的插件,在方法進入和方法退出時,將當前運行的所有引數回呼到固定的介面中,利用這一點,可以進行方法切片式開發,通過配置Hook點,達到Hook監控的目的,
利用這個插件,可以實作:
- Android 全埋點,頁面瀏覽,點擊,等無痕埋點,按需配置Hook點即可,配置方法見下文
- Android 方法耗時,性能統計
- 各種攔截器,例如,攔截某方法,在某方法執行前,先判斷權限或進行準備作業
- 在參考的第三方jar包中追加代碼,增加創建執行緒,檔案IO相關的監控等
效果展示
例如日常開發中,我們免不了可能要進行一些點擊事件埋點的任務,如果我們利用這個插件,可以直接配置一次統計所有的點擊事件埋點,下面來看一下具體做法:
- 先定義Hook點,這個hook點的目標是監控所有的點擊事件,在 app module 中的 build.gradle 添加以下代碼,詳細欄位含義會在下文說明,
apply plugin: 'com.miqt.plugin.hookmethod'
hook_method {
buildLog = true
injectJar = false //是否hook參考的第三方jar包
enable = true
hookTargets {
hook_onclick { // <-- 定義Hook點
interfaces = "android/view/View\$OnClickListener"//<-- hook條件1,繼承了這個介面
methodName = "onClick" //<-- hook條件2,方法名
descriptor = "(Landroid/view/View;)V" //<-- hook條件3,方法引數和回傳值型別
hookTiming = "Enter" //<-- hook條件4,指定是在方法進入時進行Hook
}
}
}
hook_method 是對插件的整體配置,hookTargets 是Hook點位的配置,編譯時,會按照定義的篩選條件來進行篩選,四個條件同時滿足,才會進行插樁,
注意,這里由于是位元組碼操作,因此這里的descriptor定義都是欄位描述符,和位元組碼描述符,不知道的可以百度了解下,非常簡單,如果你不想了解也沒事,我后面有介紹有一種方法可以直接查看某個方法的位元組碼資訊,利用@HookInfo注解
進行過以上配置后,我們隨便寫一個按鈕,然后添加個點擊事件,
public class MainActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "onClick", Toast.LENGTH_SHORT).show();
}
});
}
}
點擊同步按鈕,編譯一下專案,這時候,插件就會自動生成該點擊事件的hook轉發handler處理類:
類似這樣的:HookHandler
這時候我們打開Apk反編譯查看,就可以看到插樁代碼了:
public class MainActivity extends Activity {
/* access modifiers changed from: protected */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
/* class com.asm.code.MainActivity.AnonymousClass1 */
public void onClick(View view) {
HookHandler.hook_onclickEnter(this, "com/asm/code/MainActivity$1", "onClick", "[android.view.View]", "void", new Object[]{view});//<--插樁的代碼
Toast.makeText(MainActivity.this, "onClick", 0).show();
}
});
}
}
到了這里,我們只需要在hook_onclickEnter這個方法中撰寫全域點擊事件監控處理邏輯就可以了,所有的這一切都是插件自動完成的哦,我們只需要配置Hook規則就可以了,是不是非常簡單,
示例配置傳送門:https://github.com/miqt/android-plugin/blob/master/app/build.gradle
以此類推,該插件還可以通過自定義Hook規則,Hook專案中的任意方法的代碼,達到切面編程的目的,
使用配置方法
-
添加maven倉庫
maven { url 'https://raw.githubusercontent.com/miqt/maven/master' } maven { url 'https://gitee.com/miqt/maven/raw/master' }還拉取不到庫?
maven { url 'https://raw.fastgit.org/miqt/maven/master' }或者,去我的倉庫git地址下載下來,本地依賴,
最開始我使用
jcenter(),結果后來這個庫官方棄用了,不過現在 0.3.5版本 仍然可以從這個庫上拉取下來,之后版本我都使用gitee和github作為倉庫存盤地址了, -
添加插件依賴
專案根目錄:build.gradle 添加以下代碼
dependencies { classpath 'com.miqt:hook-method-plugin:0.4.0' }對應 module 中啟用插件,可以是
application也可以是libraryapply plugin: 'com.miqt.plugin.hookmethod' hook_method { buildLog = true injectJar = false //是否hook參考的第三方jar包 enable = true hookTargets { hook_onclick { // <-- 自定義Hook點 interfaces = "android/view/View\$OnClickListener"//<-- hook條件1,繼承了這個介面 methodName = "onClick" //<-- hook條件2,方法名 descriptor = "(Landroid/view/View;)V" //<-- hook條件3,方法引數和回傳值型別 hookTiming = "Enter" //<-- hook條件4,指定是在方法進入時進行Hook } } } -
對應 module 中添加類別庫依賴
dependencies { implementation 'com.miqt:hook-method-lib:0.4.0' }專案 clean 然后運行,logcat 過濾
MethodHookHandler就可以看到列印結果啦!
插件配置引數說明
hook_method {} 中的引數
這個里面是定義插件相關的配置,
| 引數 | 是否必填 | 型別 | 含義 | 默認值 |
|---|---|---|---|---|
| enable | 否 | bool | 是否啟用 | true |
| runVariant | 否 | 字串 | 插件執行環境,有的時候我們只想在debug環境進行插樁,release環境不需要,這時候可以設定為:DEBUG,表示僅DEBUG執行時,運行插件,這個引數有四個列舉值: DEBUG: 僅Debug運行時插樁 RELEASE: 僅RELEASE運行時插樁 ALWAYS: 所有運行情況 (默認) NEVER: 從不插樁,等同于enable = false | ALWAYS |
| injectJar | 否 | bool | 是否對參考的所有第三方依賴jar,aar進行按照規則插樁hook | false |
| buildLog | 否 | bool | 是否在.\app\build\plugin 檔案夾輸出插件編譯日志,編譯有錯誤時,請打開 | true |
| handler | 否 | 字串 | 插樁Hook轉發的Handler接收類,也就是插樁處理類,例如傳xxx.xxx.A,則編譯后會自動生成A.java這個類,并自動生成方法,類似這種: | com.miqt.hookplugin.HookHandler |
| handlerDir | 否 | 字串 | 插樁Hook轉發的Handler接收類在那個路徑生成,默認是.\src\main\java 一般不需要自定義 | .\src\main\java |
hookTargets {} 中的引數
結構:
hook_method {
hookTargets {
name1 {
條件1 = value1
條件2 = value2
條件3 = value3
}
name2 {
條件1 = value1
條件2 = value2
條件3 = value3
}
}
}
這里是定義hood點位篩選條件的,邏輯是,必須滿足引數 1,2,3設定的條件,才會插樁
注意:如果設定:hook_All{ } 這種無條件引數的,意思就是所有的方法都插樁,
| 引數 | 是否必填 | 型別 | 含義 | 默認值 |
|---|---|---|---|---|
| name | 是 | 字串 | 上文的name1,name2位置,意思是hook點命名,這個命名最侄訓是這個點位的Hook接收方法名,可以自己隨便定義 | null |
| access | 否 | int | 限定條件之:方法訪問修飾符,比如private,public,static,final,使用Opcodes.ACC_PRIVATE|Opcodes.ACC_STATIC這種形式來定義 | -1 |
| interfaces | 否 | 字串 | 限定條件之:方法所在類必須實作了什么介面,例如OnClickListener介面就這么寫:interfaces = "android/view/View\$OnClickListener"這里不用點,用/表示這是位元組碼識別符號,給方法或類加@HookInfo注解,插件會自動在編譯控制臺和編譯Log日志檔案中展示出對應的位元組碼識別符號, | null |
| superName | 否 | 字串 | 限定條件之:方法所在類必須是superName的子類 | null |
| className | 否 | 字串 | 限定條件之:方法所在類名稱,這里是全稱,例如com/xxx/A,支持正則限定 | null |
| methodName | 否 | 字串 | 限定條件之:方法名,支持正則限定 | null |
| descriptor | 否 | 字串 | 限定條件之:方法欄位描述符,不知道咋寫的用@HookInfo標記看下輸出就知道了, | null |
| annotation | 否 | 字串 | 限定條件之:方法或所在類必須包含這個注解 | null |
| signature | 否 | 字串 | 限定條件之:方法或所在類必須包含這個泛型 | null |
| exceptions | 否 | 字串 | 限定條件之:方法必須符合拋出某例外的形式 | null |
| hookTiming | 否 | 字串 | 限定條件之:在方法進入時插樁還是退出時Enter:方法進入時Return:方法退出時Enter|Return:進入和退出時(默認) | Enter|Return |
內置注解
@HookInfo: 輸出對應位元組碼限制條件資訊
@HookMethod: 標記hook方法,不需要寫hookTargets,可以理解為內置的
@HookMethodInherited: 標記hook方法,有類繼承性,不需要寫hookTargets,可以理解為內置的
@IgnoreMethodHook: 標記忽略方法,優先級最高
讀都讀到這里了,幫我點個star吧,你的肯定是我持續維護的動力!
如果有使用問題歡迎提交Issues ,或者加我微信一起交流,微信號在我的主頁,
致謝
這個插件是借鑒了很多大佬的代碼,并結合自己的想法進行了一些調整,在此感謝他們付出的努力,
https://github.com/novoda/bintray-release
https://github.com/JeasonWong/CostTime
https://github.com/MegatronKing/StringFog
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/293207.html
標籤:其他
上一篇:react-native錯誤Make sure you have the Android development environment set up處理
