首页
社区
课程
招聘
[原创] 某招聘 App 搜索接口逆向与纯算
发表于: 7小时前 261

[原创] 某招聘 App 搜索接口逆向与纯算

7小时前
261

最近在看某招聘 App 的职位搜索接口,把 native 层简单逆了下,实现纯算复现协议签名。

抓包:搜索请求的 query 里挂着一个 sp(几百字符的密文串)和一个 sigV3.0 开头的 32 位 hex),请求体是二进制,响应应该也是密文。这三样都出自一个 native 库 libyzwg.so(JNI 包装类 com.twl.signer.YZWG,Java 侧封装 com.twl.signer.a)。

版本是 v14.050(Y29tLmhwYnIuYm9zc3poaXBpbg==)。

环境:

搜索结果列表顺着 GeekSearchCardRequest 找到端点和关键的通用加签层 net.bosszhipin.base.m ,它的 c() 给每个请求补公共参数(curidentity/v/req_time/uniqid/client_info),然后按请求是不是批量,分流到 e()(普通)或 f()(批量)。搜索走批量,f() 反编译结果如下(去噪):

com.twl.signer.a 就是这层 native 的 Java 薄封装,每个方法都会转发到对应的 nativeXxx

com.twl.signer.a 的反编译

关键逻辑全在libyzwg.so 里:nativeSignature / nativeEncodeRequest / nativeEncodeRequestBody / nativeCalculateCRC32 / nativeDecodeContent

libyzwg.so 4.47MB、OLLVM 平坦化、字符串加密、JNI_OnLoad 里带反重打包自检,反汇编难度较大。先让这个 so 在 unidbg 里跑起来,当一个可以任意喂输入、看输出的黑盒预言机,用差分探针把算法的形状摸出来。

跑 unidbg 的门槛是 JNI_OnLoad 的反篡改自检,用 -Dvmverbose=1 看 JNI upcall 序列:getPackagesForUid 要真实包名、getPackageInfo(pkg, GET_SIGNATURES).signatures[0].toCharsString().hashCode() 要等于内嵌常量。满足它(喂真实证书 DER + 补一个真的 String.hashCode),JNI_OnLoad 就正常返回、native 方法注册上了。

签名侧:

全部是 V3.0 + 32 hex(=16 字节,MD5 家族),前导 0 恒定,key 参与。但拿真机抓的一条 (input, key, sig) 三元组去对撞 md5(input+key)md5(key+input)hmac 等等,一个都不中。哈希里还包含一个内嵌盐。黑盒推不出盐,这步必须分析汇编。

sp 探针的线索:

把 base64 解回原始字节观察:同一个 key 下,前 12 字节恒定(70e22488554382feca2200f2,换 key 就变);再把 16*'A'32*'A' 两条 payload 逐字节 XOR,结果几乎全 00,keystream 被抵消了。sp = 头(仅由 key 决定) + [ compress(input) XOR keystream(key) ],是流密码、不是分组,且密钥流只由 key 决定(sp 确定、无随机 IV)。这跟之前分析过的 GCash APSE 的 zipAndEncryptData 很类似。

JNI_OnLoad(OLLVM 平坦化,状态机在一个 v5 上转)里能看到 (*env + 1720) 就是 RegisterNatives,注册表 off_443030、10 个方法,类名走 FindClass(0x443130)。类名那串 5f 53 51 13 48 4b 50 13...XOR 0x3c 一解就是 com/twl/signer/YZWG。so 里的字符串都是 XOR 混淆,而且每个字符串用不同的单字节 key,末尾那个字节就是 key(存的是 0x00^key)。照这个规律把 name/sig 表整个解出来:

反编译 nativeSignature(0x23b6c),核心的 buffer 拼接如下:


sig = 前缀 + HASH(input ‖ qword_444470 ‖ key)。前缀 byte_443804cb ae b3 ad,XOR key 0x9d)解出来是 "V3.0"qword_444470JNI_OnLoad 里算好缓存的一个 32 字符串,由证书签名的 hashCode 派生(就是反篡改校验那个常量),对官方签名是定值。

salt 的值直接从 unidbg 内存里 dump:

真机 oracle:

sig = "V3.0" + md5(input ‖ salt ‖ key),完全匹配。

反编译 nativeEncodeRequest(0x1f6ac):

图片描述

头前 12 字节 "BZPBlock"+u32(0) 是常量明文,RC4 keystream 固定,异或出来就是固定的 12 字节密文;后面 压缩长/原长/校验 才随输入变。

sub_340CC 是标准 RC4 KSA(S[i]=i 初始化,j = j + S[i] + key[i%len] 交换);sub_34740 是 RC4 PRGA,但每个字节套了一层 TWIST(x) = (~x & 0xCF) | (x & 0x30)

0xCF | 0x30 == 0xFF 且不重叠,所以 TWIST(x) == x ^ 0xCF,两次 TWIST 在 XOR 时相互抵消,out = keystream ^ in 就是标准 RC4,TWIST 纯属混淆。

图片描述
纯 Python 复现,对照 unidbg 的全部合成样本 + 真机样本:

真机 2086 字节样本只有前 16 个 b64 字符(=那 12 字节恒定头)逐字节相同,往后就对不上了:设备的 liblz4 和 python-lz4 对同一输入做了不同但都合法的压缩选择,头里的压缩长不一样。但我的 sp round-trip 解回原文成功、校验位对,服务器只解压不比字节,所以照样接受(后面实测证明了)。

剩下三个原语:nativeCalculateCRC32 = snprintf(..., "%u", crc32(x))(IEEE CRC32 的无符号十进制串,空输入返 "");nativeEncodeRequestBody 和 sp 同管线、只是最后返回原始字节不 base64;nativeDecodeContent 是逆(RC4 解密,视情况再 BZPBlock/LZ4 解压)。

搜索走 GET /api/batch/requests,body 里带上请求:

外层 strD 的参数(client_info/curidentity/req_time/uniqid/v)要用 Java URLEncoder 风格编码:~%7E、空格→+、保留 .-*_。这点很容易踩:Python 默认 quote 会保留 ~,编出来的 strD 和服务器重算的对不上、sig 就废。按 Java 规则实现一个 java_url_encode,逐字节对拍捕获的 strD 和子 query,两个都 match=True,才敢用它重建查询。

踩着服务器的报错往前走:


传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!

最后于 1小时前 被星野安全编辑 ,原因:
收藏
免费 21
打赏
分享
最新回复 (12)
雪    币: 169
活跃值: (2217)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
6666
5小时前
0
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
感谢分享
5小时前
0
雪    币: 97
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
66
5小时前
0
雪    币: 1055
活跃值: (1515)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
66
5小时前
0
雪    币: 1827
活跃值: (7913)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
1
4小时前
0
雪    币: 245
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
666
4小时前
0
雪    币: 3708
活跃值: (4025)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
6666
3小时前
0
雪    币: 33
活跃值: (220)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
666
3小时前
0
雪    币: 79
活跃值: (450)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
666
2小时前
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
666
1小时前
0
雪    币: 248
活跃值: (716)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
12
学习一下
1小时前
0
雪    币: 109
活跃值: (605)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
666
30分钟前
0
游客
登录 | 注册 方可回帖
返回