首页
社区
课程
招聘
[原创]实现frida版的JustTrustMe(三)frida版JustTrustMe升级和混淆对抗
发表于: 2021-5-7 21:30 13069

[原创]实现frida版的JustTrustMe(三)frida版JustTrustMe升级和混淆对抗

2021-5-7 21:30
13069

上一篇《实现frida版的JustTrustMe(二)用frida实现JustTrustMe》,我们已经使用frida实现了JustTrustMe所有的hook点。但是由于不知明的原因JustTrustMe项目不更新和apk混淆常态化,使JustTrustMe有效hook点位越来越少。本篇,我们将在上篇just_trust_me.js的基础上来做升级,使它比官方的JustTrustMe更强。哦!不!是比现在所有的翻版JustTrustMe更强。

我找到3处新的hook点位,实现如下

在JustTrustMe官方源码中没有这个hook点,我觉得还有有必要加一下。

对抗混淆我们主要针对okhttp进行对抗,因为okhttp真的太火爆了。现在的商业app几乎没有不用ok库做网络请求的,当然QUIC、MMTLS这些超级大厂app的私有协议除外。对付非超一线app,我们把okhttp的混淆对抗做好就足够!!!

okhttp一共有3个混淆类需要我们定位。okhttp3.CertificatePinner、okhttp3.internal.tls.OkHostnameVerifier、okhttp3.OkHttpClient$Builder。这三个混淆类我们可以通过反射遍历内部方法特征和类的继承接口关系确定哪个时混淆类。而不管是okhttp3还是okhttp4这些类内部方法特征和继承关系不会变化太多。而且我们检测混淆的时候也不会去用类名,所以基本上可以达到通用的效果。

okhttp3.OkHttpClient.Builder为例,首先是okhttp3.OkHttpClient.Builder类的特征分析,如下

java检测是否为okhttp3.OkHttpClient.Builder混淆类

其实还可以更严谨,但是有这几项检测基本上够了。因为很少出现误判情况,至少我到现在还没遇到过。

在java层封装好了,我们打包成dex,然后就可以用frida调用了。利用frida的enumerateLoadedClasses方法遍历每个类交给OkHttp3FakeFinder做混淆检测。

apk版本android:versionName="3.10.39.000.0427.1536"
jadx打开apk发现okhttp3如下
qutoutiao.png-47kB

执行hooker/com.xxxx.qukan$ ./hooking just_trust_me_okhttp_hook_finder.js

由于某些还不知的原因,Builder类在遍历方法的时候会出现异常导致分析失败。所以只能还原到类层面,但是已经帮助非常大了。只要自己去看下jadx的代码就可以知道混淆方法名。

根据上面just_trust_me_okhttp_hook_finder.js跑的结果
把okhttp3的hook点改成混淆的类:
okhttp_justhook.png-155.7kB
修改提交记录:https://github.com/CreditTone/hooker/commit/f47d2068320a58306735a623f12bd955cbd20632

不容易!把技术突破完再讲给大家听,真难呐!还怕你们看不懂,想多写点。又怕你们觉得烦!啥也不说,就是干!!!

function processConscryptPlatform() {
    if (!classExists("com.android.org.conscrypt.Platform")) {
        return;
    }
    var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform');
    var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl');
    com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) {
        //什么都不做
    };
    var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl');
    com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) {
        //什么都不做
    };
}
function processConscryptPlatform() {
    if (!classExists("com.android.org.conscrypt.Platform")) {
        return;
    }
    var com_android_org_conscrypt_Platform_clz = Java.use('com.android.org.conscrypt.Platform');
    var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLEngineImpl');
    com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_9565.implementation = function(v0, v1, v2, v3) {
        //什么都不做
    };
    var com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928 = com_android_org_conscrypt_Platform_clz.checkServerTrusted.overload('javax.net.ssl.X509TrustManager', '[Ljava.security.cert.X509Certificate;', 'java.lang.String', 'com.android.org.conscrypt.OpenSSLSocketImpl');
    com_android_org_conscrypt_Platform_clz_method_checkServerTrusted_6928.implementation = function(v0, v1, v2, v3) {
        //什么都不做
    };
}
function processPinningTrustManager() {
    if (!classExists("appcelerator.https.PinningTrustManager")) {
        return;
    }
    var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager');
    var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload();
    pinningTrustManagerClass_checkServerTrusted.implementation = function() {
        //什么都不做
    };
}
function processPinningTrustManager() {
    if (!classExists("appcelerator.https.PinningTrustManager")) {
        return;
    }
    var pinningTrustManagerClass = Java.use('appcelerator.https.PinningTrustManager');
    var pinningTrustManagerClass_checkServerTrusted = pinningTrustManagerClass.checkServerTrusted.overload();
    pinningTrustManagerClass_checkServerTrusted.implementation = function() {
        //什么都不做
    };
}
if (classExists("okhttp3.OkHttpClient$Builder")) {
        try{
            var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder');
            var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
            okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) {
                //把参数替换成EmptySSLFactory
                var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory());
                return ret;
            };
            var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager');
            okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) {
                //把参数替换成EmptySSLFactory
                var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager);
                return ret;
            };
        } catch(error) {
            console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
if (classExists("okhttp3.OkHttpClient$Builder")) {
        try{
            var okhttp3_OkHttpClient_Builder_clz = Java.use('okhttp3.OkHttpClient$Builder');
            var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory');
            okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.implementation = function(sSLSocketFactory) {
                //把参数替换成EmptySSLFactory
                var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_one.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory());
                return ret;
            };
            var okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two = okhttp3_OkHttpClient_Builder_clz.sslSocketFactory.overload('javax.net.ssl.SSLSocketFactory', 'javax.net.ssl.X509TrustManager');
            okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.implementation = function(sSLSocketFactory, x509TrustManager) {
                //把参数替换成EmptySSLFactory
                var ret = okhttp3_OkHttpClient_Builder_clz_sslSocketFactory_two.call(this, Java.use("gz.justtrustme.Helper").getEmptySSLFactory(), x509TrustManager);
                return ret;
            };
        } catch(error) {
            console.error("okhttp3.OkHttpClient$Builder的sslSocketFactory方法可能被混淆了。你可以jadx反编译下还原回来!");
        }
    }else{
        console.error("没找到okhttp3.OkHttpClient$Builder类,可能被混淆了。你可以jadx反编译下还原回来!");
    }
processOkHttp();
processXutils();
processHttpClientAndroidLib();
//hooker添加的hook点
processConscryptPlatform();
processPinningTrustManager();
processOkHttp();
processXutils();
processHttpClientAndroidLib();
//hooker添加的hook点
processConscryptPlatform();
processPinningTrustManager();
 
//public static final 内部类 且无继承、无任何接口实现
public static final class Builder {
        //有javax.net.ssl.HostnameVerifier属性
        HostnameVerifier hostnameVerifier;
 
        //有java.net.Proxy属性
        Proxy proxy;
 
        //有java.net.ProxySelector属性
        ProxySelector proxySelector;
 
        //javax.net.SocketFactory
        SocketFactory socketFactory;
 
        //javax.net.ssl.SSLSocketFactory
        SSLSocketFactory sslSocketFactory;
 
        //有一个参数为SSLSocketFactory的方法返回值是类自己
        public Builder sslSocketFactory(SSLSocketFactory sSLSocketFactory);
 
        //有一个参数为SSLSocketFactory、X509TrustManager的方法返回值是类自己
        public Builder sslSocketFactory(SSLSocketFactory sSLSocketFactory, X509TrustManager x509TrustManager);
}
//public static final 内部类 且无继承、无任何接口实现
public static final class Builder {
        //有javax.net.ssl.HostnameVerifier属性
        HostnameVerifier hostnameVerifier;
 
        //有java.net.Proxy属性
        Proxy proxy;
 
        //有java.net.ProxySelector属性
        ProxySelector proxySelector;
 
        //javax.net.SocketFactory
        SocketFactory socketFactory;
 
        //javax.net.ssl.SSLSocketFactory
        SSLSocketFactory sslSocketFactory;
 
        //有一个参数为SSLSocketFactory的方法返回值是类自己
        public Builder sslSocketFactory(SSLSocketFactory sSLSocketFactory);
 
        //有一个参数为SSLSocketFactory、X509TrustManager的方法返回值是类自己
        public Builder sslSocketFactory(SSLSocketFactory sSLSocketFactory, X509TrustManager x509TrustManager);
}
// public static final class Builder
    public static Okhttp3FakeClass okHttpClientBuilderCheck(Class clz) {
        // java.net.Proxy proxy;
        boolean hasProxyField = false;
 
        // java.net.ProxySelector proxySelector;
        boolean hasProxySelectorField = false;
 
        // javax.net.SocketFactory socketFactory;
        boolean hasSocketFactoryField = false;
 
        // javax.net.ssl.SSLSocketFactory sslSocketFactory;
        boolean hasSSLSocketFactoryField = false;
 
        // javax.net.ssl.HostnameVerifier
        boolean hasHostnameVerifierField = false;
 
        // public Builder sslSocketFactory(javax.net.ssl.SSLSocketFactory
        // sSLSocketFactory)
        boolean hasSslSocketFactoryMethod = false;
        String sslSocketFactoryMethodName = null;
 
        // public Builder sslSocketFactory(javax.net.ssl.SSLSocketFactory
        // sSLSocketFactory, javax.net.ssl.X509TrustManager x509TrustManager)
        boolean hasSslSocketFactory2Method = false;
        String sslSocketFactoryMethod2Name = null;
 
        try {
            String className = clz.getName();
            if (clz.isInterface() || clz.isArray() || clz.isAnnotation() || clz.isEnum()
                    || className.startsWith("java.") || className.startsWith("android") || className.startsWith("com")
                    || !className.contains("$")) {
                // XLog.appendText("okHttpClientBuilderCheck 111");
                return null;
            }
            // 校验 public static final class Builder 类声明
            if (!Modifier.isFinal(clz.getModifiers()) || !Modifier.isPublic(clz.getModifiers())
                    || !Modifier.isStatic(clz.getModifiers())) {
                // XLog.appendText("okHttpClientBuilderCheck 222");
                return null;
            }
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {
                String fieldClassName = field.getType().getName();
                if ("java.net.Proxy".equals(fieldClassName)) {
                    hasProxyField = true;
                } else if ("java.net.ProxySelector".equals(fieldClassName)) {
                    hasProxySelectorField = true;
                } else if ("javax.net.SocketFactory".equals(fieldClassName)) {
                    hasSocketFactoryField = true;
                } else if ("javax.net.ssl.SSLSocketFactory".equals(fieldClassName)) {
                    hasSSLSocketFactoryField = true;
                } else if ("javax.net.ssl.HostnameVerifier".equals(fieldClassName)) {
                    hasHostnameVerifierField = true;
                }
            }
            Method[] methods = clz.getDeclaredMethods();
            for (int i = 0; i < methods.length; i++) {
                Method method = methods[i];
                // 如果方法非public或static方法直接continue
                if (!Modifier.isPublic(method.getModifiers()) || Modifier.isStatic(method.getModifiers())) {
                    continue;
                }
                // 校验方法返回值为Builder自己
                if (!clz.equals(method.getReturnType())) {
                    continue;
                }
                Class<?>[] parameterTypes = method.getParameterTypes();
                int parameterCount = parameterTypes.length;
                if (parameterCount == 1) {
                    if ("javax.net.ssl.SSLSocketFactory".equals(parameterTypes[0].getName())) {
                        hasSslSocketFactoryMethod = true;
                        sslSocketFactoryMethodName = method.toString();
                    }
                } else if (parameterCount == 2) {
                    Class parameter0Clz = parameterTypes[0];
                    Class parameter1Clz = parameterTypes[1];
                    if ("javax.net.ssl.SSLSocketFactory".equals(parameter0Clz.getName())
                            && "javax.net.ssl.X509TrustManager".equals(parameter1Clz.getName())) {
                        hasSslSocketFactory2Method = true;
                        sslSocketFactoryMethod2Name = method.toString();
                    }
                }
            }
        } catch (Throwable e) {
            // XLog.appendText(new Exception(e));
        }
        // 7项核心指标全部达标,我们可以判断这个类就是okhttp3.OkHttpClient$Builder的混淆类了
        if (hasProxyField && hasProxySelectorField && hasSocketFactoryField && hasSSLSocketFactoryField
                && hasHostnameVerifierField) {
            // 返回混淆的类名和方法名
            Okhttp3FakeClass okhttp3FakeClass = new Okhttp3FakeClass("okhttp3.OkHttpClient$Builder", clz.getName());
            if (hasSslSocketFactoryMethod && hasSslSocketFactory2Method) {
                okhttp3FakeClass.addFakeMethod(new FakeMethod(
                        "public okhttp3.OkHttpClient$Builder okhttp3.OkHttpClient$Builder.sslSocketFactory(javax.net.ssl.SSLSocketFactory)",
                        sslSocketFactoryMethodName));
                okhttp3FakeClass.addFakeMethod(new FakeMethod(
                        "public okhttp3.OkHttpClient$Builder okhttp3.OkHttpClient$Builder.sslSocketFactory(javax.net.ssl.SSLSocketFactory,javax.net.ssl.X509TrustManager)",
                        sslSocketFactoryMethod2Name));
            }
            return okhttp3FakeClass;
        }
        return null;
    }
// public static final class Builder
    public static Okhttp3FakeClass okHttpClientBuilderCheck(Class clz) {
        // java.net.Proxy proxy;
        boolean hasProxyField = false;
 
        // java.net.ProxySelector proxySelector;
        boolean hasProxySelectorField = false;
 
        // javax.net.SocketFactory socketFactory;
        boolean hasSocketFactoryField = false;
 
        // javax.net.ssl.SSLSocketFactory sslSocketFactory;
        boolean hasSSLSocketFactoryField = false;
 
        // javax.net.ssl.HostnameVerifier
        boolean hasHostnameVerifierField = false;
 
        // public Builder sslSocketFactory(javax.net.ssl.SSLSocketFactory
        // sSLSocketFactory)
        boolean hasSslSocketFactoryMethod = false;
        String sslSocketFactoryMethodName = null;
 
        // public Builder sslSocketFactory(javax.net.ssl.SSLSocketFactory
        // sSLSocketFactory, javax.net.ssl.X509TrustManager x509TrustManager)
        boolean hasSslSocketFactory2Method = false;
        String sslSocketFactoryMethod2Name = null;
 
        try {
            String className = clz.getName();
            if (clz.isInterface() || clz.isArray() || clz.isAnnotation() || clz.isEnum()
                    || className.startsWith("java.") || className.startsWith("android") || className.startsWith("com")
                    || !className.contains("$")) {
                // XLog.appendText("okHttpClientBuilderCheck 111");
                return null;
            }
            // 校验 public static final class Builder 类声明
            if (!Modifier.isFinal(clz.getModifiers()) || !Modifier.isPublic(clz.getModifiers())
                    || !Modifier.isStatic(clz.getModifiers())) {
                // XLog.appendText("okHttpClientBuilderCheck 222");
                return null;
            }
            Field[] fields = clz.getDeclaredFields();
            for (Field field : fields) {

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 4
支持
分享
最新回复 (6)
雪    币: 6573
活跃值: (3933)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
2
图片重新贴一下
2021-5-7 22:20
0
雪    币: 1037
活跃值: (1780)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
支持下
2021-5-8 09:42
0
雪    币: 138
活跃值: (542)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
大佬私发下我这里app的名字以及具体版本,还有哪里下载吧,我想测试下,谢谢
2021-5-9 14:45
0
雪    币: 160
活跃值: (177)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
mark
2021-5-13 16:15
0
雪    币: 192
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
大佬还在什么地方发过文章  除了b站
2021-5-14 16:11
0
雪    币: 2428
活跃值: (10698)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
这个和xp的hook有什么不同吗
2021-5-14 16:48
0
游客
登录 | 注册 方可回帖
返回
//