Android.Hook框架xposed篇(Http流量監控)
官方教程:https://github.com/rovo89/XposedBridge/wiki/Development-tutorial
官網:http://repo.xposed.info/module/de.robv.android.xposed.installer
apk:http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk
原始碼:https://github.com/rovo89/XposedInstaller
模組基本開發流程
1.建立工程android4.0.3(api15,測試發現其他版本也可以),可以不用activity
2.修改AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.robv.android.xposed.mods.tutorial"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="15" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<meta-data
android:name="xposedmodule"
android:value="true" />
<meta-data
android:name="xposeddescription"
android:value="Easy example" />
<meta-data
android:name="xposedminversion"
android:value="54" />
</application>
</manifest>
3.在工程目錄下新建一個lib資料夾,將下載好的XposedBridgeApi-54.jar包放入其中.
eclipse 在工程裡 選中XposedBridgeApi-54.jar 右鍵–Build Path–Add to Build Path.
IDEA 滑鼠右鍵點選工程,選擇Open Module Settings
,在彈出的視窗中開啟Dependencies選項卡.把XposedBridgeApi這個jar包後面的Scope屬性改成provided.
4.模組實現介面
#!java
package de.robv.android.xposed.mods.tutorial;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
public class Tutorial implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Loaded app: " + lpparam.packageName);
}
}
5.入口assets/xposed_init配置,宣告需要載入到 XposedInstaller 的入口類:
#!java
de.robv.android.xposed.mods.tutorial.Tutorial //完整類名:包名+類名
6.定位要hook的api
- 反編譯目標程式,檢視Smali程式碼
- 直接在AOSP(android原始碼)中檢視
7.XposedBridge to hook it
- 指定要 hook 的包名
- 判斷當前載入的包是否是指定的包
- 指定要 hook 的方法名
- 實現beforeHookedMethod方法和afterHookedMethod方法
示例如下:
#!java
package de.robv.android.xposed.mods.tutorial;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC\_MethodHook; import de.robv.android.xposed.callbacks.XC\_LoadPackage.LoadPackageParam;
public class Tutorial implements IXposedHookLoadPackage { public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable { if (!lpparam.packageName.equals("com.android.systemui")) return;
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
}
});
}
}
重寫XC_MethodHook的兩個方法beforeHookedMethod和afterHookedMethod,這兩個方法會在原始的方法的之前和之後執行.您可以使用beforeHookedMethod 方法來列印/篡改方法呼叫的引數(透過param.args) ,甚至阻止呼叫原來的方法(傳送自己的結果).afterHookedMethod 方法可以用來做基於原始方法的結果的事情.您還可以用它來操縱結果 .當然,你可以新增自己的程式碼,它將會準確地在原始方法的前或後執行.
關鍵API
IXposedHookLoadPackage
handleLoadPackage : 這個方法用於在載入應用程式的包的時候執行使用者的操作
呼叫示例
#!java
public class XposedInterface implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Kevin-Loaded app: " + lpparam.packageName); }
}
引數說明|final LoadPackageParam lpparam 這個引數包含了載入的應用程式的一些基本資訊。
XposedHelpers
findAndHookMethod ;這是一個輔助方法,可以透過如下方式靜態匯入:
#!java
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
使用示例
#!java
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "handleUpdateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method }
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method }
});
引數說明
findAndHookMethod(Class<?>clazz, //需要Hook的類名
ClassLoader, //類載入器,可以設定為 null
String methodName, //需要 Hook 的方法名
Object... parameterTypesAndCallback
該函式的最後一個引數集,包含了:
(1)Hook 的目標方法的引數,譬如:
"com.android.internal.policy.impl.PhoneWindow.DecorView"
是方法的引數的類。
(2)回撥方法:
a.XC_MethodHook
b.XC_MethodReplacement
模組開發中的一些細節
1.Dalvik 孵化器 Zygote (Android系統中,所有的應用程式程式以及系統服務程式SystemServer都是由Zygote程式孕育/fork出來的)程式對應的程式是/system/bin/app_process. Xposed 框架中真正起作用的是對方法的 hook。
#!java
因為 Xposed 工作原理是在/system/bin 目錄下替換檔案,在 install 的時候需要 root 許可權,但是執行時不需要 root 許可權。
2.log 統一管理,tag 顯示包名
#!java
Log.d(MYTAG+lpparam.packageName, "hello" + lpparam.packageName);
3.植入廣播接收器,動態執行指令
#!java
findAndHookMethod("android.app.Application", lpparam.classLoader, "onCreate", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Context context = (Context) param.thisObject;
IntentFilter filter = new IntentFilter(myCast.myAction);
filter.addAction(myCast.myCmd);
context.registerReceiver(new myCast(), filter);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
4.context 獲取
#!java
fristApplication = (Application) param.thisObject;
5.注入點選擇 application oncreate 程式真正啟動函式而是 MainActivity 的 onCreate (該類有可能被重寫,所以透過反射得到 oncreate 方法)
#!java
String appClassName = this.getAppInfo().className;
if (appClassName == null) {
Method hookOncreateMethod = null;
try {
hookOncreateMethod = Application.class.getDeclaredMethod("onCreate", new Class[] {});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
hookhelper.hookMethod(hookOncreateMethod, new ApplicationOnCreateHook());
6.排除系統 app,排除自身,確定主執行緒
#!java
if(lpparam.appInfo == null ||
(lpparam.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) !=0){
return;
}else if(lpparam.isFirstApplication && !ZJDROID_PACKAGENAME.equals(lpparam.packageName)){
7.hook method
#!java
Only methods and constructors can be hooked,Cannot hook interfaces,Cannot hook abstract methods
只能 hook 方法和構造方法,不能 hook 介面和抽象方法
抽象類中的非抽象方法是可以 hook的, 介面中的方法不能 hook (介面中的method預設是public abstract 抽象的.field 必須是public static final)
8.引數中有 自定義類
#!java
public void myMethod (String a, MyClass b)
透過反射得到自定義類,也可以用[xposedhelpers](https://github.com/rovo89/XposedBridge/wiki/Helpers#class-xposedhelpers)封裝好的方法findMethod/findConstructor/callStaticMethod....
9.注入後反射自定義類
#!java
Class<?> hookMessageListenerClass = null;
hookMessageListenerClass = lpparam.classLoader.loadClass("org.jivesoftware.smack.MessageListener");
findAndHookMethod("org.jivesoftware.smack.ChatManager", lpparam.classLoader, "createChat", String.class , hookMessageListenerClass ,new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String sendTo = (String) param.args[0];
Log.i(tag , "sendTo : + " + sendTo );
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
10.hook 一個類的方法,該類是子類並且沒有重寫父類的方法,此時應該 hook 父類還是子類.(hook 父類方法後,子類若沒重寫,一樣生效.子類重寫方法需要另外 hook) (如果子類重寫父類方法時候加上 spuer ,hook 父類依舊有效)
例如 java.net.HttpURLConnection extends URLConnection ,
方法在父類
#!java
public OutputStream getOutputStream() throws IOException {
throw new UnknownServiceException("protocol doesn't support output");
}
org.apache.http.impl.client.AbstractHttpClient extends CloseableHttpClient ,
方法在父類(注意,android的繼承的 AbstractHttpClient implements org.apache.http.client.HttpClient)
#!java
public CloseableHttpResponse execute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
return doExecute(target, request, context);
}
android.async.http複寫HttpGet導致zjdroid hook org.apache.http.impl.client.AbstractHttpClient execute 無法獲取到請求 url和method
11.hook 構造方法
#!java
public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {
return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback);
}
12.承接4,application 的onCreate 方法被重寫,例如阿里的殼,重寫為原生 native 方法.
解1:透過反射到 application 類重寫後的 onCreate 方法再對該方法進行hook
解2:hook 構造方法(構造方法被重寫,繼續解1)
13.native 方法可以 hook,不過是在 java 層呼叫時hook而不是 hook 動態連結庫.
實戰Hook android 中可能的出現 HTTP 請求
首先確定http 請求的 api,大致分為:
- apache 提供的 HttpClient
- 1) 建立 HttpClient 以及 GetMethod / PostMethod, HttpRequest等物件;
- 2) 設定連線引數;
- 3) 執行 HTTP 操作;
- 4) 處理伺服器返回結果.
- java 提供的 HttpURLConnection
- 1) 建立 URL 以及 URLConnection / HttpURLConnection 物件
- 2) 設定連線引數
- 3) 連線到伺服器
- 4) 向伺服器寫資料
- 5) 從伺服器讀取資料
- android 提供的 webview
- 第三方庫:volley/android-async-http/xutils (本質是對前兩種的方式的延伸,方法的重寫可能對接下來的 hook 產生影響)
不太瞭解 java 的 hook 前可以先看下基礎的程式碼HttpClient以及HttpURLConnection的基本使用
對 HttpClient的 hook 可以參考 賈志軍大牛的Zjdroid
#!java
Method executeRequest = RefInvoke.findMethodExact("org.apache.http.impl.client.AbstractHttpClient", ClassLoader.getSystemClassLoader(),
"execute", HttpHost.class, HttpRequest.class, HttpContext.class);
hookhelper.hookMethod(executeRequest, new AbstractBahaviorHookCallBack() {
@Override
public void descParam(HookParam param) {
// TODO Auto-generated method stub
Logger.log_behavior("Apache Connect to URL ->");
HttpHost host = (HttpHost) param.args[0];
HttpRequest request = (HttpRequest) param.args[1];
if (request instanceof org.apache.http.client.methods.HttpGet) {
org.apache.http.client.methods.HttpGet httpGet = (org.apache.http.client.methods.HttpGet) request;
Logger.log_behavior("HTTP Method : " + httpGet.getMethod());
Logger.log_behavior("HTTP GET URL : " + httpGet.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
}
}
} else if (request instanceof HttpPost) {
HttpPost httpPost = (HttpPost) request;
Logger.log_behavior("HTTP Method : " + httpPost.getMethod());
Logger.log_behavior("HTTP URL : " + httpPost.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
}
}
HttpEntity entity = httpPost.getEntity();
String contentType = null;
if (entity.getContentType() != null) {
contentType = entity.getContentType().getValue();
if (URLEncodedUtils.CONTENT_TYPE.equals(contentType)) {
try {
byte[] data = new byte[(int) entity.getContentLength()];
entity.getContent().read(data);
String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (contentType.startsWith(HTTP.DEFAULT_CONTENT_TYPE)) {
try {
byte[] data = new byte[(int) entity.getContentLength()];
entity.getContent().read(data);
String content = new String(data, contentType.substring(contentType.lastIndexOf("=") + 1));
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}else{
byte[] data = new byte[(int) entity.getContentLength()];
try {
entity.getContent().read(data);
String content = new String(data, HTTP.DEFAULT_CONTENT_CHARSET);
Logger.log_behavior("HTTP POST Content : " + content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
@Override
public void afterHookedMethod(HookParam param) {
// TODO Auto-generated method stub
super.afterHookedMethod(param);
HttpResponse resp = (HttpResponse) param.getResult();
if (resp != null) {
Logger.log_behavior("Status Code = " + resp.getStatusLine().getStatusCode());
Header[] headers = resp.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getValue());
}
}
}
}
});
對 HttpURLConnection 的 hook Zjdroid 未能提供完美的解決方案,想要取得除了 URL 之外的 data 欄位必須對I/O流操作.
#!java
Method openConnectionMethod = RefInvoke.findMethodExact("java.net.URL", ClassLoader.getSystemClassLoader(), "openConnection");
hookhelper.hookMethod(openConnectionMethod, new AbstractBahaviorHookCallBack() {
@Override
public void descParam(HookParam param) {
// TODO Auto-generated method stub
URL url = (URL) param.thisObject;
Logger.log_behavior("Connect to URL ->");
Logger.log_behavior("The URL = " + url.toString());
}
});
我採取的臨時解決方法是對I/O 進行正則匹配,類似 url 的 data 欄位就列印出來,程式碼如下(這段程式碼只能解決前文 HttpUtils而且會有誤報,大家有啥好想法歡迎指點一二)
#!java
findAndHookMethod("java.io.PrintWriter", lpparam.classLoader, "print",String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String print = (String) param.args[0];
Pattern pattern = Pattern.compile("(\\w+=.*)");
Matcher matcher = pattern.matcher(print);
if (matcher.matches())
Log.i(tag+lpparam.packageName,"data : " + print);
//Log.d(tag,"A :" + print);
}
});
因為Android-async-http重寫了 HttpGet 導致 Zjdroidhook 失敗(未進入 HttpGet 和 HttpPost 的判讀),加入一個else 語句就可以解決這個問題
#!java
else {
HttpEntityEnclosingRequestBase httpGet = (HttpEntityEnclosingRequestBase) request;
HttpEntity entity = httpGet.getEntity();
Logger.log_behavior("HttpRequestBase URL : " + httpGet.getURI().toString());
Header[] headers = request.getAllHeaders();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
Logger.log_behavior(headers[i].getName() + ":" + headers[i].getName());
}
}
if(entity!= null){
try {
String content = EntityUtils
.toString(entity);
Logger.log_behavior("HTTP entity Content : "
+ content);
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一些常用工具
- zjdroid:脫殼/api監控
- justTrustMe:忽略證照效驗
- IntentMonitor:可以監控顯/隱意圖 intent
- Xinstaller:設定應用/裝置屬性...
- XPrivacy:許可權管理
相關文章
- Android.Hook框架Cydia篇(脫殼機制作)2020-08-19AndroidHook框架
- iOS 流量監控分析2018-06-07iOS
- Linux 流量監控工具 iftop2020-04-16Linux
- 蘋果iPhone XR/XS Max怎麼監控流量?iPhone流量監控設定教程2018-12-06蘋果iPhone
- 聊聊前端監控——錯誤監控篇2020-09-02前端
- Linux伺服器---流量監控ntop2019-01-17Linux伺服器
- Linux伺服器---流量監控webalizer2019-01-10Linux伺服器Web
- Linux伺服器---流量監控MRTG2019-01-07Linux伺服器
- Linux伺服器---流量監控bandwidthd2019-01-04Linux伺服器
- 基於OkHttp的Http監控2019-09-01HTTP
- Prometheus監控神器-Rules篇2020-08-07Prometheus
- vnStatSVG: 流量監控軟體 vnStat 最佳 Web 前端2022-11-28SVGWeb前端
- 網路卡流量監控指令碼,python實現2019-07-04指令碼Python
- 前端監控基礎篇 — Docker + Sentry 搭建前端監控系統2020-02-15前端Docker
- QPM 效能監控元件<總篇>2019-01-03元件
- Prometheus監控神器-Alertmanager篇(1)2020-08-06Prometheus
- Prometheus監控神器-Alertmanager篇(4)2020-08-25Prometheus
- 騰訊效能監控框架Matrix原始碼分析之第一篇2019-03-04框架原始碼
- Linux下監控流量常用的三大工具!2023-11-02Linux
- 在Linux中,如何實時監控網路流量?2024-03-15Linux
- Linux流量監控工具 - iftop (最全面的iftop教程)2019-03-13Linux
- iftop--實時網路介面流量監控工具2024-07-17
- 監控雲流量的七種QoS最佳實踐2022-01-18
- 前端監控進階篇 — Sentry 監控 Next.js 專案實踐2020-02-15前端JS
- centos7使用ntopng進行流量監控和分析2020-11-30CentOS
- Flutter異常監控 - 伍 | 關於異常監控框架設計的思考2023-01-11Flutter框架
- Android Hook框架Xposed原理與原始碼分析2019-01-16AndroidHook框架原始碼
- 03 . Prometheus監控容器和HTTP探針應用2020-06-09PrometheusHTTP
- Angular 如何通過 HTTP Interceptor 實現 HTTP 請求的超時監控2022-05-19AngularHTTP
- Android 效能監控系列一(原理篇)2018-06-05Android
- 網路流量監控軟體:NetWorker Pro for mac 中文版2023-12-26Mac
- 北京智和信通網路流量監控分析平臺2022-09-29
- IT監控(進階篇):運維監控系統手把手部署教學2024-11-05運維
- Flutter異常監控 - 貳 | 框架Catcher原理分析2022-12-31Flutter框架
- Spring Boot 揭祕與實戰(九) 應用監控篇 – 自定義監控端點2019-03-04Spring Boot
- 北京智和信通企業級網路流量監控方案2022-09-30
- 踩坑指南:入門OpenTenBase之監控篇2024-04-11
- Prometheus監控神器-服務發現篇(二)2020-09-07Prometheus