最佳实践
经过一段时间的研究,又有了新的看法。
准备以下两种工具:
- mitmproxy
- Android Emulator(MuMu, BlueStack …)
mitmproxy
mitmproxy
最好使用pipx安装。因为mitmproxy
运行在独立的venv
中,使用pipx
方便为mitmproxy
安装额外的 python 包。
1
2
| brew install pipx
pipx install mitmproxy
|
最好按照提示说明,使用pipx ensurepath
修改环境变量。
这样我们就可以为mitmproxy
安装额外的 python 包了。
1
| pipx inject mitmproxy pycryptodome # 安装 pycryptodome 包,用于AES解码
|
将浏览器代理设置到8080
端口,打开(mitm.it)[mitm.it]下载对应平台的证书。
Android Emulator
抓包之前有两个步骤:
- 设置手动代理
- 安装并信任自签名 https 证书
设置 WiFi 代理为主机8080
端口:

以下是简化的脚本,先看看adb devices
信息,是否已经连接。如果没有成功连接需要adb kill-server
,再重试几次。或者参照后文。
直接运行以下脚本./push.sh YOUR_CRT.crt
:
1
2
3
4
5
6
7
8
9
10
11
12
13
| PEM_FILE_NAME=$1
echo "$PEM_FILE_NAME"
hash=$(openssl x509 -inform PEM -subject_hash_old -in $PEM_FILE_NAME | head -1)
OUT_FILE_NAME="$hash.0"
cp $PEM_FILE_NAME $OUT_FILE_NAME
openssl x509 -inform PEM -text -in $PEM_FILE_NAME -out /dev/null >> $OUT_FILE_NAME
echo "Saved to $OUT_FILE_NAME"
adb shell mount -o rw,remount,rw /system
adb push $OUT_FILE_NAME /system/etc/security/cacerts/
adb shell mount -o ro,remount,ro /system
adb reboot
|
如果模拟器正常重启的话应该就可以了。
抓包
运行mitmweb
,通过 web 界面观察包的发送和接收。

mitmproxy
是一个非常强大的工具,其用法如恒河沙数,无不天花乱坠。官方文档提供的用例很值得仔细参阅,这里只介绍一个简单的例子。
ContentView
是显示request
或response
负载的界面。有时需要查看的payload
不是明文的json
或plain text
,默认情况下会以raw
格式显示。

这样会很麻烦。
所以可以考虑通过插件增加一个ContentView
的选项,让请求以我想要的方式显示。模仿addons-examples/#contentview,以挂载脚本的方式启动mitmweb -S decode.py
:
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
| from mitmproxy import contentviews, flow
from mitmproxy import http
from mitmproxy.contentviews.json import format_json
from Crypto.Cipher import AES
import base64
import hashlib
import json
import random
class ViewAESDecode(contentviews.View):
name = "AES decode"
def __call__(
self,
data: bytes,
*,
content_type: Optional[str] = None,
flow: Optional[flow.Flow] = None,
http_message: Optional[http.Message] = None,
**unknown_metadata,
):
jsdata = decrypt(data)
jsdata = json.loads(jsdata)
return "AES decode", format_json(jsdata)
def render_priority(
self,
data: bytes,
*,
content_type: Optional[str] = None,
flow: Optional[flow.Flow] = None,
http_message: Optional[http.Message] = None,
**unknown_metadata,
):
if 'what I want' in flow.request.host:
return 10 # 出现的权重,为10的话会在最前面
else:
return 0
def decrypt(data):
key = '1234567890123456'
iv = '1234567890123456'
mode = AES.MODE_CBC
cryptor = AES.new(key, mode, iv)
data = base64.b64decode(data)
decrypted = cryptor.decrypt(data)
return decrypted.decode('utf-8')
def load(l):
contentviews.add(view)
def done():
contentviews.remove(view)
|
结果如下:

ADB 无法连接的问题
打开 USB 调试。夜神
的 USB 调试并不通过默认的 5037 端口与 adb 客户端通信,可能是考虑多开也许超过 16 个客户端的官方限制。
一般情况下,只需以下步骤就能连接 adb
1
2
3
4
5
6
7
8
9
| # WSL ubuntu 18.04
wesley@WIN10:/mnt/c/Users/Vita/Desktop$ adb devices
List of devices attached
wesley@WIN10:/mnt/c/Users/Vita/Desktop$ adb connect 127.0.0.1:62001
connected to 127.0.0.1:62001
wesley@WIN10:/mnt/c/Users/Vita/Desktop$ adb devices
List of devices attached
127.0.0.1:62001 device
|
虚拟机比较多时就无效了,需要手动找到端口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| # Powershell
PS C:\Users\Vita> Get-Process | where name -like "Nox*"
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
1037 184 177620 49000 161.20 28604 1 Nox
2006 173 1046392 46020 251.53 4716 1 NoxVMHandle
3058 16 6268 19040 0.67 27528 1 NoxVMSVC
PS C:\Users\Vita> Get-NetTcpConnection -OwningProcess 4716 -State Listen
LocalAddress LocalPort RemoteAddress RemotePort State AppliedSetting
------------ --------- ------------- ---------- ----- --------------
127.0.0.1 64001 0.0.0.0 0 Listen
127.0.0.1 63001 0.0.0.0 0 Listen
127.0.0.1 62001 0.0.0.0 0 Listen
127.0.0.1 61001 0.0.0.0 0 Listen
|
依次尝试连接这些LocalPort
,验证adb devices
是否正常。