
看雪ID:Mengz3
https://bbs.kanxue.com/user-home-962430.htm
目标
分析响应中的 encData 参数
判断编码/加密方式 还原客户端解密链
02
APP初始分析
APP扫描:
[TOOL] APK 扫描输出:[TOOL] APK检测工具 - 扫描配置:[TOOL] - 文件路径: xxx[TOOL] - 检测类型: ROOT(true) 模拟器(true) 反调试(true) 代理(true) SDK(true) 硬编码(false) 证书(true)[TOOL] - 最大文件大小: 500 MB[TOOL] - 递归扫描: true[TOOL] ---------------------------------------------------[TOOL] 正在扫描APK文件: xxx[TOOL][TOOL] - 加固特征扫描结果[TOOL] - 未发现加固特征[TOOL][TOOL] - 安全检测特征扫描结果[TOOL] - 模拟器检测特征[TOOL] - classes.dex -> emulator (模拟器标识)[TOOL] - classes.dex -> goldfish (Android模拟器内核标识)[TOOL] - 代理检测特征[TOOL] - classes.dex -> Ljavax/net/ssl/X509TrustManager; (自定义证书信任管理器)[TOOL] - classes.dex -> Ljavax/net/ssl/X509TrustManager; (自定义证书信任管理器)[TOOL][TOOL] - 第三方SDK特征扫描结果[TOOL] - Google[TOOL] - Flutter -> lib/arm64-v8a/libapp.so[TOOL] - Flutter -> lib/arm64-v8a/libflutter.so[TOOL] - Flutter -> lib/armeabi-v7a/libapp.so[TOOL] - Flutter -> lib/armeabi-v7a/libflutter.so[TOOL] - Flutter -> lib/x86_64/libapp.so[TOOL] - Flutter -> lib/x86_64/libflutter.so[TOOL] - Huawei[TOOL] - HMS Scan Kit -> lib/arm64-v8a/libscannative.so[TOOL] - HMS Scan Kit -> lib/armeabi-v7a/libscannative.so[TOOL] - Mpv[TOOL] - LibMpv -> lib/arm64-v8a/libmpv.so[TOOL] - LibMpv -> lib/armeabi-v7a/libmpv.so[TOOL] - LibMpv -> lib/x86/libmpv.so[TOOL] - LibMpv -> lib/x86_64/libmpv.so[TOOL] - 证书扫描结果[TOOL] - 未发现证书文件扫描发现flutter框架,搜索界面抓包分析一下。
请求包:
GET /api/search/keyWord?pageSize=20&searchType=1&searchWord=搜索关键字&page=1 HTTP/1.1user-agent: realme/RE546F/RMX3366/RMX3366_14.0.0.2100(CN01)/android_version=34/chigua_app/ver=2.0.1deviceid: 3ba82b6df20ef02829ddd378720d220fc1ba74c3accept: application/json;charset=UTF-8accept-encoding: gzipaut: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0NTE5MTc2IiwiaXNzIjoia2RtaDE0IiwiaWF0IjoxNzc4MzQxOTMxLCJuYmYiOjE3Nzg0Nzc0NzcsImV4cCI6MTkzNjE1NzQ3N30.hAoY_3MeAkUDeR5TK5zZSuY4MQMaoSD082z4XtwQrWss: 6abe2dc524aa837f52f47e46e9a1e795host: ojqpi.qbyjhuwtlh.workcontent-type: application/json;charset=UTF-8t: 1778478120170请求包中似乎没有什么重点,看看响应包:
HTTP/1.1 200Content-Type: application/json;charset=UTF-8Transfer-Encoding: chunkedConnection: keep-aliveServer: nginx/1.22.1Date: Mon, 11 May 2026 05:42:01 GMTX-XSS-Protection: 1; mode=blockContent-Encoding: gzipVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINX-Cache: Miss from cloudfrontVia: 1.1 ce03dab0723e1a81658675d5bdaf168c.cloudfront.net (CloudFront)X-Amz-Cf-Pop: LAX50-P5X-Amz-Cf-Id: FtoVgMKtfgc5SOUwWt0ULaV_A3sUDf-g-5YcmF-y_DMZaxxIoeEehw==Vary: Origin{"code":200,"msg":"success","encData":"0b/3mMo5VcwmmB+kBXR6tCZ24QWmendkcH8m0YIHs480sWU0QW2EBNyQjOLCup7ieZXHBzDYy+EyFCQ1TfJudJuEkfsYDSAtFYL8RxOhbARbs/58xaOJXUGljd+QtGainfvrw+QX5gOBUNcjzlZJh/kfz9/AycfjzW3NbegRa+BjGNRuL9Wvog2uzh/CIL8u2c4m20RtzMClzzWdQBLO7BKJ3deYUQ7vJFHx0uUkT789LBFqimIv7G1vnFpImiMf9bRuDXDhoQ1NctHy4k6DCpExydq1+DSI0eF6NHxMTXa/wYq7w5iuHLEpEABNBQ82P+ixE/o/FljzIGQkf8+lHpRQrwNp4jxVGnx0wX9czoKc76yoC0wqlFEoBQgZ8nkwDO2dLRUzV2gOciM4lcUqI9fNXv2nPPI2urKJgXs1s0ybBmtGE39/KlywyfdnJvc8advnWpaWsXcm7P0uUXCrkATMX9IFpLqNC9e8XCQVHy/RcqVL6u87pHpiPka4Tlq+zLHBmjIU3GylecFO0azigKH/XGdKJxPUqhoXEOoHvuQojM5Y1PSpxrGegJH4HaFotcS/hnvq6g9xcHjVvk/cZ1oko+XIvsR5Ys46l5ojm31bmZSIGfseJqGGBpBxwZRdv2SItIoLQJiBWIlqJiHVUjQHzcqk2uKl/bykj8nIcYgT8FNDug1V2BH6ykW7ff7Nj+QzQtERrAtCamLOX......这里太长了省略......TkI6sKEkH+K2cbei3SKChigC/UpNwbf0sYVplshbvMkfP2E="}返回了encData参数,base64加密后的。b64解不出来,乱码,大概率是经过其他加密的。
结合 Base64 解码失败后的非明文表现,初步怀疑后续仍存在对称解密层(AES/DES/SM4),并且数据很长。
使用jadx搜不到encData参数,转战so文件。
在Flutter架构中:libflutter.so 负责“怎么运行 Flutter”libapp.so 负责“这个 App 具体运行什么逻辑”
03
Flutter 场景处理
打开libapp.so之前,先用Blutter还原一下。
Blutter 是 Flutter Android Release App 的 Dart AOT 分析工具,核心价值是帮你从 libapp.so 里提取 Object Pool、字符串、符号、汇编和 Frida 模板(worawit/blutter: Flutter Mobile Application Reverse Engineering Tool)。具体的使用教程可以问AI或者上网搜索,这里就跳过了。
还原后能看一些函数的具体名字,不至于盲目分析。如果直接使用ida 打开libapp.so,所有函数都是sub_开头,几乎无法分析。
04
响应参数encData分析
在IDA中搜索encData
只搜索到一个,发现也没有交叉引用
这里尝试去hook 调用该地址也没hook到
hook代码:
constbase = Module.findBaseAddress("libapp.so");const target = base.add(0x91071);const page = ptr(target).and(ptr(-Process.pageSize));MemoryAccessMonitor.enable([{ base: page, size: Process.pageSize }], { onAccess(details) {if (!ptr(details.address).equals(target)) return;constfrom = details.from;const m = Process.findModuleByAddress(from);if (!m || m.name !== "libapp.so") return; console.log("[*] encData hit -> " + from.sub(m.base)); }});之前已经知道,参数最后编码成base64,这里搜索一下base64
这里有不少base64相关函数,全部hook一遍,看看谁被调用了,hook的时候打印参数和返回值内存,看看能不能找到encData一样的数据。
hook代码:
// 目标 so 基址const soName = "libapp.so";constbase = Module.findBaseAddress(soName);// 安全 dump 内存;失败时返回错误信息,避免脚本中断function dumpMem(p, size = 0x40) {try {return hexdump(ptr(p), { offset: 0, length: size, header: true, ansi: false }); } catch (e) {return"[dump failed] " + e; }}// 通用 hook:打印参数、参数内存,以及返回值和返回值内存function hookFunc(off, name) {const addr = base.add(off); Interceptor.attach(addr, { onEnter(args) {this.name = name;this.off = off; console.log("\n[*] " + name + " hit @0x" + off.toString(16));for (let i = 0; i < 6; i++) {try { console.log("arg" + i + " = " + args[i]); } catch (e) { console.log("arg" + i + " = <err>"); } }for (let i = 0; i < 6; i++) { console.log("\n[arg" + i + " mem]");try { console.log(dumpMem(args[i])); } catch (e) { console.log("[dump failed] " + e); } } }, onLeave(retval) {try { console.log("\n[retval] " + retval); } catch (e) { console.log("\n[retval] <err>"); } console.log("[retval mem]");try { console.log(dumpMem(retval)); } catch (e) { console.log("[dump failed] " + e); } } });}// Base64 相关关键函数:// - 编码入口 / 分块编码 / 收尾// - 解码入口 / 分块解码 / padding 检查// - 编解码过程中的 stub/辅助函数const targets = [ [0x470920, "dart_convert_::base64Encode_470920"], [0x3C2470, "dart_convert_Base64Codec::normalize_3c2470"], [0x3C2B5C, "dart_convert_Base64Codec::_checkPadding_3c2b5c"], [0x3C2CB0, "dart_convert__Base64Decoder::_inverseAlphabet_3c2cb0"], [0x4845E0, "dart_convert_Base64Codec::decode_4845e0"], [0x88C5E0, "dart_convert_Base64Codec::get_decoder_88c5e0"], [0x890988, "dart_convert_Base64Encoder::convert_890988"], [0x890A14, "dart_convert__Base64Encoder::encode_890a14"], [0x890B14, "dart_convert__Base64Encoder::encodeChunk_890b14"], [0x890E7C, "dart_convert__Base64Encoder::writeFinalChunk_890e7c"], [0x891040, "Allocate_Base64EncoderStub_891040"], [0x89104C, "dart_convert_Base64Decoder::convert_89104c"], [0x891130, "dart_convert__Base64Decoder::close_891130"], [0x8911E0, "dart_convert__Base64Decoder::decode_8911e0"], [0x8912AC, "dart_convert__Base64Decoder::decodeChunk_8912ac"], [0x8917BC, "dart_convert__Base64Decoder::_allocateBuffer_8917bc"], [0x8918A8, "dart_convert__Base64Decoder::_trimPaddingChars_8918a8"], [0x891A08, "dart_convert__Base64Decoder::_checkPadding_891a08"], [0x891C60, "Allocate_Base64DecoderStub_891c60"],];// 批量安装 hook,失败也继续处理下一个targets.forEach(([off, name]) => {try { hookFunc(off, name); console.log("[+] hook ok: " + name + " @0x" + off.toString(16)); } catch (e) { console.log("[-] hook fail: " + name + " @0x" + off.toString(16) + " err=" + e); }});结果:
Hook的结果发现不少函数参数或者返回值是encData根据 hook 命中结果筛选调用链,观察函数名字,只有dart_convert_Base64Codec::decode_4845e0最符合,其他大概率都是此函数下游调用链
- dart_convert_Base64Codec::decode_4845e0 - arg2 = 0x7201f00069- dart_convert_Base64Decoder::convert_89104c - arg2 = 0x7201f00069- dart_convert__Base64Decoder::decode_8911e0 - arg2 = 0x7201f00069- dart_convert__Base64Decoder::_allocateBuffer_8917bc - arg1 = 0x7201f00069 - arg4 = 0x7201f00069- dart_convert__Base64Decoder::_trimPaddingChars_8918a8 - arg1 = 0x7201f00069 - arg4 = 0x7201f00069- dart_convert__Base64Decoder::decodeChunk_8912ac - arg1 = 0x7201f00069- dart_convert__Base64Decoder::_checkPadding_891a08 - arg1 = 0x7201f00069- dart_convert__Base64Decoder::close_891130 - arg2 = 0x7201f00069 - arg4 = 0x7201f00069IDA中搜索看一下dart_convert_Base64Codec::decode_4845e0此函数。
很正常的base64函数,往下追可以追到其他base64函数调用链。
直接X看下此函数的被谁调用,追到了encrypt_encrypt_Encrypter::decrypt64_484308

这里将base64解密的值大概率传给了最后的return加密函数。
hook一下最后return的encrypt_encrypt_Encrypter::decrypt_48436c()看下参数和返回值。
hook代码:
const soName = "libapp.so";const base = Module.findBaseAddress(soName);functiondumpMem(p, size = 0x80) {try {returnhexdump(ptr(p), {offset: 0,length: size,header: true,ansi: false }); } catch (e) {return"[dump failed] " + e; }}Interceptor.attach(base.add(0x48436C), {onEnter(args) {this.hit = true;console.log("\n[*] encrypt_encrypt_Encrypter::decrypt_48436c hit @0x48436c");for (let i = 0; i < 6; i++) {console.log("arg" + i + " = " + args[i]); }for (let i = 0; i < 6; i++) {console.log("\n[arg" + i + " mem]");console.log(dumpMem(args[i])); } },onLeave(retval) {if (!this.hit) return;console.log("\n[retval] " + retval);console.log("[retval mem]");console.log(dumpMem(retval)); }});输出:
[*] encrypt_encrypt_Encrypter::decrypt_48436c hit @0x48436c[JS] arg0 =0x7200e40529[JS] arg1 =0x7200e40509[JS] arg2 =0x7200e448e9[JS] arg3 =0x7200e40139[JS] arg4 =0x7200e44900[JS] arg5 =0x7200e7ffe8[JS][arg0 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200e40529400700000000004005 e4 007200000000@......@...r....7200e4053900000040870000 d1 bf f7 98 ca 3955 cc 26 ...@........9U.&7200e40549981f a4 05747a b4 a3 a2 c2 9d 538c8768 a4 ....tz.....S..h.7200e405592c635278 f5 e8 1f 1a 07 a1 73 df 987d 8523,cRx......s..}.#7200e40569437790796561 c2 ff 89 cc ed ee 80 ce 1367 Cw.yea.........g7200e405795a 568f e4 7d 304a ba 84 d9 6f 4a 59 fb fd 11 ZV..}0J...oJY...7200e40589 ee e0 628a ea ed 76 ec 1d 4024383d 88 f6 f8 ..b...v..@$8=...7200e4059938776c3c e7 98 d4 3b 0d bc 0d 6837 d7 f9 918wl<...;...h7...[JS][arg1 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200e4050991 ed 00000000001902 e4 00818000001c ................7200e40519 f1 280100000000 ff ff ff ff ff ff ff ff 1c .(..............7200e40529400700000000004005 e4 007200000000@......@...r....7200e4053900000040870000 d1 bf f7 98 ca 3955 cc 26 ...@........9U.&7200e40549981f a4 05747a b4 a3 a2 c2 9d 538c8768 a4 ....tz.....S..h.7200e405592c635278 f5 e8 1f 1a 07 a1 73 df 987d 8523,cRx......s..}.#7200e40569437790796561 c2 ff 89 cc ed ee 80 ce 1367 Cw.yea.........g7200e405795a 568f e4 7d 304a ba 84 d9 6f 4a 59 fb fd 11 ZV..}0J...oJY...[JS][arg2 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200e448e9 a1 ed 00000000002905 e4 008180000081 .......)........7200e448f9 80000081800000000000000000000000 ................7200e4490900000000000000000000000000000000 ................7200e4491900000000000000000000000000000000 ................7200e4492900000000000000000000000000000000 ................7200e4493900000000000000000000000000000000 ................7200e4494900000000000000000000000000000000 ................7200e4495900000000000000000000000000000000 ................[JS][arg3 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200e40139 c1 ed 0000000000 e9 01 e4 00818000001c ................7200e40149 c2 270100000000100000000000000069 .'.............i7200e40159 01 e4 00 81 80 00 00 81 80 00 00 81 80 00 00 1c ................7200e40169 45 07 00 00 00 00 00 80 01 e4 00 72 00 00 00 00 E..........r....7200e40179 00 00 00 60 00 00 00 4a 68 62 47 63 69 4f 69 4a ...`...JhbGciOiJ7200e40189 49 55 7a 49 31 4e 69 00 00 00 00 00 00 00 00 00 IUzI1Ni.........7200e40199 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e401a9 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1c ................[JS] [arg4 mem][JS] 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF7200e44900 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44910 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44920 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44930 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44940 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44950 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44960 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200e44970 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................[JS] [arg5 mem][JS] [dump failed] Error: access violation accessing 0x7200e80000[JS] [retval] 0x7200ed50a9[JS] [retval mem][JS] 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF7200ed50a9 f0 05 00 00 00 00 00 48 7c 00 00 00 00 00 00 7b .......H|......{7200ed50b9 00 22 00 76 00 69 00 64 00 65 00 6f 00 4c 00 69 .".v.i.d.e.o.L.i7200ed50c9 00 73 00 74 00 22 00 3a 00 5b 00 7b 00 22 00 76 .s.t.".:.[.{.".v7200ed50d9 00 69 00 64 00 65 00 6f 00 49 00 64 00 22 00 3a .i.d.e.o.I.d.".:7200ed50e9 00 32 00 32 00 30 00 39 00 38 00 38 00 2c 00 22 .2.2.0.9.8.8.,."7200ed50f9 00 74 00 69 00 74 00 6c 00 65 00 22 00 3a 00 22 .t.i.t.l.e.".:."7200ed5109 00 20 00 f0 6c fd 56 76 98 a7 7e 63 00 6f 00 73 . ..l.Vv..~c.o.s7200ed5119 00 65 00 72 00 20 00 48 00 69 00 67 00 68 00 73 .e.r. .H.i.g.h.s可以看到返回值已经将encData解密出来了,参数中arg3也有值得关注的,JhbGciOiJIUzI1Ni一个16位字符串,这里留意一下可能包含着密钥或者IV信息。
4.1 加密算法分析:
接下来继续,看能不能找到加密方式/密码/IV之类的信息。
往下继续追一下encrypt_encrypt_Encrypter::decrypt_48436c(),直接进入函数。
里面套了一个加密函数4843b4(),传入的参数继承自48436c()没有变化,接着进入encrypt_encrypt_Encrypter::decryptBytes_4843b4去查看。
追到这里就恍然大悟了,AES加密,并且传入的参数也变化了。
hook一下encrypt_encrypt_Encrypter::decryptBytes_48441c()看看能不能找到一些信息。
hook代码(和上一个hook代码一样,只需修改 0x48436C --> 0x48441c 即可)
const soName = "libapp.so";const base = Module.findBaseAddress(soName);functiondumpMem(p, size = 0x80) {try {returnhexdump(ptr(p), {offset: 0,length: size,header: true,ansi: false }); } catch (e) {return"[dump failed] " + e; }}Interceptor.attach(base.add(0x48441c), {onEnter(args) {this.hit = true;console.log("\n[*] encrypt_encrypt_Encrypter::decrypt_48441c hit @0x48441c");for (let i = 0; i < 6; i++) {console.log("arg" + i + " = " + args[i]); }for (let i = 0; i < 6; i++) {console.log("\n[arg" + i + " mem]");console.log(dumpMem(args[i])); } },onLeave(retval) {if (!this.hit) return;console.log("\n[retval] " + retval);console.log("[retval mem]");console.log(dumpMem(retval)); }});输出:
[*] encrypt_encrypt_Encrypter::decrypt_48441c hit @0x48441c[JS] arg0 =0x7200c8d989[JS] arg1 =0x7200c8d989[JS] arg2 =0x7200c92059[JS] arg3 =0x7200008081[JS] arg4 =0x7200c92070[JS] arg5 =0x7200c8d8a9[JS][arg0 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200c8d989 d2 ed 0000000000 c9 d7 c8 001174950041 ............t..A7200c8d999 bb 2d 0059 dc c8 0081800000818000001c .-.Y............7200c8d9a9 a2 050000000000818000000800000071 ...............q7200c8d9b9 522800 c1 0e 2f 00 c1 e4 000041 bb 2d 005c R(.../.....A.-.\7200c8d9c9 e2 0500000000001a 0000000000000041 ...............A7200c8d9d9 45532f 4342432f 504b 4353370000001c ES/CBC/PKCS7....7200c8d9e9 a2 050000000000818000000600000001 ................7200c8d9f9 d2 2d 0091130100 c9 d9 c8 00818000005c .-.............\[JS][arg1 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200c8d989 d2 ed 0000000000 c9 d7 c8 001174950041 ............t..A7200c8d999 bb 2d 0059 dc c8 0081800000818000001c .-.Y............7200c8d9a9 a2 050000000000818000000800000071 ...............q7200c8d9b9 522800 c1 0e 2f 00 c1 e4 000041 bb 2d 005c R(.../.....A.-.\7200c8d9c9 e2 0500000000001a 0000000000000041 ...............A7200c8d9d9 45532f 4342432f 504b 4353370000001c ES/CBC/PKCS7....7200c8d9e9 a2 050000000000818000000600000001 ................7200c8d9f9 d2 2d 0091130100 c9 d9 c8 00818000005c .-.............\[JS][arg2 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200c92059 a1 ed 000000000099 dc c8 008180000081 ................7200c92069 80000081800000000000000000000000 ................7200c92079 00000000000000000000000000000000 ................7200c92089 00000000000000000000000000000000 ................7200c92099 00000000000000000000000000000000 ................7200c920a9 00000000000000000000000000000000 ................7200c920b9 00000000000000000000000000000000 ................7200c920c9 00000000000000000000000000000000 ................[JS][arg3 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200008081 b1 0a 00 db 070000000000000000000070 ...............p7200008091 f1 0300140d 0000000000000000000072 ...............r72000080a1 f1 0300 cf 040000010000000000000072 ...............r72000080b1 f1 0300 d5 040000000000000000000030 ...............072000080c1 560000 ef 14000011 f2 00008180000081 V...............72000080d1 80000081800000818000008180000081 ................72000080e180000081800000818000008180000081 ................72000080f1 80000081800000818000008180000005 ................[JS][arg4 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200c92070 00000000000000000000000000000000 ................7200c92080 00000000000000000000000000000000 ................7200c92090 00000000000000000000000000000000 ................7200c920a0 00000000000000000000000000000000 ................7200c920b0 00000000000000000000000000000000 ................7200c920c0 00000000000000000000000000000000 ................7200c920d0 00000000000000000000000000000000 ................7200c920e0 00000000000000000000000000000000 ................[JS][arg5 mem][JS]0123456789 A B C D E F0123456789ABCDEF7200c8d8a9 c1 ed 000000000059 d9 c8 00818000001c .......Y........7200c8d8b9 c2 2701000000001000000000000000 d9 .'..............7200c8d8c9 d8 c8 00 81 80 00 00 81 80 00 00 81 80 00 00 1c ................7200c8d8d9 45 07 00 00 00 00 00 f0 d8 c8 00 72 00 00 00 00 E..........r....7200c8d8e9 00 00 00 60 00 00 00 4a 68 62 47 63 69 4f 69 4a ...`...JhbGciOiJ7200c8d8f9 49 55 7a 49 31 4e 69 00 00 00 00 00 00 00 00 00 IUzI1Ni.........7200c8d909 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................7200c8d919 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1c ................[JS] [retval] 0x7200d091b9[JS] [retval mem][JS] 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF7200d091b9 40 07 00 00 00 00 00 d0 91 d0 00 72 00 00 00 00 @..........r....7200d091c9 00 00 00 28 87 00 00 7b 22 76 69 64 65 6f 4c 69 ...(...{"videoLi7200d091d9 73 74 22 3a 5b 7b 22 76 69 64 65 6f 49 64 22 3a st":[{"videoId":7200d091e9 32 32 30 39 38 38 2c 22 74 69 74 6c 65 22 3a 22 220988,"title":"7200d091f9 20 e6 b3 b0 e5 9b bd e9 a1 b6 e7 ba a7 63 6f 73 ............cos7200d09209 65 72 20 48 69 67 68 73 73 74 6e 20 e9 ab 98 e9 er Highsstn ....7200d09219 a2 9c e5 80 bc e5 a4 a7 e5 a5 b6 e5 b0 a4 e7 89 ................7200d09229 a9 20 e5 92 8c e7 98 a6 e7 8c b4 e7 94 b7 e5 8f . ..............在 encrypt_encrypt_AES::decrypt_48441c 的参数 dump 中,可以明确观察到 AES/CBC/PKCS7 字样,说明该响应字段的核心解密算法可定位为 AES-CBC-PKCS7。
同时,在参数内存中还出现了 JhbGciOiJIUzI1Ni 这一字符串片段,其与请求头 aut 中 JWT 头部内容一致,说明认证材料可能参与了解密流程或参数构造,这里猜测为密钥或者IV;返回值也是解密的正常可见字符串结果。
4.2 密钥和IV
接下来目标是确定密钥和IV的具体值。
经过长时间的分析,我们返回到encrypt_encrypt_Encrypter::decrypt64_484308()来分析。

刚才在分析的时候,我们hook了48436c(),hook结果中已经发现了JhbGciOiJIUzI1Ni字符串:
[arg3 mem][JS] 0123456789 A B C D E F 0123456789ABCDEF7200e40139 c1 ed 0000000000 e9 01 e4 00818000001c ................7200e40149 c2 270100000000100000000000000069.'.............i7200e4015901 e4 008180000081800000818000001c ................7200e40169450700000000008001 e4 007200000000 E..........r....7200e40179000000600000004a 68624763694f694a ...`...JhbGciOiJ7200e4018949557a 49314e 69000000000000000000 IUzI1Ni.........7200e4019900000000000000000000000000000000 ................7200e401a9 0000000000000000000000000000001c ................因为猜测这里大概率是密钥或者IV,但是在调用此函数时已经生成了这个字符串了,是48436c()的参数,说明密钥/IV的生成函数还在前面。
X追一下encrypt_encrypt_Encrypter::decrypt64_484308()的调用链
这里直接发现了Key和IV
第一个红框中的每一行代码的解释: 1.分配一个 Key 对象。 2.把前面 substring() 的结果取出来,放到 v18。 3.把这个 Key 对象保存到局部变量里,后面还要继续用。 4.Key.fromUtf8(sub)
第二个红框中的每一行代码的解释: 1.分配一个 IV 对象。 2.把前面 substring() 的结果取出来,放到 v20。 3.把这个 IV 对象保存到局部变量里,后面还要继续用。 4.IV.fromUtf8(sub)
再往上看代码,无论是v18还是v20都是从一个变量里被赋值的,这样就能解释了Key和IV的值一样。
//伪代码:String s = StorageService.read(...);String sub = s.substring(2, 36); Key key = Key.fromUtf8(sub);IV iv = IV.fromUtf8(sub);AES aes = new AES(key);Encrypter encrypter = new Encrypter(aes);String plain = encrypter.decrypt64(encData, iv);return jsonDecode(plain);StorageService::_read_456074:读取本地存储值 substring_3afbf8:固定切片 AllocateKeyStub + ctor_fromUtf8:构造 key AllocateIVStub + ctor_fromUtf8:构造 iv decrypt64:对 encData 做 base64 + AES 解密
实在不放心的话,可以hook一下encrypt_encrypt_Encrypted::ctor_fromUtf8_4ac39c(),用之前hook的代码,改个地址就可以。
输出截取关键部分:
第一次hook到4ac39c():[arg2 mem][JS]0123456789 A B C D E F0123456789ABCDEF720129c5b9 e2 05000000000020000000000000004a ....... .......J720129c5c9 68624763694f 694a 49557a 49314e 691c hbGciOiJIUzI1Ni.720129c5d9 b1 ed 0000000000818000008180000081 ................720129c5e9 80000081800000000000000000000000 ................720129c5f9 00000000000000000000000000000000 ................720129c609 00000000000000000000000000000000 ................720129c619 00000000000000000000000000000000 ................720129c629 00000000000000000000000000000000 ................第二次hook到4ac39c():[arg2 mem][JS]0123456789 A B C D E F0123456789ABCDEF720129c5b9 e2 05000000000020000000000000004a ....... .......J720129c5c9 68624763694f 694a 49557a 49314e 691c hbGciOiJIUzI1Ni.720129c5d9 b1 ed 000000000089 c6 2901818000001c .........)......720129c5e9 c2 270100000000100000000000000009 .'..............720129c5f9 c6 29 01 81 80 00 00 81 80 00 00 81 80 00 00 1c .)..............720129c609 45 07 00 00 00 00 00 20 c6 29 01 72 00 00 00 00 E...... .).r....720129c619 00 00 00 60 00 00 00 4a 68 62 47 63 69 4f 69 4a ...`...JhbGciOiJ720129c629 49 55 7a 49 31 4e 69 00 00 00 00 00 00 00 00 00 IUzI1Ni.........这里完全可以证明key和iv是一个值,都是JhbGciOiJIUzI1Ni
最后解密验证一下:
解密一下,结果出来了。
05
encData 处理链为:
Base64 decode AES/CBC/PKCS7 decrypt UTF8 decode / jsonDecode key / iv 来源于本地存储固定切片
key 与 iv 在当前函数中来源相同
懂的都懂:WVVoU01HTklUVFpNZVRrelpESkdNV1ZwTlhOWlZ6VTJZak5XTTB4dFRuWmlVemx3Vmtob2JsbDZUbmRaZW14dFRXMW5TelZoSzBjMU5rTkNUMnBLZGsxVVRUMD0=
推荐一下比较好用的 Frida 工具:Frida-Hookers
https://github.com/Mengz3/Frida-Hookers
来源:看雪学苑


夜雨聆风