抓包进阶之通杀okhttp证书
zsk Lv4

三层检验

okhttp在客户端有三种检验证书方法
证书验证sslSocketFactory
证书锁定certificatePinner
域名验证hostmaneVerfier

image

如何做到通杀?
三个检验方法都基于 OkHttpClient.Builder()
要先找到 OkHttpClient,由于经过混淆的,无法直接定位

步骤

先把内存中的类的存到数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var classesNames = new Array()   // 存放加载的类

// 1.把已经加载内存中的类枚举出来,存到一个数组里
function loadClasses(){
Java.perform(function (){
Java.enumerateLoadedClasses({
onMatch: function(clsName, handle){
classesNames.push(clsName);
console.log(clsName)
},
onComplete: function(){
console.log("Search Class Completed!");
}
})
})
}


function main(){
loadClasses()
}
setImmediate(main)

查找 okhttp3.OkHttpClient类和证书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
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
function findOkhttpClass(){
Java.perform(function (){
var Modifier = Java.use("java.lang.reflect.Modifier") // 反射框架,可以提供类的一些属性,例如是不是final,sttic等
// 判断类是否是OkhttpClient
function isOkhttpClient(clsName){
// Okhttp3.OkHttpClient
if(clsName.split('.').length != 2){
return false;
}

try{
var cls = Java.use(clsName)
var interfaces = cls.class.getInterfaces() // 通过反射获得当前类的一些接口
const count = interfaces.length
//console.log(count)
// 当前类如果实现的接口小于2,就不是,okhttp实现的接口Cloneable, Call.Factory, WebSocket.Factory
if(count < 2){
return false
}
var flag = false
// 对实现的接口进行遍历,进行判断
for(var i = 0; i < count; i++){
var interface_ = interfaces[i]
var interface_name = interface_.getName()

if(interface_name.indexOf("Cloneable") > 0){
flag = true
}else{
if(interface_name.indexOf("$") <= 0){ // 内部类Call.Factory
return false
}
}
}
if(!flag) return false;

// 过滤当前类没有内部类
if(cls.class.getDeclaredClasses().length < 1){
return false
}
// 过滤父类不是Object,OkHttpClient没有继承其他类
if(cls.class.getSuperclass().getName() != 'java.lang.Object'){
return false
}

}catch(e){
return false
}
return true;
}

function isCertificatePinner(clsName,prefix){
// prefix=Okhttp3.
// CertificatePinner类
if(!clsName.startsWith(prefix)){
return false
}

if(clsName.indexOf("$") > 0){
return false
}

if(clsName.split('.').length != 2){
return false;
}

var cls = Java.use(clsName)
if(cls.class.isInterface()){
return false
}


if(cls.class.getInterfaces().length > 0){
return false
}


if(cls.class.getDeclaredClasses().length < 1){
return false
}

if(cls.class.getSuperclass().getName() != "java.lang.Object"){
return false
}
// 类属性是否有Final
if(!Modifier.isFinal(cls.class.getModifiers())){
return false
}
var flag = false
// 获得所有方法,然后遍历
var methods = cls.class.getDeclaredMethods()
for(var i = 0; i < methods.length; i++){
var method = methods[i]
// 过滤方法没有参数的
if(method.getParameterCount() < 1){
continue
}
// 方法的第一个参数是java.security.cert.Certificate
if(method.getParameterTypes()[0].getName() == "java.security.cert.Certificate"){
flag = true
break
}
}
if(!flag) return false

flag = false
// 获取类的所有成员变量
var fields = cls.class.getDeclaredFields()
// 类包含set集合
for(var k = 0; k < fields.length; k++){
var field = fields[k];
if(field.getType().getName() == "java.util.Set"){
flag = true
break
}
}
if(!flag) return false

console.log(clsName)
return true
}

for(var i = 0; i < classesNames.length; i++){
// 如果类名是OkhttpClient,就保存为变量
if(isOkhttpClient(classesNames[i])){
OkhttpClientClassName = classesNames[i]
//console.log(OkhttpClientClassName)
var splits = classesNames[i].split('.')
var len = splits.length
for(var j = 0; j < len-1; j++){
// Okhttp3.OkHttpClient
prefix = prefix + splits[j] + '.' // 当前包名的一个前缀,prefix=Okhttp3.
}
}
}
// 找CertificatePinner类
for(var i = 0; i < classesNames.length; i++){
if(isCertificatePinner(classesNames[i],prefix)){
CertificatePinnerClassName = classesNames[i]
//console.log(CertificatePinnerClassName)
}
}

console.error("Found Class: "+classesNames.length)
console.error("Okhttp's package prefix: "+prefix)
console.error("Find the OkhttpClient: "+OkhttpClientClassName)
console.error("Find the OkhttpCertificatePinner: "+CertificatePinnerClassName)

if(OkhttpClientClassName == "" || CertificatePinnerClassName == "" || prefix == ""){
console.error("Can't find the okhttp class")
}
})
}

attach模式运行

image

找到之后进行hook

找builder内部类
image

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
function hook(){
Java.perform(function (){
var Modifier = Java.use("java.lang.reflect.Modifier")
//TrustAllManager,自定义类,hook方法置空,什么都不干
var TrustAllManagerClass = Java.registerClass({
name: "TrustAllManager",
implements:[Java.use("javax.net.ssl.X509TrustManager")],
methods: {
checkClientTrusted(chain, authType) {
console.log("checkClientTrusted Called!!")
},
checkServerTrusted(chain, authType) {
console.log("checkServerTrusted Called!!")
},
getAcceptedIssuers() {
return [];
},
}
})
var trustAllManagerHandle = TrustAllManagerClass.$new()
// 仿造
// SSLContext context = SSLContext.getInstance("TLS");
// context.init(null, trustManagers ,new SecureRandom());
// SSLSocketFactory factory = context.getSocketFactory();
var sslContext = Java.use("javax.net.ssl.SSLContext").getInstance("TLS")
sslContext.init(null,Java.array("Ljavax.net.ssl.X509TrustManager;",[trustAllManagerHandle]),null)
var sslSocketFactory = sslContext.getSocketFactory()

//HostnameVerify,自定义类
var MyHostnameVerify = Java.registerClass({
name: "MyHostnameVerify",
implements:[Java.use("javax.net.ssl.HostnameVerifier")],
methods: {
verify(hostname, session){
console.log(hostname)
return true
}
}
})
var myHostnameVerifyHandle = MyHostnameVerify.$new()

// 已经拿到okhttp3.OkHttpClient,拿它的内部类builder
var BuilderClassName = Java.use(OkhttpClientClassName).class.getDeclaredClasses()[0].getName()
var OkhttpClient$Buidler = Java.use(BuilderClassName)
// 通过反射获取builder的所有方法
var methods = OkhttpClient$Buidler.class.getDeclaredMethods()

for(var i = 0; i < methods.length; i++){
var method = methods[i]
// 过滤方法没有参数的,目标方法都有参数
// builder.sslSocketFactory(factory, (X509TrustManager) trustManagers[0]);
// builder.certificatePinner(cerBuilder.build());
// builder.hostnameVerifier(new HostnameVerifier(){});
if(method.getParameterCount() < 1){
continue
}
// 第一道校验,第一个参数的类名是javax.net.ssl.SSLSocketFactory就进行hook,参数替换为自定义参数
if(method.getParameterTypes()[0].getName() == "javax.net.ssl.SSLSocketFactory"){
var sslSocketFacotryMethodName = method.getName()
// 获取该方法的重载个数
var len = OkhttpClient$Buidler[sslSocketFacotryMethodName].overloads.length
// 遍历重载
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[sslSocketFacotryMethodName].overloads[j].implementation = function(SSLSocketFactory){
// 把参数替换为自定义的类
arguments[0] = sslSocketFactory
return this[sslSocketFacotryMethodName].apply(this,arguments)
}
}
console.log(sslSocketFacotryMethodName,"Hooked!")
}
// 第三道校验,第一个参数是javax.net.ssl.HostnameVerifier就进行hook,参数替换为自定义参数
if(method.getParameterTypes()[0].getName() == "javax.net.ssl.HostnameVerifier"){
var hostnameVerifierMethodName = method.getName()

var len = OkhttpClient$Buidler[hostnameVerifierMethodName].overloads.length
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[hostnameVerifierMethodName].overloads[j].implementation = function(hostnameVerifier){
arguments[0] = myHostnameVerifyHandle
return this[hostnameVerifierMethodName].apply(this,arguments)
}
}
console.log(hostnameVerifierMethodName, "Hooked!")
}
// 第二到校验,参数替换为它默认的,builder.certificatePinner(cerBuilder.build());
// 默认public static final CertificatePinner DEFAULT = new Builder().build();
if(method.getParameterTypes()[0].getName() == CertificatePinnerClassName){
var CertificatePinnerClass = Java.use(CertificatePinnerClassName)
var certificatePinnerMethodName = method.getName()
var len = OkhttpClient$Buidler[certificatePinnerMethodName].overloads.length
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[certificatePinnerMethodName].overloads[j].implementation = function(){
console.log("certificatePinner add called!")
// 拿到所有的域,遍历
var fields = CertificatePinnerClass.class.getDeclaredFields()
for(var k = 0; k < fields.length; k++){
var field = fields[k];
var modifiers = field.getModifiers()
// public static final CertificatePinner DEFAULT = new Builder().build();
if(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)){
arguments[0] = field.get(CertificatePinnerClass.class)
}
}
return this[certificatePinnerMethodName].apply(this,arguments)
}
}
console.log( method.getName(),"Hooked!")
}
}

var CertificatePinnerClass = Java.use(CertificatePinnerClassName)
var methods = CertificatePinnerClass.class.getDeclaredMethods()
for (var i = 0; i < methods.length; i++){
var method = methods[i]
if(method.getReturnType().getName() == 'void'){
var methodName = method.getName()
console.log(methodName+" Hooked!")
var m_len = CertificatePinnerClass[methodName].overloads.length

for (var j = 0; j < m_len; j++){
if(CertificatePinnerClass[methodName].overloads[j].returnType.name == 'V'){
CertificatePinnerClass[methodName].overloads[j].implementation = function(){
console.log("certificatePinner check called!")
}
}
}
}
}

})
}

运行

image

使用spwan模式主动加载类名

1
2
3
4
5
6
7
8
9
10
11
function loadOkhttpClient(){
Java.perform(function (){
try{
// 把找到的类名替换这里
Java.use("okhttp3.OkHttpClient")
}catch(e){
//console.error(e)
}
})

}

测试

滴答清单app
image

完整代码

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
var classesNames = new Array()  // 存放加载的类
var OkhttpClientClassName = ""
var CertificatePinnerClassName = ""
var prefix = ""

function loadOkhttpClient(){
Java.perform(function (){
try{
Java.use("q.x")
}catch(e){
//console.error(e)
}
})

}
// 1.把已经加载内存中的类枚举出来,存到一个数组里
function loadClasses(){
Java.perform(function (){
Java.enumerateLoadedClasses({
onMatch: function(clsName, handle){
classesNames.push(clsName)
},
onComplete: function(){
console.log("Search Class Completed!")
}
})
})
}

function findOkhttpClass(){
Java.perform(function (){
var Modifier = Java.use("java.lang.reflect.Modifier") // 反射框架,可以提供类的一些属性,例如是不是final,sttic等
// 判断类是否是OkhttpClient
function isOkhttpClient(clsName){
// Okhttp3.OkHttpClient
if(clsName.split('.').length != 2){
return false;
}

try{
var cls = Java.use(clsName)
var interfaces = cls.class.getInterfaces() // 通过反射获得当前类的一些接口
const count = interfaces.length
//console.log(count)
// 当前类如果实现的接口小于2,就不是,okhttp实现的接口Cloneable, Call.Factory, WebSocket.Factory
if(count < 2){
return false
}
var flag = false
// 对实现的接口进行遍历,进行判断
for(var i = 0; i < count; i++){
var interface_ = interfaces[i]
var interface_name = interface_.getName()

if(interface_name.indexOf("Cloneable") > 0){
flag = true
}else{
if(interface_name.indexOf("$") <= 0){ // 内部类Call.Factory
return false
}
}
}
if(!flag) return false;

// 过滤当前类没有内部类
if(cls.class.getDeclaredClasses().length < 1){
return false
}
// 过滤父类不是Object,OkHttpClient没有继承其他类
if(cls.class.getSuperclass().getName() != 'java.lang.Object'){
return false
}

}catch(e){
return false
}
return true;
}

function isCertificatePinner(clsName,prefix){
// prefix=Okhttp3.
// CertificatePinner类
if(!clsName.startsWith(prefix)){
return false
}

if(clsName.indexOf("$") > 0){
return false
}

if(clsName.split('.').length != 2){
return false;
}

var cls = Java.use(clsName)
if(cls.class.isInterface()){
return false
}


if(cls.class.getInterfaces().length > 0){
return false
}


if(cls.class.getDeclaredClasses().length < 1){
return false
}

if(cls.class.getSuperclass().getName() != "java.lang.Object"){
return false
}
// 类属性是否有Final
if(!Modifier.isFinal(cls.class.getModifiers())){
return false
}
var flag = false
// 获得所有方法,然后遍历
var methods = cls.class.getDeclaredMethods()
for(var i = 0; i < methods.length; i++){
var method = methods[i]
// 过滤方法没有参数的
if(method.getParameterCount() < 1){
continue
}
// 方法的第一个参数是java.security.cert.Certificate
if(method.getParameterTypes()[0].getName() == "java.security.cert.Certificate"){
flag = true
break
}
}
if(!flag) return false

flag = false
// 获取类的所有成员变量
var fields = cls.class.getDeclaredFields()
// 类包含set集合
for(var k = 0; k < fields.length; k++){
var field = fields[k];
if(field.getType().getName() == "java.util.Set"){
flag = true
break
}
}
if(!flag) return false

console.log(clsName)
return true
}

for(var i = 0; i < classesNames.length; i++){
// 如果类名是OkhttpClient,就保存为变量
if(isOkhttpClient(classesNames[i])){
OkhttpClientClassName = classesNames[i]
//console.log(OkhttpClientClassName)
var splits = classesNames[i].split('.')
var len = splits.length
for(var j = 0; j < len-1; j++){
// Okhttp3.OkHttpClient
prefix = prefix + splits[j] + '.' // 当前包名的一个前缀,prefix=Okhttp3.
}
}
}
// 找CertificatePinner类
for(var i = 0; i < classesNames.length; i++){
if(isCertificatePinner(classesNames[i],prefix)){
CertificatePinnerClassName = classesNames[i]
//console.log(CertificatePinnerClassName)
}
}

console.error("Found Class: "+classesNames.length)
console.error("Okhttp's package prefix: "+prefix)
console.error("Find the OkhttpClient: "+OkhttpClientClassName)
console.error("Find the OkhttpCertificatePinner: "+CertificatePinnerClassName)

if(OkhttpClientClassName == "" || CertificatePinnerClassName == "" || prefix == ""){
console.error("Can't find the okhttp class")
}
})
}

function hook(){
Java.perform(function (){
var Modifier = Java.use("java.lang.reflect.Modifier")
//TrustAllManager,自定义类,hook方法置空,什么都不干
var TrustAllManagerClass = Java.registerClass({
name: "TrustAllManager",
implements:[Java.use("javax.net.ssl.X509TrustManager")],
methods: {
checkClientTrusted(chain, authType) {
console.log("checkClientTrusted Called!!")
},
checkServerTrusted(chain, authType) {
console.log("checkServerTrusted Called!!")
},
getAcceptedIssuers() {
return [];
},
}
})
var trustAllManagerHandle = TrustAllManagerClass.$new()
// 仿造
// SSLContext context = SSLContext.getInstance("TLS");
// context.init(null, trustManagers ,new SecureRandom());
// SSLSocketFactory factory = context.getSocketFactory();
var sslContext = Java.use("javax.net.ssl.SSLContext").getInstance("TLS")
sslContext.init(null,Java.array("Ljavax.net.ssl.X509TrustManager;",[trustAllManagerHandle]),null)
var sslSocketFactory = sslContext.getSocketFactory()

//HostnameVerify,自定义类
var MyHostnameVerify = Java.registerClass({
name: "MyHostnameVerify",
implements:[Java.use("javax.net.ssl.HostnameVerifier")],
methods: {
verify(hostname, session){
console.log(hostname)
return true
}
}
})
var myHostnameVerifyHandle = MyHostnameVerify.$new()

// 已经拿到okhttp3.OkHttpClient,拿它的内部类builder
var BuilderClassName = Java.use(OkhttpClientClassName).class.getDeclaredClasses()[0].getName()
var OkhttpClient$Buidler = Java.use(BuilderClassName)
// 通过反射获取builder的所有方法
var methods = OkhttpClient$Buidler.class.getDeclaredMethods()

for(var i = 0; i < methods.length; i++){
var method = methods[i]
// 过滤方法没有参数的,目标方法都有参数
// builder.sslSocketFactory(factory, (X509TrustManager) trustManagers[0]);
// builder.certificatePinner(cerBuilder.build());
// builder.hostnameVerifier(new HostnameVerifier(){});
if(method.getParameterCount() < 1){
continue
}
// 第一道校验,第一个参数的类名是javax.net.ssl.SSLSocketFactory就进行hook,参数替换为自定义参数
if(method.getParameterTypes()[0].getName() == "javax.net.ssl.SSLSocketFactory"){
var sslSocketFacotryMethodName = method.getName()
// 获取该方法的重载个数
var len = OkhttpClient$Buidler[sslSocketFacotryMethodName].overloads.length
// 遍历重载
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[sslSocketFacotryMethodName].overloads[j].implementation = function(SSLSocketFactory){
// 把参数替换为自定义的类
arguments[0] = sslSocketFactory
return this[sslSocketFacotryMethodName].apply(this,arguments)
}
}
console.log(sslSocketFacotryMethodName,"Hooked!")
}
// 第三道校验,第一个参数是javax.net.ssl.HostnameVerifier就进行hook,参数替换为自定义参数
if(method.getParameterTypes()[0].getName() == "javax.net.ssl.HostnameVerifier"){
var hostnameVerifierMethodName = method.getName()

var len = OkhttpClient$Buidler[hostnameVerifierMethodName].overloads.length
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[hostnameVerifierMethodName].overloads[j].implementation = function(hostnameVerifier){
arguments[0] = myHostnameVerifyHandle
return this[hostnameVerifierMethodName].apply(this,arguments)
}
}
console.log(hostnameVerifierMethodName, "Hooked!")
}
// 第二到校验,参数替换为它默认的,builder.certificatePinner(cerBuilder.build());
// 默认public static final CertificatePinner DEFAULT = new Builder().build();
if(method.getParameterTypes()[0].getName() == CertificatePinnerClassName){
var CertificatePinnerClass = Java.use(CertificatePinnerClassName)
var certificatePinnerMethodName = method.getName()
var len = OkhttpClient$Buidler[certificatePinnerMethodName].overloads.length
for(var j = 0; j < len; j++){
OkhttpClient$Buidler[certificatePinnerMethodName].overloads[j].implementation = function(){
console.log("certificatePinner add called!")
// 拿到所有的域,遍历
var fields = CertificatePinnerClass.class.getDeclaredFields()
for(var k = 0; k < fields.length; k++){
var field = fields[k];
var modifiers = field.getModifiers()
// public static final CertificatePinner DEFAULT = new Builder().build();
if(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)){
arguments[0] = field.get(CertificatePinnerClass.class)
}
}
return this[certificatePinnerMethodName].apply(this,arguments)
}
}
console.log( method.getName(),"Hooked!")
}
}

var CertificatePinnerClass = Java.use(CertificatePinnerClassName)
var methods = CertificatePinnerClass.class.getDeclaredMethods()
for (var i = 0; i < methods.length; i++){
var method = methods[i]
if(method.getReturnType().getName() == 'void'){
var methodName = method.getName()
console.log(methodName+" Hooked!")
var m_len = CertificatePinnerClass[methodName].overloads.length

for (var j = 0; j < m_len; j++){
if(CertificatePinnerClass[methodName].overloads[j].returnType.name == 'V'){
CertificatePinnerClass[methodName].overloads[j].implementation = function(){
console.log("certificatePinner check called!")
}
}
}
}
}

})
}


function main(){
loadOkhttpClient()
loadClasses()
findOkhttpClass()
hook()


}
setImmediate(main)
 评论