声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关;本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文涉及的技术而导致的任何意外,作者均不负责。
抓包分析
打开app下拉抓主页抓包

可以看到抓不到商品信息的数据只有一些零散的无用数据
先猜测可能是常见的sslpinning
ios里面用ssl kill switch 开启下试一试

还是不行依旧是一些无用数据
脱壳分析
脱壳
依旧用CrackerXI 先关app 再脱壳
一般情况下都在这个路径
iPhone:/var/mobile/Documents/CrackerXI root# pwd
/var/mobile/Documents/CrackerXI
iPhone:/var/mobile/Documents/CrackerXI root#
解压
unzip JD4iPhone_170234_CrackerXI.ipa之后可以看到有一个Payload文件夹里面就是脱壳好的macho以及依赖文件
分析脱壳文件
参考来源
https://bbs.kanxue.com/thread-277996.htm
这文章介绍了cronet怎么才能抓到包,值得一看
所以我们需要找到cronet二进制文件并导入到本地
找到了在这个路径下
JD4iPhone.app\Frameworks\Cronet.framework
我们把下面的文件导入到本地并进行反编译分析
cronet反编译分析
谷歌源码链接
https://github.com/huningxin/chromium-old/blob/ddccd3f6dba62870a038ecb7d68b28038fcbe5d9/net/socket/ssl_client_socket_impl.cc#L347
源码中校验位置
SSL_CTX_set_custom_verify(ssl_ctx_.get(), SSL_VERIFY_PEER,
CertVerifyCallback);
我们直接搜关键字没搜索到可以找一些特征值搜索
可以看到他下面有一个60 * 60 也就是3600
转一下16进制就是 0xE10
!!!这里不用考虑大小端因为是立即数
ida 搜索一下

一共涉及到三个函数我们一个一个看
前两个明显不是
第三个感觉比较像 3600是最后一个参数
贴一下反编译代码
__int64 sub_25A3DC()
{
__int64 v0; // x19
__int64 v1; // x0
__int64 v2; // x0
__int64 v3; // x0
__int64 v4; // x21
__int64 v5; // x0
v0 = sub_BA758(16);
*(_QWORD *)(v0 + 8) = 0;
sub_340DBC();
*(_DWORD *)v0 = sub_2E9678(0, 0, 0, 0, 0);
v1 = sub_2F1BD4();
v2 = sub_2E7BB8(v1);
sub_2E9950(v0 + 8, v2);
sub_2E50CC(*(_QWORD *)(v0 + 8), sub_25A4F0, 0);
sub_2E970C(*(_QWORD *)(v0 + 8), 1);
sub_2E9130(*(_QWORD *)(v0 + 8), 1, sub_2597E0);
sub_2E9074(*(_QWORD *)(v0 + 8), 769);
sub_2EBE88(*(_QWORD *)(v0 + 8), sub_25A520);
sub_2EBDBC(*(_QWORD *)(v0 + 8), 3600);
v3 = sub_2E9764(*(_QWORD *)(v0 + 8), 1);
v4 = *(_QWORD *)(v0 + 8);
v5 = sub_16F420(v3);
sub_2E8E8C(v4, v5);
sub_2E96C0(*(_QWORD *)(v0 + 8), sub_25A550);
sub_2892E4(*(_QWORD *)(v0 + 8));
return v0;
}同时满足三个参数和最后一个函数的只有这个
sub_2E9130(*(_QWORD *)(v0 + 8), 1, sub_2597E0);
所以说关键的函数应该是sub_2597E0
我们需要看下sub_2597E0干了什么
__int64 __fastcall sub_2597E0(__int64 a1)
{
__int64 v2; // x0
__int64 v3; // x0
v2 = sub_25A340();
v3 = sub_259808(v2, a1);
return sub_259818(v3);
}实际上应该返回数字代表状态,是否检验通过 实际的检测在sub_259818里面
如下 有兴趣的可以简单看下逻辑
__int64 __fastcall sub_259818(__int64 result)
{
__int64 v1; // x19
__int64 v2; // x0
__int64 v3; // x0
__int64 v4; // x22
__int64 v5; // x1
__int64 v6; // x0
__int64 v7; // x1
__int64 v8; // x22
__int64 v9; // x23
__int64 v10; // x24
__int64 v11; // x0
char v12; // nf
char v13; // vf
__int64 v14; // x8
unsigned __int8 v15; // w9
__int64 v16; // x10
__int64 v17; // x11
__int64 v18; // x21
_QWORD *v19; // x25
__int64 v20; // x26
__int64 v21; // [xsp+18h] [xbp-168h] BYREF
_QWORD v22[2]; // [xsp+20h] [xbp-160h] BYREF
__int64 v23; // [xsp+30h] [xbp-150h] BYREF
_QWORD v24[2]; // [xsp+38h] [xbp-148h] BYREF
__int64 v25; // [xsp+50h] [xbp-130h] BYREF
_QWORD v26[14]; // [xsp+58h] [xbp-128h] BYREF
_QWORD v27[2]; // [xsp+C8h] [xbp-B8h] BYREF
unsigned __int64 v28; // [xsp+D8h] [xbp-A8h] BYREF
unsigned __int64 v29; // [xsp+E0h] [xbp-A0h] BYREF
_QWORD v30[2]; // [xsp+E8h] [xbp-98h] BYREF
unsigned __int64 v31; // [xsp+F8h] [xbp-88h] BYREF
unsigned __int64 v32; // [xsp+100h] [xbp-80h] BYREF
__int64 v33; // [xsp+108h] [xbp-78h] BYREF
int v34; // [xsp+114h] [xbp-6Ch]
_QWORD v35[3]; // [xsp+118h] [xbp-68h] BYREF
v1 = result;
if ( *(_DWORD *)(result + 296) != 1 )
return sub_259B78(result);
if ( !*(_QWORD *)(result + 136) )
{
v2 = sub_2E50D8(*(_QWORD *)(result + 304));
v3 = sub_16F564(v2);
sub_1E784(v1 + 136, v3);
if ( *(_QWORD *)(v1 + 136) )
{
v4 = *(_QWORD *)(v1 + 904);
if ( *(_DWORD *)(v4 + 68) )
{
memset(v35, 170, sizeof(v35));
sub_1229F8(v35);
sub_16F0D4(v26, *(_QWORD *)(v1 + 136));
sub_122B50(v35, "certificates", 12, v26);
sub_12232C(v26);
sub_122298(v26, v35);
sub_122A54(v35);
sub_25B0BC(v4, 74, v1 + 888);
sub_12232C(v26);
}
v34 = -1431655766;
if ( (unsigned int)sub_259D88(v1) )
{
sub_15E058(v1 + 144);
*(_DWORD *)(v1 + 184) = v34;
sub_25B194(&v33);
sub_1E784(v1 + 176, v33);
*(_DWORD *)(v1 + 296) = 0;
return sub_259B78(v1);
}
*(_QWORD *)(v1 + 288) = sub_12090C();
v6 = sub_259DD4(v1);
v8 = v6;
v9 = v7;
if ( !v7 || (*(_BYTE *)(v1 + 799) = 1, !(unsigned int)sub_258BC4(v6, v7)) )
{
v31 = 0xAAAAAAAAAAAAAAAALL;
v32 = 0xAAAAAAAAAAAAAAAALL;
sub_2E91C0(*(_QWORD *)(v1 + 304), &v32, &v31);
v30[0] = v32;
v30[1] = v31;
v28 = 0xAAAAAAAAAAAAAAAALL;
v29 = 0xAAAAAAAAAAAAAAAALL;
sub_2E916C(*(_QWORD *)(v1 + 304), &v29, &v28);
v27[0] = v29;
v27[1] = v28;
v10 = *(_QWORD *)(*(_QWORD *)(v1 + 272) + 64LL);
v11 = sub_25B194(&v25);
if ( !v9 )
{
sub_25B168(v11);
if ( v12 != v13 )
v8 = v16;
else
v8 = v14;
if ( v12 != v13 )
v9 = v17;
else
v9 = v15;
}
v18 = sub_28B974(v1 + 384);
v19 = v35;
sub_EF4A4(v35, v30);
if ( v35[2] >= 0 )
{
v20 = HIBYTE(v35[2]);
}
else
{
v19 = (_QWORD *)v35[0];
v20 = v35[1];
}
sub_EF4A4(v24, v27);
sub_15AEFC(v26, v25, v8, v9, v18, v19, v20);
v22[0] = sub_259DF8;
v22[1] = 0;
v21 = v1;
v23 = sub_25AB48(sub_25AB18, v22, &v21);
*(_DWORD *)(v1 + 296) = (*(__int64 (__fastcall **)(__int64, _QWORD *, __int64, __int64 *, __int64, __int64))(*(_QWORD *)v10 + 16LL))(
v10,
v26,
v1 + 144,
&v23,
v1 + 280,
v1 + 888);
sub_C822C(&v23);
sub_15AFA8(v26);
sub_BACF0(v24);
sub_BACF0(v35);
return sub_259B78(v1);
}
sub_D58C8(v26, "VerifyCert", "../../net/socket/ssl_client_socket_impl.cc", 1189);
v5 = 4294967114LL;
}
else
{
sub_D58C8(v26, "VerifyCert", "../../net/socket/ssl_client_socket_impl.cc", 1143);
v5 = 4294967129LL;
}
sub_2893C4(v26, v5);
return 1;
}
__break(0);
return result;
}我们已经定位到了检测点 我们开始写hook 看看能不能抓到包
frida hook
有两个思路,
1是hook检测函数
2是hookub_2E9130(*(_QWORD *)(v0 + 8), 1, sub_2597E0);这个函数入参
把最后一个参数函数进行替换
大家可以试一下
我用的是第二种
当然可以改一下源码重打包
一定记得用-f com.360buy.jdmobile 模式hook
我们这启动一下试一试
hook成功


夜雨聆风