比较经典的虚拟机的题目是南邮的cg-ctf的题目,
然后先写了前面的cg-ctf的两个题目,其实当时做的时候还没太大虚拟机的感觉,后面接触到hgame的题目,然后再看的时候就感觉到有些那个意思,但是两个题目是基础题目也就不是太难为人,看成数据处理的考点也做的出来,
后面那个hagem的题目中间有一个栈的结构,也是比较有感觉的虚拟机题目了
另外hgame已经结束:https://hgame.vidar.club/#/user/login?return=Challenge-List
cg-ctf: https://cgctf.nuptsast.com/challenges#Re
题目文件:
链接: https://pan.baidu.com/s/1iizvnWWcDncj8uGVNmsmNA 密码: 1vle
我们的flag,先是经过一个函数的加密,然后判定了下长度为24,之后和一个数组比较,而且这是一个已知的数组。
我们看到了大量的数据处理过程,通过寻址找到一个大数组的其中的位数,然后其中的连续三位拿出,一个指示运算方式,一个指示操作的flag的哪一位,一个作为运算数,
我们首先看看指示运算方式的数,直接使用ida-python脚本直接调用那个大数组即可
addr = 0x6010c0
arr = []
for i in range(0,15000,3):
arr.append(Byte(addr + i))
print(arr)
print(1 in arr)
print(2 in arr)
print(3 in arr)
print(4 in arr)
print(5 in arr)
我们知道了只会存在前三种运算,即加减异或,这都是很好逆的算法。
我们查看第二个值,指向flag的位数的,可以知道会出现很多重复的,我们只能逆着写这个算法,然后逆向得到flag,这时候就需要注意,我们的i,从0开始,到<15000,每次增3,不会取到15000,i取到的最大为14997,我们的脚本也应该是14997开始,每次增量-3,但是注意python的前包后不包,我们想最后取到0,就要写为结束在-1,
我们还需要注意的是数据溢出的处理,最开始可能数据大一些,但是到后期数据会变得很小,然后我们在计算完成以后再使用x & 0xff
来空值数据溢出。
得到:
arr = [4294967236, 52, 34, 4294967217, 4294967251, 17, 4294967191, 7, 4294967259, 55, 4294967236, 6, 29, 4294967292, 91, 4294967277, 4294967192, 4294967263, 4294967188, 4294967256, 4294967219, 4294967172, 4294967244, 8]
addr = 0x6010c0
for i in range(len(arr)):
arr[i] &= 0xffffffff
for i in range(14997,-1,-3):
v0 = Byte(addr + i)
v3 = Byte(addr + i + 2)
result = Byte(addr + i + 1)
if v0 == 1:
arr[result] -= v3
if v0 == 2:
arr[result] += v3
if v0 == 3:
arr[result] ^= v3
for i in range(len(arr)):
arr[i] &= 0xff
print(''.join(map(chr, arr)))
这也是一个ida-python的脚本,直接在ida运行可以直接获取到大数组,更方便。
然后就可以得到flag:nctf{Embr4ce_Vm_j0in_R3}
WxyVM 2
首先应该是看到我们的main函数中存在一个红色区块,提示说这个代码太多了就不显示了,我们切换界面后发现是很多很多的
首先伪代码有可能会显示无法编译的情况,这是由于长度限制。我们要修改配置文件:ida\cfg\hexrays.cfg,MAX_FUNCSIZE
一项改为1024即可;
我们的伪代码也会出现一大堆的数据处理,我们全复制下来,python操作后使用,然后我们简单进行排版:
首先将最后的一个分号去除, 将++或–写为-=1,+=1,然后保存在一个’data-1.txt’文件中我们发现需要处理的字符都是byte类型,而垃圾数据是以dword类型。我们使用脚本将数据处理为脚本:
import os
file_1 = open('data-1.txt', 'r')
s = file_1.read()
s = s.replace('\n', '')
s = s.replace('u','')
a = s.split(';')
ss = ''
for i in a[::-1]:
if i[0] == 'b':
ss += i + '\n'
file_1.close()
ss = ss.replace('+','$')
ss = ss.replace('-','+')
ss = ss.replace('$','-')
ss = ss.replace('byte_','arr[')
for i in range(65,71,1):
ss = ss.replace(chr(i),chr(i+32))
for i in range(25):
ss = ss.replace(str(hex(0x694100+i)).replace('0x',''),str(i)+']')
file_2 = open('data-2.txt','w')
file_2.write(ss)
file_2.close()
然后我们得到"data-2.txt"文件,将列表赋值,然后最后打印出来:
arr = [4294967232, 4294967173, 4294967289, 108, 4294967266, 20, 4294967227, 4294967268, 13, 89, 28, 35, 4294967176, 110, 4294967195, 4294967242, 4294967226, 92, 55, 4294967295, 72, 4294967256, 31, 4294967211, 4294967205]
#这里是直接复制过来data-2文件
for i in range(25):
print(chr(arr[i]&0xff),end='')
hagem-week4-easy
首先是 函数逻辑比较简单。输入一个data, 然后设定了一个大数组, 然后进入函数vm_fun,结束后和一段写好的数据对比,
我们看一下函数vm_fun,一个看着比较经典的使用while和switch构成的虚拟机的样子,
值得注意的是这个内存区:
这个形成一个结构体,用于形成一个栈的结构,
struct StackInfo{
QWORD Size; //栈尺寸
QWORD CurrentStackTop; //栈顶
QWORD pStack; // 指向分配出来的栈
}
其中的一些函数就是push、pop和取值的任务,
将控制程序流的数组vm导出, 然后动调注释一下:
5 push flag_0
2 pop var_a(flag_0)
1 push var_a
5 push 0x28
8 pop var_b(0x28), mov rsp,if var_a[0x28] == 0 ? bool_a = 1
1 push var_a
5 push 1
2 pop var_a(1)
>> 14 mov rsp, if
5 push 0xc
<< 12 pop var_b(0xc), if bool_a : vm += 0xc (jmp)
4 pop var_c(flag_0)
3 push var_c
3 push var_c
6 pop var_b(flag_0), mov var_d, [var_b], push var_d;
5 push 0x52
10 pop var_b(0x52), xor rsp, var_b,
7 pop var_b(xor), pop var_f(flag_0), mov var_f, var_b;
3 push var_c
1 push var_a
8 pop var_b(1), mov
5 push -15
<> 13
首先前面的数据是数组vm,程序的流程控制,中间缩进相同的>>和<< 配合表示一个跳转的位置,
其实是一个循环,然后中间的操作就是从前面设置好的那些值里面每三位取出第二个进行一次异或, 共进行循环0x20 次,主要是动调观察下就好,然后注意那个栈结构,
然后我们ida-python 导出这个数据,然后直接异或就ok,
cipher = [58, 84, 47, 42, 47, 54, 19, 1, 46, 3, 53, 64, 71, 14, 95, 89, 1, 105, 39, 8, 61, 76, 51, 26, 45, 11, 64, 14, 75, 36, 65, 39, 37, 40, 41, 42, 2, 2, 93, 36]
xor = [82, 51, 78, 71, 74, 77, 103, 105, 71, 112, 106, 54, 42, 81, 54, 42, 94, 54, 84, 103, 78, 35, 64, 117, 94, 100, 51, 97, 56, 75, 50, 72, 86, 71, 118, 79, 99, 113, 36, 89]
for i in range(40):
print(chr(cipher[i] ^ xor[i]), end='')