0
前言
文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口均已做脱敏处理。
严正声明禁止用于商业和非法用途,否则由此产生的一切后果与作者本人无关。若有侵权,请在vx【amuncocoL】联系作者。
1
抓包及定位
图中可见content像是base64编码,复制出来解码看是否为明文
很显然不行奥...
那大概率是经过了某个算法加密后base64encode的。
okay~
content是由接口返回,客户端进行解密后正常展示给用户浏览的
ps: 能直接解码就没必要记录咯 家银们...
2
content解密定位
第一步中我们已经怀疑了content是由base64编码的,那客户端收到响应后那第一步肯定是要先base64decode的,那我们先trace下看看 是否是这样的吧。
在Objective-C中base64解码是使用NSData类中的实例方法
initWithBase64EncodedString:options:
使用frida-trace trace下 执行以下命令:
frida-trace -UF -m "-[NSData initWithBase64EncodedString:options:]"
可以看到抓包的content和trace打印的入参是一致,那就证明我们的判断是正确的。content确实先进行base64decode,打印下堆栈继续往下跟~
0x103f51b60 Rxxxxxxg!0x36d9b60 (0x1036d9b60)
0x1008f1038 +[BBAES dataFromString:encoding:]
0x1053ba140 -[SSBookCrypto decryptContent:keyVersion:shouldDecompress:]
0x1053b99cc -[SSBookCrypto decryptChapterContent:completion:]
0x105428f04 Rxxxxxxg!0x4bb0f04 (0x104bb0f04)
0x105429158 Rxxxxxxg!0x4bb1158 (0x104bb1158)
0x10542aa84 Rxxxxxxg!0x4bb2a84 (0x104bb2a84)
0x101175b10 Rxxxxxxg!0x8fdb10 (0x1008fdb10)
0x1ba0d69a8 libdispatch.dylib!_dispatch_call_block_and_release
0x1ba0d7524 libdispatch.dylib!_dispatch_client_callout
0x1ba0ba6fc libdispatch.dylib!_dispatch_main_queue_callback_4CF$VARIANT$armv81
0x1ba38f6bc CoreFoundation!__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
0x1ba38a590 CoreFoundation!__CFRunLoopRun
0x1ba389ba8 CoreFoundation!CFRunLoopRunSpecific
0x1c44f9344 GraphicsServices!GSEventRunModal
0x1be4c53e4 UIKitCore!UIApplicationMain
从上面堆栈中可以推断这两个函数比较可疑
函数一:
-[SSBookCrypto decryptContent:keyVersion:shouldDecompress:]
函数二:
-[SSBookCrypto decryptChapterContent:completion:]
那我们到IDA中看下这两个函数做了什么事情~
我们在IDA的fuctions窗口搜索要分析的函数名时,搜索不到~ 看来该样本品符号都给抹除了。
感兴趣的可以去试着恢复符号。
-[SSBookCrypto decryptChapterContent:completion:]
地址减去moduleBaseAddr = 0x4b41934定位到ida
2
-[SSBookCrypto decryptChapterContent:completion:]
先附IDA伪代码:
__int64 __fastcall sub_104B41934(__int64 a1)
{
__int64 v1; // x23
__int64 v2; // x19
__int64 v3; // x20
void *v4; // x0
int v5; // w24
void *v6; // x0
void *v7; // x0
id v8; // x0
void *v9; // x24
__int64 v10; // x0
void *v11; // x0
void *v12; // x0
id v13; // x0
id v14; // x0
id v15; // x0
__int64 v16; // x0
__int64 v17; // x0
__int64 v18; // x0
__int64 v19; // x1
__int64 v20; // x0
void *v21; // x0
void *v22; // x0
id v23; // x0
id v24; // x0
id v25; // x0
__int64 v26; // x0
__int64 v27; // x0
__int64 v28; // x0
__int64 v29; // x0
__int64 v30; // x1
__int64 v31; // x0
void *v32; // x0
void *v33; // x0
id v34; // x0
id v35; // x0
id v36; // x0
__int64 v37; // x0
__int64 v38; // x0
__int64 v39; // x0
void *v40; // x0
id v41; // x0
id v42; // x0
__int64 v43; // x0
void *v44; // x0
void *v45; // x0
id v46; // x0
id v47; // x0
id v48; // x0
__int64 v49; // x0
__int64 v50; // x0
__int64 v51; // x0
id v52; // x0
__int64 v53; // x1
__int64 v54; // x0
__int64 v55; // x0
__int64 v56; // x0
__int64 v58; // [xsp+30h] [xbp-E0h]
id v59; // [xsp+58h] [xbp-B8h]
char v60; // [xsp+B0h] [xbp-60h]
v1 = a1;
v2 = sub_104B4249C();
v3 = ((__int64 (*)(void))sub_104B42500)();
v4 = (void *)sub_1001F8800(v2);
objc_retainAutoreleasedReturnValue(v4);
v5 = sub_1001DDF38(v2);
sub_104B425EC();
sub_100329CA8(v2);
if ( v5 == 2 )
{
objc_autoreleasePoolPush();
v20 = sub_104B42468(&v60);
sub_104B424CC(v20);
v21 = (void *)sub_1001C6AF0(v2);
objc_retainAutoreleasedReturnValue(v21);
v22 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v23 = objc_retainAutoreleasedReturnValue(v22);
v24 = objc_retainAutorelease(v23);
v25 = sub_100196A30(v24);
v26 = sub_104B4259C(v25, v25);
v27 = sub_104B4255C(v26);
v28 = sub_104B42494(v27);
v29 = sub_104B424E0(v28);
v58 = sub_104B42500(v29);
v18 = sub_104B425FC(v58, v30);
LABEL_13:
v42 = (id)sub_104B42470(v18, v19);
goto LABEL_14;
}
if ( (unsigned __int16)v5 == 1 )
{
objc_autoreleasePoolPush();
v31 = sub_104B42468(&v60);
sub_104B424CC(v31);
v32 = (void *)sub_1001C6AF0(v2);
objc_retainAutoreleasedReturnValue(v32);
v33 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v34 = objc_retainAutoreleasedReturnValue(v33);
v35 = objc_retainAutorelease(v34);
v36 = sub_100196A30(v35);
v37 = sub_104B4259C(v36, v36);
v38 = sub_104B42494(v37);
v39 = sub_104B42460(v38);
sub_104B424E0(v39);
v40 = (void *)sub_104B42624(&OBJC_CLASS___SSResult);
v41 = sub_1002AA5B0(v40);
objc_retainAutoreleasedReturnValue(v41);
v42 = (id)sub_1002CF858(v2);
if ( v3 )
v42 = (id)sub_104B42508(v42);
}
else
{
if ( !(_WORD)v5 )
{
v6 = (void *)sub_1001E4748(v1);
objc_retainAutoreleasedReturnValue(v6);
if ( sub_10024D838() )
{
sub_1002CF858(v2);
v7 = (void *)sub_104B42624(&OBJC_CLASS___SSResult);
v8 = sub_1002AA5B0(v7);
objc_retainAutoreleasedReturnValue(v8);
v9 = objc_autoreleasePoolPush();
v10 = sub_104B42468(&v60);
sub_104B424CC(v10);
v11 = (void *)sub_1001C6AF0(v2);
objc_retainAutoreleasedReturnValue(v11);
v12 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v13 = objc_retainAutoreleasedReturnValue(v12);
v14 = objc_retainAutorelease(v13);
v15 = sub_100196A30(v14);
v16 = sub_104B4259C(v15, v15);
v17 = sub_104B4255C(v16);
sub_104B42494(v17);
objc_autoreleasePoolPop(v9);
if ( v3 )
v18 = sub_104B42508(v18);
}
else
{
v59 = (id)sub_104B42500(0LL);
sub_104B425FC(v59, v53);
objc_release(v59);
}
goto LABEL_13;
}
objc_autoreleasePoolPush();
v43 = sub_104B42468(&v60);
sub_104B424CC(v43);
v44 = (void *)sub_1001C6AF0(v2);
objc_retainAutoreleasedReturnValue(v44);
v45 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v46 = objc_retainAutoreleasedReturnValue(v45);
v47 = objc_retainAutorelease(v46);
v48 = sub_100196A30(v47);
v49 = sub_104B4259C(v48, v48);
v50 = sub_104B42494(v49);
v51 = sub_104B42460(v50);
sub_104B424E0(v51);
v52 = sub_1002AA5B0(&OBJC_CLASS___SSResult);
v42 = objc_retainAutoreleasedReturnValue(v52);
if ( v3 )
v42 = (id)sub_104B42508(v42);
}
LABEL_14:
v54 = sub_104B42458(v42);
v55 = sub_104B42460(v54);
v56 = sub_104B42450(v55);
return sub_104B42448(v56);
}
先打印下函数入参 <SSChapterContent: 0x28329bde0>发现是对象,frida打印输出对象属性
var obj = ObjC.Object(args[2]).$ivars;
for (var k in obj){
log(k, obj[k]);
}
上边图中对象属性中encryptChapterContent正是抓包的content密文。
目前可以确定在调用函数:
-[SSBookCrypto decryptChapterContent:completion:]
之前是没有解密过的,思路正确往下接着分析。
sub_1001E4748()
调用:ObjC_msgsend
调用:decryptContent函数
对应地址:0x4b420c4
3
sub_4b420c4
该函数对应
:
:
] :
繁琐的分析过程就不再说了,重点在注释这块~
总结下:
第一步调用oc中的json序列表处理为oc对象。
第二步取出对象中的content密文调用sub_10007904进行base64 decode
第三步实例化BBAES、SSCryptoManager对象没啥好说的
第四步调用sub_1037bd3a4即-[SSCryptoManager decryptByData:version:error:]
version:error:]函数 :
4
-[SSCryptoManager decyptByData:version:error:]
__int64 __fastcall sub_1037BD3A4(__int64 a1, __int64 a2, __int64 a3, __int64 a4, id a5, __int64 a6, __int64 a7, __int64 a8, int a9, int a10, __int64 a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31, int a32, int a33, int a34, int a35, int a36, int a37, int a38, int a39, int a40, int a41, int a42, int a43, int a44, int a45, int a46, int a47, __int64 a48)
{
__int64 v48; // x30
__int64 v49; // x0
_QWORD *v50; // x4
_QWORD *v51; // x21
int v52; // w3
int v53; // w22
void *v54; // x23
__int64 v55; // x1
void *v56; // x2
id v57; // x19
id v58; // x0
id v59; // x0
id v60; // x0
id v61; // x0
id v62; // x26
__int64 v63; // x0
void *v64; // x0
id v65; // x0
id v66; // x0
id v67; // x0
__int64 v68; // x0
__int64 v69; // x0
__int64 v70; // x0
__int64 v71; // x0
__int64 v72; // x0
void *v73; // x0
id v74; // x0
id v75; // x0
id v76; // x0
__int64 v77; // x0
__int64 v78; // x1
__int64 v79; // x2
__int64 v80; // x3
__int64 v81; // x0
void *v82; // x0
id v83; // x0
id v84; // x0
__int64 v85; // x0
__int64 v86; // x0
void *v87; // x0
id v88; // x0
id v89; // x0
__int64 v90; // x0
__int64 v91; // x0
__int64 v92; // x0
void *v93; // x0
id v94; // x0
id v95; // x0
__int64 v96; // x1
id v97; // x0
id v98; // x0
id v99; // x0
id v100; // x25
__int64 v101; // x0
__int64 v102; // x0
void *v103; // x0
id v104; // x0
id v105; // x0
__int64 v106; // x0
__int64 v107; // x0
__int64 v108; // x0
__int64 v109; // x0
id v110; // x0
__int64 v111; // x1
__int64 v112; // x2
__int64 v113; // x3
void *v114; // x0
id v115; // x0
id v116; // x0
__int64 v117; // x0
id v119; // [xsp+18h] [xbp+18h]
v49 = sub_1037BE298(a1, v48, a3, a4, a5);
v51 = v50;
v53 = v52;
v54 = (void *)v49;
v57 = sub_1037BE360(v49, v55, v56);
if ( !v53 )
sub_100247FE8(v54);
v58 = sub_10020FF00(v54);
if ( objc_retainAutoreleasedReturnValue(v58) )
{
// dataFromString:encoding:
v59 = sub_1001E3068(&OBJC_CLASS___BBAES);
objc_retainAutoreleasedReturnValue(v59);
// originKeyData
v60 = sub_100270FF8(v54);
objc_retainAutoreleasedReturnValue(v60);
// decryptedDataFromData:IV:key:
// 0x180adb66feac8a6ceec7755ed624561a
v61 = sub_1001E4808(&OBJC_CLASS___BBAES);
v62 = objc_retainAutoreleasedReturnValue(v61);
// objc_release
sub_1037BE220();
// objc_release
sub_1037BE210();
objc_autoreleasePoolPush();
sub_1037BE1F0();
sub_1037BE2FC();
sub_1037BE258(v63);
sub_1037BE2E4();
v64 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v65 = objc_retainAutoreleasedReturnValue(v64);
v66 = objc_retainAutorelease(v65);
v67 = sub_100196A30(v66);
sub_1037BE1FC(v67);
sub_1037BE220();
sub_1037BE370(v68);
v119 = v62;
// true
if ( (unsigned __int64)sub_1037BE2E4() > 0xF )
{
v97 = sub_100343040(v57);
objc_retainAutoreleasedReturnValue(v97);
sub_1037BE2E4();
v98 = sub_100343040(v57);
objc_retainAutoreleasedReturnValue(v98);
// decryptedDataFromData:IV:key:
// 0x1036d99dc
v99 = sub_1001E4808(&OBJC_CLASS___BBAES);
v100 = objc_retainAutoreleasedReturnValue(v99);
v62 = objc_autoreleasePoolPush();
sub_1037BE1F0();
sub_1037BE2FC();
v102 = sub_1037BE258(v101);
sub_1037BE324(v102);
v103 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v104 = objc_retainAutoreleasedReturnValue(v103);
v105 = objc_retainAutorelease(v104);
v106 = sub_1037BE3C8(v105);
v107 = sub_1037BE1FC(v106);
v108 = sub_1037BE31C(v107);
v109 = sub_1037BE3A8(v108);
if ( v100 && sub_1037BE324(v109) )
{
v110 = objc_retain(v100);
v62 = v110;
}
else
{
v114 = (void *)sub_1001FB710(&OBJC_CLASS___NSError);
v115 = objc_retainAutoreleasedReturnValue(v114);
v116 = objc_autorelease(v115);
v110 = (id)sub_1037BE344(v116);
}
sub_1037BE274(v110, v111, v112, v113);
sub_1037BE220();
sub_1037BE210();
}
else
{
objc_autoreleasePoolPush();
v69 = sub_1037BE1F0();
v70 = sub_1037BE354(v69);
v71 = sub_1037BE258(v70);
v72 = sub_1037BE3D0(v71);
v73 = (void *)sub_1003421A0(v72);
v74 = objc_retainAutoreleasedReturnValue(v73);
v75 = objc_retainAutorelease(v74);
v76 = sub_100196A30(v75);
v77 = sub_1037BE1FC(v76);
v81 = sub_1037BE274(v77, v78, v79, v80);
sub_1037BE370(v81);
v82 = (void *)sub_1001FB710(&OBJC_CLASS___NSError);
v83 = objc_retainAutoreleasedReturnValue(v82);
v84 = objc_autorelease(v83);
sub_1037BE344(v84);
}
objc_release(v119);
}
else
{
objc_autoreleasePoolPush();
v85 = sub_1037BE1F0();
v86 = sub_1037BE354(v85);
sub_1037BE27C(v86);
v87 = (void *)sub_1003421A0(&OBJC_CLASS___NSString);
v88 = objc_retainAutoreleasedReturnValue(v87);
v89 = objc_retainAutorelease(v88);
v90 = sub_1037BE368(v89);
v91 = sub_1037BE1FC(v90);
v92 = sub_1037BE208(v91);
sub_1037BE370(v92);
v93 = (void *)sub_1001FB710(&OBJC_CLASS___NSError);
v94 = objc_retainAutoreleasedReturnValue(v93);
v95 = objc_autorelease(v94);
v62 = 0LL;
*v51 = v95;
}
v117 = sub_1037BE26C(v95, v96);
sub_1037BE290(v117);
return sub_1037BE23C(v62);
}
上图为分析的思路,最终可以倒推到:
v99=sub_101E4808()
恢复符号后是
+[BBAES decryptedDataFromData:IV:key:]
trace下这个函数看看~
从上图可以看到这个函数被多次调用
其中相关的调用 key和iv都可以拿到~
把content进行base64解码后转成hex,
仔细分析可以看出iv为前16字节。
撒花~到这AES解密基本结束了 不过还没完~
5
gzip压缩
decryptByData:version:error: 分析完了,接着回到
-[SSBookCrypto decryptChapterContent:completion:]
继续分析
可以看到AES解密后的bytes由v13接收, v14=v13。
紧接着调用sub_1001E2E28()
即
-[NSData dataByGZipDecompressingDataWithError:]
从函数名可以知道是gzip解压,验证下看看
古德古德 歪瑞古德!
你以为到这里就结束了吗?
不不不
6
动态key、iv
不绕圈了 该样本的key、iv是动态下发的。
其实站在开发者的角度思考,如果采用客户端固定key和iv的话 没必要将上面的iv放到response由服务端下发~
接着再细细分析一番 发现端倪~
过程细节就不说了,上图getService函数取了crypt/register的响应进行base64解码前16字节为iv 后为key
7
总结
该样本难度一般, 适合练手~
🐴赛克有点重 望理解~
旨在记录,大佬轻喷~
文中有错误的地方,还请各位大佬斧正~
撒花~ ✿✿ヽ(°▽°)ノ✿下次再会。
原文始发于微信公众号(小白逆向之旅):【iOS逆向】某某小说content逆向分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论