forked from Trietptm-on-Security/WooYun-2
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Android.Hook框架xposed篇(Http流量监控).html
561 lines (420 loc) · 132 KB
/
Android.Hook框架xposed篇(Http流量监控).html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
<html>
<head>
<title>Android.Hook框架xposed篇(Http流量监控) - 瘦蛟舞</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<h1>原文地址:<a href="http://drops.wooyun.org/tips/7488">http://drops.wooyun.org/tips/7488</a></h1>
<p>
<p>官方教程:<a href="https://github.com/rovo89/XposedBridge/wiki/Development-tutorial">https://github.com/rovo89/XposedBridge/wiki/Development-tutorial</a></p>
<p>官网:<a href="http://repo.xposed.info/module/de.robv.android.xposed.installer">http://repo.xposed.info/module/de.robv.android.xposed.installer</a></p>
<p>apk:<a href="http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk">http://dl-xda.xposed.info/modules/de.robv.android.xposed.installer_v33_36570c.apk</a></p>
<p>源码:<a href="https://github.com/rovo89/XposedInstaller">https://github.com/rovo89/XposedInstaller</a></p>
<h3>模块基本开发流程</h3>
<hr />
<p>1.创建工程android4.0.3(api15,测试发现其他版本也可以),可以不用activity</p>
<p>2.修改AndroidManifest.xml</p>
<!--more-->
<pre><code><?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>
</code></pre>
<p>3.在工程目录下新建一个lib文件夹,将下载好的XposedBridgeApi-54.jar包放入其中.</p>
<p>eclipse 在工程里 选中XposedBridgeApi-54.jar 右键–Build Path–Add to Build Path.</p>
<p>IDEA 鼠标右键点击工程,选择<code>Open Module Settings</code>,在弹出的窗口中打开Dependencies选项卡.把XposedBridgeApi这个jar包后面的Scope属性改成provided.</p>
<p>4.模块实现接口</p>
<pre><code>#!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);
}
}
</code></pre>
<p>5.入口assets/xposed_init配置,声明需要加载到 XposedInstaller 的入口类:</p>
<pre><code>#!java
de.robv.android.xposed.mods.tutorial.Tutorial //完整类名:包名+类名
</code></pre>
<p>6.定位要hook的api</p>
<ul>
<li>反编译目标程序,查看Smali代码</li>
<li>直接在AOSP(android源码)中查看</li>
</ul>
<p>7.XposedBridge to hook it</p>
<ul>
<li>指定要 hook 的包名</li>
<li>判断当前加载的包是否是指定的包</li>
<li>指定要 hook 的方法名</li>
<li>实现beforeHookedMethod方法和afterHookedMethod方法</li>
</ul>
<p>示例如下:</p>
<pre><code>#!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
}
});
}
}
</code></pre>
<p>重写XC_MethodHook的两个方法beforeHookedMethod和afterHookedMethod,这两个方法会在原始的方法的之前和之后执行.您可以使用beforeHookedMethod 方法来打印/篡改方法调用的参数(通过param.args) ,甚至阻止调用原来的方法(发送自己的结果).afterHookedMethod 方法可以用来做基于原始方法的结果的事情.您还可以用它来操纵结果 .当然,你可以添加自己的代码,它将会准确地在原始方法的前或后执行.</p>
<h3>关键API</h3>
<p><strong>IXposedHookLoadPackage</strong></p>
<p>handleLoadPackage : 这个方法用于在加载应用程序的包的时候执行用户的操作</p>
<p>调用示例</p>
<pre><code>#!java
public class XposedInterface implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Kevin-Loaded app: " + lpparam.packageName); }
}
</code></pre>
<p>参数说明|final LoadPackageParam lpparam 这个参数包含了加载的应用程序的一些基本信息。</p>
<p><strong>XposedHelpers</strong></p>
<p>findAndHookMethod ;这是一个辅助方法,可以通过如下方式静态导入:</p>
<pre><code>#!java
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
</code></pre>
<p>使用示例</p>
<pre><code>#!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 }
});
</code></pre>
<p>参数说明</p>
<pre><code>findAndHookMethod(Class<?>clazz, //需要Hook的类名
ClassLoader, //类加载器,可以设置为 null
String methodName, //需要 Hook 的方法名
Object... parameterTypesAndCallback
</code></pre>
<p>该函数的最后一个参数集,包含了:</p>
<p>(1)Hook 的目标方法的参数,譬如:</p>
<pre><code>"com.android.internal.policy.impl.PhoneWindow.DecorView"
</code></pre>
<p>是方法的参数的类。</p>
<p>(2)回调方法:</p>
<pre><code>a.XC_MethodHook
b.XC_MethodReplacement
</code></pre>
<h3>模块开发中的一些细节</h3>
<hr />
<p>1.Dalvik 孵化器 Zygote (Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育/fork出来的)进程对应的程序是/system/bin/app_process. Xposed 框架中真正起作用的是对方法的 hook。</p>
<pre><code>#!java
因为 Xposed 工作原理是在/system/bin 目录下替换文件,在 install 的时候需要 root 权限,但是运行时不需要 root 权限。
</code></pre>
<p>2.log 统一管理,tag 显示包名</p>
<pre><code>#!java
Log.d(MYTAG+lpparam.packageName, "hello" + lpparam.packageName);
</code></pre>
<p>3.植入广播接收器,动态执行指令</p>
<pre><code>#!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);
}
});
</code></pre>
<p>4.context 获取</p>
<pre><code>#!java
fristApplication = (Application) param.thisObject;
</code></pre>
<p>5.注入点选择 application oncreate 程序真正启动函数而是 MainActivity 的 onCreate (该类有可能被重写,所以通过反射得到 oncreate 方法)</p>
<pre><code>#!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());
</code></pre>
<p>6.排除系统 app,排除自身,确定主线程</p>
<pre><code>#!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)){
</code></pre>
<p>7.hook method</p>
<pre><code>#!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)
</code></pre>
<p>8.参数中有 自定义类</p>
<pre><code>#!java
public void myMethod (String a, MyClass b)
通过反射得到自定义类,也可以用[xposedhelpers](https://github.com/rovo89/XposedBridge/wiki/Helpers#class-xposedhelpers)封装好的方法findMethod/findConstructor/callStaticMethod....
</code></pre>
<p>9.注入后反射自定义类</p>
<pre><code>#!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);
}
});
</code></pre>
<p>10.hook 一个类的方法,该类是子类并且没有重写父类的方法,此时应该 hook 父类还是子类.(hook 父类方法后,子类若没重写,一样生效.子类重写方法需要另外 hook) (如果子类重写父类方法时候加上 spuer ,hook 父类依旧有效)</p>
<pre><code>例如 java.net.HttpURLConnection extends URLConnection ,
</code></pre>
<p>方法在父类</p>
<pre><code>#!java
public OutputStream getOutputStream() throws IOException {
throw new UnknownServiceException("protocol doesn't support output");
}
org.apache.http.impl.client.AbstractHttpClient extends CloseableHttpClient ,
</code></pre>
<p>方法在父类(注意,android的继承的 AbstractHttpClient implements org.apache.http.client.HttpClient)</p>
<pre><code>#!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
</code></pre>
<p>11.hook 构造方法</p>
<pre><code>#!java
public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) {
return findAndHookConstructor(findClass(className, classLoader), parameterTypesAndCallback);
}
</code></pre>
<p>12.承接4,application 的onCreate 方法被重写,例如阿里的壳,重写为原生 native 方法.</p>
<p>解1:通过反射到 application 类重写后的 onCreate 方法再对该方法进行hook</p>
<p>解2:hook 构造方法(构造方法被重写,继续解1)</p>
<p>13.native 方法可以 hook,不过是在 java 层调用时hook而不是 hook 动态链接库.</p>
<h3>实战Hook android 中可能的出现 HTTP 请求</h3>
<hr />
<p>首先确定http 请求的 api,大致分为:</p>
<ul>
<li>apache 提供的 HttpClient
<ul>
<li>1) 创建 HttpClient 以及 GetMethod / PostMethod, HttpRequest等对象;</li>
<li>2) 设置连接参数; </li>
<li>3) 执行 HTTP 操作; </li>
<li>4) 处理服务器返回结果.</li>
</ul></li>
<li>java 提供的 HttpURLConnection
<ul>
<li>1) 创建 URL 以及 URLConnection / HttpURLConnection 对象 </li>
<li>2) 设置连接参数</li>
<li>3) 连接到服务器</li>
<li>4) 向服务器写数据</li>
<li>5) 从服务器读取数据</li>
</ul></li>
<li>android 提供的 webview </li>
<li>第三方库:volley/android-async-http/xutils (本质是对前两种的方式的延伸,方法的重写可能对接下来的 hook 产生影响)</li>
</ul>
<p>不太了解 java 的 hook 前可以先看下基础的代码<a href="http://www.codefrom.com/paper/Android%20Http%E8%AF%B7%E6%B1%82%E4%B8%A4%E7%A7%8D%E5%B7%A5%E5%85%B7%E7%B1%BB%28HttpClient.HttpURLConnection%29">HttpClient以及HttpURLConnection的基本使用</a></p>
<p>对 HttpClient的 hook 可以参考 贾志军大牛的<a href="">Zjdroid</a></p>
<pre><code>#!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());
}
}
}
}
});
</code></pre>
<p>对 HttpURLConnection 的 hook Zjdroid 未能提供完美的解决方案,想要取得除了 URL 之外的 data 字段必须对I/O流操作.</p>
<pre><code>#!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());
}
});
</code></pre>
<p>我采取的临时解决方法是对I/O 进行正则匹配,类似 url 的 data 字段就打印出来,代码如下(这段代码只能解决前文 HttpUtils而且会有误报,大家有啥好想法欢迎指点一二)</p>
<pre><code>#!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);
}
});
</code></pre>
<p>因为Android-async-http重写了 HttpGet 导致 Zjdroidhook 失败(未进入 HttpGet 和 HttpPost 的判读),加入一个else 语句就可以解决这个问题</p>
<pre><code>#!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();
}
}
}
</code></pre>
<h3>一些常用工具</h3>
<hr />
<ul>
<li>zjdroid:脱壳/api监控</li>
<li>justTrustMe:忽略证书效验</li>
<li>IntentMonitor:可以监控显/隐意图 intent</li>
<li>Xinstaller:设置应用/设备属性...</li>
<li>XPrivacy:权限管理</li>
</ul> </p>
</body>
</html>