前言
没有前言。终于搞定第二题了。费劲是真的费。
题目在nssctf上有。
这一题写完才看到有提示,是关于构建结构体的,下次试试。
0x1[HGAME 2023 week4]vm
这里有几个技巧:
- 将__int64 a1 改为 _DWORD *a1
- a1 可以 改名为 reg
- 函数返回值改为void
这样方便观看,还有其他一些函数的名称,不一一做解释。
sub_1400010B0 点进来,一个里面放着字节码改为opcode,一个是调度器。 当opcode当前的索引的值不为255的时候就一直读取下去。
这里是做过修过后的,可以看到特别的简洁明了。下面具体分析一下,汇编的实现过程。先给出完整的。
提取汇编指令
opcode = [0x00, 0x03, 0x02, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x02, 0x32,
0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x03, 0x02, 0x64, 0x03, 0x00, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x03,
0x00, 0x08, 0x00, 0x02, 0x02, 0x01, 0x03, 0x04, 0x01, 0x00,
0x03, 0x05, 0x02, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x02,
0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00,
0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28,
0x04, 0x06, 0x5F, 0x05, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02,
0x01, 0x00, 0x03, 0x02, 0x96, 0x03, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x00, 0x04, 0x07, 0x88, 0x00, 0x03, 0x00, 0x01,
0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03,
0x01, 0x28, 0x04, 0x07, 0x63, 0xFF, 0xFF]
input1 = []
i = 0
while opcode[i] != 0xFF:
match opcode[i]:
case 0x00:
print(f'{i}', end=' ')
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print("mov input[reg[2]], reg[0]")
case 0x02:
print("mov reg[%d], reg[%d]" % (opcode[i+2],opcode[i+3]))
case 0x03:
print("mov reg[%d], %d" % (opcode[i+2], opcode[i+3]))
else:
print("mov reg[0], input[reg[2]]")
i += 4
case 0x01:
print(f'{i}', end=' ')
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print("push reg[0]")
case 0x02:
print("push reg[2]")
case 0x03:
print("push reg[3]")
else:
print("push reg[0]")
i += 2
case 0x02:
print(f'{i}', end=' ')
o = i + 1
if opcode[o]:
match opcode[o]:
case 0x01:
print("pop reg[1]")
case 0x02:
print("pop reg[2]")
case 0x03:
print("pop reg[3]")
else:
print("pop reg[0]")
i += 2
case 0x03:
print(f'{i}', end=' ')
o = i + 1
match opcode[o]:
case 0:
print("add reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
case 1:
print("sup reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
case 2:
print("mul reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
case 3:
print("xor reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
case 4:
print("shl reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
case 5:
print("shr reg[%d],reg[%d]" % (opcode[i + 2], opcode[i + 3]))
i += 4
case 0x04:
print(f'{i} cmp reg[0], reg[1]')
i += 1
case 0x05:
print(f'{i} jmp %d ' % (opcode[i+1]))
i += 2
case 0x06:
print(f'{i} je %d ' % (opcode[i+1]))
i += 2
case 0x07:
print(f'{i} jne %d ' % (opcode[i+1]))
i += 2
我第一次写,没有区分函数,最好可以写成 def mov(): 这样的形式。
mov指令
其实我也不太理解,为什么是reg[6](a1[6])这样子, 最后也只理解了它 等同于 i 索引值。
这里经过修改,还是蛮清楚的,reg是寄存器,reg[6] 当做是 i;这里例如case 1:可以理解为将reg[0] 的东西放入 input[reg[2]]里下面也是类似的。case2 是俩个寄存器,case 3就是将opcode[reg[3]]的数据放入寄存器。注意的是最后有个 i += 4 。
其实 i 可以理解为 rip。也就是读取指令的位置。
push指令&pop指令
栈 + 1,然后 将 reg[0] 的值放进去 ,就是push 指令 ,最后有 i += 2
pop指令同理,寄存器取了栈当前位置的值,然后栈再-1,
运算操作
这里其实蛮清楚的,就是寄存器之间的加减之类的运算。
cmp指令
比较函数,返回 0 或者 1;
jmp指令
改完后简单明了,就是 i = 下一个位置
je 和 jne
一个是不为 0 跳转 一个 是为零 跳转。
这里就分析完了。运行一开始的exp
得到近似的汇编
0 mov reg[2], 0
4 add reg[2],reg[3]
8 mov reg[0], input[reg[2]]
12 mov reg[1], reg[0]
16 mov reg[2], 50
20 add reg[2],reg[3]
24 mov reg[0], input[reg[2]]
28 add reg[1],reg[0]
32 mov reg[2], 100
36 add reg[2],reg[3]
40 mov reg[0], input[reg[2]]
44 xor reg[1],reg[0]
48 mov reg[0], 8
52 mov reg[2], reg[1]
56 shl reg[1],reg[0]
60 shr reg[2],reg[0]
64 add reg[1],reg[2]
68 mov reg[0], reg[1]
72 push reg[0]
74 mov reg[0], 1
78 add reg[3],reg[0]
82 mov reg[0], reg[3]
86 mov reg[1], 40
90 cmp reg[0], reg[1]
91 je 95
93 jmp 0
95 mov reg[3], 0
99 pop reg[1]
101 mov reg[2], 150
105 add reg[2],reg[3]
109 mov reg[0], input[reg[2]]
113 cmp reg[0], reg[1]
114 jne 136
116 mov reg[0], 1
120 add reg[3],reg[0]
124 mov reg[0], reg[3]
128 mov reg[1], 40
132 cmp reg[0], reg[1]
133 jne 99
分析后大概就是这样
- a1 是 input[50 + i]
- a2 是 input[100 + i]
- a3 是 input[150 + i]
就是,取 input[50 + i]开始的值 和我们输入的flag 相加,然后和input[100+i]的值异或 。将异或后的值再做俩个移位处理相加起来,得到的值等于 input[150+i]的值。
将值都取出来,写个exp
exp
a1 = [155, 168, 2, 188, 172, 156, 206, 250, 2, 185, 255, 58, 116, 72, 25, 105, 232, 3, 203, 201,
255, 252, 128, 214, 141, 215, 114, 0, 167, 29, 61, 153, 136, 153, 191, 232, 150, 46, 93, 87]
a2 = [201, 169, 189, 139, 23, 194, 110, 248, 245, 110, 99, 99, 213, 70, 93, 22, 152, 56, 48, 115, 56,
193, 94, 237, 176, 41, 90, 24, 64, 167, 253, 10, 30, 120, 139, 98, 219, 15, 143, 156]
a3 = [18432, 61696, 16384, 8448, 13569, 25600, 30721, 63744, 6145, 20992, 9472, 23809, 18176, 64768, 26881, 23552,
44801, 45568, 60417,
20993, 20225, 6657, 20480, 34049, 52480, 8960, 63488, 3072, 52992, 15617, 17665, 33280, 53761, 10497, 54529, 1537,
41473, 56832, 42497, 51713]
a4 = a3[::-1]
# a4 = [51713, 42497, 56832, 41473, 1537, 54529, 10497, 53761, 33280, 17665, 15617, 52992, 3072, 63488, 8960, 52480, 34049, 20480, 6657, 20225, 20993, 60417, 45568, 44801, 23552, 26881, 64768, 18176, 23809, 9472, 20992, 6145, 63744, 30721, 25600, 13569, 8448, 16384, 61696, 18432]
flag = [0] * 40
for i in range(40):
flag[i] = ((a4[i] >> 8) & 0xff + (a4[i] << 8))
flag[i] ^= a2[i]
flag[i] -= a1[i]
print("".join([chr(a&0xff) for a in flag]))
# hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}