APP流量安全与协议逆向工程实验部分记录

二进制磨剑公众号(P4nda0s师傅)推出的一个限时靶机

平台:https://www.bin-lab.com/labs

实验 0: 自签名证书与中间人攻击

1.使用 OpenSSL 生成自签名 CA 证书

2.用 CA 签发服务器证书

1
2
3
4
5
根 CA (Root CA)
└── 服务器证书 (Server Certificate)

根 CA:自签名,被操作系统/浏览器信任
服务器证书:由 CA 签发,用于标识网站身份
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
# 生成针对 aaa.local 的配置文件
cat > /data/aaa.cnf << 'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[dn]
C=CN
O=MyOrganization
CN=aaa.local

[v3_req]
subjectAltName = @alt_names
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = aaa.local
EOF

# 生成私钥 a.key
openssl genrsa -out /data/a.key 2048

# 使用配置文件生成 CSR (a.csr)
openssl req -new -key /data/a.key -out /data/a.csr -config /data/aaa.cnf

# 使用 Root CA 签发 a.crt (有效期 1 年 / 365 天)
openssl x509 -req -in /data/a.csr \
-CA /data/myCA.crt -CAkey /data/myCA.key -CAcreateserial \
-out /data/a.crt -days 365 -sha256 \
-extfile /data/aaa.cnf -extensions v3_req

中间人攻击需要把myCA.crt(自签名根证书)偷偷安装到受害者的操作系统或浏览器中,并设置为“受信任的根证书颁发机构;burp suite会要求先在手机或电脑上安装它们自己生成的 Root CA 证书,然后它们才能解密并抓取HTTPS 流量

实验 1: App MITM 基础抓包与请求构造实验

本实验希望app的流量经过电脑代理转发,从而被电脑上的抓包工具捕获请求

典型的抓包环境配置方法,我这里用Charles来抓包

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
第一阶段:配置 Charles 与证书(打通抓包通道)
1. 开启 Charles 的 SSL 抓包
默认情况下,Charles 只能抓 HTTP。我们需要让它能抓 HTTPS:

打开 Charles,点击菜单栏的 Proxy -> SSL Proxying Settings。

勾选 Enable SSL Proxying。

在 Include 列表中点击 Add,Host 填 *,Port 填 *(代表抓取所有域名的 HTTPS 请求),点击 OK 保存。

2. 获取电脑局域网 IP

在 Charles 菜单栏点击 Help -> Local IP Address,记下您电脑的 IP(例如 192.168.1.100)。Charles 的默认代理端口是 8888。

3. 配置雷电模拟器代理

打开雷电模拟器,进入系统自带的 设置 -> 网络和互联网 -> WLAN。

鼠标左键长按连接中的 WiFi(通常叫 WiredSSID),选择 修改网络。

点开 高级选项,将 代理 改为 手动。

代理主机名填刚刚记下的电脑 IP,端口填 8888,保存。

此时 Charles 会弹出一个提示框问您是否允许连接,务必点击 Allow。

4. 在模拟器中安装 Charles 证书(非常关键!)

打开雷电模拟器自带的浏览器,在地址栏输入 chls.pro/ssl 并回车。

浏览器会自动下载一个证书文件(通常叫 charles-proxy-ssl-proxying-certificate.pem 或 .crt)。

下载完成后点击打开它,给证书随便起个名字(比如 charles),如果系统提示您设置锁屏密码,按提示设置一个简单的 PIN 码即可,最后完成安装。

第二阶段:应用内操作与抓包
1. 准备 App

将下载好的 app-debug-easy-1.apk 拖入雷电模拟器完成安装。

打开 App。

2. 填写目标信息

根据您的实验平台提示:

【Domain】 填写:nc.bin-lab.com

【Port】 填写:31755

3. 触发注册请求

在 App 随便输入一个测试账号(比如用户名 testuser1,密码 123456),点击注册/提交按钮。

4. 在 Charles 中寻找目标

回到 Charles,您会看到很多请求。找到域名为 nc.bin-lab.com 的请求,展开它。

找到那个注册接口(通常是 POST 请求,路径可能是 /register 或 /api/user 之类的)。

点击这个请求,查看下方的 Contents 标签页,确保您能清楚地看到请求的 URL、Headers(请求头) 以及 Body(提交的表单数据或 JSON)

imgimg

发现路由和请求方式后直接循环100次即可

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
import requests
import urllib3
import time

# 消除使用 verify=False 时控制台满屏的安全警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url = "https://nc.bin-lab.com:31755/api"

headers = {
"content-type": "application/x-www-form-urlencoded",
"user-agent": "okhttp/4.12.0",
"accept-encoding": "gzip"
}
print("🚀 开始执行 100 个账号的批量注册...")

for i in range(1, 101):
current_username = f"easy_user_{i}"
payload = {
"username": current_username,
"passwd": "098f6bcd4621d373cade4e832627b4f6", # 密码保持原样即可
"operation": "1"
}
try:
# 注意: 因为是表单格式,所以这里用 data=payload; 必须加 verify=False
response = requests.post(url, data=payload, headers=headers, verify=False, timeout=5)
print(f"[{i:03d}/100] 账号: {current_username} | 状态码: {response.status_code} | 返回: {response.text}")
time.sleep(0.1)

except Exception as e:
print(f"❌ 注册 {current_username} 时发生错误: {e}")

print("✅ 100 个账号注册脚本执行完毕!")

实验 2: VPN、Socks5 与网关抓包

Flutter 框架自带Google的BoringSSL 库,完全无视系统的 WiFi 代理设置, 有自己独立的一套网络栈

VPN(虚拟专用网络)本质上是工作在 IP 层的一个虚拟网卡,经过该网卡的流量经过隧道转发,在 Android 上通过 VpnService 建立本地加密隧道:

  • 虚拟网络接口:创建 tun0 设备作为新的网络出口
  • 流量重定向:系统将所有数据包导入 tun0
  • 数据拦截:VPN 应用可读取、修改、记录经过的所有数据
  • 再转发:处理后再由真实网卡(wlan0/eth0)

SocksTun利用 Android 系统的 VpnService API 在手机内部建立了一个虚拟网卡tun0,所有出栈流量强制走VPN隧道,SOCKS5协议负责原封不动地把 VPN 截获的字节流转发给服务器,这时候提前通过中间人攻击解密HTTPS流量就能拿到flag

安装SocksTun并设置参数

Charles在content能看到flag

后面改用Reqable,自动安装证书到本机

设置代理ip和port,以及SSL代理全部拦截

点击调试就可以开始捕获流量

实验 3: eBPF 抓包与 Wireshark 分析实验

eBPF(extended Berkeley Packet Filter) 是运行在 Linux 内核中的一种“安全沙箱程序”,可以在不修改内核源码、不重启系统的前提下,动态挂载到内核或用户态函数上,进行观测与分析

在抓包场景中,常用方式是通过 eBPF 挂载到 SSL/TLS 库的加密函数 上,在加密前/解密后直接获取明文数据,这类方式通常被称为 eBPF TLS 抓包

核心特点

  • 内核级观测:不改 App、不装自签 CA 证书,也无需中间人代理
  • 抓取明文:可在 SSL_read() / SSL_write() 处获得加解密前后的应用层明文 性能影响小:BPF 程序在内核中执行,开销可控,适合长时间运行
  • 透明性好:对业务代码零侵入,适合黑盒应用流量分析与安全审计。
  • 本课程中使用的 eCapture 工具,正是基于 eBPF,通过在用户态库(如 OpenSSL/BoringSSL)上挂载 uprobe,不用实现对 TLS 明文数据的捕获,可以绕过对Charles等CA文件的检测

实验环境:

由于雷电模拟器9Linux内核版本不够高,这里用AndroidStudio上的Android14,注意模拟器需要能root(不能有Google Play)

下载v2.0.0的eCapture传到虚拟机的/data/local/tmp目录,同时安装附件到模拟器,port写容器里的port(相当于本地作为客户端发送TLS流量,容器作为服务端接受),中间显示verify passsed表示连接成功

以root身份运行查看功能

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
emu64xa:/data/local/tmp # ./ecapture                                                                                                                                                                    
NAME:
eCapture - Capturing SSL/TLS plaintext without a CA certificate using eBPF. Supported on Linux/Android kernels for amd64/arm64.

USAGE:
eCapture [flags]

VERSION:
ecap_android_amd64:v2.0.0:6.8.0-1044-azure

COMMANDS:
bash capture bash command
gotls Capturing plaintext communication from Golang programs encrypted with TLS/HTTPS.
help Help about any command
tls Used to capture TLS/SSL text content without the need for a CA certificate. (Supports OpenSSL 1.0.x/1.1.x/3.x or newer).

DESCRIPTION:
eCapture(旁观者) is a tool that can capture plaintext packets
such as HTTPS and TLS without installing a CA certificate.
It can also capture bash commands, which is suitable for
security auditing scenarios, such as database auditing of mysqld, etc (disabled on Android).
Support Linux(Android) X86_64 4.18/aarch64 5.5 or newer.
Repository: https://github.com/gojue/ecapture
HomePage: https://ecapture.cc

Usage:
ecapture tls -h
ecapture bash -h

Docker usage:
docker pull gojue/ecapture:latest
docker run --rm --privileged=true --net=host -v ${HOST_PATH}:${CONTAINER_PATH} gojue/ecapture -h

OPTIONS:
-b, --btf=0 enable BTF mode.(0:auto; 1:core; 2:non-core)
-d, --debug[=false] enable debug logging
--ecaptureq="" listening server, waiting for clients to connect before sending events and logs; false: send directly to the remote server.
--eventaddr="" the server address that receives the captured event. --eventaddr ws://127.0.0.1:8090/ecapture or tcp://127.0.0.1:8090, default: same as logaddr
--eventroratesize=0 the rorate size(MB) of the event collector file, 1M~65535M, only works for eventaddr server is file. --eventaddr=tls.log --eventroratesize=1 --eventroratetime=30
--eventroratetime=0 the rorate time(s) of the event collector file, 1s~65535s, only works for eventaddr server is file. --eventaddr=tls.log --eventroratesize=1 --eventroratetime=30
-h, --help[=false] help for eCapture
--hex[=false] print byte strings as hex encoded strings
--listen="localhost:28256" Listens on a port, receives HTTP requests, and is used to update the runtime configuration, default: 127.0.0.1:28256
-l, --logaddr="" send logs to this server. -l /tmp/ecapture.log or -l ws://127.0.0.1:8090/ecapture or -l tcp://127.0.0.1:8080
--mapsize=1024 eBPF map size per CPU,for events buffer. default:1024 * PAGESIZE. (KB)
-p, --pid=0 if pid is 0 then we target all pids
-t, --tsize=0 the truncate size in text mode, default: 0 (B), no truncate
-u, --uid=0 if uid is 0 then we target all users
-v, --version[=false] version for eCapture

./ecapture tls -m text以text格式显示

一开始的301猜测可能是ecapture比附件app先启动的原因,后面能看到http/2.0协议,说明能解密,但是数据还是被压缩了所以显示乱码

./ecapture tls -m pcap:以pcap格式导出pcapng,但是发现还有TLS加密

ls发现还有log文件,点击一次app按钮就会产生一块流量,下面的log是点击三次得到的log

重新点击其中一个按钮得到pcapng和log

尝试用log解密流量包,发现解密失败

查看log发现生成的随机数一致,但是调整log格式也无法解密

参考https://github.com/gojue/ecapture/issues/752里的./ecapture tls -p -m pcap -w /data/local/tmp/save.pcapng –ssl_version=”boringssl_a_14”强制指定 Android 的底层 SSL 版本来纠正内存偏移量,发现还是不行

询问公众号得知和ecapture版本有关,有些版本会有问题,于是寻找其他版本ecapture:https://github.com/gojue/ecapture/releases?page=2

后面使用v1.5.0发现可行,这个时候没有log文件,直接就是解密后的流量包

直接打开就能看到解密后的http流

第二问okhttp库版本可以通过text格式直接查看

追踪http流也能看到okhttp版本

实验 8: okhttp 证书验证与签名验证实验

单向认证:只有客户端验证服务器的身份(标准 HTTPS)

证书有效是什么?

  • 1.由 CA 签发
  • 2.有效期内
  • 3.域名正确

验证点 是否默认存在 谁负责实现 重点

证书链信任验证 ✅ 默认 系统 / 平台 HTTPS 的最低安全保障

主机名验证 ✅ 默认 系统 / 平台 默认有,但极易被人为关闭

证书 / 公钥绑定(Pinning) ❌ 非默认 开发者 最重要的应用层身份校验

抓包器 CA 签发的证书,如果安装到系统 CA 目录,就实现了(1)证书链信任验证,抓包器会自动针对主机名签发证书,自动绕过(2)主机名验证

Pinning

HTTPS/SSL 网络库(okhttp3 这类)默认情况下,只会保证【证书有效】,抓包器 CA 只要合理装进系统,也可以通过校验

为了防止系统被植入不可信 CA ( App 不再信任系统 CA ),App 可以对证书的公钥进行绑定,这个就是所谓的 Pining。MITM 攻击者无法拿到公钥的私钥,因此无法伪造该公钥对应的证书,即使系统信任了 MITM 提供的 CA 证书。为什么是对公钥进行绑定,而不是整个证书进行绑定? 因为现代证书更替的时间比较快,通常几个月换一次,而公钥则可以保持长期不变

公钥 Pinning 校验 是客户端代码执行校验,因此客户端逆向工程方法仍然是最有效的突破口,通过逆向工程手段关闭 Pinning 校验以后,服务端对此将无法感知。目前大部分 Pinning 的技术实战就仅局限于客户端验证,几乎没有看到服务端验证的例子

网络库/协议栈

Android App 通常使用 okhttp 这类的通用网络库,这类网络库可以使用 hook 脚本通杀。例如 frida 脚本 https://codeshare.frida.re/@Q0120S/bypass-ssl-pinning/可以通杀大部分证书校验

另外,使用 Android 默认网络协议栈的 App 程序,你可以使用 ecapture 这类基于 ebpf 的抓包工具抓取加密流量

非标准网络库/协议栈

这个就很复杂了,例如 Flutter 的 DIO、Golang 的 gotls,OpenSSL、BoringSSL。虽然 Android 提供了 BoringSSL,但是却没有暴露接口,App 可以自己编译 BoringSSL 使用,也可以编译 OpenSSL、Fizz 等网络通信库。对于这些 App 自己编译的 Native 网络库,通用脚本几乎无法处理,ecapture 也不行。更有极端情况,例如 Flutter 的 DIO 自带 CA 库,不去系统读取

主动探测证书

客户端在 Native 层可以主动发起 TLS 握手流程,获取服务端证书,解析服务端证书,解析公钥,记录该公钥作为验证数据。基于这样的思路,所有通杀工具将失效。DEMO 已经开源:https://github.com/P4nda0s/probe-pin

尝试通过上面的frida脚本去绕过Pinning 校验然后中间人攻击拿到解密后的流量,但是一直有ssl报错导致最后用ebpf抓的包

1
2
3
verify failed:SSL handshake aborted:
ssl=0x7fff6c047d88:I/0 error during
system call,Connection reset by peer

APP流量安全与协议逆向工程实验部分记录
https://j1ya-22.github.io/2026/04/11/APP流量安全与协议逆向工程实验部分记录/
作者
j1ya
发布于
2026年4月11日
许可协议