设置抓包代理
Android ADB 修改手机代理方式
手动设置单个 WLAN 代理
- 进入设置
- 进入 WLAN
- 找对应 WiFi 连接
- 进入详情
- 找到代理
- 选择手动
- 输入主机名:10.102.230.26
- 输入端口号(你的抓包工具如 Charles):8888
- 最后保存
缺点:
- 需要手动输出 ip
- 步骤繁琐,容易输错
adb shell 设置全局代理
设置代理 put global http_proxy
1
2
3
4
5
6
7
8
# 设置全局代理
adb shell settings put global http_proxy 代理IP地址:端口号
# 查询已有全局代理
adb shell settings get global http_proxy
# 如你的抓包机器是10.102.230.26:8888
adb shell settings put global http_proxy 10.102.230.26:8888
移除代理 delete global http_proxy
因为设置的是全局代理,不管连接的是哪个 wifi,都是会通过这个代理转发请求。所以在抓包完之后相应的需要移除代理信息。
1
2
3
4
adb shell settings delete global http_proxy
adb shell settings delete global global_http_proxy_host
adb shell settings delete global global_http_proxy_port
adb reboot
移除代理信息后,需要重启机器生效
清除全局代理需要重启,可以通过输入一个错误的代理地址来实现清除代理而不需要重启
1
adb shell settings put global http_proxy :0
缺点
- 手动输入 ip 地址
遇到的问题
错误 1:writing to settings requires: android.permission.WRITE_SECURE_SETTINGS
报错信息: java.lang.SecurityException: Permission denial: writing to settings requires: android.permission.WRITE_SECURE_Settings
java.lang.SecurityException:权限拒绝:写入设置需要:android.permission.WRITE_SECURE_SETTINGS
解决办法:
- 小米:在开发者选项里,把 “
USB调试(安全设置)
” 打开即可。 允许 USB 调试修改权限或模拟点击 - Oppo/一加:在开发者选项里,把 “
禁止权限监控
” 打开即可。(Android 14 后不好使了)
错误 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Exception occurred while executing 'put':
java.lang.SecurityException: Permission denial, must have one of: [android.permission.WRITE_SECURE_SETTINGS]
at com.android.providers.settings.SettingsProvider.enforceHasAtLeastOnePermission(SettingsProvider.java:2641)
at com.android.providers.settings.SettingsProvider.mutateGlobalSetting(SettingsProvider.java:1604)
at com.android.providers.settings.SettingsProvider.insertGlobalSetting(SettingsProvider.java:1558)
at com.android.providers.settings.SettingsProvider.call(SettingsProvider.java:518)
at android.content.ContentProvider.call(ContentProvider.java:2716)
at android.content.ContentProvider$Transport.call(ContentProvider.java:641)
at com.android.providers.settings.SettingsService$MyShellCommand.putForUser(SettingsService.java:385)
at com.android.providers.settings.SettingsService$MyShellCommand.onCommand(SettingsService.java:281)
at com.android.modules.utils.BasicShellCommandHandler.exec(BasicShellCommandHandler.java:97)
at android.os.ShellCommand.exec(ShellCommand.java:38)
at com.android.providers.settings.SettingsService.onShellCommand(SettingsService.java:53)
at android.os.Binder.shellCommand(Binder.java:1092)
at android.os.Binder.onTransact(Binder.java:912)
at android.os.Binder.execTransactInternal(Binder.java:1392)
at android.os.Binder.execTransact(Binder.java:1299)
解决:用 root 权限
adb shell 脚本自动化设置全局代理
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
#!/bin/bash
echo "\n---------------- Support ------------------"
echo "设置默认代理(本机IP) easyproxy set"
echo "设置自定义代理 easyproxy set ****:8888"
echo "删除代理 easyproxy clear"
echo "--------------------------------------------\n"
if [ "$1" == "set" ];then
if [ -n "$2" ];then
echo "设置自定义代理 $2"
adb shell settings put global http_proxy $2
else
# 获取 IP
ip=$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')
echo $ip
count=$(echo $ip | tr ' ' '\n' | wc -l )
if [ $count -gt 1 ];then
echo "多个ip, 请手动选择一个"
exit
fi
default_proxy=${ip}":8888"
echo "本机IP为: $default_proxy"
echo "设置代理为本机IP: $default_proxy"
adb shell settings put global http_proxy $default_proxy
fi
elif [ "$1" == "clear" ];then
echo "清除代理成功"
adb shell settings put global http_proxy :0
# 下面的方式需要重启手机
# adb shell settings delete global http_proxy
# adb shell settings delete global global_http_proxy_host
# adb shell settings delete global global_http_proxy_port
else
echo "请输入合法的操作符!"
fi
新增 easyproxy.sh
到 ~.sh/
后
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
#!/bin/bash
echo "\n---------------- Support ------------------"
echo "设置默认代理(本机IP) easyproxy set"
echo "设置自定义代理 easyproxy set ****:8888"
echo "删除代理 easyproxy clear"
echo "--------------------------------------------\n"
if [ "$1" == "set" ];then
if [ -n "$2" ];then
echo "设置自定义代理 $2"
adb shell settings put global http_proxy $2
else
# 获取 IP
ip=$(ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p')
echo $ip
count=$(echo $ip | tr ' ' '\n' | wc -l )
if [ $count -gt 1 ];then
echo "多个ip, 请手动选择一个"
exit
fi
default_proxy=${ip}":8888"
echo "本机IP为: $default_proxy"
echo "设置代理为本机IP: $default_proxy"
adb shell settings put global http_proxy $default_proxy
fi
elif [ "$1" == "clear" ];then
echo "清除代理成功"
adb shell settings put global http_proxy :0
# 下面的方式需要重启手机
# adb shell settings delete global http_proxy
# adb shell settings delete global global_http_proxy_host
# adb shell settings delete global global_http_proxy_port
else
echo "请输入合法的操作符!"
fi
1
2
3
4
5
6
7
# 默认设置本机ip为默认手机代理
sh easyproxy.sh set
# 设置定义ip代理
sh easyproxy.sh set 192.168.100.1:8888
# 清除手机代理
sh easyproxy.sh clear
也可以添加 alias 简化调用:
1
2
alias proxy_set='sh ~/.sh/easyproxy.sh set'
alias proxy_clear='sh ~/.sh/easyproxy.sh clear'
Python 脚本
自动设置 PAC
什么是 PAC?
PAC,全称 Proxy Auto Config,中文名代理自动配置。PAC 类似于配置文件,通过这个配置文件,浏览器或者其他 UA 可以对每一个 url 配置代理服务,比如这个 url 要走这个代理,那个 url 要走那个代理等。
简单的 PAC 示例
1
2
3
4
5
6
7
function FindProxyForURL(url, host)
{
if (host == "www.mydomain.com") {
return "DIRECT";
}
return "PROXY myproxy:80; PROXY myotherproxy:8080; DIRECT";
}
解释:
- www.mydomain.com的流量直接连接,不走代理;
- www.mydomain.com以外的流量默认走代理,先走myproxy:80代理,如果超时那就再走myotherproxy:8080,如果这个还走不通,不走代理了,直连
一个可用的 proxy.pac
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
var direct = 'DIRECT';
//var http_proxy = 'SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT';
var http_proxy = 'PROXY 10.102.230.26:8888; DIRECT';
// 需要代理的域名,即需要抓包的网址
var proxy_list = [
"xxx.com",
"yyy.cn",
"appsflyer.com",
"googleapis.com",
"facebook.com"
];
var proxyed = {};
for (var i = 0; i < proxy_list.length; i += 1) {
proxyed[proxy_list[i]] = true;
}
function host2domain(host) {
var dotpos = host.lastIndexOf(".");
if (dotpos === -1)
return host;
// Find the second last dot
dotpos = host.lastIndexOf(".", dotpos - 1);
if (dotpos === -1)
return host;
return host.substring(dotpos + 1);
};
function FindProxyForURL(url, host) {
return proxyed[host2domain(host)] ? http_proxy : direct;
};
- 下载 Tomcat 或者其他 web 服务器,启动
- 访问看下:http://10.102.230.26:8080/proxy.pac是否能访问
- 配置到连接的 WiFi
- 代理服务器→自动配置,PAC 网址:http://10.102.230.26:8080/proxy.pac
也可以用 adb shell input text http://10.102.230.26:8080/proxy.pac
proxy. pac:
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
// // 一个很简单的调试app pac文件
// function FindProxyForURL(url, host) {
// alert(host);
// // 这里填写你们公司的host
// if (dnsDomainIs(host, 'xxx.com')) {
// // 这里填你自己的局域网ip,端口号是你在Charles里设置的 默认8888
// return "PROXY 10.102.230.26:8888; DIRECT;";
// } else {
// return "DIRECT";
// }
// }
var direct = 'DIRECT';
//var http_proxy = 'SOCKS5 127.0.0.1:1080; SOCKS 127.0.0.1:1080; DIRECT';
var http_proxy = 'PROXY 10.102.230.26:8888; DIRECT';
var proxy_list = [
"xxx.com",
"xxx.cn",
"appsflyer.com",
"googleapis.com",
"facebook.com"
];
var proxyed = {};
for (var i = 0; i < proxy_list.length; i += 1) {
proxyed[proxy_list[i]] = true;
}
function host2domain(host) {
var dotpos = host.lastIndexOf(".");
if (dotpos === -1)
return host;
// Find the second last dot
dotpos = host.lastIndexOf(".", dotpos - 1);
if (dotpos === -1)
return host;
return host.substring(dotpos + 1);
};
function FindProxyForURL(url, host) {
return proxyed[host2domain(host)] ? http_proxy : direct;
};
缺点: 当代理服务器 (charles) 没有启动时, 使用了 pac 配置代理, 那么也是无法联网的. 此时, 只能在手机 wifi 上手动关闭代理。下一次启动 pac 配置代理, 还是需要手动输入代理 pac 的地址
编写 PAC 文件
PAC 的一些基础知识
PAC 本质上就是一文本文件,一般以.pac 结尾,如 proxy.pac。
PAC 文件里面包含一个 JavaScript 函数 FindProxyForURL(url, host)
,函数返回一个字符串,这个字符串就是代理的配置。函数有两个参数:url 和 host,url 是浏览的地址 url 全路径,host 是这个 url 中的主机名部分。
整个 PAC 文件就包含 FindProxyForURL(url, host) 这一个函数,该函数的返回值可能有三种情况:
- DIRECT 就是直接连接,不通过代理
- PROXY http://www.example.com:8080,http 代理的主机和端口,指定代理服务器的地址和端口号>
- SOCKS socks5sample.com:1080,socks5 代理的主机和端口,主机也可以用 IP 表示
一个自动代理可以是多个选择的组合,各个选择间用分号; 隔开,PAC 的容灾性是比较好的,因为你可以同时给一个 url 配置多个代理,以分号隔开,如果第一个代理挂了,会自动选择第二个;如果第二个挂了,则会继续自动选择第三个……
PAC 的一些函数
- isPlainHostName(host):判断是否是简单域名,例如 localhost 就是一个简单域名
- dnsDomainIs(host,domain) 判断给定的 host 是否属于某个域名
- localHostOrDomainIs(host, “”):判断访问主机是否属于某个域和某个域名
- dnsResolve(host) 做 DNS 解析,返回 host 的 ip,注意:DNS 解析可能会 block 住浏览器
- isInNet(ip, subnet, netmask) 判断 ip 是否属于某个子网
- shExpMatch(host, “”):判断两个字符串是否匹配,pattern 中可以包含 shell 使用的通配符
- url.substring(0, n):字符串截取
- myIpAddress():返回本机的 ip (貌似不太可靠,见 wikipedia 的说明)
完整的 PAC支持的函数列表
PAC 和 Charles 配合?
Ref
Python 设置代理
adb 安装证书
1
2
3
cd ~/.sh
adb push charles-ssl-proxying-certificate.pem /data/local/tmp
adb shell am start -n com.android.certinstaller/.CertInstallerMain -a android.intent.action.VIEW -t "application/x-x509-ca-cert" -d file:///data/local/tmp/charles-ssl-proxying-certificate.pem
在 Android 14 手机上,报错了
只能在 Settings 页手动安装:
辅助脚本:
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
# install 证书
## 将当前第1个变量给CERT_FILE
## 如果没有,默认取当前脚本路径下的charles-ssl-proxying-certificate.pem,push到/sdcard/Download/
## 然后跳转到安全设置页面,引导用户手动安装证书
install_cert() {
local CERT_FILE="$1"
# 获取当前脚本所在目录的绝对路径
local SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
echo "SCRIPT_DIR=$SCRIPT_DIR"
# 检查ADB是否已安装
if ! command -v adb &> /dev/null; then
echo "ADB未安装,请先安装ADB。"
return 1
fi
# 检查是否提供了证书文件路径
if [ -z "$CERT_FILE" ]; then
CERT_FILE="$SCRIPT_DIR/charles-ssl-proxying-certificate.pem"
echo "未提供证书文件路径,默认使用 $CERT_FILE。"
fi
# 检查证书文件是否存在
if [ ! -f "$CERT_FILE" ]; then
echo "证书文件 $CERT_FILE 不存在。"
return 1
fi
# 检查证书文件是否可读
if [ ! -r "$CERT_FILE" ]; then
echo "证书文件 $CERT_FILE 不可读。请检查文件权限。"
return 1
fi
# if ! grep -qF "-----BEGIN CERTIFICATE-----" "$CERT_FILE" || ! grep -qF "-----END CERTIFICATE-----" "$CERT_FILE"; then
# echo "证书文件 $CERT_FILE 不是有效的PEM格式。"
# return 1
# fi
# 检查设备是否连接
if ! adb devices | grep -q "device"; then
echo "未检测到连接的Android设备。"
return 1
fi
# 推送证书到设备的Download目录
echo "推送证书到设备的Download目录…"
adb push "$CERT_FILE" /sdcard/Download/
if [ $? -ne 0 ]; then
echo "推送证书到设备失败。"
return 1
fi
# 获取设备信息
echo "获取设备信息…"
local DEVICE_INFO=$(adb shell getprop ro.product.model)
local ANDROID_VERSION=$(adb shell getprop ro.build.version.release)
echo "设备信息:"
echo "设备型号: $DEVICE_INFO"
echo "Android版本: $ANDROID_VERSION"
# 跳转到安全设置页面
echo "跳转到安全设置页面…"
adb shell am start -a android.settings.SECURITY_SETTINGS
echo "已跳转到安全设置页面。"
# 引导用户手动安装证书
echo "请按照以下步骤手动安装证书:"
echo "1. 在安全设置页面,选择 'More security & privacy'。"
echo "2. 选择 'Encryption & credentials'。"
echo "3. 选择 'Install a certificate'。"
echo "4. 选择 'CA certificate'。"
echo "5. 在文件管理器中,导航到 'Download' 目录,选择 '$(basename "$CERT_FILE")' 文件。"
echo "6. 完成证书安装步骤。"
}