首页
论坛
课程
招聘
[基础理论] [逆向分析] [程序开发] [HOOK注入] [系统相关] [源码分析] [工具脚本] [其他] [原创]实现frida版的JustTrustMe(二)用frida实现JustTrustMe
2021-5-7 10:53 5644

[基础理论] [逆向分析] [程序开发] [HOOK注入] [系统相关] [源码分析] [工具脚本] [其他] [原创]实现frida版的JustTrustMe(二)用frida实现JustTrustMe

2021-5-7 10:53
5644

前言

有了上一篇JustTrustMe源码解读作铺垫,下面我讲解用frida实现JustTrustMe就很容易了。但是还是有些难点,比如JustTrustMe中有一些自定义的类和方法。那到底如何用frida的方式来实现自定义的类和方法?

怎么实现JustTrustMe自定义类和方法问题

自定义类虽然在frida语法中可以用registerClass的方式实现,但是由于类太复杂。我们可以把实现好的java代码打包成dex然后inject到目标进程中再跑hook脚本。

 

梳理JustTrustMe Main.java中内部类有如下3个:

1. ImSureItsLegitTrustManager

2. ImSureItsLegitHostnameVerifier

3. TrustAllSSLSocketFactory

直接用java实现上面三个类

同学们要对技术要有自己的判断力,虽然frida有registerClass让你用js语法定义一个类这样的功能。但是,这只是frida的附属价值。你看到这样的技术,了解一下就得了。不用学它,浪费精力!frida的openClassFile摆在那里让你可以注入dex,你居然还用registerClass。那你跟放弃用计算器,手动算数有什么区别?有什么本质的提高吗?并没有,还JB浪费时间。别人在进步,你在原地踏步!
IDA和GDB都说自己反汇编,哪个更好你没点B数吗?
Java和C++都说自己是面向对象,面向对象封装性哪个更好你没点B数吗?

1. ImSureItsLegitTrustManager实现

2. ImSureItsLegitHostnameVerifier实现

3. TrustAllSSLSocketFactory实现

Helper类实现

除了以上3个类,JustTrustMe还有几个静态方法需要我们实现。可以说上面几个类,最终都是通过这几个方法使用的。比如getSCCM()、getEmptySSLFactory()、getCCM()等
我们还是用Java直接实现,那么这些方法。

打包dex

关于怎么打包涉及到一些技术细节,暂时不便透露。感兴趣的朋友可以去https://github.com/CreditTone/hooker看我的radar.dex里面有所有打包好的代码。

 

 

现在JustTrustMe除hook逻辑之外的所有自定义类和方法我们现在全部搞定了。我们可以正式开始用frida来实现JustTrustMe所有hook点了。

用frida实现JustTrustMe的hook点

我们还是按上一篇《实现frida版的JustTrustMe(一)JustTrustMe官方源码学习》的源码解读顺序来逐一实现。为了让大家更容易理解,我不贴JustTrustMe的代码了。我贴每个hook点的伪代码,然后跟上frida的实现。这样大家会理解的清晰一点。

1、DefaultHttpClient的3个构造方法hook

上节分析:三个构造方法的目的都是在new DefaultHttpClient之后替换connManager参数。而getSCCM()方法就是返回一个不安全的ClientConnectionManager。

JustTrustMe实现的伪代码如下:

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);
}

frida的实现如下:

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
Java.perform(function() {
    var Helper = Java.use("gz.justtrustme.Helper");
    var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient");
    //被强奸的构造方法
    var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams');
    DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) {
        //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getCCM(v0, v1), v1);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!");
        return returnObj;
    };
    var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload();
    DefaultHttpClientClassInit_1602.implementation = function() {
        //使用DefaultHttpClientClassRapeConstructor强奸它
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!");
        return returnObj;
    };
    var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams');
    DefaultHttpClientClassInit_1603.implementation = function(v0) {
        //使用DefaultHttpClientClassRapeConstructor强奸它
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!");
        return returnObj;
    };
    //以上DefaultHttpClient的三个构造方法逻辑全部被我们用frida的方式替换了
}

2、X509TrustManagerExtensions的checkServerTrusted方法hook

上节分析:X509TrustManagerExtensions原方法会进行一系列的效应证书和服务器是否可信。这里xposed用了XC_MethodReplacement直接替换方法的执行体。

JustTrustMe实现的伪代码:

1
2
3
public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException {
        return chain;
}

frida的实现如下:

1
2
3
4
5
6
7
8
Java.perform(function() {
    var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions');
    var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String');
    X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) {
        console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!");
        return certsArr;
    };
}

3、NetworkSecurityTrustManager的checkPins方法hook

上节分析:原NetworkSecurityTrustManager类的checkPins方法List参数原型是List<X509Certificate>。校验X509Certificate集合是否合法,如果不合法就会抛CertificateException。这里xposed用了XC_MethodReplacement直接替换方法的执行体,并且什么都没做。

JustTrustMe实现的伪代码:

1
2
3
private void checkPins(List<X509Certificate> chain) throws CertificateException {
    //什么都不做
}

frida的实现如下:

1
2
3
4
5
6
7
8
Java.perform(function() {
    var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager');
    var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List');
    NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) {
        //什么都不做
        console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!");
    };
}

4. SSLSocketFactory的6参构造方法

上节分析:这个稍微有点复杂,但目的很简单。就是为了替换TrustManager,而TrustManager不可以直接传进来,是内部创建一个sslcontext来包装一个TrustManager[]。所以要new一个自定义的sslcontext然后把ImSureItsLegitTrustManager传进去。sslcontext对象要进行init()所以整个代码看起来比较多。

JustTrustMe实现的伪代码:

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;
    }

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Java.perform(function() {
    var Helper = Java.use("gz.justtrustme.Helper");
    //替换trustmanagers参数
    var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
    var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver');
    SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) {
        var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5);
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!");
        if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) {
            console.log("替换trustmanagers参数成功!");
        }else{
            console.log("替换trustmanagers参数失败!");
        }
        return returnObj;
    };
}

5. SSLSocketFactory的getSocketFactory方法

分析:逻辑过于简单,不解释了

JustTrustMe实现的伪代码:

1
2
3
4
public static SSLSocketFactory getSocketFactory() {
    //return NoPreloadHolder.DEFAULT_FACTORY;
    return new SSLSocketFactory();
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
Java.perform(function() {
    var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
    var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload();
    var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload();
    SSLSocketFactoryGetSocketFactoryMethod.implementation = function() {
        //强制用空的构造方法
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!");
        return SSLSocketFactoryEmptyConstructor.call(this);
    };
}

6. SSLSocketFactory的isSecure

上节分析:强制让isSecure返回true

JustTrustMe实现的伪代码:

1
2
3
4
public boolean isSecure(Socket sock)
        throws IllegalArgumentException {
        return true;
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
Java.perform(function() {
    var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
    var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket');
    SSLSocketFactoryIsSecure.implementation = function(v0) {
        //强制返回true
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!");
        return true;
    };
}

7. TrustManagerFactory的getTrustManagers方法

上节分析: 如果存在com.android.org.conscrypt.TrustManagerImpl这个类的,并且返回的TrustManager[]的长度大于0,并且TrustManager[]第0个是com.android.org.conscrypt.TrustManagerImpl实例,则什么都不操作。否则直接把返回值改成TrustManager[]{new ImSureItsLegitTrustManager()}

JustTrustMe实现的伪代码:

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()};
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
11
Java.perform(function() {
    var Helper = Java.use("gz.justtrustme.Helper");
    var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');
    var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload();
    TrustManagerFactoryGetTrustManagers.implementation = function() {
        var ret = TrustManagerFactoryGetTrustManagers.call(this);
        //替换getTrustManagers方法逻辑
        console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!");
        return Helper.replaceGetTrustManagers(this, ret);
    };
}

8. HttpsURLConnection的3个方法

上节分析:让setDefaultHostnameVerifier方法失效,让setSSLSocketFactory失效,让setHostnameVerifier失效

JustTrustMe实现的伪代码:

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) {
    //啥都不干
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Java.perform(function() {
    var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
    var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
    HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
    };
 
    var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
    HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
    };
 
    var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
    HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!");
    };
}

9. javax.net.ssl.SSLContext的init

上节分析:不用第0和低2个参数,而第1个参数使用TrustManager[]{new ImSureItsLegitTrustManager()}

JustTrustMe实现的伪代码:

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);
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
Java.perform(function() {
    var SSLContextClz = Java.use('javax.net.ssl.SSLContext');
    var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
    SSLContextClzInit.implementation = function(v0, v1, v2) {
        //将第二个参数强制替换为我们自己构造的不安全的TrustManagers
        SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2);
        console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!");
    };
}

10. Application的attach

上节分析:由于xposed基于zygote进程孵化app进程,hook非常早。所以如果它想对应用层的类进行hook的话,必须等Application的attach调用之后才能拿到应用级的ClassLoader。那么在attach之后他又进行了processOkHttp、processHttpClientAndroidLib、processXutils方法的调用,分别对应okhttp库的hook、httpclientandroidlib库的hook、org.xutils.http的hook。这三个是应用喜欢用的三方http库。

JustTrustMe实现的伪代码:

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);
    }

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
Java.perform(function() {
     var ApplicationClz = Java.use('android.app.Application');
    var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context');
    ApplicationClzAttach.implementation = function(context) {
        ApplicationClzAttach.call(this, context);
        //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码
        var classLoader = context.getClassLoader();
 
    };
}

11. processOkHttp

上节分析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替代了原来的逻辑。

OkHostnameVerifier.verify伪代码:

1
2
3
4
5
6
7
public boolean verify(String str, SSLSession sSLSession) {
    return true;
}
 
public boolean verify(String str, X509Certificate x509Certificate) {
    return true;
}

frida的实现如下:

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
function processOkHttp() {
    //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦
    if (classExists("com.squareup.okhttp.CertificatePinner")) {
        var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner');
        var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List');
        squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) {
            //什么都不做
            console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!");
        };
    }else{
        console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!");
    }
 
    if (classExists("okhttp3.CertificatePinner")) {
        try {
            var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner');
            var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List');
            okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) {
                //什么都不做
                console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!");
            };
        } catch (error) {
            console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
 
    if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) {
        try {
            var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier');
            var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession');
            OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) {
                //强制返回true
                console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!");
                return true;
            };
            var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate');
            OkHostnameVerifierVerify_8978.implementation = function(v0, v1) {
                //强制返回true
                console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!");
                return true;
            };
        } catch (error) {
            console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
 
}

12. processHttpClientAndroidLib

上节分析:一个冷门http库的hook

JustTrustMe实现的伪代码:

1
2
3
public final void verify(String arg1, String[] arg2, boolean arg3){
    //什么都不做
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
function processHttpClientAndroidLib() {
    if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) {
        var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
        var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean');
        OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) {
            //什么都不做
            console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!");
        };
    }else{
        console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!");
    }
}

13. processXutils

上节分析:一个冷门http库的hook

JustTrustMe实现的伪代码:

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对象替代
        //继续下面的代码逻辑
    }
}

frida的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function processXutils() {
    if (classExists("org.xutils.http.RequestParams")) {
        var RequestParamsClass = Java.use("org.xutils.http.RequestParams");
        var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
        RequestParamsClassSetSslSocketFactory.implementation = function(v0) {
            console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!");
            var Helper = Java.use("gz.justtrustme.Helper");
            //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory
            RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory());
        };
        var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
        RequestParamsClassSetHostnameVerifier.implementation = function(v0) {
            console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
            var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier");
            //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier
            RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new());
        };
    }
}

合并所有的frida实现

到此为止,JustTrustMe所有的hook点除了webviewclient之外全部用frida实现了。我们merge一下。

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
function classExists(className) {
    var exists = false;
    try {
        var clz = Java.use(className);
        exists = true;
    } catch(err) {
        //console.log(err);
    }
    return exists;
};
 
function loadDexfile(dexfile) {
    Java.perform(function() {
        Java.openClassFile(dexfile).load();
        //console.log("load " + dexfile);
    });
};
 
var loadedXRadar = false;
function loadXRadarDexfile() {
    loadedXRadar = true;
    loadDexfile('/data/user/0/com.xxxxx.xxx/radar.dex');
};
 
 
loadXRadarDexfile();
 
function hasTrustManagerImpl() {
    return classExists("com.android.org.conscrypt.TrustManagerImpl");
}
 
function newArrayList() {
    var ArrayListClz = Java.use('java.util.ArrayList');
    return ArrayListClz.$new();
}
 
 
function processOkHttp() {
    //知道你为什么有时候用JustTrustMe失败吗,因为app代码混淆了下面这些类你改到对应的类和方法就行啦
    if (classExists("com.squareup.okhttp.CertificatePinner")) {
        var squareupOkhttp3CertificatePinnerClz = Java.use('com.squareup.okhttp.CertificatePinner');
        var squareupOkhttp3CertificatePinnerClzCheck = squareupOkhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List');
        squareupOkhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) {
            //什么都不做
            console.log("com.squareup.okhttp.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!");
        };
    }else{
        console.error("没找到com.squareup.okhttp.CertificatePinner类,这是android系统自带的类没找到就算求了。不同系统不一样,不用找了!!!");
    }
 
    if (classExists("okhttp3.CertificatePinner")) {
        try {
            var okhttp3CertificatePinnerClz = Java.use('okhttp3.CertificatePinner');
            var okhttp3CertificatePinnerClzCheck = okhttp3CertificatePinnerClz.check.overload('java.lang.String', 'java.util.List');
            okhttp3CertificatePinnerClzCheck.implementation = function(v0, v1) {
                //什么都不做
                console.log("okhttp3.CertificatePinner.check('java.lang.String', 'java.util.List') was hooked!");
            };
        } catch (error) {
            console.error("okhttp3.CertificatePinner的check方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.CertificatePinner类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
 
    if (classExists("okhttp3.internal.tls.OkHostnameVerifier")) {
        try {
            var OkHostnameVerifierClz = Java.use('okhttp3.internal.tls.OkHostnameVerifier');
            var OkHostnameVerifierClzVerify_5791 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession');
            OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1) {
                //强制返回true
                console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'javax.net.ssl.SSLSession') was hooked!");
                return true;
            };
            var OkHostnameVerifierVerify_8978 = OkHostnameVerifierClz.verify.overload('java.lang.String', 'java.security.cert.X509Certificate');
            OkHostnameVerifierVerify_8978.implementation = function(v0, v1) {
                //强制返回true
                console.log("okhttp3.internal.tls.OkHostnameVerifier.verify('java.lang.String', 'java.security.cert.X509Certificate') was hooked!");
                return true;
            };
        } catch (error) {
            console.error("okhttp3.internal.tls.OkHostnameVerifier的verify方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.internal.tls.OkHostnameVerifier类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
 
}
 
 
function processXutils() {
    if (classExists("org.xutils.http.RequestParams")) {
        var RequestParamsClass = Java.use("org.xutils.http.RequestParams");
        var RequestParamsClassSetSslSocketFactory = RequestParamsClass.setSslSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
        RequestParamsClassSetSslSocketFactory.implementation = function(v0) {
            console.log("org.xutils.http.RequestParams.setSslSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!");
            var Helper = Java.use("gz.justtrustme.Helper");
            //替换javax.net.ssl.SSLSocketFactory参数为我们的EmptySSLFactory
            RequestParamsClassSetSslSocketFactory.call(this, Helper.getEmptySSLFactory());
        };
        var RequestParamsClassSetHostnameVerifier = RequestParamsClass.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
        RequestParamsClassSetHostnameVerifier.implementation = function(v0) {
            console.log("org.xutils.http.RequestParams.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
            var ImSureItsLegitHostnameVerifier = Java.use("gz.justtrustme.ImSureItsLegitHostnameVerifier");
            //替换javax.net.ssl.HostnameVerifier参数为我们的ImSureItsLegitHostnameVerifier
            RequestParamsClassSetHostnameVerifier.call(this, ImSureItsLegitHostnameVerifier.$new());
        };
    }
}
 
function processHttpClientAndroidLib() {
    if (classExists("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier")) {
        var AbstractVerifierClass = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
        var OkHostnameVerifierClzVerify_5791 = AbstractVerifierClass.verify.overload('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean');
        OkHostnameVerifierClzVerify_5791.implementation = function(v0, v1, v2, v3) {
            //什么都不做
            console.log("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier.verify('java.lang.String', '[Ljava.lang.String;', '[Ljava.lang.String;', 'boolean') was hooked!");
        };
    }else{
        console.error("没找到ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier类,但是这个类用的少。不用找了!");
    }
}
 
Java.perform(function() {
    var Helper = Java.use("gz.justtrustme.Helper");
    var DefaultHttpClientClass = Java.use("org.apache.http.impl.client.DefaultHttpClient");
    //被强奸的构造方法
    var DefaultHttpClientClassRapeConstructor = DefaultHttpClientClass.$init.overload('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams');
    DefaultHttpClientClassRapeConstructor.implementation = function(v0, v1) {
        //被强奸的构造方法被调用的话,我们替换调ClientConnectionManager参数为我们的
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v1);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.conn.ClientConnectionManager', 'org.apache.http.params.HttpParams') was hooked!");
        return returnObj;
    };
    var DefaultHttpClientClassInit_1602 = DefaultHttpClientClass.$init.overload();
    DefaultHttpClientClassInit_1602.implementation = function() {
        //使用DefaultHttpClientClassRapeConstructor强奸它
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), null);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init() was hooked!");
        return returnObj;
    };
    var DefaultHttpClientClassInit_1603 = DefaultHttpClientClass.$init.overload('org.apache.http.params.HttpParams');
    DefaultHttpClientClassInit_1603.implementation = function(v0) {
        //使用DefaultHttpClientClassRapeConstructor强奸它
        var returnObj = DefaultHttpClientClassRapeConstructor.call(this, Helper.getSCCM(), v0);
        console.log("org.apache.http.impl.client.DefaultHttpClient.$init('org.apache.http.params.HttpParams') was hooked!");
        return returnObj;
    };
    //以上DefaultHttpClient的三个构造方法逻辑全部被我们替换了
    var X509TrustManagerExtensionsClass = Java.use('android.net.http.X509TrustManagerExtensions');
    var X509TrustManagerExtensionsClassCheckServerTrusted = X509TrustManagerExtensionsClass.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String');
    X509TrustManagerExtensionsClassCheckServerTrusted.implementation = function(certsArr, v1, v2) {
        console.log("android.net.http.X509TrustManagerExtensions.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!");
        return certsArr;
    };
    var NetworkSecurityTrustManagerClass = Java.use('android.security.net.config.NetworkSecurityTrustManager');
    var NetworkSecurityTrustManagerClassCheckPins = NetworkSecurityTrustManagerClass.checkPins.overload('java.util.List');
    NetworkSecurityTrustManagerClassCheckPins.implementation = function(v0) {
        //什么都不做
        console.log("android.security.net.config.NetworkSecurityTrustManager.checkPins('java.util.List') was hooked!");
    };
 
    //替换trustmanagers参数
    var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
    var SSLSocketFactoryRapeConstructor = SSLSocketFactory.$init.overload('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver');
    SSLSocketFactoryRapeConstructor.implementation = function(v0, v1, v2, v3, v4, v5) {
        var returnObj = SSLSocketFactoryRapeConstructor.call(this, v0, v1, v2, v3, v4, v5);
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.$init('java.lang.String', 'java.security.KeyStore', 'java.lang.String', 'java.security.KeyStore', 'java.security.SecureRandom', 'org.apache.http.conn.scheme.HostNameResolver') was hooked!");
        if (Helper.reInitSSLSocketFactory(returnObj, v0, v1, v2, v3, v4, v5)) {
            console.log("替换trustmanagers参数成功!");
        }else{
            console.log("替换trustmanagers参数失败!");
        }
        return returnObj;
    };
 
    var SSLSocketFactoryGetSocketFactoryMethod = SSLSocketFactory.getSocketFactory.overload();
    var SSLSocketFactoryEmptyConstructor = SSLSocketFactory.$init.overload();
    SSLSocketFactoryGetSocketFactoryMethod.implementation = function() {
        //强制用空的构造方法
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.getSocketFactory() was hooked!");
        return SSLSocketFactoryEmptyConstructor.call(this);
    };
 
    var SSLSocketFactoryIsSecure = SSLSocketFactory.isSecure.overload('java.net.Socket');
    SSLSocketFactoryIsSecure.implementation = function(v0) {
        //强制返回true
        console.log("org.apache.http.conn.ssl.SSLSocketFactory.isSecure('java.net.Socket') was hooked!");
        return true;
    };
 
    var TrustManagerFactory = Java.use('javax.net.ssl.TrustManagerFactory');
    var TrustManagerFactoryGetTrustManagers = TrustManagerFactory.getTrustManagers.overload();
    TrustManagerFactoryGetTrustManagers.implementation = function() {
        var ret = TrustManagerFactoryGetTrustManagers.call(this);
        //替换getTrustManagers方法逻辑
        console.log("javax.net.ssl.TrustManagerFactory.getTrustManagers() was hooked!");
        return Helper.replaceGetTrustManagers(this, ret);
    };
 
    var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
    var HttpsURLConnectionSetDefaultHostnameVerifier = HttpsURLConnection.setDefaultHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
    HttpsURLConnectionSetDefaultHostnameVerifier.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
    };
 
    var HttpsURLConnectionSetHostnameVerifier = HttpsURLConnection.setHostnameVerifier.overload('javax.net.ssl.HostnameVerifier');
    HttpsURLConnectionSetHostnameVerifier.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.HttpsURLConnection.setHostnameVerifier('javax.net.ssl.HostnameVerifier') was hooked!");
    };
 
    var HttpsURLConnectionSetSSLSocketFactory = HttpsURLConnection.setSSLSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
    HttpsURLConnectionSetSSLSocketFactory.implementation = function(v0) {
        //什么都不做
        console.log("javax.net.ssl.SSLSocketFactory.setSSLSocketFactory('javax.net.ssl.SSLSocketFactory') was hooked!");
    };
 
    var SSLContextClz = Java.use('javax.net.ssl.SSLContext');
    var SSLContextClzInit = SSLContextClz.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');
    SSLContextClzInit.implementation = function(v0, v1, v2) {
        //将第二个参数强制替换为我们自己构造的不安全的TrustManagers
        SSLContextClzInit.call(this, v0, Helper.getImSureTrustManagers(), v2);
        console.log("javax.net.ssl.SSLContext.init('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom') was hooked!");
    };
 
    var ApplicationClz = Java.use('android.app.Application');
    var ApplicationClzAttach = ApplicationClz.attach.overload('android.content.Context');
    ApplicationClzAttach.implementation = function(context) {
        ApplicationClzAttach.call(this, context);
        //注意justTrustMe使用的是afterHookedMethod,所以我们用frida也必须在原方法call完之后执行我们的代码
        var classLoader = context.getClassLoader();
 
    };
 
    if (hasTrustManagerImpl()) {
        var TrustManagerImplClz = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        var TrustManagerImplCheckServerTrusted_8813 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String');
        TrustManagerImplCheckServerTrusted_8813.implementation = function(v0, v1, v2) {
            console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String') was hooked!");
            return newArrayList();
        };
        var TrustManagerImplCheckServerTrusted_7015 = TrustManagerImplClz.checkServerTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession');
        TrustManagerImplCheckServerTrusted_7015.implementation = function(v0, v1, v2) {
            console.log("com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession') was hooked!");
            return newArrayList();
        };
        var TrustManagerImplCheckTrusted_5587 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean');
        TrustManagerImplCheckTrusted_5587.implementation = function(v0, v1, v2, v3) {
            console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'java.lang.String', 'boolean') was hooked!");
            return newArrayList();
        };
        var TrustManagerImplCheckTrusted_9999 = TrustManagerImplClz.checkTrusted.overload('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean');
        TrustManagerImplCheckTrusted_9999.implementation = function(v0, v1, v2, v3, v4) {
            console.log("com.android.org.conscrypt.TrustManagerImpl.checkTrusted('[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'javax.net.ssl.SSLSession', 'javax.net.ssl.SSLParameters', 'boolean') was hooked!");
            return newArrayList();
        };
    }
 
    processOkHttp();
    processXutils();
    processHttpClientAndroidLib();
})

总结

JustTrustMe的官方源码hook点虽然全部实现,但是由于Android一直在更新和app的混淆不断变强使得我们hook点经常hook不到。那么下一篇我们就来看看如何添加这些新的hook点和对抗混淆frida版本的JustTrustMe


[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年秋季班火热招生!!

最后于 2021-5-7 10:54 被爬虫不看学历编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (12)
雪    币: 133
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
非易 活跃值 2021-5-7 11:01
2
1
期待大佬新作,逆向武功打穿看雪。
雪    币: 21
活跃值: 活跃值 (115)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AunCss 活跃值 2021-5-7 11:09
3
0
期待大佬新作,逆向武功打穿看雪。
雪    币: 819
活跃值: 活跃值 (1575)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
爬虫不看学历 活跃值 2021-5-7 11:25
4
0

************

最后于 2021-5-7 11:54 被kanxue编辑 ,原因: 就事论事,勿人身攻击!
雪    币: 164
活跃值: 活跃值 (476)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ogli324 活跃值 2021-5-7 11:29
5
0


有威胁、恐吓行为的,尚未构成犯罪的,会被公安机关处五日以下拘留或者五百元以下罚款;情节较重的,处五日以上十日以下拘留,可以并处五百元以下罚款。

最后于 2021-5-7 11:35 被ogli324编辑 ,原因:
雪    币: 2185
活跃值: 活跃值 (362)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
丶咖啡猫丶 活跃值 2021-5-7 11:31
6
0
https://blog.csdn.net/tl3shi/article/details/112001225 这个自述就是大佬吗?
雪    币: 616
活跃值: 活跃值 (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
萝卜炖羊肉 活跃值 2021-5-7 11:40
7
0
期待大佬新作,逆向武功打穿看雪。
雪    币: 2579
活跃值: 活跃值 (364)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
VNRKDOEA 活跃值 2021-5-7 11:47
8
0

雪    币: 819
活跃值: 活跃值 (1575)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
爬虫不看学历 活跃值 2021-5-7 11:48
9
0
丶咖啡猫丶 https://blog.csdn.net/tl3shi/article/details/112001225 这个自述就是大佬吗?
@咖啡猫 不是
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
吃瓜大王 活跃值 2021-5-7 11:53
10
0
爬虫不看学历 有一天我看见他,我一定会卸他几个手指头
届时务必直播
雪    币: 37
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
zrail 活跃值 2021-5-7 12:11
11
0
雪    币: 13
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
网络小男孩 活跃值 2021-5-7 13:30
12
0

恭喜大佬炒作成功

最后于 2021-5-7 13:31 被网络小男孩编辑 ,原因:
雪    币: 1167
活跃值: 活跃值 (647)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
lushanu 活跃值 2021-5-7 16:12
13
0
郭钟哥哥yyds
游客
登录 | 注册 方可回帖
返回