Hgame 2023 week4 (NSSCTF上下载) VM
本人第一次尝试VM类型的逆向CTF题,若有错误,欢迎指出交流QAQ
Hgame 2023 week4 (NSSCTF上下载) VM
第一部分
IDA打开,主程序如图:
我们点击第一个qmemcpy里的sub_140001000函数,跟进sub_140001060(第一个)函数:
这里应该是对虚拟机初始化,初始化几个关键值(指向字符串的索引位置,临时存放变量位置,输入的每个值位置等),但是我们并不知道这几个值对应的是什么,所以先放一边。回到主函数,我们看到有一个for循环,应该是最后比较flag的地方
我们将这个数组记为data[i],然后看下面if语句,很明显是最后的告诉我们是否正确的输出,大致分析了一下,接着我们上主菜:分析这个VM的模拟器(sub_1400010B0):
第二部分
进入这个函数,我们可以看到一个数组,点进去发现有很多的数据,最关键的是它有一个 != 255,
那么我们可以大胆的猜测这个数组是opcode,那么这个a1 + 24大概就是ip了,我们接着点击下面的函数进行分析:
这里很明显是一个cpu的模拟器,也是这个vm题最重要的部分,我们挨个分析:
第一个函数:
发现它是一个将data赋值给a1,还有a1赋值给data的一个操作所以,我们猜测这里是一个mov命令的模拟,但是要注意这里出现了a1[6] + 2/3这个东西,所以猜测这里一个有几个不同的模拟寄存器(模拟rax,rbx等)
第二个函数:
出现++a1的形式,还有将a1数组里的值赋值,可以猜测这里是一个push命令的模拟
第三个函数:
出现a1–与上一个函数相反的操作,那应该是pop命令的模拟:
这里我们看了三个函数,大致确认有一个reg[6]的数组,还有一个ip,一个esp指针,所以我们可以开始构造一个结构题辅助我们解题:
点击IDA上方的WIndow,再点击Local Types(如果没有这个选项尝试shift + F1快捷打开试试),再按下INS按键(键盘上),创造一个结构体(删除是DEL键,如果要重新编辑这个结构体可以右键这个结构体有一个Edit选项)
1 | struct vm |
先大概创造成这样,然后我们将之前几个函数类型改一下(选择输入参数前的_DWORD类型按下Y键,再输入我们定义的结构体名称)
一下子我们可以看见这个函数清晰了很多,说明我们大概是改对了,后面每一个函数都先这么改后再分析
第四个函数:
看到很多运算符号,所以我们将这个函数命名为calc
第五个函数:
发现如果直接修改类型为结构体,那么就会出现LOBYTE:
这个时候我们定义的结构体就可能不全或出现问题,我们重新分析一下这里有一个*(_BYTE *)(a1 + 32),那么这里的(a1 + 32)类型就不是我们定义的 _DWORD类型,应该为int类型,修改一下我们的结构体:
1 | struct vm |
这里的函数很明显是一个cmp命令的模拟,所以我们直接把(a1 + 32)认为是zf寄存器
修改后的函数:
很清晰明了。
第六,七,八个函数:
后面这几个函数都是jmp命令的模拟,只不过第七第八个函数用到了zf,所以可能是jne命令和je命令(可以再后面得到汇编代码时再改哪一个是jne哪一个是je)
第三部分
然后这里我们就已经将这个vm的逻辑基本理清了,现在就是写脚本还原这个汇编指令,然后读汇编解flag:
注意每一个模拟汇编指令后ip+的数都不一样,所以我们要一一对应。
1 | 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] |
小技巧:可以在最后print时,在前面print出此时的ip数,这样我们在看汇编时才知道jmp到了哪里,当然也可以让他print横线找到jmp的落点,下面为汇编代码:
1 | 0 mov reg[2],0 |
读代码大概是进行如下的运算:
1 | ((((data[0 + i] + data[50 + i] )^ data[100 + i]) << 8) & 0xff00 ) + (((data[0 + i] + data[50 + i] )^ data[100 + i]) >> 8) |
这个相当于是将异或后的二进制数据的前八位和后八位调换顺序
1 | # 95 mov reg[3],0 |
这里相当于是将改变了顺序的数据与data[150 + i]进行比较,不过要注意的是出栈的顺序,先进后出,所以这里是一个倒序比较,exp如下:
1 | data = [0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0000009B, 0x000000A8, 0x00000002, 0x000000BC, 0x000000AC, 0x0000009C, 0x000000CE, 0x000000FA, 0x00000002, 0x000000B9, 0x000000FF, 0x0000003A, 0x00000074, 0x00000048, 0x00000019, 0x00000069, 0x000000E8, 0x00000003, 0x000000CB, 0x000000C9, 0x000000FF, 0x000000FC, 0x00000080, 0x000000D6, 0x0000008D, 0x000000D7, 0x00000072, 0x00000000, 0x000000A7, 0x0000001D, 0x0000003D, 0x00000099, 0x00000088, 0x00000099, 0x000000BF, 0x000000E8, 0x00000096, 0x0000002E, 0x0000005D, 0x00000057, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000C9, 0x000000A9, 0x000000BD, 0x0000008B, 0x00000017, 0x000000C2, 0x0000006E, 0x000000F8, 0x000000F5, 0x0000006E, 0x00000063, 0x00000063, 0x000000D5, 0x00000046, 0x0000005D, 0x00000016, 0x00000098, 0x00000038, 0x00000030, 0x00000073, 0x00000038, 0x000000C1, 0x0000005E, 0x000000ED, 0x000000B0, 0x00000029, 0x0000005A, 0x00000018, 0x00000040, 0x000000A7, 0x000000FD, 0x0000000A, 0x0000001E, 0x00000078, 0x0000008B, 0x00000062, 0x000000DB, 0x0000000F, 0x0000008F, 0x0000009C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00004800, 0x0000F100, 0x00004000, 0x00002100, 0x00003501, 0x00006400, 0x00007801, 0x0000F900, 0x00001801, 0x00005200, 0x00002500, 0x00005D01, 0x00004700, 0x0000FD00, 0x00006901, 0x00005C00, 0x0000AF01, 0x0000B200, 0x0000EC01, 0x00005201, 0x00004F01, 0x00001A01, 0x00005000, 0x00008501, 0x0000CD00, 0x00002300, 0x0000F800, 0x00000C00, 0x0000CF00, 0x00003D01, 0x00004501, 0x00008200, 0x0000D201, 0x00002901, 0x0000D501, 0x00000601, 0x0000A201, 0x0000DE00, 0x0000A601, 0x0000CA01, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000] |
flag:
1 | hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!} |
总结
第一次做vm的题还是有很多的不足,菜就多练.jpg QWQ