对这个APP的逆向在8月份左右就已经完成,只是笔者本人最近在找工作,这才想着顺带将以前做过的项目整理一下,在看雪发篇文章让简历好看些,也算是复习了。
该APP在我看来相当有代表性,不是因为难度有多高,而是搭载了一堆花里胡哨的防护。
在这篇文章中,你能碰见frida检测、SSL pinning 证书验证、设备风控 ,但每个防护环节又都跟过家家闹着玩似的非常不严谨,并且由于该APP没有套壳,因而哪怕对新手而言,分析起来也不会太吃力。
hook库:
反汇编:
编辑器: vscode
IDE: IntelliJ
目标app: 5bCP5ouJ5Ye66KGMIDIzMDI=
通过frida的spawn模式启动目标app,可以看见进程很快便自动退出。
这就没什么好说的,目标app必定对frida的特征做了检测。
按照惯例,首先我们得定位检测点;
考虑到检测功能大多数情况下都会在Native层实现,那么hook加载so文件相关的标准库函数就成了首选,该案例中我们选择使用dlopen以及android_dlopen_ext这两个较为常用的函数
随后spawn模式注入
如图所示,目标进程在libmsaoaidsec.so加载后便退出,可以推测检测点就在该so文件内
现在我们有两种方案:
第二种方案是否适用取决于目标,大部分情况下都会导致目标进程的崩溃,但尝试一下也无妨
出乎意料的是目标app没有崩溃,也就是说frida检测就这么被绕过了
如果懒得折腾的话,现在就可以继续分析了。
但考虑到这招并非总是管用,我们还是按照常规逆向的思路去so文件里看下检测逻辑的实现。
目标APP没有壳保护,可以直接从apk里将libmsaoaidsec.so拖取出来,随后丢入IDA分析。
考虑到检测逻辑大多会需要创建一个线程来运行,我们直接交叉搜索pthread_create的引用,如图所示
一共有四个引用被定位到,理论上来说我们只需要对pthread_create的第三个参数,也就是即将被创建的检测实例进行patch即可;
但在我们这么做之前可以向上跟踪调用逻辑,看看调用时机
通过向前追溯,我们可以发现检测线程的创建逻辑是在init_proc内进行的
调用链条:.init_proc->sub_1A8A0->sub_1A5B0(包含全部检测逻辑)
而这对我们会产生哪些影响呢?首先你不能再直接hook pthread_create,因为init_proc内的逻辑是先于JNI_OnLoad 之前执行的,这需要我们的hook时机非常靠前。
其次,我试过直接对pthread_create进行hook随后判断当前实例地址是否属于目标so,这样做的后果就是导致目标卡死,并且会发现根本就没进入我们的Hook逻辑。
通过多次调试,我怀疑是由于对pthread_create进行hook的时机不对导致了死锁
那么如何在init_proc中的检测逻辑加载之前进行Hook呢?我们可以寻找.init_proc内调用的标准库函数,随后以标准库函数的参数作为特征,当参数特征符合,就进行hook,以此作为跳板安装新的hook。
而符合我们需求的标准库函数需要满足两个条件:
现在我们回到init_proc的伪代码中:
根据之前的调用链追溯已知sub_1A8A0内创建了检测线程,那么我们就查看该函数之前的逻辑
首先进入sub_118FC函数,伪代码如下:
能大致看出该函数是通过atoi将字符串转换成整数返回,随后在init_proc内通过*off_43FB8接收sub_118FC的返回值,并与23比较;
写过一些简单安卓应用的朋友对23这个数字肯定不陌生,sub_118FC的功能大概率就是返回当前安卓的SDK版本,接着在后续做相应的版本兼容操作。
IDA对这段伪代码的生成明显有缺陷,可以看见_system_property_get没有被传入任何参数,我们切换成汇编来看看
可以看见字符串参数"ro.build.version.sdk"被传递给了__system_property_get,而该函数就符合我们此前的所设立的条件,既处于.init_proc的开头,又有能够利用的特征参数,现在我们就可以将该函数作为安装hook的跳板了,代码如下
在跳板定位成功后我们就可以开始手动patch了,如果不嫌麻烦的话可以直接查看IDA的交叉引用搜索结果,一个个跳转过去查看线程地址,随后计算基址+偏移进行nop即可
或者直接hook pthread_create,将实例参数替换成空函数
至此,目标app的frida检测便成功绕过了,目标程序也不再卡死或退出,总结一下可行的方案:
接下来就是证书校验,哪怕我们的证书是系统证书,抓包仍旧会提示证书错误,也就是说目标app只会信任由后端传递的证书 既然存在证书验证,那么就先做信息收集,确定目标使用的是哪个网路框架,由于系统包的类名不会受到编译期的控制,因而也不会出现类名混淆的情况,我们选择startHandshake进行Hook,打印堆栈(不过我还是建议你hook file$init)
堆栈显示是okhttp,没啥可说,很常见的网络框架。
能看见一些混淆函数,但okhttp的类名未做混淆,进入connectTls函数查看,这里是okhttp库正式发起请求的地方
能看见做了两个校验,先是host验证,随后通过checkadcK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6i4M7K6i4K6u0W2L8%4u0Y4i4K6u0r3x3e0V1&6z5q4)9J5c8V1#2S2N6r3S2Q4x3V1k6y4j5i4c8Z5e0f1H3`.">o k h t t p h a s h h o o k c h e c k okhttp校验证书的hash值,我们hook check o kh ttp 校验证书的 ha s h 值,我们 h oo k c h ec k okhttp,直接返回防止异常抛出。
可以看见hook确实命中了,但证书校验仍旧存在,并且通过打印参数会发现是获取IP物理地址的网站请求以及某宝的第三方域名,而非我们的目标域名。
也就是说访问第三方域名的服务时走的是默认证书校验,而只有目标域名添加了SSL证书的保护逻辑。
这样的话,就很可能是client在build时重写了X509TrustManager类;
不过在继续分析之前我们先明确一下调用顺序,以下示例是一个典型的自实现重载X509TrustManager,信任全部证书的例子
X509TrustManager封装在SSLSocketFactory内部,设置sslContext,并传入自实现重载X509TrustManager后的trustAllCerts,再将sslSocketFactory交给builder,最后使用配置好的builder构建Client,而所有构建的request都通过该Client发送。
随后我们查看AOSP的源码,能看见 verifyCertificateChain被注册为doHandshake执行时的回调
此刻调用链条便可以简化为:
okHttpClient.newCall发送请求->connectTls->NativeSsl.doHandshake->触发callback函数->ConscryptFileDescriptorSocket.verifyCertificateChain->
信任证书->回到connectTls继续执行check->
不信任证书->抛出异常终止请求
这再次证明了我们之前的推测,由于目标域名添加了自实现的验证逻辑,所有向目标域名发送的请求因为verifyCertificateChain验证不通过抛出了异常,导致根本就没有执行connectTls下面的逻辑,自然check也不会执行,而所有命中check hook的参数则都是未做证书验证的第三方域名。
我本试图hook verifyCertificateChain,直接返回,但目标app似乎对该函数也做了防护,一旦Hook就会退出进程,从头再去逆向对该函数的检测点太麻烦,先试着继续跟AOSP源码看看verifyCertificateChain内部的实现,是否存在其它hook点,AOSP源码如下
可以看见上半部分的异常抛出都是些常规检测,不涉及对证书本身进行校验的逻辑,那么就剩下checkServerTrusted与checkClientTrusted,问下gemini看看这两个函数的作用
抓包的错误显示在握手阶段就失败了,我们可以假设是由客户端校验导致的,选择跟进checkServerTrusted查看逻辑
AOSP中没有多少有价值的信息,毕竟这只是个接口,你很难确定负责实现的tm从哪传来,不过既然该函数在必经之路,那么无论是自实现tm也好还是标准库也好,我们都可以选择hook此处,随后打印tm的源头。
实际上使用以下脚本就已经能够过掉证书校验,进行抓包了 ,无论tm从哪传来,我们直接hook该函数不做任何操作,自然就不会有任何异常被抛出,不过我们可以继续深入,看看校验逻辑是如何实现的。
其中看起来最可疑的是自实现MyTrustManager与UnsafeOkHttpClient$1,而出乎意料的两者的自实现均没有任何验证证书的逻辑
既然如此,就只能关注android.security.net.config.RootTrustManager了,我们再来问下gemini这个类的作用。
而我们果真在app中找到了xml配置文件:
至此,校验逻辑就很清晰了,目标并非如我们一开始猜测的那样通过重载tm内的校验逻辑来自实现证书验证,而是通过配置network_security_config.xml文件来验证证书,负责校验的tm并非是自定义逻辑,而是android.security.net.config.RootTrustManager内的默认配置。
那么理论上来说,连此前的脚本都用不上,直接修改xml随后重打包就可以绕过证书验证,感兴趣的可以尝试复现。
现在,我们解决了frida检测、SSL证书校验,接下来就可以正式开始抓包分析了。
简单删减了请求包的几个字段来测试发包,发现后端真正检验的字段只有X-Sign-Signature、X-Sign-Timestamp、X-Sign-Nonce。
幸运的是在这些字段名并未被混淆,直接在jadx中明文搜索就能够定位到,jadx没能成功将这部分的smail完全反汇编成java,只留下一段伪代码,但好在逻辑很简单
一眼能看出r7作为字段名,r6则是对应的键值,r4.get(r6)根据不同的字段名返回键值给r6,最后通过r3.OOOO对两者进行组装。而对r4的声明如下
这函数命名就已经说明一切了,hook genSignature2并打印参数来验证猜想
确认无疑,genSignature2就是获得X-Sign-Signature、X-Sign-Timestamp、X-Sign-Nonce这三个键值的函数。
接下来我们自己将参数构建出来,写个基于python的RPC脚本,将得到返回字段字段加入到请求头中,最后调requests的包发请求就完工了
参数明文有些敏感数据,就不放出来了,该函数的各个参数分别是:
我们的目标是再封装短信接口的api,那么需要传递的关键数据就是hashmap中的手机号,并且hashmap的值实际上就是请求包内的json格式请求体。
并且请求体/hashmap内的值同时也包含设备风控字段;如果你直接修改手机号,而没有解决风控问题,后端就会提示多个设备登录。
听起来挺高端,其实就是通过一个随机生成的唯一字符串来作为当前设备的标识,我们只需要在每次发送请求的时候按照这串字符的格式,随机生成一段新字符串替换掉,就能够绕过设备登录多个账号的限制。
先在js脚本内组装好大部分参数,而hashmap内的post_body字段才是真正与api对应业务相关的值,涉及的键值对较多,我们在RPC脚本内封装后再传入。
JS脚本:
frida rpc脚本:
结束,收工!
function loadSohook(){
var android_dlopen_ext_Addr = Module.findExportByName(null,"android_dlopen_ext")
Interceptor.attach(android_dlopen_ext_Addr,{onEnter: function(args){
let PathPtr = args[0]
let soPath = ptr(PathPtr).readCString()
console.log("[info] Load from android_dlopen_ext-> "+soPath)
}})
var dlopenAddr = Module.findExportByName(null,"dlopen")
Interceptor.attach(dlopenAddr,{onEnter: function(args){
let PathPtr = args[0]
let soPath = ptr(PathPtr).readCString()
console.log("[info] Load from dlopen-> "+soPath)
}})
}
setImmediate(loadSohook)
function loadSohook(){
var android_dlopen_ext_Addr = Module.findExportByName(null,"android_dlopen_ext")
Interceptor.attach(android_dlopen_ext_Addr,{onEnter: function(args){
let PathPtr = args[0]
let soPath = ptr(PathPtr).readCString()
console.log("[info] Load from android_dlopen_ext-> "+soPath)
}})
var dlopenAddr = Module.findExportByName(null,"dlopen")
Interceptor.attach(dlopenAddr,{onEnter: function(args){
let PathPtr = args[0]
let soPath = ptr(PathPtr).readCString()
console.log("[info] Load from dlopen-> "+soPath)
}})
}
setImmediate(loadSohook)
frida -U -l hook-loadso.js -f com.xiaolachuxing.user --no-pause
frida -U -l hook-loadso.js -f com.xiaolachuxing.user --no-pause
Interceptor.attach(Module.findExportByName(null,"android_dlopen_ext"), {
onEnter: function(args) {
var soName = ptr(args[0]).readCString();
console.log(soName);
if(soName.includes("libmsaoaidsec.so")) {
console.log("Blocking:", soName);
args[0] = ptr(0);
}
}
});
Interceptor.attach(Module.findExportByName(null,"android_dlopen_ext"), {
onEnter: function(args) {
var soName = ptr(args[0]).readCString();
console.log(soName);
if(soName.includes("libmsaoaidsec.so")) {
console.log("Blocking:", soName);
args[0] = ptr(0);
}
}
});
__int64 __fastcall init_proc()
{
__int64 result;
FILE *stream;
char s_1[2000];
char s[2000];
__int64 v5;
v5 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
*off_43FB8 = sub_118FC();
sub_11A50();
sub_1194C();
memset(s, 0, sizeof(s));
getpid();
_sprintf_chk((__int64)s, 0LL, 2000LL, "/proc/%d/cmdline");
stream = fopen(s, "r");
if ( stream )
{
memset(s_1, 0, sizeof(s_1));
fscanf(stream, "%s", s_1);
fclose(stream);
if ( !strchr(s_1, 58) )
sub_1A8A0();
}
sub_12C28();
if ( !(sub_16C08() & 1) )
sub_114B0();
if ( (sub_18754() & 1) == 0 )
sub_114B0();
sub_23280();
sub_21D9C();
if ( *off_43FB8 > 23 )
*off_43ED8 = 1;
result = sub_BE2C();
if ( (_DWORD)result == 1 )
return sub_8C38();
return result;
}
__int64 __fastcall init_proc()
{
__int64 result;
FILE *stream;
char s_1[2000];
char s[2000];
__int64 v5;
v5 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
*off_43FB8 = sub_118FC();
sub_11A50();
sub_1194C();
memset(s, 0, sizeof(s));
getpid();
_sprintf_chk((__int64)s, 0LL, 2000LL, "/proc/%d/cmdline");
stream = fopen(s, "r");
if ( stream )
{
memset(s_1, 0, sizeof(s_1));
fscanf(stream, "%s", s_1);
fclose(stream);
if ( !strchr(s_1, 58) )
sub_1A8A0();
}
sub_12C28();
if ( !(sub_16C08() & 1) )
sub_114B0();
if ( (sub_18754() & 1) == 0 )
sub_114B0();
sub_23280();
sub_21D9C();
if ( *off_43FB8 > 23 )
*off_43ED8 = 1;
result = sub_BE2C();
if ( (_DWORD)result == 1 )
return sub_8C38();
return result;
}
__int64 sub_118FC()
{
_QWORD nptr[2];
nptr[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
nptr[0] = 0LL;
_system_property_get();
return atoi((const char *)nptr);
}
__int64 sub_118FC()
{
_QWORD nptr[2];
nptr[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
nptr[0] = 0LL;
_system_property_get();
return atoi((const char *)nptr);
}
LOAD:00000000000118FC sub_118FC ; CODE XREF: .init_proc+2C↓p
LOAD:00000000000118FC ; sub_15064+630↓p ...
LOAD:00000000000118FC
LOAD:00000000000118FC var_20 = -0x20
LOAD:00000000000118FC var_18 = -0x18
LOAD:00000000000118FC var_10 = -0x10
LOAD:00000000000118FC var_8 = -8
LOAD:00000000000118FC
LOAD:00000000000118FC SUB SP, SP,
LOAD:0000000000011900 STP X19, X30, [SP,
LOAD:0000000000011904 MRS X19, TPIDR_EL0
LOAD:0000000000011908 LDR X8, [X19,
LOAD:000000000001190C ADRL X0, aRoBuildVersion ; "ro.build.version.sdk"
LOAD:0000000000011914 MOV X1, SP
LOAD:0000000000011918 STR X8, [SP,
LOAD:000000000001191C STR XZR, [SP,
LOAD:0000000000011920 BL __system_property_get
LOAD:0000000000011924 MOV X0, SP ; nptr
LOAD:0000000000011928 BL atoi
LOAD:000000000001192C LDR X8, [X19,
LOAD:0000000000011930 LDR X9, [SP,
LOAD:0000000000011934 CMP X8, X9
LOAD:0000000000011938 B.NE loc_11948
LOAD:000000000001193C LDP X19, X30, [SP,
LOAD:0000000000011940 ADD SP, SP,
LOAD:0000000000011944 RET
LOAD:00000000000118FC sub_118FC ; CODE XREF: .init_proc+2C↓p
LOAD:00000000000118FC ; sub_15064+630↓p ...
LOAD:00000000000118FC
LOAD:00000000000118FC var_20 = -0x20
LOAD:00000000000118FC var_18 = -0x18
LOAD:00000000000118FC var_10 = -0x10
LOAD:00000000000118FC var_8 = -8
LOAD:00000000000118FC
LOAD:00000000000118FC SUB SP, SP,
LOAD:0000000000011900 STP X19, X30, [SP,
LOAD:0000000000011904 MRS X19, TPIDR_EL0
LOAD:0000000000011908 LDR X8, [X19,
LOAD:000000000001190C ADRL X0, aRoBuildVersion ; "ro.build.version.sdk"
LOAD:0000000000011914 MOV X1, SP
LOAD:0000000000011918 STR X8, [SP,
LOAD:000000000001191C STR XZR, [SP,
LOAD:0000000000011920 BL __system_property_get
LOAD:0000000000011924 MOV X0, SP ; nptr
LOAD:0000000000011928 BL atoi
LOAD:000000000001192C LDR X8, [X19,
LOAD:0000000000011930 LDR X9, [SP,
LOAD:0000000000011934 CMP X8, X9
LOAD:0000000000011938 B.NE loc_11948
LOAD:000000000001193C LDP X19, X30, [SP,
LOAD:0000000000011940 ADD SP, SP,
LOAD:0000000000011944 RET
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
}
}
}
});
}
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
}
}
}
});
}
function nop_64(addr) {
Memory.protect(addr, 4 , 'rwx');
var w = new Arm64Writer(addr);
w.putRet();
w.flush();
w.dispose();
console.log("replace addr: " + addr);
}
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
console.log("[INFO] __system_property_get found");
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
var base = Process.getModuleByName("libmsaoaidsec.so").base
nop_64(base.add(0x18C88))
nop_64(base.add(0x199E0))
nop_64(base.add(0x1A574))
nop_64(base.add(0x1AEE4))
}
}
}
});
}
function nop_64(addr) {
Memory.protect(addr, 4 , 'rwx');
var w = new Arm64Writer(addr);
w.putRet();
w.flush();
w.dispose();
console.log("replace addr: " + addr);
}
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
console.log("[INFO] __system_property_get found");
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
var base = Process.getModuleByName("libmsaoaidsec.so").base
nop_64(base.add(0x18C88))
nop_64(base.add(0x199E0))
nop_64(base.add(0x1A574))
nop_64(base.add(0x1AEE4))
}
}
}
});
}
function hook_pthread_create() {
var pthread_create = Module.findExportByName("libc.so", "pthread_create");
var libmsaoaidsec = Process.findModuleByName("libmsaoaidsec.so");
if (!libmsaoaidsec) {
console.log("libmsaoaidsec.so not found");
return;
}
if (!pthread_create) {
console.log("pthread_create not found");
return;
}
Interceptor.attach(pthread_create, {
onEnter: function(args) {
var thread_ptr = args[2];
if (thread_ptr.compare(libmsaoaidsec.base) < 0 || thread_ptr.compare(libmsaoaidsec.base.add(libmsaoaidsec.size)) >= 0) {
} else {
var offset = thread_ptr.sub(libmsaoaidsec.base)
console.log("pthread_create libmsaoaidsec.so thread: " + thread_ptr + " offset: " + offset);
Interceptor.replace(thread_ptr,new NativeCallback(function(){
console.log("[hook] replace offset: " + offset)
},"void",[]))
}
},
onLeave: function(retval) {}
});
}
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
console.log("[INFO] __system_property_get found");
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
hook_pthread_create()
}
}
}
});
}
function hook_pthread_create() {
var pthread_create = Module.findExportByName("libc.so", "pthread_create");
var libmsaoaidsec = Process.findModuleByName("libmsaoaidsec.so");
if (!libmsaoaidsec) {
console.log("libmsaoaidsec.so not found");
return;
}
if (!pthread_create) {
console.log("pthread_create not found");
return;
}
Interceptor.attach(pthread_create, {
onEnter: function(args) {
var thread_ptr = args[2];
if (thread_ptr.compare(libmsaoaidsec.base) < 0 || thread_ptr.compare(libmsaoaidsec.base.add(libmsaoaidsec.size)) >= 0) {
} else {
var offset = thread_ptr.sub(libmsaoaidsec.base)
console.log("pthread_create libmsaoaidsec.so thread: " + thread_ptr + " offset: " + offset);
Interceptor.replace(thread_ptr,new NativeCallback(function(){
console.log("[hook] replace offset: " + offset)
},"void",[]))
}
},
onLeave: function(retval) {}
});
}
function hook_system_property_get() {
var system_property_get_addr = Module.findExportByName(null,"__system_property_get")
if(!system_property_get_addr){
console.log("[INFO] __system_property_get not found");
return
}
console.log("[INFO] __system_property_get found");
Interceptor.attach(system_property_get_addr, {
onEnter: function (args) {
var nameptr = args[0];
if (nameptr) {
var name = ptr(nameptr).readCString();
if (name.indexOf("ro.build.version.sdk") >= 0) {
console.log("Found ro.build.version.sdk, need to patch");
hook_pthread_create()
}
}
}
});
}
function hookssl(){
Java.perform(function(){
var SSLSocket = Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket");
console.log("SSLSocket ",SSLSocket);
SSLSocket.startHandshake.implementation = function () {
console.log("startHandshake");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))
return this.startHandshake();
}
});
}
hookssl()
function hookssl(){
Java.perform(function(){
var SSLSocket = Java.use("com.android.org.conscrypt.ConscryptFileDescriptorSocket");
console.log("SSLSocket ",SSLSocket);
SSLSocket.startHandshake.implementation = function () {
console.log("startHandshake");
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))
return this.startHandshake();
}
});
}
hookssl()
startHandshake
java.lang.Exception
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(Native Method)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.waitForHandshake(ConscryptFileDescriptorSocket.java:476)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.getOutputStream(ConscryptFileDescriptorSocket.java:463)
at okio.Okio__JvmOkioKt.sink(JvmOkio.kt:127)
at okio.Okio.sink(Unknown Source:1)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:422)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.delivery.wp.argus.android.hook.okhttp.RecordBodyInterceptor.intercept(RecordBodyInterceptor.kt:35)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
at gnet.android.OkHttpRawCall$RawCallInterceptor.intercept(OkHttpRawCall.java:170)
at gnet.android.Interceptor$Chain.OOOO(Interceptor.java:33)
at gnet.android.OkHttpRawCall$MetricsInterceptor.intercept(OkHttpRawCall.java:125)
at gnet.android.Interceptor$Chain.OOOO(Interceptor.java:33)
at gnet.android.OkHttpRawCall.OO00(OkHttpRawCall.java:88)
at gnet.android.OkHttpRawCall.OOOO(OkHttpRawCall.java:61)
at gnet.android.GNetRawCall.OOoO(GNetRawCall.java:47)
at com.delivery.wp.argus.android.performance.performanceconfig.PerformanceConfig$Companion.OOOO(PerformanceConfig.kt:77)
at com.delivery.wp.argus.android.performance.performanceconfig.PerformanceConfig$Companion$createLooperGetConfig$1.realAction(PerformanceConfig.kt:269)
at com.delivery.wp.argus.android.schedule.AbstractFixedDelayPoller$scheduleAction$1.invoke(AbstractFixedDelayPoller.kt:17)
at com.delivery.wp.argus.android.schedule.AbstractFixedDelayPoller$scheduleAction$1.invoke(AbstractFixedDelayPoller.kt:17)
at com.delivery.wp.argus.android.schedule.GlobalTaskScheduler.schedule$lambda-0(GlobalTaskScheduler.kt:40)
at com.delivery.wp.argus.android.schedule.GlobalTaskScheduler.lambda$hMW5Kte4X_Lonty_nd3X3zcuuyg(Unknown Source:0)
at com.delivery.wp.argus.android.schedule.-$$Lambda$GlobalTaskScheduler$hMW5Kte4X_Lonty_nd3X3zcuuyg.run(Unknown Source:2)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
startHandshake
java.lang.Exception
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(Native Method)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.waitForHandshake(ConscryptFileDescriptorSocket.java:476)
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.getOutputStream(ConscryptFileDescriptorSocket.java:463)
at okio.Okio__JvmOkioKt.sink(JvmOkio.kt:127)
at okio.Okio.sink(Unknown Source:1)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:422)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.delivery.wp.argus.android.hook.okhttp.RecordBodyInterceptor.intercept(RecordBodyInterceptor.kt:35)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
at gnet.android.OkHttpRawCall$RawCallInterceptor.intercept(OkHttpRawCall.java:170)
at gnet.android.Interceptor$Chain.OOOO(Interceptor.java:33)
at gnet.android.OkHttpRawCall$MetricsInterceptor.intercept(OkHttpRawCall.java:125)
at gnet.android.Interceptor$Chain.OOOO(Interceptor.java:33)
at gnet.android.OkHttpRawCall.OO00(OkHttpRawCall.java:88)
at gnet.android.OkHttpRawCall.OOOO(OkHttpRawCall.java:61)
at gnet.android.GNetRawCall.OOoO(GNetRawCall.java:47)
at com.delivery.wp.argus.android.performance.performanceconfig.PerformanceConfig$Companion.OOOO(PerformanceConfig.kt:77)
at com.delivery.wp.argus.android.performance.performanceconfig.PerformanceConfig$Companion$createLooperGetConfig$1.realAction(PerformanceConfig.kt:269)
at com.delivery.wp.argus.android.schedule.AbstractFixedDelayPoller$scheduleAction$1.invoke(AbstractFixedDelayPoller.kt:17)
at com.delivery.wp.argus.android.schedule.AbstractFixedDelayPoller$scheduleAction$1.invoke(AbstractFixedDelayPoller.kt:17)
at com.delivery.wp.argus.android.schedule.GlobalTaskScheduler.schedule$lambda-0(GlobalTaskScheduler.kt:40)
at com.delivery.wp.argus.android.schedule.GlobalTaskScheduler.lambda$hMW5Kte4X_Lonty_nd3X3zcuuyg(Unknown Source:0)
at com.delivery.wp.argus.android.schedule.-$$Lambda$GlobalTaskScheduler$hMW5Kte4X_Lonty_nd3X3zcuuyg.run(Unknown Source:2)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
private final void connectTls(ConnectionSpecSelector connectionSpecSelector) throws Throwable {
final Address address = this.route.address();
SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
SSLSocket sSLSocket = null;
try {
Intrinsics.checkNotNull(sslSocketFactory);
Socket socketCreateSocket = sslSocketFactory.createSocket(this.rawSocket, address.url().host(), address.url().port(), true);
if (socketCreateSocket == null) {
throw new NullPointerException("null cannot be cast to non-null type javax.net.ssl.SSLSocket");
}
SSLSocket sSLSocket2 = (SSLSocket) socketCreateSocket;
try {
ConnectionSpec connectionSpecConfigureSecureSocket = connectionSpecSelector.configureSecureSocket(sSLSocket2);
if (connectionSpecConfigureSecureSocket.supportsTlsExtensions()) {
Platform.INSTANCE.get().configureTlsExtensions(sSLSocket2, address.url().host(), address.protocols());
}
sSLSocket2.startHandshake();
SSLSession sslSocketSession = sSLSocket2.getSession();
Handshake.Companion companion = Handshake.INSTANCE;
Intrinsics.checkNotNullExpressionValue(sslSocketSession, "sslSocketSession");
final Handshake handshake = companion.get(sslSocketSession);
HostnameVerifier hostnameVerifier = address.hostnameVerifier();
Intrinsics.checkNotNull(hostnameVerifier);
if (hostnameVerifier.verify(address.url().host(), sslSocketSession)) {
final CertificatePinner certificatePinner = address.certificatePinner();
Intrinsics.checkNotNull(certificatePinner);
this.handshake = new Handshake(handshake.tlsVersion(), handshake.cipherSuite(), handshake.localCertificates(), new Function0<List<? extends Certificate>>() {
@Override
public final List<? extends X509Certificate> invoke() {
Handshake handshake2 = RealConnection.this.handshake;
Intrinsics.checkNotNull(handshake2);
List<Certificate> listPeerCertificates = handshake2.peerCertificates();
ArrayList arrayList = new ArrayList(CollectionsKt.collectionSizeOrDefault(listPeerCertificates, 10));
Iterator<T> it2 = listPeerCertificates.iterator();
while (it2.hasNext()) {
arrayList.add((X509Certificate) ((Certificate) it2.next()));
}
return arrayList;
}
});
String selectedProtocol = connectionSpecConfigureSecureSocket.supportsTlsExtensions() ? Platform.INSTANCE.get().getSelectedProtocol(sSLSocket2) : (String) null;
this.socket = sSLSocket2;
this.source = Okio.buffer(Okio.source(sSLSocket2));
this.sink = Okio.buffer(Okio.sink(sSLSocket2));
this.protocol = selectedProtocol != null ? Protocol.INSTANCE.get(selectedProtocol) : Protocol.HTTP_1_1;
Platform.INSTANCE.get().afterHandshake(sSLSocket2);
return;
}
List<Certificate> listPeerCertificates = handshake.peerCertificates();
if (!(!listPeerCertificates.isEmpty())) {
throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified (no certificates)");
}
X509Certificate x509Certificate = (X509Certificate) listPeerCertificates.get(0);
throw new SSLPeerUnverifiedException(StringsKt.trimMargin$default("\n |Hostname " + address.url().host() + " not verified:\n | certificate: " + CertificatePinner.INSTANCE.pin(x509Certificate) + "\n | DN: " + ((Object) x509Certificate.getSubjectDN().getName()) + "\n | subjectAltNames: " + OkHostnameVerifier.INSTANCE.allSubjectAltNames(x509Certificate) + "\n ", null, 1, null));
} catch (Throwable th) {
th = th;
sSLSocket = sSLSocket2;
if (sSLSocket != null) {
Platform.INSTANCE.get().afterHandshake(sSLSocket);
}
if (sSLSocket != null) {
Util.closeQuietly((Socket) sSLSocket);
}
throw th;
}
} catch (Throwable th2) {
th = th2;
}
}
private final void connectTls(ConnectionSpecSelector connectionSpecSelector) throws Throwable {
final Address address = this.route.address();
SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
SSLSocket sSLSocket = null;
try {
Intrinsics.checkNotNull(sslSocketFactory);
Socket socketCreateSocket = sslSocketFactory.createSocket(this.rawSocket, address.url().host(), address.url().port(), true);
if (socketCreateSocket == null) {
throw new NullPointerException("null cannot be cast to non-null type javax.net.ssl.SSLSocket");
}
SSLSocket sSLSocket2 = (SSLSocket) socketCreateSocket;
try {
ConnectionSpec connectionSpecConfigureSecureSocket = connectionSpecSelector.configureSecureSocket(sSLSocket2);
if (connectionSpecConfigureSecureSocket.supportsTlsExtensions()) {
Platform.INSTANCE.get().configureTlsExtensions(sSLSocket2, address.url().host(), address.protocols());
}
sSLSocket2.startHandshake();
SSLSession sslSocketSession = sSLSocket2.getSession();
Handshake.Companion companion = Handshake.INSTANCE;
Intrinsics.checkNotNullExpressionValue(sslSocketSession, "sslSocketSession");
final Handshake handshake = companion.get(sslSocketSession);
HostnameVerifier hostnameVerifier = address.hostnameVerifier();
Intrinsics.checkNotNull(hostnameVerifier);
if (hostnameVerifier.verify(address.url().host(), sslSocketSession)) {
final CertificatePinner certificatePinner = address.certificatePinner();
Intrinsics.checkNotNull(certificatePinner);
this.handshake = new Handshake(handshake.tlsVersion(), handshake.cipherSuite(), handshake.localCertificates(), new Function0<List<? extends Certificate>>() {
@Override
public final List<? extends X509Certificate> invoke() {
Handshake handshake2 = RealConnection.this.handshake;
Intrinsics.checkNotNull(handshake2);
List<Certificate> listPeerCertificates = handshake2.peerCertificates();
ArrayList arrayList = new ArrayList(CollectionsKt.collectionSizeOrDefault(listPeerCertificates, 10));
Iterator<T> it2 = listPeerCertificates.iterator();
while (it2.hasNext()) {
arrayList.add((X509Certificate) ((Certificate) it2.next()));
}
return arrayList;
}
});
String selectedProtocol = connectionSpecConfigureSecureSocket.supportsTlsExtensions() ? Platform.INSTANCE.get().getSelectedProtocol(sSLSocket2) : (String) null;
this.socket = sSLSocket2;
this.source = Okio.buffer(Okio.source(sSLSocket2));
this.sink = Okio.buffer(Okio.sink(sSLSocket2));
this.protocol = selectedProtocol != null ? Protocol.INSTANCE.get(selectedProtocol) : Protocol.HTTP_1_1;
Platform.INSTANCE.get().afterHandshake(sSLSocket2);
return;
}
List<Certificate> listPeerCertificates = handshake.peerCertificates();
if (!(!listPeerCertificates.isEmpty())) {
throw new SSLPeerUnverifiedException("Hostname " + address.url().host() + " not verified (no certificates)");
}
X509Certificate x509Certificate = (X509Certificate) listPeerCertificates.get(0);
throw new SSLPeerUnverifiedException(StringsKt.trimMargin$default("\n |Hostname " + address.url().host() + " not verified:\n | certificate: " + CertificatePinner.INSTANCE.pin(x509Certificate) + "\n | DN: " + ((Object) x509Certificate.getSubjectDN().getName()) + "\n | subjectAltNames: " + OkHostnameVerifier.INSTANCE.allSubjectAltNames(x509Certificate) + "\n ", null, 1, null));
} catch (Throwable th) {
th = th;
sSLSocket = sSLSocket2;
if (sSLSocket != null) {
Platform.INSTANCE.get().afterHandshake(sSLSocket);
}
if (sSLSocket != null) {
Util.closeQuietly((Socket) sSLSocket);
}
throw th;
}
} catch (Throwable th2) {
th = th2;
}
}
function host_bypass(){
var HostnameVerifierInterface = Java.use('javax.net.ssl.HostnameVerifier')
const MyHostnameVerifier = Java.registerClass({
name: 'org.dummyPackage.MyHostnameVerifier',
implements: [HostnameVerifierInterface],
methods: {
verify: [{
returnType: 'boolean',
argumentTypes: ['java.lang.String', 'javax.net.ssl.SSLSession'],
implementation(hostname, session) {
console.log('[+] Hostname verification bypass');
return true;
}
}],
}
});
var hostnameVerifierRef = Java.use('okhttp3.OkHttpClient')['hostnameVerifier'].overload();
hostnameVerifierRef.implementation = function() {
return MyHostnameVerifier.$new();
}
console.log("[+] Hostname verifier replaced")
}
function check_bypass(){
let CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner["check$okhttp"].implementation = function (hostname, cleanedPeerCertificatesFn) {
console.log("[hook] check bypass...");
console.log("[info] host:" + hostname)
var SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
console.log("==================================")
console.log("[info] stack: " + SSLstackread)
console.log("==================================")
return
};
}
function main(){
host_bypass()
check_bypass()
}
Java.perform(main)
function host_bypass(){
var HostnameVerifierInterface = Java.use('javax.net.ssl.HostnameVerifier')
const MyHostnameVerifier = Java.registerClass({
name: 'org.dummyPackage.MyHostnameVerifier',
implements: [HostnameVerifierInterface],
methods: {
verify: [{
returnType: 'boolean',
argumentTypes: ['java.lang.String', 'javax.net.ssl.SSLSession'],
implementation(hostname, session) {
console.log('[+] Hostname verification bypass');
return true;
}
}],
}
});
var hostnameVerifierRef = Java.use('okhttp3.OkHttpClient')['hostnameVerifier'].overload();
hostnameVerifierRef.implementation = function() {
return MyHostnameVerifier.$new();
}
console.log("[+] Hostname verifier replaced")
}
function check_bypass(){
let CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner["check$okhttp"].implementation = function (hostname, cleanedPeerCertificatesFn) {
console.log("[hook] check bypass...");
console.log("[info] host:" + hostname)
var SSLstackread = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()).toString();
console.log("==================================")
console.log("[info] stack: " + SSLstackread)
console.log("==================================")
return
};
}
function main(){
host_bypass()
check_bypass()
}
Java.perform(main)
Attaching...
[+] Hostname verifier replaced
[LM G710::xx出行 ]-> [hook] check bypass...
[info] host:ip.qaros.com
==================================
[info] stack: java.lang.Throwable
at okhttp3.CertificatePinner.check$okhttp(Native Method)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:410)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.delivery.wp.argus.android.hook.okhttp.RecordBodyInterceptor.intercept(RecordBodyInterceptor.kt:56)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
at gnet.android.ClientIPReporter.fetchClientIPAddress$lambda-1(ClientIPReporter.kt:60)
at gnet.android.ClientIPReporter.lambda$1G-0OwxFS5VN_JvFsG079eY3uKY(Unknown Source:0)
at gnet.android.-$$Lambda$ClientIPReporter$1G-0OwxFS5VN_JvFsG079eY3uKY.run(Unknown Source:6)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
==================================
Attaching...
[+] Hostname verifier replaced
[LM G710::xx出行 ]-> [hook] check bypass...
[info] host:ip.qaros.com
==================================
[info] stack: java.lang.Throwable
at okhttp3.CertificatePinner.check$okhttp(Native Method)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:410)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.delivery.wp.argus.android.hook.okhttp.RecordBodyInterceptor.intercept(RecordBodyInterceptor.kt:56)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-10-14 14:05
被Dr_Knox编辑
,原因: 笔误