前言
- 有一段时间没更新博客了,主要是事情太多。这是上半年的比赛遗留下的问题,决心好好琢磨一番。后续会更新hackgame2019的部分题解和其他平台上的题解。
分析
- 这是一道ELF64位的虚拟机逆向。首先通过初始化虚拟机函数:
void *__fastcall init_vm(VM *vm, const void *input)
{
vm->reg_0 = 0;
vm->reg_1 = 0;
vm->reg_2 = 0;
vm->reg_3 = 0;
LODWORD(vm->field_10) = 0;
*&vm->flag = 0;
LOBYTE(vm->v0) = 0xF0u;
vm->func_0 = mov_reg0_mem;
vm->gap30[0] = 0xF1u;
*&vm->func_1 = xor_reg0_reg1;
LOBYTE(vm->v2) = 0xF2u;
vm->func_2 = cmp_reg0_mem;
LOBYTE(vm->v3) = 0xF4u;
vm->func_3 = func_add_reg0_reg1;
LOBYTE(vm->v4) = 0xF5u;
vm->func_4 = func_sub_reg0_reg1;
LOBYTE(vm->v5) = 0xF3u;
vm->func_5 = nullptr;
LOBYTE(vm->v6) = 0xF6u;
vm->func_6 = jz_opcode;
LOBYTE(vm->v7) = 0xF7u;
vm->func_7 = move_flag_mem;
LOBYTE(vm->v8) = 0xF8u;
vm->func_8 = Encode_reg_0;
::input = malloc(0x400uLL);
return memccpy(::input + 48, input, 1, 18uLL);
}
struct VM{
int reg_0;
int reg_1;
int reg_2;
int reg_3;
int field_10;
char* opcode;
int v0;
...
void* func_0;
...
void* func_1;
...
int v2 ;
...
void* func_2;
int v3;
...
void* func_3;
int v4;
...
void* func_4;
int v5;
...
void* func_5;
int v6;
...
void* func_6;
int v7;
...
void* func_7;
int v8;
...
void* func_8;
int flag;
};
- 动态调试,跟一下程序,不难发现其流程:
- 首先输入19个字符,然后初始化虚拟机。
- 虚拟机的执行流程如下:
1) vm->reg_0=*(vm->opcode+2),opcode+=6
2) vm->reg_0=Encode(vm->reg_0,2),vm->opcode++//凯撒加密,key=2
3) vm->field_10=vm->reg_0==input[0],vm->opcode+=2//相等转到(5),否则转到(6)
5) vm->field_0=0,vm->opcode+=2//转到(8)
6) vm->opcode+=*(vm->opcode+1),vm->opcode+=2//转到(7)
7) vm->flag=*(vm->opcode+1),vm->opcode+=5//转到(9)
8) 重复 (1)~(5) 19 次后转到(7)
9) 跳出虚拟机
如果 vm->flag==1 成功,否则失败
- 可见其实就是执行19次 Encode(opcode[2+11*i])=input[i]? 的操作。
脚本
code=[\
240,16,102,0,0,0,248,242,48,246,193,240,16,99,0,0,0,248,242,49,246,182,240,16,106,0,0,0,248,242,50,246,171,240,16,106,0,0,0,248,242,51,246,160,240,16,109,0,0,0,248,242,52,246,149,240,16,87,0,0,0,248,242,53,246,138,240,16,109,0,0,0,248,242,54,246,127,240,16,115,0,0,0,248,242,55,246,116,240,16,69,0,0,0,248,242,56,246,105,240,16,109,0,0,0,248,242,57,246,94,240,16,114,0,0,0,248,242,58,246,83,240,16,82,0,0,0,248,242,59,246,72,240,16,102,0,0,0,248,242,60,246,61,240,16,99,0,0,0,248,242,61,246,50,240,16,68,0,0,0,248,242,62,246,39,240,16,106,0,0,0,248,242,63,246,28,240,16,121,0,0,0,248,242,64,246,17,240,16,101,0,0,0,248,242,65,246,6,247,1,0,0,0,243,247,0,0,0,0,243,93,195,15,31,132,0,0,0,0,0\
]
flag=''
def Encode(v,k):
v5=False
if v>64:
v5=v<=90
if v5:
v3=(v+k-65)%26+65
else:
v4=False
if v>96:
v4=v<=122
if v4:
v3=(v+k-97)%26+97
else:
v3=v
return v3
for i in range(19):
flag+=chr(Encode(code[2+i*11],2))
print flag
总结
- 这是VM逆向中一种类型,即给定了opcode,要求输入的数据要符合opcode的执行结果。在DDCTF2018中还有个刚好反过来的,要求自己构造一个opcode,使虚拟机执行结果与程序给定的一致。目前看来,关于虚拟机的考察我就遇到这两种。
- 也没什么好的方法,就是先要摸清楚虚拟机的结构,然后跟踪程序,摸清意图。再不行就侧信道吧~