某短视频指纹和纯算sig3逆向分析

admin 2026-01-12 01:19:13 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文详细分析某短视频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

image-20251214201725361

第二个显然就不可能,第一个就是之前的原语!

我开始怀疑是不是我插件的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_3D5F4image-20251216142527223

就是上述我们填入的时间戳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)
&nbsp; &nbsp;&nbsp;int&nbsp;feature_mask;// [5] <--- 这里就是 v72 (特性掩码) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0xd00
};

之前以为到这里还没结束,其实是已经结束了,因为之前说是需要48个字节是因为默认其通过编码存储,但是发现都是通过Hex存储的,之后只需要整理一下写个纯算python即可

这里就是异或,还原一下即可,没什么难度。之后就可以按照这个写出sig3的纯算python了

贴一下这部分的代码:

&nbsp;复制代码&nbsp;隐藏代码
defsimulate_obfuscation(packed_data_bytes):
&nbsp; &nbsp; final_result =&nbsp;bytearray(packed_data_bytes)

&nbsp; &nbsp; v71 =&nbsp;0
&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;inrange(23):
&nbsp; &nbsp; &nbsp; &nbsp; v73 = final_result[i]
&nbsp; &nbsp; &nbsp; &nbsp; v71 += v73

&nbsp; &nbsp;&nbsp;if&nbsp;v71 >&nbsp;0xFF:
&nbsp; &nbsp; &nbsp; &nbsp; v71 = -v71

&nbsp; &nbsp; v72 =&nbsp;0xd00

&nbsp; &nbsp; v71_32bit = v71 &&nbsp;0xFFFFFFFF
&nbsp; &nbsp; shifted_v71 = (v71_32bit <<&nbsp;24) &&nbsp;0xFFFFFFFF

&nbsp; &nbsp; new_int_val = v72 | shifted_v71

&nbsp; &nbsp; struct.pack_into('<I', final_result,&nbsp;20, new_int_val)

&nbsp; &nbsp; v71_low_byte = v71 &&nbsp;0xFF

&nbsp; &nbsp;&nbsp;for&nbsp;v74&nbsp;inrange(23):
&nbsp; &nbsp; &nbsp; &nbsp; xor_key = v71_low_byte ^ (v74 &&nbsp;0xFF)
&nbsp; &nbsp; &nbsp; &nbsp; final_result[v74] ^= xor_key

&nbsp; &nbsp;&nbsp;return&nbsp;final_result

也不会特别困难,其中v72是之前的0xd00,有个可能会出现的问题就是range(23)而不是range(24)根据逆向可知其不会对最后一位进行异或处理

&nbsp;复制代码&nbsp;隐藏代码
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',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; magic,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; user_id,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; seq_id,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; crc32_result,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; time,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; feature_mask)
result1 = simulate_obfuscation(packed_data)

总结

这个算法总的来说就是HMAC-SHA256 -> AES-128 -> CRC32

总体难度不高QwQ

-官方论坛

www.52pojie.cn

👆👆👆

公众号设置“星标”,不会错过新的消息通知

开放注册、精华文章和周边活动等公告


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:吾爱破解论坛 吾爱pojie《某短视频指纹和纯算 sig3 逆向分析》

评论:0   参与:  0