首页
社区
课程
招聘
[原创]某短视频虚拟机分析和还原
发表于: 2024-6-26 17:51 39369

[原创]某短视频虚拟机分析和还原

2024-6-26 17:51
39369

在windows流行的时候,虚拟机保护是多人不敢碰的东西现在依然也是如此,pc的性能比移动端性能要高出不少,虚拟化和变异的代码多到令人发指,因此在加密保护强度上要比移动端要强很多很多,为了移动端App更好的体验(ANR率)移动端加密强度短时间内不会达到pc上的强度,随着移动cpu性能越来越好相信加密强度会逐年加强。

早年兴趣使然分析研究过windows端VMProtect、Safengine Shielden、Themida、VProtect、Enigma Protector等等虚拟机,最近发现国内流行的短视频也有虚拟机加密同时也比较感兴趣,便开始了我的分析之旅。

分析任何虚拟机必须要扣汇编指令级细节。

安卓诞生这么多年了至今没有像windows端olldbg、x64dbg那样友好的调试器,IDA PRO虽然自带了安卓调试器总是没有相像中的稳定。lldb作为移动端iOS和android开发的御用调试器,带源码调试在开发环境中还算比较友好,而汇编级调试只能输入命令行了,这是很多用惯了gui调试器的人接受不了的,但是个人发现lldb调试稳定性出奇的好,功能上比IDA Pro的安卓调试器强大太多了。

libEncryptor.so一共包含了三套虚拟机,三套虚拟机各自独立并且代码一模一样,本文重点只分析vm2虚拟机
图片描述
虚拟机指令编解码参考借鉴了arm64的一部分规则,并实现了自己的一套规则,在后面的解码分析中会有很多和arm64解码相似的地方。
另外虚拟机并没有像VMProtect那样将一条指令分割成多条"微指令"的方式,此虚拟机没有把当前真实的上下文放到虚拟机上下文去模拟执行,而是运行了一套自己单独的上下文。

在函数中调用虚拟机时会传入一个指针数组类型参数变量,这是传入到虚拟机入口的唯一参数。

c伪代码来表示函数调用虚拟机入口

反汇编版本:
图片描述

IDA Pro查看入口的cfg图,复杂程序看似很难其实一点都不简单,话说回来cfg看起来和ollvm的混淆平坦化非常相似,其实和ollvm混淆关系不大,只不过一部分的switch被拉平了,在了解调度逻辑后分析也不算复杂。
在代码中依然能看到两个ollvm swtich var变量,其作用没有详细分析,但整个cfg图确定与ollvm关系不是很大,猜测开启ollvm后性能会大幅下降影响app启动速度了。
图片描述
在进入虚拟机运行时前,在入口需要准备虚拟机所需的内存和参数。对虚拟机内存布局情况必须了解如指掌,这样在动态和静态分析时才不会迷失方向。
图片描述

vm2_run仅仅只分配了保存被调用者寄存器的堆栈内存空间,并没有分配空闲的堆栈内存,在虚拟机真实开始之前会将传递进来的5个参数即0x-x4对虚拟机中的虚拟寄存器和真实专用寄存器进行初始化。
图片描述
vm_run还初始化了解码opcode的switch表,在初始化时发现一共初始化了6张switch表,当然在handler中还存在其他switch表,这么多表是如何来的?猜测编写时只有1-2张表,在编译器优化后表就被分割成多块了。
图片描述

虚拟寄存器初始化
vm2_run初始化时会将传递的5个参数赋值给虚拟寄存器,其中包括PC和SP的值。

真实专用寄存器
虚拟机运行时使用了真实虚拟器,其中包括临时寄存器和专用寄存器,临时寄存器保存多种类型的值,而专用寄存器在虚拟机从开始到退出只保存一种指定类型数据或恒定不变。
opcode位域伪代码:
w12保存了4位32字节的opcode,在opcode首次解码时,位域中的变量会放到真实寄存器。
位域伪代码示例:
w12[26]: 取第26位到放到入目标的26位
w12[26->0]: 取第26位并放入到目标的指定位
w12[27-26->1-0]: 取27位和26位放入到目标第1位和第0位
|: 按位或

虚拟机context
虚拟机中也有专用寄存器,在调用外部函数时,其中x4虚拟机中保存外部函数地址,x5虚拟机中保存参数指针,x25虚拟机中调用外部函数时的跳板地址。另外虚拟寄存器和aarch64中的寄存器并不是一一对应的,在这里只是对每个虚拟寄存器启了一个相应的名字方便理解和记忆

pOpcode取出4个字节的opcode并取得低5位解码出op1。

图片描述

在opcode首次解码时会解码4个寄存器操作数,从中提取4个位域的字段,分别保存到真实专用寄存器w8、w9、w10、w11,在arm64指令集中当指令是MADD、MSUB、UMADDL、UMSUBL、SMADDL、SMADDL等会有4个寄存器操作数的情况,在这里同样也是如此。
首次解码时位域布局:w12[31,30-26,25-21,20-16,15-12,11-6,5-0]
w12[5-0:6] = op1
w12[11-6:6] = op2
w12[15-12:4]|[31:1] = Xm/Wm
w12[20-16:5] = Xt/Wt
w12[25-21:5] = Xn/Wn
w12[30-26:5] =Xa/Wa
w8: Rd(Xt/Wt)目标寄存器操作数,w12[20-16->5-0],取出16-20位保存最低位,5位可以表示0-31个寄存器。
w9: Rn(Xn/Wn)(第一个源寄存器操作数,w12[25-21->5-0],取出21-25位保存最低位,5位可以表示0-31个寄存器。
w10: Rm(Xm/Wm)第二个源寄存器操作数,w12[15->4] | w12[14->3] | w12[13->2] | w12[12->1] | w12[31->0],5位可以表示0-31个寄存器。
w11: Ra(Xa/Wa)第三个源寄存器操作数,w12[30->4] | w12[29->3] | w12[28->2] | w12[27-26->1-0],5位可以表示0-31个寄存器。
寄存器操作数伪代码表示如下:
操作数大小: X为64位操作数、W为32位操作数。
目标操作数: d=destination register, t=target register。
源寄存器:第一个源寄存器为n,第二个寄存器为m,第三个寄存器为a。
R: 表示寄存器64或32位操作数,X是64位操作数、W代表32位操作数。
图片描述

在高级语言中如果一个switch太多,在某些编译器编译优化后出现在多张switch子表和子表的子表的情况,对于一些相同代码的多个case会合并到一个case中再次switch分发的情况。
根据编译器的优化编译的特性,在处理switch的case为了减少查找次数找到最终的case,在代码中会经常看到大于(GT)、小于(LE)等分支跳转,看到这个不要迷惑,这是编译器优化case的结果,这样做的目的是减少查找次数使用了类似二分查找的算法,当看到B.EQ的跳转目标和B.NE的下一条指令就是匹配到了case常量了。
首次解码后分发过程中通常还会有二次解码,除了MADD和MSUB等等指令有4个寄存器操作数,指令只需一般指令只有2-3个寄存器操作数,一条完整的arm64指令至少需要二个寄存器操作数,这里也同样如此,当指令只有二个寄存器操作数时即一个目标操作数Xt和第一个源操作数Xn,其他的位域字段就会空闲下来,例如:op2、Xm、Xa,在二次解码时这些空闲的位域字段原有的值就会覆盖被再次利用组成其他寻址方式例如:shift、extend、imm等等。
在虚拟机指令op1的值是11(0xb)时,分发处理会解码op2,解码op2时Xt、Xn、Xm等操作数位置会发生变化,原有的x10/w10第三个源操作数在op2中变成目标操作数,第一个源操作数变成x8/w8,第二个源操作数变成x9/w9,各操作数的位域解码方式不变,变的只是操作数角色。
Xt:Rd(Xt/Wt)目标寄存器操作数(x10/w10),w12[15->4] | w12[14->3] | w12[13->2] | w12[12->1] | w12[31->0],5位可以表示0-31个寄存器。
Xn: Rn(Xn/Wn)(第一个源寄存器操作数(x8/w8),w12[20-16->5-0],取出16-20位保存最低位,5位可以表示0-31个寄存器。
Xm: Rm(Xm/Wm)第二个源寄存器操作数(x9/w9),w12[25-21->5-0],取出21-25位保存最低位,5位可以表示0-31个寄存器。

从op1的取值范围为0-63一共64条指令,当op1是11时还有存在op2和op3的情况,由于虚拟机的handler太过庞大分析所有的指令太过耗时,我们目的是还原代码逻辑,只需分析执行过的handler,对于没有执行过的handler放弃分析。
在了解了op1解码方式后,通过脚本得到执行次数最多的指令并优先分析:

得到执行次数最多的指令:

从python打印的结果来看23、11、40前面三个指令使用最为频繁,因篇幅关系只分析这三条指令,其中op1值是11的还存在第二个操作码op2,这里选择op1=11 & op2=12的指令进行分析。

准备下一条指令: pc指针地址加4

当op1值等于11时会解码第二个或第三个操作码。

分析完取指、解码、执行后大体得到了一个模糊的虚拟机执行框架,为了方便记忆和理解使用c伪代码来描述。
图片描述

使用脚本解析opcode的op1和op2打印所有需要分析的handler。

一共打印出15个handler,好在需要分析还原的指令不多:

在15个handler并还原后,现在重新编写脚本decode_opcode.py解码opcode,将打印还原的指令打印出伪汇编代码并保存文件ttencryptor.asmgenerate_aes_key_iv.asm

vm2还原的伪汇编代码,经过分析主要做了这些事件:

由于vm2会调用vm3生成aes的key和iv,因此vm3的代码也需要解析还原generate_aes_key_iv.asm

这就是libEncryptor.so中ttEncrypt函数的加密算法了。

为了验证算法是否有效,这里使用了dy模块中的ttEncrypt算法和tt的设备注册代码:

结果:

在分析完之后虚拟机保护的强度没有想像中的那么好,在分析过的虚拟化加密强度非要分10个等级的话,VMProtect为10级、Safengine Shielden强度为9级、Themida强度9级、VProtect强度7级、Enigma Protector强度3级,而它的强度等级仅为1级。

void vm_entry(*int args)
void vm_entry(*int args)
void ttEncrypt(char* buff, int buff_size) {
    int dst_size = size +0x76;
    char* pDstBuff = malloc(size +0x76);
    vm_entry(buff, size, pDstBuff, &dst_size);
}
void ttEncrypt(char* buff, int buff_size) {
    int dst_size = size +0x76;
    char* pDstBuff = malloc(size +0x76);
    vm_entry(buff, size, pDstBuff, &dst_size);
}
SP 类型 变量名 注释
0x0 未使用
0x8 char * pSrcBuffer
0x10 int srcSize
0x18 char * pDstBuffer
0x20 int * pDstBufferSize
0x28 void * pCall_register_trampoline
0x30 void * pVMMemoryEnd 偏移0x510
0x38 vmMemoryStart 虚拟机运行时专用内存起始位置
...
0x510 vmMemoryEnd 虚拟机运行时内存结束位置
0x518
0x520 x28
0x528 x19
0x530 x29 上一个栈桢地址
0x538 x30
vm2_run(void* pOpcode,  void* pArgs,  void* pReserve,  void* pExternalFunc, void* pVmData);
vm2_run(void* pOpcode,  void* pArgs,  void* pReserve,  void* pExternalFunc, void* pVmData);
虚拟寄存器 参数 注释
x0 x0始终为0,XZR寄存器?
x4 arg2: pArgs
x5 arg3: pReserve
x6 arg4: pExternalFunc
x7 arg4: pVmData->pCallRegisterTrampolineFunction
x29(SP) arg5: pVmData->pVmMemoryLimit - 0x150 SP的初始值
x31(LR) 初始值为0,寄存器名不确定,vm退出时保存退出代码编号
PC arg1: pOpcode
真实寄存器 注释
x0 temp/pcode
x1 temp,保存[x19-0x20]的值某种流程控制
x2 temp
x3 temp
x4 虚拟寄存x4,初始化时保存pBufferInfo(x1),虚拟机中保存跳板函数地址
x5 虚拟寄存x5,初始化时保存数值为0,虚拟机运行时跳板函数的参数指针
x6 ollvm混淆switch_var,初始值:0x400000b,这个应该是llvm混淆的switch var
w7 ollvm混淆switch_var,初始值:0x200fff,这个应该是llvm混淆的switch var
w8 w12[20-16] operand1 Xt/Xm,det register index?
w9 w12[25-21] operand2 Xn,src register index?
w10 w12[15->4] | w12[14->3] | w12[13->2] | w12[12->1] | w12[31->0],5个位合并到低5位
w11 w12[30->4] | w12[29->3] | w12[28->2] | w12[27-26->0-1],组成的低5位
w12 32位的opcode
w13 w12[31]
w14 w12[30]
w15 w12[29]
w16 w12[28]
w17 w12[27]
w18 w12[26]
x19 虚拟机下文负偏移指针指向可使用内存最高上限的地址与vmMemoryLimit值相同,使用负偏移对上下文进行访问。
x20 call_register_trampoline 跳板地址
x21 Context,上下文指针
x22 switch_table7
x23 switch_table6
w24 默认为1
x25 switch_table5
x26 switch_table3
x27 switch_table_main
x28 switch_table2
x29(fp) 未使用
x30(lr) switch_table_4(3A)
负偏移 虚拟寄存器 注释
-0x150 虚拟机堆栈SP初始位置
-0x148
-0x140
-0x138 pc pc指针,真实寄存器x21中的值指向此地址,handler使用
-0x130 x0 初始化值:0,虚拟机中开始到结束始终为0,XZR寄存器?
-0x128 x1
-0x120 x2
-0x118 x3
-0x110 x4 初始化值:pBufferInfo,专用寄存器:虚拟机中调用外部函数时保存外部函数地址
-0x108 x5 初始化值: 0,专用寄存器:虚拟机中调用外部函数时保存参数指针
-0x100 x6 初始化值: vm2_external_func_list
-0xF8 x7 初始化值: call_register_trampoline_function
-0xF0 x8
-0xE8 x9
-0xE0 x10
-0xD8 x11
-0xD0 x12
-0xC8 x13
-0xC0 x14
-0xB8 x15
-0xB0 x16
-0xA8 x17
-0xA0 x18
-0x98 x19
-0x90 x20
-0x88 x21
-0x80 x22
-0x78 x23
-0x70 x24
-0x68 x25 虚拟机中的专用寄存器:虚拟机中调用外部函数时的跳板地址
-0x60 x26
-0x58 x27
-0x50 x28
-0x48 x29(SP) 虚拟机堆栈SP, 初始化时指向-0x150
-0x40 x30
-0x38 x31(LR) 当值为0时退出虚拟机
-0x30
-0x28
-0x20 0 流程控制状态标记,值范围0-3,0:正常执行状态,2和3:程序中包含分支跳转某种流程,3和1:call某个地址函数
-0x18 new pc/call某个地址函数的地址/为0时退出虚拟机
-0x10 LR,在调用调用函数结束后返回虚拟机地址
-0x8 起始pc指针,一部分分支跳转指令参考的起始基址,例如: 跳转目标地址=pc起始地址+offset
# 初始化
LDP             X20, X19, [X4]              ;x20:call_register_trampoline,0x19:pVmMemoryLimit
SUB             X21, X19, #0x138        ;x19-0x138=x21计算出pc指针在负偏移指针的内存位置
STR             X0, [X21]                      ;保存pc到x21,此时x0和x21中的值相同并被关联。
# 取opcode
LDR            W12, [X0]                    ;取出4字节opcode
AND           W10, W12, #0x3F       ;opcode的低5位为op1
CMP           W10, #0x3F                ;op1的值最大只能小于63,说明op1只有0-63一共64个
B.HI            next_pc                      ;下一条指令
# 初始化
LDP             X20, X19, [X4]              ;x20:call_register_trampoline,0x19:pVmMemoryLimit
SUB             X21, X19, #0x138        ;x19-0x138=x21计算出pc指针在负偏移指针的内存位置
STR             X0, [X21]                      ;保存pc到x21,此时x0和x21中的值相同并被关联。
# 取opcode
LDR            W12, [X0]                    ;取出4字节opcode
AND           W10, W12, #0x3F       ;opcode的低5位为op1
CMP           W10, #0x3F                ;op1的值最大只能小于63,说明op1只有0-63一共64个
B.HI            next_pc                      ;下一条指令
import struct
pcode_start = 0xB090
pcode_size = 0x2D8
with open("libEncryptor.so", "rb") as f:
        content = f.read()
bytes_code = content[pcode_start : pcode_start + pcode_size]
insts_sets = {}
for pc in range(0, pcode_size, 4):
    word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
    if len(word) > 0:
        opcode = word[0]
        op1 = opcode & 0x3F
        if insts_sets.get(op1, None) == None:
             insts_sets[op1] = 1
        else:
             insts_sets[op1] += 1
sorted_dict = dict(sorted(insts_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in sorted_dict.items():
     print(f'op1: {inst}, count: {count}')
import struct
pcode_start = 0xB090
pcode_size = 0x2D8
with open("libEncryptor.so", "rb") as f:
        content = f.read()
bytes_code = content[pcode_start : pcode_start + pcode_size]
insts_sets = {}
for pc in range(0, pcode_size, 4):
    word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
    if len(word) > 0:
        opcode = word[0]
        op1 = opcode & 0x3F
        if insts_sets.get(op1, None) == None:
             insts_sets[op1] = 1
        else:
             insts_sets[op1] += 1
sorted_dict = dict(sorted(insts_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in sorted_dict.items():
     print(f'op1: {inst}, count: {count}')
op1: 23, count: 56
op1: 11, count: 51
op1: 40, count: 38
op1: 21, count: 27
op1: 24, count: 2
op1: 12, count: 2
op1: 17, count: 2
op1: 48, count: 2
op1: 52, count: 1
op1: 7, count: 1
op1: 23, count: 56
op1: 11, count: 51
op1: 40, count: 38
op1: 21, count: 27
op1: 24, count: 2
op1: 12, count: 2
op1: 17, count: 2
op1: 48, count: 2
op1: 52, count: 1
op1: 7, count: 1
import struct
pcode_start = 0xB090
pcode_size = 0x2D8
with open("libEncryptor.so", "rb") as f:
                content = f.read()
bytes_code = content[pcode_start : pcode_start + pcode_size]
insts_op1_sets = {}
insts_op2_sets = {}
for pc in range(0, pcode_size, 4):
        word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
        if len(word) > 0:
                opcode = word[0]
                op1 = opcode & 0x3F
                if op1 == 11:
                        op2 = (opcode >> 6) & 0x3F
                        if insts_op2_sets.get(op2, None) == None:
                                insts_op2_sets[op2] = 1
                        else:
                                insts_op2_sets[op2] += 1
                else:
                        if insts_op1_sets.get(op1, None) == None:
                                insts_op1_sets[op1] = 1
                        else:
                                insts_op1_sets[op1] += 1
 
print("op1指令统计:")
op1_sorted_dict = dict(sorted(insts_op1_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in op1_sorted_dict.items():
         print(f'op1: {inst}, count: {count}')
print("op2指令统计:")
op2_sorted_dict = dict(sorted(insts_op2_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in op2_sorted_dict.items():
         print(f'op1: 11, op2: {inst}, count: {count}')
import struct
pcode_start = 0xB090
pcode_size = 0x2D8
with open("libEncryptor.so", "rb") as f:
                content = f.read()
bytes_code = content[pcode_start : pcode_start + pcode_size]
insts_op1_sets = {}
insts_op2_sets = {}
for pc in range(0, pcode_size, 4):
        word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
        if len(word) > 0:
                opcode = word[0]
                op1 = opcode & 0x3F
                if op1 == 11:
                        op2 = (opcode >> 6) & 0x3F
                        if insts_op2_sets.get(op2, None) == None:
                                insts_op2_sets[op2] = 1
                        else:
                                insts_op2_sets[op2] += 1
                else:
                        if insts_op1_sets.get(op1, None) == None:
                                insts_op1_sets[op1] = 1
                        else:
                                insts_op1_sets[op1] += 1
 
print("op1指令统计:")
op1_sorted_dict = dict(sorted(insts_op1_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in op1_sorted_dict.items():
         print(f'op1: {inst}, count: {count}')
print("op2指令统计:")
op2_sorted_dict = dict(sorted(insts_op2_sets.items(), key=lambda item: item[1], reverse=True))
for inst, count in op2_sorted_dict.items():
         print(f'op1: 11, op2: {inst}, count: {count}')
def decode(pcode_start, pcode_size, liner_disasm = False):
        with open("libEncryptor.so", "rb") as f:
                content = f.read()
 
        print(f"{'pc':^8} {'指令':^8}  {'op1':^8}  {'op2':^8}\t{'助记符':^30}")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx0, #0\t\t\t;x0始终为0,XZR寄存器?")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx4, pArgs\t\t;参数列表指针")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx5, #0")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx6, pfn_external_func_list\t\t;外部函数列表指针")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx7, pCallRegisterTrampolineFunction\t;保存跳转函数地址")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx29, pVirualStackBottom;\t\t;虚拟机堆栈栈底")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tlr, #0\t\t\t;x31=0")
 
        # 已解析的指令
        known_insns_op1 = {}        # dcode_insns_status=1
        known_insns_op2 = {}        # dcode_insns_status=2
        # 未解析的指令
        unknown_insts_op1 = set()   # dcode_insns_status=3
        unknown_insts_op2 = set()   # dcode_insns_status=4
        bytes_code = content[pcode_start : pcode_start + pcode_size]
 
        for pc in range(0, pcode_size, 4):
                word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
                asm = ""        
                dcode_insns_status = 0
                if len(word) > 0:
                        opcode = word[0]           
                        op1 = get_op1(opcode)
                        Xt = get_openand1(opcode)   # x8/w8
                        Xn = get_operand2(opcode)   # x9/w9   
                        Xm = get_operand3(opcode)   # x10/w10
                        X4 = get_operand4(opcode)   # x11/w11
                        match op1:
                                case 11:
                                        Xt = get_operand3(opcode)   # x10/w10
                                        Xn = get_openand1(opcode)   # x8/w8
                                        Xm = get_operand2(opcode)   # x9/w9                
                                        op2 = get_op2(opcode)                   
                                        match op2:
                                                case 7:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tORR\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                                case 12:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                                case 25:
                                                        dcode_insns_status=2
                                                        if X4 == 0:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tNOP\t\t\t\t;LSL\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{X4}")
                                                        else:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tLSL)\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{X4}")
                                                case 39:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tCMP\t{get_regsiter_name(Xm)}, {get_regsiter_name(Xn)}")
                                                        print(f"{' '*8}  {' '*8}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tCSET\t{get_regsiter_name(Xt)}, CC")
                                                case 43:
                                                        dcode_insns_status=2
                                                        if liner_disasm:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tBR\t{get_regsiter_name(Xm)}\t\t\t;LR={get_regsiter_name(Xt)}")
                                                        else:
                                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tBR\t{get_regsiter_name(Xm)}\t\t\t;LR={get_regsiter_name(Xt)}"
                                                                set_branch_control(asm)
                                                case 62:
                                                        dcode_insns_status=2
                                                        if liner_disasm:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tExitVm\t0\t\t\t;{get_regsiter_name(Xm)}")
                                                        else:
                                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tExitVm\t0\t\t\t;{get_regsiter_name(Xm)}"
                                                                set_branch_control(asm)
                                                case _:
                                                        dcode_insns_status=4
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\t>> op2_xxx\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                case 7:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tORR\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 12:
                                        dcode_insns_status = 1
                                        imm26 = get_imm26(opcode)
                                        offset = imm26 * 4
                                        if liner_disasm:
                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB\t{hex(offset)}")
                                        else:
                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB\t{hex(offset)}"
                                                set_branch_control(asm)
                                case 17:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 21:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 23:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSTR\t{get_regsiter_name(Xt)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 24:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        offset = pc + imm16 * 4 + 4
                                        if liner_disasm:
                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB.HS\t#{hex(offset)}\t\t\t;{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, ${hex(imm16 * 4)}")
                                        else:
                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB.HS\t#{hex(offset)}\t\t\t;{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, ${hex(imm16 * 4)}"
                                                set_branch_control(asm)
                                case 40:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tLDR\t{get_regsiter_name(Xt)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 48:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSTR\t{get_regsiter_name(Xt, 32)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 52:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode, False)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tMOVZ\t{get_regsiter_name(Xt, 32)}, #{hex(imm16)}, LSL#16")
                                        print(f"{' '*8}  {' '*8}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSXTW\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xt, 32)}")
                                case _:
                                        dcode_insns_status=3                  
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\t>> op1_xxx\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                        if not liner_disasm:
                                branch_pipeline_process()
                        record_insns(dcode_insns_status, known_insns_op1, known_insns_op2, unknown_insts_op1, unknown_insts_op2, opcode)
                else:
                        print("error")   
        decode_statistics(known_insns_op1, known_insns_op2, unknown_insts_op1, unknown_insts_op2)
 
def main():
        # 解码vm2
        pcode_start = 0xB090
        pcode_size = 0x2D8
        decode(pcode_start, pcode_size)
 
        # # 解码vm3,获取aes的key和iv
        pcode_start = 0xBDE0
        pcode_size = 0x1C8
        decode(pcode_start, pcode_size)
 
        # 解码vm1,JNI_OnLoad
        pcode_start = 0x85C0
        pcode_size = 0xCC
        decode(pcode_start, pcode_size)
 
if __name__ == "__main__":
        main()
def decode(pcode_start, pcode_size, liner_disasm = False):
        with open("libEncryptor.so", "rb") as f:
                content = f.read()
 
        print(f"{'pc':^8} {'指令':^8}  {'op1':^8}  {'op2':^8}\t{'助记符':^30}")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx0, #0\t\t\t;x0始终为0,XZR寄存器?")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx4, pArgs\t\t;参数列表指针")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx5, #0")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx6, pfn_external_func_list\t\t;外部函数列表指针")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx7, pCallRegisterTrampolineFunction\t;保存跳转函数地址")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tx29, pVirualStackBottom;\t\t;虚拟机堆栈栈底")
        print(f"{'-'*8}  {'-'*8}  {'-'*8}  {'-'*8}  MOV\tlr, #0\t\t\t;x31=0")
 
        # 已解析的指令
        known_insns_op1 = {}        # dcode_insns_status=1
        known_insns_op2 = {}        # dcode_insns_status=2
        # 未解析的指令
        unknown_insts_op1 = set()   # dcode_insns_status=3
        unknown_insts_op2 = set()   # dcode_insns_status=4
        bytes_code = content[pcode_start : pcode_start + pcode_size]
 
        for pc in range(0, pcode_size, 4):
                word = struct.unpack("<I", bytes_code[pc : pc + 4]) 
                asm = ""        
                dcode_insns_status = 0
                if len(word) > 0:
                        opcode = word[0]           
                        op1 = get_op1(opcode)
                        Xt = get_openand1(opcode)   # x8/w8
                        Xn = get_operand2(opcode)   # x9/w9   
                        Xm = get_operand3(opcode)   # x10/w10
                        X4 = get_operand4(opcode)   # x11/w11
                        match op1:
                                case 11:
                                        Xt = get_operand3(opcode)   # x10/w10
                                        Xn = get_openand1(opcode)   # x8/w8
                                        Xm = get_operand2(opcode)   # x9/w9                
                                        op2 = get_op2(opcode)                   
                                        match op2:
                                                case 7:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tORR\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                                case 12:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                                case 25:
                                                        dcode_insns_status=2
                                                        if X4 == 0:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tNOP\t\t\t\t;LSL\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{X4}")
                                                        else:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tLSL)\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{X4}")
                                                case 39:
                                                        dcode_insns_status=2
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tCMP\t{get_regsiter_name(Xm)}, {get_regsiter_name(Xn)}")
                                                        print(f"{' '*8}  {' '*8}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tCSET\t{get_regsiter_name(Xt)}, CC")
                                                case 43:
                                                        dcode_insns_status=2
                                                        if liner_disasm:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tBR\t{get_regsiter_name(Xm)}\t\t\t;LR={get_regsiter_name(Xt)}")
                                                        else:
                                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tBR\t{get_regsiter_name(Xm)}\t\t\t;LR={get_regsiter_name(Xt)}"
                                                                set_branch_control(asm)
                                                case 62:
                                                        dcode_insns_status=2
                                                        if liner_disasm:
                                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tExitVm\t0\t\t\t;{get_regsiter_name(Xm)}")
                                                        else:
                                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\tExitVm\t0\t\t\t;{get_regsiter_name(Xm)}"
                                                                set_branch_control(asm)
                                                case _:
                                                        dcode_insns_status=4
                                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {op2:02d}(0x{op2:02X})\t>> op2_xxx\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                                case 7:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tORR\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 12:
                                        dcode_insns_status = 1
                                        imm26 = get_imm26(opcode)
                                        offset = imm26 * 4
                                        if liner_disasm:
                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB\t{hex(offset)}")
                                        else:
                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB\t{hex(offset)}"
                                                set_branch_control(asm)
                                case 17:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 21:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tADD\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, #{hex(imm16)}")
                                case 23:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSTR\t{get_regsiter_name(Xt)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 24:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        offset = pc + imm16 * 4 + 4
                                        if liner_disasm:
                                                print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB.HS\t#{hex(offset)}\t\t\t;{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, ${hex(imm16 * 4)}")
                                        else:
                                                asm = f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tB.HS\t#{hex(offset)}\t\t\t;{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, ${hex(imm16 * 4)}"
                                                set_branch_control(asm)
                                case 40:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tLDR\t{get_regsiter_name(Xt)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 48:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSTR\t{get_regsiter_name(Xt, 32)}, [{get_regsiter_name(Xn)}, #{hex(imm16)}]")
                                case 52:
                                        dcode_insns_status = 1
                                        imm16 = get_imm16(opcode, False)
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tMOVZ\t{get_regsiter_name(Xt, 32)}, #{hex(imm16)}, LSL#16")
                                        print(f"{' '*8}  {' '*8}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\tSXTW\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xt, 32)}")
                                case _:
                                        dcode_insns_status=3                  
                                        print(f"{pc:08X}  {opcode:08X}  {opcode & 0x3F:02d}(0x{opcode & 0x3F:02X})  {'-'*8}\t>> op1_xxx\t{get_regsiter_name(Xt)}, {get_regsiter_name(Xn)}, {get_regsiter_name(Xm)}")
                        if not liner_disasm:
                                branch_pipeline_process()
                        record_insns(dcode_insns_status, known_insns_op1, known_insns_op2, unknown_insts_op1, unknown_insts_op2, opcode)
                else:
                        print("error")   
        decode_statistics(known_insns_op1, known_insns_op2, unknown_insts_op1, unknown_insts_op2)
 
def main():
        # 解码vm2
        pcode_start = 0xB090
        pcode_size = 0x2D8
        decode(pcode_start, pcode_size)
 
        # # 解码vm3,获取aes的key和iv
        pcode_start = 0xBDE0
        pcode_size = 0x1C8
        decode(pcode_start, pcode_size)
 
        # 解码vm1,JNI_OnLoad
        pcode_start = 0x85C0
        pcode_size = 0xCC
        decode(pcode_start, pcode_size)
 
if __name__ == "__main__":
        main()
import secrets
import hashlib
from Crypto.Cipher import AES   # pip install pycryptodome
from Crypto.Util.Padding import pad, unpad
 
def generate_rand_number():
        return secrets.token_bytes(32)
 
def decrypt_seeds():
        key1 =  [0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB]
        key1 += [0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB]
        key1 += [0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E]
        key1 += [0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25]
 
        key2 =  [0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F]
        key2 += [0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF]
        key2 += [0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61]
        key2 += [0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D]
 
        results = bytearray()
        for i in range(len(key1)):
                results.append(key1[i] ^ key2[i])
        return results
 
def sha512(buff):
        sha512_hash = hashlib.sha512()
        sha512_hash.update(buff)
        return sha512_hash.digest()
 
def get_aes_key_iv(rand_num):   
        rand_num_hash = sha512(rand_num)   
        seeds = decrypt_seeds()
        data = rand_num_hash + seeds   
        key_iv_hash = sha512(data)
 
        print(f"rand_num:              {rand_num.hex()}")
        print(f"rand_num_hash:         {rand_num_hash.hex()}")
        print(f"seeds:                 {seeds.hex()}")   
        print(f"rand_num_hash + seeds: {data.hex()}")
        print(f"key_iv_hash:           {key_iv_hash.hex()}")
        print(f"aes key:               {key_iv_hash[0:0x10].hex()}")
        print(f"aes iv:                {key_iv_hash[0x10:0x20].hex()}")
        return key_iv_hash[0:0x10], key_iv_hash[0x10:0x20]
 
def aes_128_cbc_encrypt(plaintext, key, iv):
        # 创建一个 AES 加密器对象
        cipher = AES.new(key, AES.MODE_CBC, iv)
 
        # 添加填充并加密数据
        padded_data = pad(plaintext, AES.block_size)
        ciphertext = cipher.encrypt(padded_data)
        return ciphertext
 
def get_magic():
        return b"\x74\x63\x05\x10\x00\x00"
 
def ttEncrypt(buff):
        magic = get_magic()
        rand_number = generate_rand_number()
        aes_key, aes_iv = get_aes_key_iv(rand_number)
        buff_hash = sha512(buff)
        aes_plaintext = buff_hash + buff
        aes_ciphertext = aes_128_cbc_encrypt(aes_plaintext, aes_key, aes_iv)
 
        print(f"plaintext:     {buff.hex()}")
        print(f"rand_number:   {rand_number.hex()}")  
        print(f"buff hash:     {buff_hash.hex()}")
        print(f"aes_plaintext: {aes_plaintext.hex()}")
        print(f"ciphertext:    {aes_ciphertext.hex()}")
 
        return magic + rand_number + aes_ciphertext
 
def main():
        buff = b'aabbccddeeffgg'   
        result = ttEncrypt(buff)
        print(f"ttEncrypt: {result.hex()}")
 
def test_aes():
        buff =  b"\x61\x02\xbe\x54\xa6\x2a\x73\xe7\x65\xba\x38\xc9\x87\x34\x09\xbd" +\
                        b"\xeb\xb6\xb0\xd3\x7e\xa0\x60\x40\x3d\x0c\x26\xfe\xa5\xeb\xb6\xba" +\
                        b"\x5a\x0c\x7f\x36\xec\xb7\x58\xc7\x7e\x19\x37\x50\x5f\xa8\x5b\x4e" +\
                        b"\x77\xce\x82\x7a\x70\x09\xd2\x2b\x2f\xaf\xc4\x68\x00\xd7\xa9\xff" +\
                        b"\x62\x69\x61\x6e\x66\x65\x6e\x67"
        aes_key = b"\xe8\xaf\x6e\x91\xde\x99\x7e\xf0\xfa\xfb\xcd\xbe\x97\x73\xb2\xc5"
        aes_iv = b"\x03\x7e\xed\x97\x4e\x1e\xc5\x19\xdc\xc2\xb4\x35\x5b\x26\xf0\x1b"  
        ciphertext = aes_128_cbc_encrypt(buff, aes_key, aes_iv)
        print(f"ciphertext: {ciphertext.hex()}")
 
if __name__ == "__main__":
        main()
        # test_aes()
import secrets
import hashlib
from Crypto.Cipher import AES   # pip install pycryptodome
from Crypto.Util.Padding import pad, unpad
 
def generate_rand_number():
        return secrets.token_bytes(32)
 
def decrypt_seeds():
        key1 =  [0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB]
        key1 += [0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB]
        key1 += [0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E]
        key1 += [0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25]
 
        key2 =  [0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F]
        key2 += [0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF]
        key2 += [0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61]
        key2 += [0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D]
 
        results = bytearray()
        for i in range(len(key1)):
                results.append(key1[i] ^ key2[i])
        return results
 
def sha512(buff):
        sha512_hash = hashlib.sha512()
        sha512_hash.update(buff)
        return sha512_hash.digest()
 
def get_aes_key_iv(rand_num):   
        rand_num_hash = sha512(rand_num)   
        seeds = decrypt_seeds()
        data = rand_num_hash + seeds   
        key_iv_hash = sha512(data)
 
        print(f"rand_num:              {rand_num.hex()}")
        print(f"rand_num_hash:         {rand_num_hash.hex()}")
        print(f"seeds:                 {seeds.hex()}")   
        print(f"rand_num_hash + seeds: {data.hex()}")
        print(f"key_iv_hash:           {key_iv_hash.hex()}")
        print(f"aes key:               {key_iv_hash[0:0x10].hex()}")
        print(f"aes iv:                {key_iv_hash[0x10:0x20].hex()}")
        return key_iv_hash[0:0x10], key_iv_hash[0x10:0x20]
 
def aes_128_cbc_encrypt(plaintext, key, iv):
        # 创建一个 AES 加密器对象
        cipher = AES.new(key, AES.MODE_CBC, iv)
 
        # 添加填充并加密数据
        padded_data = pad(plaintext, AES.block_size)
        ciphertext = cipher.encrypt(padded_data)
        return ciphertext

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2024-6-27 15:37 被金罡编辑 ,原因:
上传的附件:
收藏
免费 29
支持
分享
打赏 + 25.00雪花
打赏次数 3 雪花 + 25.00
 
赞赏  mb_vjpiriil   +10.00 2024/08/11 学习使我快乐,感谢大佬分享
赞赏  flashgg   +10.00 2024/06/28 感谢分享~
赞赏  orz1ruo   +5.00 2024/06/27 原创内容~
最新回复 (40)
雪    币: 2023
活跃值: (155)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
金罡大佬发帖了,学习新姿势
2024-6-26 18:02
0
雪    币: 102
活跃值: (2045)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
mark
2024-6-26 19:04
0
雪    币: 2253
活跃值: (6638)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
4
Windows逆向都转安卓了吗
2024-6-26 19:10
1
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
牛逼
2024-6-26 19:59
0
雪    币: 4439
活跃值: (6681)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
时隔多年发帖了,还是精贴
2024-6-26 22:02
0
雪    币: 14824
活跃值: (6063)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
没搞懂意思,总结的时侯为什么扯上了x86/x64虚拟机?你分析的是ARM虚拟机。
2024-6-27 06:32
2
雪    币: 774
活跃值: (4357)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
pReserve:未使用,用途暂时未知
这个参数也是一个常量列表,其中的常量会参与函数地址解密运算。
2024-6-27 11:23
0
雪    币: 47
活跃值: (422)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
短视频:我这么不值钱吗?
2024-6-27 11:40
0
雪    币: 1489
活跃值: (1033)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
10

这个参数暂时没看到用途,可能分析的场景还不够多吧。
我这边分析到的解密常量是在opcode中拿到的:
ttencryptor.asm:

最后于 2024-6-27 11:45 被金罡编辑 ,原因:
2024-6-27 11:43
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
大佬,能不能把pc的vmp的自动化模拟,与还原应用到安卓呢
2024-6-27 12:09
0
雪    币: 4715
活跃值: (4255)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
给Android逆向的兄弟们留点面子
2024-6-27 12:23
0
雪    币: 774
活跃值: (4357)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
金罡 这个参数暂时没看到用途,可能分析的场景还不够多吧。我这边分析到的解密常量是在opcode中拿到的:ttencryptor.asm:

a_vm_entry_2D9E4_new(g_mc_count_2040_dword_EBF10, (uint64_t*)&param_addr, g_global_value_qword_125E50, g_func_addr_qword_125F00, a5);

确实是先从微码中解密出常量,再拉参数3常量列表里的一个常量参与运算,最后拉参数4函数地址表中的一个常量参与运算,算出外部函数地址。

2024-6-27 14:29
0
雪    币: 1489
活跃值: (1033)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
14
令狐双 a_vm_entry_2D9E4_new(g_mc_count_2040_dword_EBF10, (uint64_t*)&param_addr, g_global_value_qwo ...

看出来了app版本不一样,我的版本29.9.0没看到这个参数的作用见笑了。

最后于 2024-6-27 15:18 被金罡编辑 ,原因:
2024-6-27 15:00
0
雪    币: 774
活跃值: (4357)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
金罡 令狐双 a_vm_entry_2D9E4_new(g_mc_count_2040_dword_EBF10, (uint64_t*)& ...

嗯,有些接口没有这个参数,我是去年看的,当时版本是26.1.0

最后于 2024-6-28 13:36 被令狐双编辑 ,原因:
2024-6-27 15:12
0
雪    币: 9687
活跃值: (2491)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
膜拜大佬
2024-6-27 20:52
0
雪    币: 225
活跃值: (251)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
new bee plus ......
2024-6-28 11:16
0
雪    币: 163
活跃值: (1623)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
18
这虚拟机的函数栈结构是怎么样的
2024-6-28 13:56
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
大神辛苦了,这么多代码编的也够辛苦了
2024-6-28 15:39
0
雪    币: 2223
活跃值: (95)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
大佬招工作嘛,高薪稳定工作
2024-7-1 14:45
0
雪    币: 1489
活跃值: (1033)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
21
leeshenyou 大佬招工作嘛,高薪稳定工作
看有多高了
2024-7-1 15:19
0
雪    币: 4870
活跃值: (18870)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
22
感谢分享!
2024-7-4 08:32
0
雪    币: 1841
活跃值: (1285)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
23
感谢分享
2024-7-7 12:36
0
雪    币: 1293
活跃值: (611)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
24
注册时间09年的大佬果然new bee
2024-7-8 12:54
1
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
吾爱来的把
2024-7-12 18:09
0
游客
登录 | 注册 方可回帖
返回
//