一、原理分析
访问百度(https://www.baidu.com)的过程:
- 发送 UDP 请求,解析域名
www.baidu.com
,获取 IP 地址; - 使用 TCP 协议与该 IP 建立连接,获取网页数据。
在 Android 中通过
VpnService
拦截所有网络数据,分析其 TCP 和 UDP 数据包并实现抓包。
打开Wireshark选择以太网
抓包,通过CMD
执行下面命令
# 清空dns缓存
ipconfig /flushdns
# 解析百度域名IP
ping www.baidu.com
# curl请求百度
curl https://www.baidu.com
数据包结构概览
在网络传输中:
- 以太网帧:包含 MAC 地址;
- IP 数据包:包含源/目标 IP;
- TCP/UDP 数据包:包含端口号和数据内容。
但 VpnService
拿到的数据是 从 IP 层开始,没有以太网帧,因此我们只需解析 IP 数据包即可。
工作流程简述
从虚拟网卡读取数据:
- 若是 TCP 包:提取目标 IP 和端口,使用 Socket 建立连接,读取响应后封装成 TCP 包写回;
- 若是 UDP 包:直接使用 DatagramSocket 发送并接收响应,封装后写回。
二、TCP 协议要点
TCP 是面向连接、可靠、有序的协议,基于字节流。其主要过程:
三次握手
- 客户端发送
SYN
; - 服务端回复
SYN + ACK
; - 客户端回复
ACK
,连接建立完成。
四次挥手
- 客户端发送
FIN
; - 服务端回复
ACK
; - 服务端发送
FIN
; - 客户端回复
ACK
,连接关闭。
我们需要处理的:
SYN
:表示发起连接 —— 创建 Socket;PUSH
:表示有数据 —— 写入 Socket;ACK
:忽略(确认);FIN
或RST
:关闭连接。
三、UDP 协议要点
UDP 是无连接、不可靠的协议:
- 不需要建立连接;
- 直接发送数据报文;
- 超时或无响应可主动关闭连接。
四、VpnService 使用
VpnService
是 Android 提供的类,用于创建一个虚拟的网络接口。
1. 权限与 Service 注册
AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<service android:name=".MyVpnService"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
</application>
2. 启动/停止 VPN
MyVpnService.java
核心实现:
public class MyVpnService extends VpnService {
private ParcelFileDescriptor descriptor;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
switch (intent.getStringExtra("status")) {
case "start": setupVpn(); break;
case "stop": stopVpn(); break;
}
return START_STICKY;
}
private synchronized void stopVpn() {
try {
if (descriptor != null) descriptor.close();
} catch (IOException ignored) {}
finally {
descriptor = null;
sendStatusUpdate("stop");
stopSelf();
}
}
private synchronized void setupVpn() {
if (descriptor != null) return;
try {
descriptor = new Builder()
.addAddress("10.0.0.1", 32)
.addRoute("0.0.0.0", 0)
.addDnsServer("114.114.114.114")
.addAllowedApplication(getPackageName())
.addAllowedApplication("com.android.browser")
.establish();
sendStatusUpdate("start");
} catch (Exception e) {
throw new RuntimeException("VPN setup failed", e);
}
}
private void sendStatusUpdate(String status) {
LocalBroadcastManager.getInstance(this)
.sendBroadcast(new Intent("com.example.UPDATE_TEXT")
.putExtra("status", status));
}
}
3. 权限请求和 UI 控制
MainActivity.java
核心代码:
private void setupVpnLauncher() {
vpnLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
startVpnService();
} else {
((Switch)findViewById(R.id.vpnSwitch)).setChecked(false);
appendOutput("VPN权限被拒绝");
}
});
}
private void handleVpnToggle(boolean isChecked) {
Intent intent = VpnService.prepare(this);
if (isChecked) {
if (intent != null) vpnLauncher.launch(intent);
else startVpnService();
} else {
stopVpnService();
}
}
private void startVpnService() {
vpnIntent = new Intent(this, MyVpnService.class).putExtra("status", "start");
startService(vpnIntent);
}
private void stopVpnService() {
if (vpnIntent != null) {
startService(vpnIntent.putExtra("status", "stop"));
}
}
五、数据包解析:使用 PCAP4J
1. 添加依赖
libs.versions.toml
:
[libraries]
pcap4j-core = { group = "org.pcap4j", name = "pcap4j-core", version.ref = "pcap4j" }
pcap4j-packetfactory = { group = "org.pcap4j", name = "pcap4j-packetfactory-static", version.ref = "pcap4j" }
build.gradle.kts
:
dependencies {
implementation(libs.pcap4j.core)
implementation(libs.pcap4j.packetfactory)
}
六、完整示例代码
项目地址:https://github.com/aLavaGolem/vpnservice-learn
七、总结
- 使用 VpnService 可以截获 Android 网络流量;
- TCP 需维护连接状态,UDP 相对简单;
- 通过 PCAP4J 实现数据包解析;
- 注意 VPN 权限管理与服务稳定性。