安卓抓包总结

关于抓包这一块的总结

[toc]

环境配置

下面环境配置都是在真机上进行的,模拟器同理

Charles

官网下载 Charles 并安装

按提示设置代理并下载根证书到模拟器/真机

ip 和 端口设置成上面提到的,ip 就是本机 ip,注意此时手机与电脑需要在同一个局域网下(比如连接同一个 wifi 或热点),Charles Proxy -> Proxy Settings 内可以修改端口

如需配置抓包 https 可以在 Charles Proxy ->SSL Proxy Settings 添加 443 端口

  • Android 7.0 以上默认不信任用户安装的证书,为了让我们安装的证书成为系统证书需要安装插件,MagiskTrustUserCerts 这个插件可以让用户证书成为系统信任证书

Burp

burp 设置代理,ip 为本机 ip,端口随意

手机跟配置 charles 时一样设置对应的 ip 和端口,同样安装 burp 的 CA 证书到系统证书

抓不到包的情况

比如 OkHttp 中配置 NO_PROXY、curl 发送 http 请求等没有经过系统代理的情况下按之前的抓包方法是抓不到的,需要在手机上通过 VPN 转发后才能抓包

VPN 代理软件有

  • HttpCanary(直接手机上抓包,个人感觉比较好用)
  • Postern(抓包工具需配置 socks 代理)
  • Drony

绕过 SSL Pinning

SSL Pinning 会让我们前面提到的抓包方法失效,绕过后即可正常抓包,在讲 SSL Pinning 前先来了解下加密通信相关的前置知识。

CA 证书

HTTPS 单向验证与双向验证

单向认证

  • 客户端向服务端发送一个明文 http 请求
  • 服务器向客户端发送 CA 证书
  • 客户端收到 CA 证书后用公钥(操作系统和浏览器自带)解密,验证数字签名成功后获得 B_公钥
  • 客户端生成随机数密钥 F,用前面的 B_公钥加密发给服务端
  • 服务端用自己的 B_私钥解密得到 F
  • 之后双方用密钥 F 通信

客户端通过 CA 证书验证服务器,以此防止中间人攻击

双向认证

和单向认证相比,双向认证增加了服务器验证客户端的环节,客户端会发送证书给服务端

SSL pinning 及其实现

SSL Pinning 是一种防止中间人攻击的策略,通过客户端内置证书防范伪造服务器证书,SSL Pinning 分为证书绑定和公钥绑定

  • 证书绑定:客户端代码内置服务端证书的一部分字节码,与服务器通信时会将证书与内置部分比对,不匹配就会报错
  • 公钥绑定:客户端内置证书公钥,通过与服务器证书公钥比对确认连接

HttpsURLConnection 实现

  1. 自定义 SSLSocketFactory 实现,继承并重写 TrustManager 的 checkClientTrusted 和 checkServerTrusted 方法
    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
    public HttpsURLConnection getHttpsURLConnection(String url){
    HttpsURLConnection conn = null;
    try{
    SSLContext sc = SSLContext.getInstance("TLS");
    sc.init(null,new TrustManager[]{new MyTrustManager()},new SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    HttpsURLConnection.setDefaultHostnameVerifier(new MyHostnameVerifier());
    conn = (HttpsURLConnection) new URL(url).openConnection();
    conn.setDoOutput(true);
    conn.setDoInput(true);
    }catch (Exception e){
    e.printStackTrace();
    }
    return conn;
    }

    class MyHostnameVerifier implements HostnameVerifier{
    @Override
    public boolean verify(String s, SSLSession sslSession) {
    return true;
    }
    }

    class MyTrustManager implements X509TrustManager{
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
    return new X509Certificate[0];
    }
    }

OkHttp 实现

  1. CertificatePinner 证书锁定,证书公钥的哈希值可以在这里获取
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
final String CA_DOMAIN ="www.baidu.com";

public void OkHttpCertificatePinner() throws IOException {
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add(CA_DOMAIN,"sha256/Zhv4cvwdHmEmE0edWEcIdmLfwsqxrrOmp+vbngwNnrU=")
.add(CA_DOMAIN,"sha256/hETpgVvaLC0bvcGG3t0cuqiHvr4XyP2MTwCiqhgRWwU=")
.add(CA_DOMAIN,"sha256/cGuxAXyFXFkWm61cF4HPWX8S0srS9j0aSqN0k4AP+4A=")
.build();

OkHttpClient client = new OkHttpClient.Builder().certificatePinner(certificatePinner)
.build();

Request request = new Request.Builder()
.url("https://www.baidu.com")
.build();

Call call = client.newCall(request);
Response response = call.execute();
Log.d("OkHttp",response.body().string());
// 异步请求
/*
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d("OkHttp", e.getMessage());
}

@Override
public void onResponse(Call call, okhttp3.Response response) throws IOException {
Log.d("OkHttp", response.body().string());
}
});
*/
}

如何绕过

justTrustMe 这一插件能够 hook 掉证书验证从而实现绕过 SSL Pinning,不过 https://github.com/Fuzion24/JustTrustMe Release 里的版本是 2016 年的,需要自己编译一份再使用

当然 justTrustMe 不能绕过所有的 SSL Pinning ,部分魔改的证书校验需要自己写 hook 脚本绕过,因此需要了解下 justTrustMe 都 hook 了哪些函数

justTrustMe 源码分析

这里主要关注 OkHttp,函数 processOkHttp 是来处理 OkHttp 部分的

  • 首先针对 2.5 版本的 okhttp hook 了 com.squareup.okhttp.CertificatePinner.check(String,List),该方法内对传入的 hostname 的证书和代码内置的公钥哈希做一个匹配,如不匹配则返回 false,这里通过 hook 使其始终返回 true
  • 接下来针对 3.X 版本的 okhttp ,同样 hook 了 okhttp3.CertificatePinner.check(String,List) 始终返回 true,还 hook 了 okhttp3.internal.tls.OkHostnameVerifier.verify 的两个函数,绕过域名校验
  • 4.2.0+ 版本 okhttp 源码改用 kotlin 写,同样hook 了 okhttp3.CertificatePinner.check 三个重载
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
void processOkHttp(ClassLoader classLoader) {
/* hooking OKHTTP by SQUAREUP */
/* com/squareup/okhttp/CertificatePinner.java available online @ https://github.com/square/okhttp/blob/okhttp_27/okhttp/src/main/java/com/squareup/okhttp/CertificatePinner.java */
/* public void check(String hostname, List<Certificate> peerCertificates) throws SSLPeerUnverifiedException{}*/
/* Either returns true or a exception so blanket return true */
/* Tested against version 2.5 */
Log.d(TAG, "Hooking com.squareup.okhttp.CertificatePinner.check(String,List) (2.5) for: " + currentPackageName);

try {
classLoader.loadClass("com.squareup.okhttp.CertificatePinner");
findAndHookMethod("com.squareup.okhttp.CertificatePinner",
classLoader,
"check",
String.class,
List.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch (ClassNotFoundException e) {
// pass
Log.d(TAG, "OKHTTP 2.5 not found in " + currentPackageName + "-- not hooking");
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/CertificatePinner.java#L144
Log.d(TAG, "Hooking okhttp3.CertificatePinner.check(String,List) (3.x) for: " + currentPackageName);

try {
classLoader.loadClass("okhttp3.CertificatePinner");
findAndHookMethod("okhttp3.CertificatePinner",
classLoader,
"check",
String.class,
List.class,
DO_NOTHING);
} catch (ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking");
// pass
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java
try {
classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier");
findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier",
classLoader,
"verify",
String.class,
javax.net.ssl.SSLSession.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch (ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, SSLSession)");
// pass
}

//https://github.com/square/okhttp/blob/parent-3.0.1/okhttp/src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java
try {
classLoader.loadClass("okhttp3.internal.tls.OkHostnameVerifier");
findAndHookMethod("okhttp3.internal.tls.OkHostnameVerifier",
classLoader,
"verify",
String.class,
java.security.cert.X509Certificate.class,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return true;
}
});
} catch (ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking OkHostnameVerifier.verify(String, X509)(");
// pass
}

//https://github.com/square/okhttp/blob/okhttp_4.2.x/okhttp/src/main/java/okhttp3/CertificatePinner.kt
Log.d(TAG, "Hooking okhttp3.CertificatePinner.check(String,List) (4.2.0+) for: " + currentPackageName);

try {
classLoader.loadClass("okhttp3.CertificatePinner");
findAndHookMethod("okhttp3.CertificatePinner",
classLoader,
"check$okhttp",
String.class,
"kotlin.jvm.functions.Function0",
DO_NOTHING);
} catch (XposedHelpers.ClassNotFoundError | ClassNotFoundException | NoSuchMethodError e) {
Log.d(TAG, "OKHTTP 4.2.0+ (check$okhttp) not found in " + currentPackageName + " -- not hooking");
// pass
}

try {
classLoader.loadClass("okhttp3.CertificatePinner");
findAndHookMethod("okhttp3.CertificatePinner",
classLoader,
"check",
String.class,
List.class,
DO_NOTHING);
} catch (XposedHelpers.ClassNotFoundError | ClassNotFoundException | NoSuchMethodError e) {
Log.d(TAG, "OKHTTP 4.2.0+ (check) not found in " + currentPackageName + " -- not hooking");
// pass
}

}

这块还是需要看看 OkHttp 源码的

参考

作者

0wl

发布于

2022-10-05

更新于

2022-11-19

许可协议

评论