[网鼎杯 2020 青龙组]singal
查壳,无壳32位
main函数看起来非常简单,就只有几行。这就有点做数学证明题的感觉了,就给你一行信息,让你去证明某个东西。
久闻vm的大名,第一次去尝试把它做出来,之前也是看过一个wp,感觉还行,就是有点麻烦。所以这次好好记录,好好地去分析,去学习,去做题。
vm_operad函数里面是指令集吧?这个说法应该没错,列一下vm函数
int __cdecl vm_operad(int *a1, int a2)
{
int result; // eax
char Str[200]; // [esp+13h] [ebp-E5h] BYREF
char v4; // [esp+DBh] [ebp-1Dh]
int v5; // [esp+DCh] [ebp-1Ch]
int v6; // [esp+E0h] [ebp-18h]
int v7; // [esp+E4h] [ebp-14h]
int v8; // [esp+E8h] [ebp-10h]
int v9; // [esp+ECh] [ebp-Ch]
v9 = 0;
v8 = 0;
v7 = 0;
v6 = 0;
v5 = 0;
while ( 1 )
{
result = v9;
if ( v9 >= a2 )
return result;
switch ( a1[v9] )
{
case 1:
Str[v6 + 100] = v4;
++v9;
++v6;
++v8;
break;
case 2:
v4 = a1[v9 + 1] + Str[v8];
v9 += 2;
break;
case 3:
v4 = Str[v8] - LOBYTE(a1[v9 + 1]);
v9 += 2;
break;
case 4:
v4 = a1[v9 + 1] ^ Str[v8];
v9 += 2;
break;
case 5:
v4 = a1[v9 + 1] * Str[v8];
v9 += 2;
break;
case 6:
++v9;
break;
case 7:
if ( Str[v7 + 100] != a1[v9 + 1] )
{
printf("what a shame...");
exit(0);
}
++v7;
v9 += 2;
break;
case 8:
Str[v5] = v4;
++v9;
++v5;
break;
case 0xA:
read(Str);
++v9;
break;
case 0xB:
v4 = Str[v8] - 1;
++v9;
break;
case 0xC:
v4 = Str[v8] + 1;
++v9;
break;
default:
continue;
}
}
}
这个unk_403040里面的是操作数,到时候提出来解码就行了
主要分析这个vm_operad,大致应该是这样,case8不知道是啥
然后找把操作数提取出来,0x07后面的就是待比较的密文了,把他们提出来,看看前面对flag进行了什么操作
def disassemble_bytecode(bytecode):
# 操作码映射表
opcode_map = {
1: "STORE",
2: "ADD",
3: "SUB",
4: "XOR",
5: "MUL",
6: "NOP",
7: "VERIFY",
8: "LOAD",
10: "READ",
11: "DEC",
12: "INC"
}
instructions = []
i = 0
length = len(bytecode)
while i < length:
op = bytecode[i]
instr = opcode_map.get(op, f"UNKNOWN({op})")
# 处理带操作数的指令
if op in [2, 3, 4, 5, 7]: # 这些指令带1个操作数
if i+1 >= length:
instructions.append(("INCOMPLETE", op))
break
operand = bytecode[i+1]
instructions.append((instr, operand))
i += 2
elif op in [1, 6, 8, 10, 11, 12]: # 无操作数指令
instructions.append((instr, None))
i += 1
else: # 未知指令
instructions.append((f"UNKNOWN({op})", None))
i += 1
return instructions
# 测试数据
bytecode = [
0x0A, 0x04, 0x10, 0x08,
0x03, 0x05, 0x01, 0x04,
0x20, 0x08, 0x05, 0x03,
0x01, 0x03, 0x02, 0x08,
0x0B, 0x01, 0x0C, 0x08,
0x04, 0x04, 0x01, 0x05,
0x03, 0x08, 0x03, 0x21,
0x01, 0x0B, 0x08, 0x0B,
0x01, 0x04, 0x09, 0x08,
0x03, 0x20, 0x01, 0x02,
0x51, 0x08, 0x04, 0x24,
0x01, 0x0C, 0x08, 0x0B,
0x01, 0x05, 0x02, 0x08,
0x02, 0x25, 0x01, 0x02,
0x36, 0x08, 0x04, 0x41,
0x01, 0x02, 0x20, 0x08,
0x05, 0x01, 0x01, 0x05,
0x03, 0x08, 0x02, 0x25,
0x01, 0x04, 0x09, 0x08,
0x03, 0x20, 0x01, 0x02,
0x41, 0x08, 0x0C, 0x01,
]
# 反汇编输出
instructions = disassemble_bytecode(bytecode)
# 打印带注释的反汇编结果
print("地址\t操作码\t参数\t指令描述")
print("----------------------------------------")
pc = 0
for instr in instructions:
op, arg = instr
desc = f"{op}"
if arg is not None:
desc += f" 0x{arg:x}"
# 添加注释说明
if op == "READ":
desc += " # 读取flag到内存"
elif op == "VERIFY":
desc += f" # 验证内存[100+rc] == 0x{arg:x}"
elif op == "STORE":
desc += " # 存储v4到内存[100+rb]"
elif op == "LOAD":
desc += " # 加载v4到内存[rd]"
print(f"0x{pc:04x}\t0x{bytecode[pc]:02x}\t{arg if arg is not None else '':4} \t{desc}")
# 计算PC偏移
if op in ["ADD", "SUB", "XOR", "MUL", "VERIFY"]:
pc += 2
else:
pc += 1
根据翻译的结果写脚本逆flag
enc=[0x22,0x3F,0x34,0x32,0x72,0x33,0x18,0xA7,0x31,0xF1,0x28,0x84,0xC1,0x1E,0x7A]
enc[0]=(enc[0]+5)^16
enc[1]=(enc[1]//3)^32
enc[2]=enc[2]+1+2
enc[3]=(enc[3]^4)-1
enc[4]=(enc[4]+33)//3
enc[5]=enc[5]+1+1
enc[6]=(enc[6]+32)^9
enc[7]=(enc[7]^36)-81
enc[8]=enc[8]
enc[9]=(enc[9]-37)//2
enc[10]=(enc[10]^65)-54
enc[11]=enc[11]-32
enc[12]=(enc[12]-37)//3
enc[13]=(enc[13]+32)^9
enc[14]=enc[14]-1-65
for i in range(len(enc)):
print(chr(enc[i]),end='')
#757515121f3d478