文章总结: 本文详细分析某短视频APP的sig3签名生成算法。通过抓包、Java层Hook与Native层unidbg模拟,作者还原了算法流程:包含HMAC-SHA256、白盒AES-128加密及CRC32校验。文章详细记录了提取AES密钥与反混淆过程,并提供了纯算Python复现代码,具有较高的逆向实战参考价值。 综合评分: 88 文章分类: 逆向分析,移动安全,二进制安全,实战经验
Native层浅析
入口
Hook registerNative函数获取
复制代码 隐藏代码
[+] Captured RegisterNatives for: com.kuaishou.android.security.internal.dispatch.JNICLibrary
Method count: 5
-----------------------------------------
[TARGET FOUND] Method: doCommandNative
Signature: (I[Ljava/lang/Object;)Ljava/lang/Object;
Absolute Address: 0x75f4981680
Module: libkwsgmain.so
Base Address: 0x75f4940000
!!! OFFSET: 0x41680 !!!
-----------------------------------------
在libkwsgmain.so

第二个显然就不可能,第一个就是之前的原语!
我开始怀疑是不是我插件的BUG了?但是这么简单的插件怎么可能有问题呢,unidbg检测一下这个地址附近的值
复制代码 隐藏代码
emulator.traceWrite(0x404e4e80,0x404e4e80+0x30);
复制代码 隐藏代码
[20:12:51483] Memory WRITE at 0x404e4e80, datasize=1, datavalue=0x63, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e81, datasize=1, datavalue=0x35, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e82, datasize=1, datavalue=0x64, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e83, datasize=1, datavalue=0x34, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e84, datasize=1, datavalue=0x61, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
xxxxxxx
[20:12:51483] Memory WRITE at 0x404e4e8f, datasize=1, datavalue=0x63, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e90, datasize=1, datavalue=0x38, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
xxxxxx
[20:12:51483] Memory WRITE at 0x404e4e96, datasize=1, datavalue=0x38, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e97, datasize=1, datavalue=0x66, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e98, datasize=1, datavalue=0x37, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51483] Memory WRITE at 0x404e4e99, datasize=1, datavalue=0x32, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
xxxx
[20:12:51484] Memory WRITE at 0x404e4ea5, datasize=1, datavalue=0x37, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51484] Memory WRITE at 0x404e4ea6, datasize=1, datavalue=0x65, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51484] Memory WRITE at 0x404e4ea7, datasize=1, datavalue=0x33,
xxxx
[20:12:51484] Memory WRITE at 0x404e4eaf, datasize=1, datavalue=0x34, PC=RX@0x40013bf0[libkwsgmain.so]0x13bf0, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51484] Memory WRITE at 0x404e4eb0, datasize=1, datavalue=0x00, PC=RX@0x40013c34[libkwsgmain.so]0x13c34, LR=RX@0x40013bd0[libkwsgmain.so]0x13bd0
[20:12:51485] Memory WRITE at 0x404e4e80, datasize=8, datavalue=0x3738346134643563, PC=RX@0x401dc17c[libc.so]0x1c17c, LR=RX@0x4000d908[libkwsgmain.so]0xd908
[20:12:51485] Memory WRITE at 0x404e4e88, datasize=8, datavalue=0x6366633234336439, PC=RX@0x401dc17c[libc.so]0x1c17c,
xxxxx
[20:12:51485] Memory WRITE at 0x404e4ea8, datasize=8, datavalue=0x3438323963393039, PC=RX@0x401dc18c[libc.so]0x1c18c, LR=RX@0x4000d908[libkwsgmain.so]0xd908
根据这个trace可以找到调用逻辑
复制代码 隐藏代码
sub_A720->sub_1E5B8->sub_3D5F4->sub_13B1C
其中有几个函数的部分函数调用是不会调用的,直接可以nop
大致知道调用顺序了,那么开始分析吧,从尾巴开始往上分析
HMAC-SHA256
sub_3D5F4
就是上述我们填入的时间戳1960949932368L / 1000
2:
复制代码 隐藏代码
LDAXR W9, [X8]
STLXR W10, W9, [X8]
LD和ST分别是加载和载入,X是独占的语义,说明在这个期间X8指向的内存只有这条汇编可以改写,如果写入期间发现有其他的线程修改了这个内存地址,那么会返回1,继续读取然后自增,如果没有就直接返回0退出这个循环
其实说白了,就是一个自增器,确保内容的唯一性
而v68就是一个原子递增后的新序列号,这样每发一次包,就会生成一个唯一的请求ID!
猜测服务端接收时会按照这个ID来进行对抗
5:
这里调试发现结果都是0xd00(QwQ这里感觉如果是固定的就很简单,估计不同的功能这里是不一样的,但是有点复杂,这里只考虑当前情况)
可以做个简单的小总结
复制代码 隐藏代码
structSignatureBlock {
int magic; // [0] 0x4151 (ASCII 'AQ') - 头部魔数 0x41512200
int user_id; // [1] 用户ID 或 盐值 (从 qword_72998 获取) 0x1dbxxe7f
int seq_id; // [2] 序列号 (原子自增 v68)
int crc32; // [3] 数据的校验和 (CRC32_120C4)
int timestamp; // [4] 当前时间戳 (gettimeofday)
int feature_mask;// [5] <--- 这里就是 v72 (特性掩码) 0xd00
};
之前以为到这里还没结束,其实是已经结束了,因为之前说是需要48个字节是因为默认其通过编码存储,但是发现都是通过Hex存储的,之后只需要整理一下写个纯算python即可
这里就是异或,还原一下即可,没什么难度。之后就可以按照这个写出sig3的纯算python了
贴一下这部分的代码:
复制代码 隐藏代码
defsimulate_obfuscation(packed_data_bytes):
final_result = bytearray(packed_data_bytes)
v71 = 0
for i inrange(23):
v73 = final_result[i]
v71 += v73
if v71 > 0xFF:
v71 = -v71
v72 = 0xd00
v71_32bit = v71 & 0xFFFFFFFF
shifted_v71 = (v71_32bit << 24) & 0xFFFFFFFF
new_int_val = v72 | shifted_v71
struct.pack_into('<I', final_result, 20, new_int_val)
v71_low_byte = v71 & 0xFF
for v74 inrange(23):
xor_key = v71_low_byte ^ (v74 & 0xFF)
final_result[v74] ^= xor_key
return final_result
也不会特别困难,其中v72是之前的0xd00,有个可能会出现的问题就是range(23)而不是range(24)根据逆向可知其不会对最后一位进行异或处理
复制代码 隐藏代码
hmac = HMAC_SHA256(input_val)
aes_result = aes_ecb_encrypt(hmac, aes_key)
crc32_result = sub_120C4_simulation(aes_result)
packed_data = struct.pack('<6I',
magic,
user_id,
seq_id,
crc32_result,
time,
feature_mask)
result1 = simulate_obfuscation(packed_data)
总结
这个算法总的来说就是HMAC-SHA256 -> AES-128 -> CRC32
总体难度不高QwQ
-官方论坛
www.52pojie.cn
👆👆👆
公众号设置“星标”,您不会错过新的消息通知
如开放注册、精华文章和周边活动等公告
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:吾爱破解论坛 吾爱pojie《某短视频指纹和纯算 sig3 逆向分析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论