论坛首页 逆向工程技术区 阅读主题

[原创]ttEncrypt加密逆向分析(详细版)

471 浏览 10 回复
#1 楼主 2026-06-01 21:08:44
EncryptorUtil 类的 ttEncrypt 函数用于对一段二进制数据加密(用于http请求body加密),函数定义如下:函数传入 byte[] 和 长度,返回一个 byte[],其 native 层代码位于 libEncryptor.so 中
IDA 反编译 so 发现并没有直接jni 绑定,因此 hook RegisterNatives 函数寻找绑定的 native 层函数地址:打印输出:
[RegisterNatives] method_count: 0x1 0x76d0acbd6c libEncryptor.so!0xd6c
[RegisterNatives] java_class: com.bytedance.frameworks.encryptor.EncryptorUtil name: ttEncrypt sig: ([BI)[B fnPtr: 0x76d0ad2d88 module_name: libEncryptor.so module_base: 0x76d0acb000 offset: 0x7d88native 层函数位于文件偏移 0x7d88 处。反编译代码如下:这是补全变量类型后的反编译代码,获取传入的 jbytearray 后,转成 jbyte*,重新申请 size+118大小的内存,然后调用 sub_2BD4函数处理,如果处理的长度大于 0,则调用NewByteArray、SetByteArrayRegion 拷贝处理的结果并返回,否则返回 NULL。因此函数sub_2BD4 内部会处理加密逻辑,并且加密后的大小是 size + 118.汇编代码对应如下:

反编译 sub_2BD4:实际是调用 sub_2D28,并传入了一些参数,我们看汇编代码看参数是怎么传递的:

这个函数有 5 个参数,
参数 1:地址 0xB2C0,是一个数据段的地址,看起来像是一个字节码序列
参数 2:是一个参数指针,里面保存了调用者传入的 4 个参数,即 jbyte和 jsize char 和 int*
参数 3:0
参数 4:地址 0x21CE0,像是一个地址数组
参数 5:是一个地址,里面保存了函数 sub_2D1C 和 sp +0x510函数 sub_2D28 反编译代码:控制流图:

反编译代码完全看不懂处理的逻辑,并且控制流非常复杂。有点像控制流平坦化混淆,结合了虚假控制流等,因为在控制流上有非常多平行的基本块,通过一个 dispatch 在分发。但是控制流平坦化的逻辑是每个基本块末尾会有一个常量衔接了另一个基本块。而且控制流平坦化一般不涉及到对函数参数的处理,涉及的一般是基本块或指令。这个应该是虚拟机混淆。再次查看函数 sub_2BD4 的汇编指令,发现函数头部 SP, SP, #0x520 申请的栈空间特别大,函数内部只需要很小的栈空间,然后参数5中传入 sp+0x510,栈中的一个地址,所以参数5指向的栈地址,应该是虚拟机的内存和context所需的内存大小,在函数sub_2BD4栈中分配,但是并没有初始化操作,只是将这个栈内存指针传递到了 sub_2D28 这个虚拟机函数,那么在虚拟机函数中一定会做初始化。分析虚拟机函数:虚拟机函数的第一个基本块,保存了虚拟机函数所需要的 5 个参数,参数1保存在 [X19 - 0x138],同时也在寄存器 X0中。
参数2保存在 [X19 - 0x110],这是一个参数指针,指针里有4个原函数运行参数。
参数3保存在 [X19 - 0x108],值为0。
参数4保存在 [X19-0x100],是一个地址数组
参数5的部分 [X19- 0xF8],是一个函数地址接着给部分寄存器赋值常量和跳转表,无跳转跳转到 loc_2DD8 运行,因此继续分析 loc_2DD8 处的汇编指令:上述的代码块像是取值和分发环节,从 X0 寄存器取出4字节,保存到 W12中, 低 6 位W12[0-5]作为操作码,是一个索引,根据这个索引定位到跳转地址。W8 = W12[16:20],W9 = W12[21:25],W10 = W12[ 31,12:15],W11 = W12[26:30],还有 6-11位域没有处理。接下来就是指令的译码环节:
字节码指令的起始位置是 0xB2C0,结束位置在B5B3:
字节码的总长度是 0x2F4,每个指令占4个字节,因此一共有0xBD个指令,十进制是 189个 ,写一个脚本统计字节码中出现的所有操作码:输出如下:
40: 28
59: 56
47: 57
2: 38
15: 2
42: 2
4: 1
62: 1
21: 2
54: 2先分析出现最多的操作码:47,X28存放了跳转表的起始地址,文件偏移 0xA960处:

跳转地址保存在 0xA960 + 47 * 4 = 0xAA1C处:

跳转地址是 loc_333C:再次解析字节码,当 opcode = 47时,输出第二个操作码:输出如下:
op1: 40: 28
op1: 59: 56
op1: 2: 38
op1: 15: 2
op1: 42: 2
op1: 4: 1
op1: 62: 1
op1: 21: 2
op1: 54: 2
op1: 47, op2: 24: 30
op1: 47, op2: 3: 1
op1: 47, op2: 49: 2
op1: 47, op2: 39: 8
op1: 47, op2: 22: 15
op1: 47, op2: 26: 1当 op2 = 24时,跳转地址保存在 0xAFD8 + 4 * 24 = 0xB038:

因此 op1 = 47 & op2 = 24的解码地址是 loc_411c:虚拟机函数的入口代码块有对 W23 和 W24寄存器赋值:loc_41AC:def_4104(应该是虚拟指令执行结束后的尾部):这个基本块执行后,更新了当前字节码的地址,保存到了X21寄存器只想的内存中。但是还没有完成当前指令的功能。跳回到了 loc_2DD4 ,回到了指令分发块。 可能这个指令的前面会设置控制变量 [X19 - 0x20] 的值,1、2、3会跳转到不同分支继续执行。先跳过这个指令,分析 op1 = 59 的情况:
跳转地址保存在 0xA960 + 59 * 4 = 0xAA4C处:

跳转地址位于 loc_2F70:上述基本块从W12寄存器中提取出一个16位的常数,具体提取方式是 W11[0:5] = W12[6:11] ,W11[6] = W12[26] W11[7:11] = W12[27:31],W11[12:15] = W12[12:15] , W9 = W12[21:25] 取出了一个虚拟寄存器的地址,这是一个索引,ADD X9, X21, W9,UXTW#3 计算虚拟寄存器的具体地址,接着 LDR X9, [X9,#8] 取出虚拟寄存器的值 ADD X9, X9, W11,SXTH,虚拟寄存器的值+常数偏移 → X9 = vXn + imm

跳转到 loc_356C继续执行:

W8 = W12[16:20],也是从字节码中提取的一个虚拟寄存器索

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-286273.htm
#2 2026-06-01 21:08:44
支持ing
#3 2026-06-01 21:08:44
谢谢分享!
#4 2026-06-01 21:08:44
感谢分享
#5 2026-06-01 21:08:44
辛苦 。。。。。。。。。。。。
#6 2026-06-01 21:08:44
这个讲的真的好全面
#7 2026-06-01 21:08:44
good
#8 2026-06-01 21:08:44
真的是详细版
#9 2026-06-01 21:08:44
你的分享对大家帮助很大,非常感谢!
#10 2026-06-01 21:08:44
ttEncrypt
#11 2026-06-01 21:08:44
本文只是公开分析过程

请登录后参与讨论

立即登录 注册账号