1.前言
论坛里已经有很多人发过关于"2023腾讯游戏安全大赛-安卓赛道决赛"的文章了我就不多介绍,本人有幸参加过这次比赛,很可惜的是我PC和安卓两个赛道都参加了时间不够,最近有时间了花了两天重新看了一下题目,发现这个"tvm"还是比较简单的给大家分析还原一下。
2.TVM
虚拟机流程
进入虚拟机
a64.dat不懂的可以看一下,里面主要包含了arm64虚拟化指令("tvm"shellcode)。
uint32_t sub_99ef4(uint32_t arg1) {
char var_80[0x10] = "builtin";
char var_90[0x10] = "vm_main.img";
char PK[0x10] = "PK";
char var_d8[0x10] = "";
char var_e0[0x10] = "";
uintptr_t ctx = sub_95970(var_d8, var_80, var_90, PK, 0x409, var_e0);
// vm_Start(ctx, step)
sub_98e50(ctx + 0x10, 0x249f0);
return 0;
}
上面是虚拟机初始化代码,通过sub_9570函数初始化虚拟机环境得到vContext(包含虚拟寄存器、虚拟化指令、handler表、跳转表、调用表...),调用sub_98e50开始执行虚拟机。
vContext结构如下
struct vContext {
uint64_t x[29]; // 0x00 ~ 0xE0
uint64_t fp; // + 0xE8
uint64_t lr; // + 0xF0
uint64_t xzr; // +0xF8
uint64_t sp; // + 0x100
uint64_t pc; // + 0x108
...
}
handler分发
对于确认sub_98e50是vm_start的问题,可以根据上面的流程图进行分析。在反汇编中,可以观察到这里进行了表查找调用,也就是Handler的分发过程。
sub_98e50(vm_start)还原大致内容
uint64_t sub_98e50(uint64_t ctx, uint64_t step) {
// 0x00098e90 获取 opcode_tab
int64_t opcode_tab =
// 000996ec 获取 handler_tab
int64_t handler_tab =
// ctx + 0x00 X0
// ctx + 0x08 X1
// ctx + 0x10 X2 ... 以此类推 上面有vContext结构
// 0x00098ea4 获取 pc 寄存器
uint64_t* pc = (uint64_t*)(ctx + 0x108);
while(1) {
// 0x00099354 虚拟机状态置 1
*(uint32_t*)((char*)ctx + 0x120) = 1;
uint64_t i = (*pc >> 2);
// 取出 handler = *(handler_tab+i*8);
uint64_t handler = *(uint64_t*)(handler_tab + (i << 3));
// 取出 opcode = *(opcode_tab+i*4);
uint32_t opcode = *(uint32_t*)(opcode_tab + (i << 2));
// 0x00099368 Handler 分发
uintptr_t result = (funCall(handler))(opcode, ctx);
if (!result)
break;
*pc += 4;
}
return 0;
}
导出所有handler和对应虚拟化指令
tvmAsm to Asm
对arm汇编熟悉的人那么不难看出来,这个handler内部解析opcode执行对应操作,实际上就对应一条arm64指令。我的还原方法是通过观察arm64-v8a解析opcode的方式,与反汇编操作进行对比,以判断是否相符。
把sub_D0A88代码还原出来和对比一样,由此判定该方法为ADRP。
void sub_D0A88(uint64_t opcode, uint64_t ctx) {
// integer d = UInt(Rd);
uint32_t Rd = opcode & 0x1f;
// imm = SignExtend(immhi:immlo:Zeros(12), 64);
uint64_t imm = (0x3fff & ((opcode >> 0x11) & 0x3000)) | ((0x3ffff & (opcode >> 5)) << 0xe);
uint64_t PC = *(uint64_t*)(ctx + 0x108);
// bits(64) base = PC[]; base<11:0> = Zeros(12);
uint64_t base = (PC & 0xfffffffffffff000);
// 写回寄存器 X[d] = base + imm
*(uint64_t*)(ctx + (Rd << 3)) = base + imm;
}
还原例子
这里我进行了对sec2023决赛题目的还原操作。左边是我所还原"tvm"的arm64汇编代码,右边是大佬的解题算法。可以发现它们在大体上非常相似。虽然我只花了一两天的时间来完成这个工作,但是已经有了一个相当不错的结果。
3.总结
编程也就图一乐收收心找个电子厂上班了
更多【2023腾讯游戏安全大赛-安卓赛道决赛"TVM"分析与还原】相关视频教程:www.yxfzedu.com