前言
对于绕过ssl pinning的产品有很多,其中最著名的当属JustTrustMe。其他的sslUnPinning产品思想很多都是从JustTrustMe那里抄来的,由于JustTrustMe原作者也不更新了但是反sslUnPinning技术一直在前进,所以我决定用1一个月的业余时间来重新打造一款实用的sslUnPinning产品。
JustTrustMe我是很认可他的插桩点位的思路的。但是,本人是不认可xposed的。xposed的hook机制从设计之初就走了一条“不归路”,因为xposed有太多的特征可以被app检测。主要是xposed必须基于替换受精卵进程来实现,并且还要安装对应的app来管理插件,而且插件开发也要是单独的android工程。这个对于被攻击app来讲非常好从多个维度去检测xposed的存在,只是人家不想搞的这么明显大多不会强制退出app罢了。比如微信大家去jadx搜索代码,里面妥妥去检测了xposed,人家早就把你标记了。有些小team拿着官方已经停止更新的xposed改改包名,然后称之为魔改过反检测。其实,我要检测你,你怎么改都反不了。你包名改掉,但是你自定义的包名依然在调用栈里面。通过对比纯净android系统的正常堆栈,你立马被标记为灰度用户。还可以通过扫描已经安装的app列表,发现类似justtrustme之类的应用立马标记。还有你xposed管理本身也是一个app,你怎么隐藏? 改操作系统吗?但是,话又回来了,你都可以改操作系统了那你为什么不直接在framework层直接嵌入hook机制呢?因为,你技术还没到那一步,你只是停留在andorid项目改改包名的阶段,只能靠着这个去收割一些新人的韭菜。不好意思阿,我这人说话就是这么直!你费劲巴拉的搞一个大型项目,完了效果并不是真的全面反检测。我觉得没必要去做。
我想xposed的作者rovo89也是看到了这一点,所以没有再对xposed进行更新了。大庄家都不玩了,我们这些蝼蚁就要认清局势。该废弃的技术废弃,要舍得抛弃陈旧的技术这样你才能不断进步。我要把JustTrustMe用frida实现一版。所以先出一篇JustTrustMe源码分析,等大家熟悉了JustTrustMe源码我再带着大家用frida去搞一个"JustTrustMe":
JustTrustMe源码阅读和剖析
请大家先打开JustTrustMe的Main.java,我下面会一一列举JustTrustMe的每个方法,以及它的作用和对应java的伪代码。
1、DefaultHttpClient的3个构造方法hook
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 | / * external / apache - http / src / org / apache / http / impl / client / DefaultHttpClient.java * /
/ * public DefaultHttpClient() * /
findAndHookConstructor(DefaultHttpClient. class , new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
setObjectField(param.thisObject, "defaultParams" , null);
setObjectField(param.thisObject, "connManager" , getSCCM());
}
});
/ * external / apache - http / src / org / apache / http / impl / client / DefaultHttpClient.java * /
/ * public DefaultHttpClient(HttpParams params) * /
findAndHookConstructor(DefaultHttpClient. class , HttpParams. class , new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
setObjectField(param.thisObject, "defaultParams" , (HttpParams) param.args[ 0 ]);
setObjectField(param.thisObject, "connManager" , getSCCM());
}
});
/ * external / apache - http / src / org / apache / http / impl / client / DefaultHttpClient.java * /
/ * public DefaultHttpClient(ClientConnectionManager conman, HttpParams params) * /
findAndHookConstructor(DefaultHttpClient. class , ClientConnectionManager. class , HttpParams. class , new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
HttpParams params = (HttpParams) param.args[ 1 ];
setObjectField(param.thisObject, "defaultParams" , params);
setObjectField(param.thisObject, "connManager" , getCCM(param.args[ 0 ], params));
}
});
|
分析:三个构造方法的目的都是在new DefaultHttpClient之后替换connManager参数。而getSCCM()方法就是返回一个不安全的ClientConnectionManager。
getSCCM()和getCCM()实现如下:
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 | / / Create a SingleClientConnManager that trusts everyone!
public ClientConnectionManager getSCCM() {
KeyStore trustStore;
try {
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new TrustAllSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme( "http" , PlainSocketFactory.getSocketFactory(), 80 ));
registry.register(new Scheme( "https" , sf, 443 ));
ClientConnectionManager ccm = new SingleClientConnManager(null, registry);
return ccm;
} catch (Exception e) {
return null;
}
}
/ / This function creates a ThreadSafeClientConnManager that trusts everyone!
public ClientConnectionManager getTSCCM(HttpParams params) {
KeyStore trustStore;
try {
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new TrustAllSSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme( "http" , PlainSocketFactory.getSocketFactory(), 80 ));
registry.register(new Scheme( "https" , sf, 443 ));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return ccm;
} catch (Exception e) {
return null;
}
}
/ / This function determines what object we are dealing with.
public ClientConnectionManager getCCM( Object o, HttpParams params) {
String className = o.getClass().getSimpleName();
if (className.equals( "SingleClientConnManager" )) {
return getSCCM();
} else if (className.equals( "ThreadSafeClientConnManager" )) {
return getTSCCM(params);
}
return null;
}
|
那么,由此我们得到经过hook之后三个方法的伪代码逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 | public DefaultHttpClient(final ClientConnectionManager conman, final HttpParams params) {
super (getCCM(conman, params), params);
}
public DefaultHttpClient(final HttpParams params) {
super (getSCCM(), params);
}
public DefaultHttpClient() {
super (getSCCM(), null);
}
|
2、X509TrustManagerExtensions的checkServerTrusted方法hook
1 2 3 4 5 6 | findAndHookMethod(X509TrustManagerExtensions. class , "checkServerTrusted" , X509Certificate[]. class , String. class , String. class , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return param.args[ 0 ];
}
});
|
分析:X509TrustManagerExtensions原方法会进行一系列的效应证书和服务器是否可信。这里xposed用了XC_MethodReplacement直接替换方法的执行体。
那么,伪代码逻辑如下:
1 2 3 | public List <X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException {
return chain;
}
|
3、NetworkSecurityTrustManager的checkPins方法hook
1 2 3 4 5 6 | findAndHookMethod( "android.security.net.config.NetworkSecurityTrustManager" , lpparam.classLoader, "checkPins" , List . class , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});
|
分析:原NetworkSecurityTrustManager类的checkPins方法List参数原型是List<X509Certificate>。校验X509Certificate集合是否合法,如果不合法就会抛CertificateException。这里xposed用了XC_MethodReplacement直接替换方法的执行体,并且什么都没做。
那么,伪代码逻辑如下:
1 2 3 | private void checkPins( List <X509Certificate> chain) throws CertificateException {
}
|
4. SSLSocketFactory的6参构造方法
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 | / * external / apache - http / src / org / apache / http / conn / ssl / SSLSocketFactory.java * /
/ * public SSLSocketFactory( ... ) * /
findAndHookConstructor(SSLSocketFactory. class , String. class , KeyStore. class , String. class , KeyStore. class ,
SecureRandom. class , HostNameResolver. class , new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String algorithm = (String) param.args[ 0 ];
KeyStore keystore = (KeyStore) param.args[ 1 ];
String keystorePassword = (String) param.args[ 2 ];
SecureRandom random = (SecureRandom) param.args[ 4 ];
KeyManager[] keymanagers = null;
TrustManager[] trustmanagers = null;
if (keystore ! = null) {
keymanagers = (KeyManager[]) callStaticMethod(SSLSocketFactory. class , "createKeyManagers" , keystore, keystorePassword);
}
trustmanagers = new TrustManager[]{new ImSureItsLegitTrustManager()};
setObjectField(param.thisObject, "sslcontext" , SSLContext.getInstance(algorithm));
callMethod(getObjectField(param.thisObject, "sslcontext" ), "init" , keymanagers, trustmanagers, random);
setObjectField(param.thisObject, "socketfactory" ,
callMethod(getObjectField(param.thisObject, "sslcontext" ), "getSocketFactory" ));
}
});
|
分析:这个稍微有点复杂,但目的很简单。就是为了替换TrustManager,而TrustManager不可以直接传进来,是内部创建一个sslcontext来包装一个TrustManager[]。所以要new一个自定义的sslcontext然后把ImSureItsLegitTrustManager传进去。sslcontext对象要进行init()所以整个代码看起来比较多。
给你看伪代码,那逻辑将一目了然:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public SSLSocketFactory(
String algorithm,
final KeyStore keystore,
final String keystorePassword,
final KeyStore truststore,
final SecureRandom random,
final HostNameResolver nameResolver)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException
{
super ();
if (algorithm = = null) {
algorithm = TLS;
}
KeyManager[] keymanagers = null;
if (keystore ! = null) {
keymanagers = createKeyManagers(keystore, keystorePassword);
}
TrustManager[] trustmanagers = new TrustManager[]{new ImSureItsLegitTrustManager()};; / / 这里被替换了,就这么简单
this.sslcontext = SSLContext.getInstance(algorithm);
this.sslcontext.init(keymanagers, trustmanagers, random);
this.socketfactory = this.sslcontext.getSocketFactory();
this.nameResolver = nameResolver;
}
|
5. SSLSocketFactory的getSocketFactory方法
1 2 3 4 5 6 | findAndHookMethod( "org.apache.http.conn.ssl.SSLSocketFactory" , lpparam.classLoader, "getSocketFactory" , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return (SSLSocketFactory) newInstance(SSLSocketFactory. class );
}
});
|
分析:很好理解
伪代码:
1 2 3 4 | public static SSLSocketFactory getSocketFactory() {
/ / return NoPreloadHolder.DEFAULT_FACTORY;
return new SSLSocketFactory();
}
|
6. SSLSocketFactory的isSecure
1 2 3 4 5 6 | findAndHookMethod( "org.apache.http.conn.ssl.SSLSocketFactory" , lpparam.classLoader, "isSecure" , Socket. class , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return true;
}
});
|
分析:强制让isSecure返回true
伪代码:
1 2 3 4 | public boolean isSecure(Socket sock)
throws IllegalArgumentException {
return true;
}
|
7. TrustManagerFactory的getTrustManagers方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | findAndHookMethod( "javax.net.ssl.TrustManagerFactory" , lpparam.classLoader, "getTrustManagers" , new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
if (hasTrustManagerImpl()) {
Class<?> cls = findClass( "com.android.org.conscrypt.TrustManagerImpl" , lpparam.classLoader);
TrustManager[] managers = (TrustManager[]) param.getResult();
if (managers.length > 0 && cls . isInstance (managers[ 0 ]))
return ;
}
param.setResult(new TrustManager[]{new ImSureItsLegitTrustManager()});
}
});
|
分析:如果存在com.android.org.conscrypt.TrustManagerImpl这个类的,并且返回的TrustManager[]的长度大于0,并且TrustManager[]第0个是com.android.org.conscrypt.TrustManagerImpl实例,则什么都不操作。否则直接把返回值改成TrustManager[]{new ImSureItsLegitTrustManager()}
伪代码:
1 2 3 4 5 6 7 8 9 10 | public final TrustManager[] getTrustManagers() {
TrustManager[] originResult = factorySpi.engineGetTrustManagers(); / / 原代码是直接 return factorySpi.engineGetTrustManagers()
if (hasTrustManagerImpl()) {
Class<?> cls = Class. from ( "com.android.org.conscrypt.TrustManagerImpl" );
if (originResult.length > 0 && cls . isInstance (originResult[ 0 ])) {
return originResult;
}
}
return new TrustManager[]{new ImSureItsLegitTrustManager()};
}
|
8. HttpsURLConnection的3个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | findAndHookMethod( "javax.net.ssl.HttpsURLConnection" , lpparam.classLoader, "setDefaultHostnameVerifier" ,
HostnameVerifier. class , new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});
findAndHookMethod( "javax.net.ssl.HttpsURLConnection" , lpparam.classLoader, "setSSLSocketFactory" , javax.net.ssl.SSLSocketFactory. class ,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});
findAndHookMethod( "javax.net.ssl.HttpsURLConnection" , lpparam.classLoader, "setHostnameVerifier" , HostnameVerifier. class ,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
return null;
}
});
|
分析:让setDefaultHostnameVerifier方法失效,让setSSLSocketFactory失效,让setHostnameVerifier失效
伪代码:
1 2 3 4 5 6 7 8 9 10 11 | public static void setDefaultHostnameVerifier(HostnameVerifier v) {
/ / 啥都不干
}
public void setSSLSocketFactory(SSLSocketFactory sf) {
/ / 啥都不干
}
public static void setDefaultHostnameVerifier(HostnameVerifier v) {
/ / 啥都不干
}
|
9. javax.net.ssl.SSLContext的init
1 2 3 4 5 6 7 8 9 10 11 | findAndHookMethod( "javax.net.ssl.SSLContext" , lpparam.classLoader, "init" , KeyManager[]. class , TrustManager[]. class , SecureRandom. class , new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[ 0 ] = null;
param.args[ 1 ] = new TrustManager[]{new ImSureItsLegitTrustManager()};
param.args[ 2 ] = null;
}
});
|
分析:不用第0和低2个参数,而第1个参数使用TrustManager[]{new ImSureItsLegitTrustManager()}
伪代码:
1 2 3 4 5 6 | public final void init(KeyManager[] km, TrustManager[] tm,
SecureRandom random)
throws KeyManagementException {
tm = new TrustManager[]{new ImSureItsLegitTrustManager()};
contextSpi.engineInit(null, tm, null);
}
|
10. Application的attach
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | findAndHookMethod( "android.app.Application" ,
lpparam.classLoader,
"attach" ,
Context. class ,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
/ / Hook OkHttp or third party libraries.
Context context = (Context) param.args[ 0 ];
processOkHttp(context.getClassLoader());
processHttpClientAndroidLib(context.getClassLoader());
processXutils(context.getClassLoader());
}
}
);
|
分析:由于xposed基于zygote进程孵化app进程,hook非常早。所以如果它想对应用层的类进行hook的话,必须等Application的attach调用之后才能拿到应用级的ClassLoader。那么在attach之后他又进行了processOkHttp、processHttpClientAndroidLib、processXutils方法的调用,分别对应okhttp库的hook、httpclientandroidlib库的hook、org.xutils.http的hook。这三个是应用喜欢用的三方http库。
伪代码:
1 2 3 4 5 6 7 8 | final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
ClassLoader appClassLoader = context.getClassLoader();
processOkHttp(appClassLoader);
processHttpClientAndroidLib(appClassLoader);
processXutils(appClassLoader);
}
|
11. processOkHttp
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 | void processOkHttp(ClassLoader classLoader) {
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" );
}
try {
classLoader.loadClass( "okhttp3.CertificatePinner" );
findAndHookMethod( "okhttp3.CertificatePinner" ,
classLoader,
"check" ,
String. class ,
List . class ,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return null;
}
});
} catch (ClassNotFoundException e) {
Log.d(TAG, "OKHTTP 3.x not found in " + currentPackageName + " -- not hooking" );
}
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)" );
}
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)(" );
}
|
分析okhttp包名不统一问题:由于okhttp 2.x和3.x包名相差比较大。所以这里第一个hook你可以看到justTrustMe试图去找com.squareup.okhttp.CertificatePinner类,这是为了尽量兼容老安卓项目。
分析CertificatePinner.check:无论是okhttp2.x还是3.x、4.x都是有CertificatePinner.check(String str, List<Certificate> list)这个方法的。可以上面第一二个hook做的是XC_MethodReplacement,并且什么都不操作。对比原check方法,我们知道这个目的是为了阻止check抛SSLPeerUnverifiedException异常。
CertificatePinner.check伪代码:
1 2 3 4 | public void check(String str , List <Certificate> list ) {
/ / 原代码校验失败会throw new throw new SSLPeerUnverifiedException(sb.toString());
/ / hook之后相当于什么都不操作
}
|
分析:第三四个hook OkHostnameVerifier.verify(),校验hostname是否合法最终会调用verify(str, (X509Certificate) sSLSession.getPeerCertificates()[0]);说明还是对远程服务证书进行了校验,这里直接用return true替代了原来的逻辑。
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 | public boolean verify(String str , SSLSession sSLSession) {
return true;
}
public boolean verify(String str , X509Certificate x509Certificate) {
return true;
}
``
```java
void processHttpClientAndroidLib(ClassLoader classLoader) {
try {
classLoader.loadClass( "ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier" );
findAndHookMethod( "ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier" , classLoader, "verify" ,
String. class , String[]. class , String[]. class , boolean. class ,
new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
return null;
}
});
} catch (ClassNotFoundException e) {
/ / pass
Log.d(TAG, "httpclientandroidlib not found in " + currentPackageName + "-- not hooking" );
}
}
|
分析:一个冷门http库的hook
伪代码:
1 2 3 | public final void verify(String arg1, String[] arg2, boolean arg3){
/ / 什么都不做
}
|
13. processXutils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private void processXutils(ClassLoader classLoader) {
try {
classLoader.loadClass( "org.xutils.http.RequestParams" );
findAndHookMethod( "org.xutils.http.RequestParams" , classLoader, "setSslSocketFactory" , javax.net.ssl.SSLSocketFactory. class , new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super .beforeHookedMethod(param);
param.args[ 0 ] = getEmptySSLFactory();
}
});
findAndHookMethod( "org.xutils.http.RequestParams" , classLoader, "setHostnameVerifier" , HostnameVerifier. class , new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super .beforeHookedMethod(param);
param.args[ 0 ] = new ImSureItsLegitHostnameVerifier();
}
});
} catch (Exception e) {
Log.d(TAG, "org.xutils.http.RequestParams not found in " + currentPackageName + "-- not hooking" );
}
}
|
分析:一个冷门http库的hook
伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package org.xutils.http;
public class RequestParams {
public void setSslSocketFactory(javax.net.ssl.SSLSocketFactory arg1) {
arg1 = getEmptySSLFactory(); / / 把参数用getEmptySSLFactory();返回值替代
/ / 继续下面的代码逻辑
}
public void setHostnameVerifier(javax.net.ssl.HostnameVerifier arg1) {
arg1 = new ImSureItsLegitHostnameVerifier(); / / 把参数用ImSureItsLegitHostnameVerifier对象替代
/ / 继续下面的代码逻辑
}
}
|
总结
到目前为止,我们已经把JustTrustMe所有的hook点位全部分析了一遍。中间如果大家还是有不解的地方可以去查看每个类对应的源码,下列我帮大家找了一些
JustTrustMe Main.java
apache http DefaultHttpClient.java
android framework X509TrustManagerExtensions.java
android framework NetworkSecurityTrustManager.java
apache http SSLSocketFactory.java
javax TrustManagerFactory.java
javax HttpsURLConnection.java
javax SSLContext.java
android framework TrustManagerImpl.java
okhttp3 CertificatePinner.java
okhttp3 OkHostnameVerifier.java
[培训]《安卓高级研修班(网课)》月薪三万计划
最后于 2021-5-6 10:34
被爬虫不看学历编辑
,原因: