AndroidMonitor抓包原理及使用方法
Android抓包現狀
目前常見的抓包工具有Charles、Fiddler、Wireshark等,這些或多或少都需要一些配置,略顯麻煩,只適合開發及測試人員玩,如果產品也想看數據怎么辦呢,別急,本文的主角登場了,你可以在項目中集成AndroidMonitor,只需兩步簡單配置即可實現抓包數據可視化功能,隨時隨地,人人都可以方便快捷的查看請求數據了。
https://github.com/lygttpod/AndroidMonitor
效果展示
俗話說無圖無真相




如何使用
抓包工具有兩個依賴需要添加:monito和monitor-plugin
源碼地址:
https://github.com/lygttpod/AndroidMonitor
1.monitor接入
添加依賴
debugImplementation 'io.github.lygttpod:monitor:0.0.4'
-備注:使用debugImplementation是為了只在測試環境中引入。
2.monitor-plugin接入
? 根目錄build.gradle下添加如下依賴。
buildscript {
dependencies {
......
//monitor-plugin需要
classpath 'io.github.lygttpod:monitor-plugin:0.0.1'
}
}
? 添加插件。
在APP的build.gradle中添加: //插件內部會自動判斷debug模式下hook到okhttp apply plugin: 'monitor-plugin'
原則上完成以上兩步你的APP就成功集成了抓包工具,很簡單有沒有,如需定制化服務請看下邊的個性化配置。
3.個性化配置
1、修改桌面抓包工具入口名字:在主項目string.xml中添加 monitor_app_name即可,例如: <string name="monitor_app_name">XXX-抓包string> 2、定制抓包入口logo圖標: 添加 monitor_logo.png 即可 3、單個項目使用的話,添加依賴后可直接使用,無需初始化,庫里會通過ContentProvider方式自動初始化 默認端口8080(端口號要唯一) 4、多個項目都集成抓包工具,需要對不同項目設置不同的端口和數據庫名字,用來做區分 在主項目assets目錄下新建 monitor.properties 文件,文件內如如下:對需要變更的參數修改即可 # 抓包助手參數配置 # Default port = 8080 # Default dbName = monitor_db # ContentTypes白名單,默認application/json,application/xml,text/html,text/plain,text/xml # Default whiteContentTypes = application/json,application/xml,text/html,text/plain,text/xml # Host白名單,默認全部是白名單 # Default whiteHosts = # Host黑名單,默認沒有黑名單 # Default blackHosts = # 如何多個項目都集成抓包工具,可以設置不同的端口進行訪問 monitor.port=8080 monitor.dbName=app_name_monitor_db
4.proguard(默認已經添加混淆,如遇到問題可以添加如下混淆代碼)
# monitor
-keep class com.lygttpod.monitor.** { *; }
5.溫馨提示
雖然monitor-plugin只會在debug環境hook代碼, 但是release版編譯的時候還是會走一遍Transform操作(空操作), 為了保險起見建議生產包禁掉此插件。 在jenkins打包機器的《生產環境》的local.properties中添加monitor.enablePlugin=false,全面禁用monitor插件
6.如何使用
? 集成之后編譯運行項目即可在手機上自動生成一個抓包入口的圖標,點擊即可打開可視化頁面查看網絡請求數據,這樣就可以隨時隨地的查看我們的請求數據了。
? 雖然可以很方便的查看請求數據了但是手機屏幕太小,看起來不方便怎么辦吶,那就去尋找在PC上展示的方法,首先想到的是能不能直接在瀏覽器里邊直接看吶,這樣不用安裝任何程序在瀏覽輸入一個地址就可以直接查看數據。
? PC和手機在同一局域網的前提下:直接在任意瀏覽器輸入 手機ip地址+抓包工具設置的端口號即可(地址可以在抓包app首頁TitleBar上可以看到)。
原理介紹
1.攔截APP的OKHTTP請求(添加攔截器處理抓包請求,使用ASM字節碼插裝技術實現)
? 寫一個Interceptor攔截器,獲取請求及響應的數據,轉化為需要的數據結構。
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
if (!MonitorHelper.isOpenMonitor) {
return chain.proceed(request)
}
val monitorData = MonitorData()
monitorData.method = request.method
val url = request.url.toString()
monitorData.url = url
if (url.isNotBlank()) {
val uri = Uri.parse(url)
monitorData.host = uri.host
monitorData.path = uri.path + if (uri.query != null) "?" + uri.query else ""
monitorData.scheme = uri.scheme
}
......以上為部分代碼展示
}
? 有了攔截器就可以通過字節碼插樁技術在編譯期自動為OKHTTP添加攔截器了,避免了使用者自己添加攔截器的操作。
mv?.let {
it.visitVarInsn(ALOAD, 0)
it.visitFieldInsn(GETFIELD, "okhttp3/OkHttpClient\$Builder", "interceptors", "Ljava/util/List;")
it.visitFieldInsn(GETSTATIC, "com/lygttpod/monitor/MonitorHelper", "INSTANCE", "Lcom/lygttpod/monitor/MonitorHelper;")
it.visitMethodInsn(INVOKEVIRTUAL, "com/lygttpod/monitor/MonitorHelper", "getHookInterceptors", "()Ljava/util/List;", false)
it.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "addAll", "(Ljava/util/Collection;)Z", true)
it.visitInsn(POP)
}
2. 數據保存到本地數據庫(room)
? 數據庫選擇官方推薦Room進行數據操作。
@Dao
interface MonitorDao {
@Query("SELECT * FROM monitor WHERE id > :lastId ORDER BY id DESC")
fun queryByLastIdForAndroid(lastId: Long): LiveData>
@Query("SELECT * FROM monitor ORDER BY id DESC LIMIT :limit OFFSET :offset")
fun queryByOffsetForAndroid(limit: Int, offset: Int): LiveData>
@Query("SELECT * FROM monitor")
fun queryAllForAndroid(): LiveData>
@Query("SELECT * FROM monitor WHERE id > :lastId ORDER BY id DESC")
fun queryByLastId(lastId: Long): MutableList
@Query("SELECT * FROM monitor ORDER BY id DESC LIMIT :limit OFFSET :offset")
fun queryByOffset(limit: Int, offset: Int): MutableList
@Query("SELECT * FROM monitor")
fun queryAll(): MutableList
@Insert
fun insert(data: MonitorData)
@Update
fun update(data: MonitorData)
@Query("DELETE FROM monitor")
fun deleteAll()
}
3.APP本地開啟一個socket服務AndroidLocalService。
https://github.com/lygttpod/android-local-service
? AndroidLocalService基于NanoHttpd實現的一個本地微服務庫,底層是通過socket實現,同時使用注解加上javapoet框架自動生成模版代碼,這樣就可以很方便的創建服務了,下邊是創建服務并啟動服務示例代碼。
//@Service標記這是一個服務,端口號是服務器的端口號,注意端口號唯一
@Service(port = 9527)
abstract class AndroidService {
//@Page標注頁面類,打開指定h5頁面
@Page("index")
fun getIndexFileName() = "test_page.html"
//@Get注解在方法上邊
@Get("query")
fun query(aaa: Boolean, bbb: Double, ccc: Float, ddd: String, eee: Int,): List {
return listOf("$aaa", "$bbb", "$ccc", "$ddd", "$eee")
}
@Get("saveData")
fun saveData(content: String) {
LiveDataHelper.saveDataLiveData.postValue(content + UUID.randomUUID());
}
@Get("queryAppInfo")
fun getAppInfo(): HashMap {
return hashMapOf(
"applicationId" to BuildConfig.APPLICATION_ID,
"versionName" to BuildConfig.VERSION_NAME,
"versionCode" to BuildConfig.VERSION_CODE,
"uuid" to UUID.randomUUID(),
)
}
}
//初始化
ALSHelper.init(this)
//啟動服務
ALSHelper.startService(ServiceConfig(AndroidService::class.java))
然后就可以通過 ip地址 + 端口號 訪問了,例如:http://172.18.41.157:9527/index
使用AndroidLocalService之后創建和啟動服務就是這么簡單有沒有,具體用法及細節請查看其說明文檔。
4.與本地socket服務通信
? 剩下的就是與服務器的通信了,無論使用前端使用aJax還是客戶端使用okhttp都可以正常請求數據了。
5.UI展示數據(手機端和PC端)
? 有了接口和數據具體展示就看可以隨意定制了,如果你不喜歡默認的UI風格,那就拉源碼自己定制UI哦。