上一篇《实现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如下
执行hooker/com.xxxx.qukan$ ./hooking just_trust_me_okhttp_hook_finder.js
由于某些还不知的原因,Builder类在遍历方法的时候会出现异常导致分析失败。所以只能还原到类层面,但是已经帮助非常大了。只要自己去看下jadx的代码就可以知道混淆方法名。
根据上面just_trust_me_okhttp_hook_finder.js跑的结果 把okhttp3的hook点改成混淆的类: 修改提交记录: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期)