Straw's B1og.

Vm-master

字数统计: 970阅读时长: 5 min
2024/11/12

VM-master

前言

​ 之前出题的时候,想在github上找个好点的vm板子,但是这样的资源好少好少,就看到个外国的库写的,我用这个改编了一份简单点的,出了2024newstar新生赛的vm题。(虽然被小登非预期了,应该加个前后加减预防单字节爆破的)

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#include <iostream>
#include <cstdint>
#include <string.h>
class VirtualMachine {
public:
static const int MEM_SIZE = 256;
static const int NUM_REGISTERS = 8;

uint8_t MEMORY[MEM_SIZE] = {0}; // 内存
uint8_t REGISTERS[NUM_REGISTERS] = {0}; // 寄存器
uint8_t* COMMANDS; // 程序指令
int PC = 0; // 程序计数器
bool ZF = false;

void run() {
bool running = true;
while (running) {
uint8_t opcode = COMMANDS[PC];
switch (opcode) {
case 0x01: // LOAD_IMM reg, imm
{
uint8_t reg = COMMANDS[PC + 1];
uint8_t imm = COMMANDS[PC + 2];
REGISTERS[reg] = imm;
PC += 3;
}
break;
case 0x02: // LOAD_ADDR reg, addr
{
uint8_t reg1 = COMMANDS[PC + 1];
uint8_t addr = COMMANDS[PC + 2];
uint8_t reg2 = COMMANDS[PC + 3];
REGISTERS[reg1] = MEMORY[addr+REGISTERS[reg2]];
PC += 4;
}
break;
case 0x03: // STORE_ADDR addr, reg, reg
{
uint8_t addr = COMMANDS[PC + 1];
uint8_t reg1 = COMMANDS[PC + 2];
uint8_t reg2 = COMMANDS[PC + 3];
MEMORY[addr+REGISTERS[reg1]] = REGISTERS[reg2];
PC += 4;
}
break;
case 0x04: // ADD reg1, reg2, dest
{
uint8_t reg1 = COMMANDS[PC + 1];
uint8_t reg2 = COMMANDS[PC + 2];
uint8_t dest = COMMANDS[PC + 3];
REGISTERS[dest] = REGISTERS[reg1] + REGISTERS[reg2];
PC += 4;
}
break;
case 0x05: // MOD reg1, reg2, dest
{
uint8_t reg1 = COMMANDS[PC + 1];
uint8_t imm = COMMANDS[PC + 2];
uint8_t dest = COMMANDS[PC + 3];
REGISTERS[dest] = REGISTERS[reg1] % imm;
PC += 4;
}
break;
case 0x06: // XOR reg1, reg2, dest
{
uint8_t reg1 = COMMANDS[PC + 1];
uint8_t reg2 = COMMANDS[PC + 2];
uint8_t dest = COMMANDS[PC + 3];
REGISTERS[dest] = REGISTERS[reg1] ^ REGISTERS[reg2];
PC += 4;
}
break;
case 0x07: // INC reg
{
uint8_t reg = COMMANDS[PC + 1];
REGISTERS[reg]++;
PC += 2;
}
break;
case 0x08: // CMP reg1, imm
{
uint8_t reg1 = COMMANDS[PC + 1];
uint8_t imm = COMMANDS[PC + 2];
ZF = (REGISTERS[reg1] == imm);
PC += 3;
}
break;
case 0x09: // JMP addr
{
uint8_t addr = COMMANDS[PC + 1];
PC = addr;
}
break;
case 0x0A: // JZ addr
{
uint8_t addr = COMMANDS[PC + 1];
if (ZF) {
PC = addr;
} else {
PC += 2;
}
}
break;
case 0x0B: // LOAD_ADDR reg, R(addr)
{
uint8_t reg = COMMANDS[PC + 1];
uint8_t addr = COMMANDS[PC + 2];
REGISTERS[reg] = MEMORY[REGISTERS[addr]];
PC += 3;
}
break;
case 0xFF: // HALT
{
running = false;
}
break;
default:
std::cerr << "Unknown opcode: " << static_cast<int>(opcode) << std::endl;
running = false;
break;
}
}
}
};
unsigned char array[128];

void __attribute__((constructor)) before_main() {
for (int i = 0; i < 128; i++) {
array[i] = i;
}
srand(0x114514);
for (int i = 127; i > 0; i--) {
int j = rand() % (i + 1);
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}

uint8_t program[] = {
0x01, 0x00, 0x00, // LOAD_IMM R0, 0 ; i = 0
0x08, 0x00, 0x18, // CMP R0, 0x18 ; 比较 i < 24
0x0A, 35, // JZ end_loop ; 如果 i >= 24,跳到 end_loop
0x02, 0x01, 0x80, 0x00, // LOAD_ADDR R1, 0x80+R0 ; flag[i]
0x04, 0x01, 0x00, 0x01, // ADD R1, R0, R1 ; R1 = flag[i] + i
0x05, 0x01, 0x80, 0x01, // MOD R1, 128, R1 ; R1 = (flag[i] + i) % 128
0x0B, 0x02, 0x01, // LOAD_ADDR R2, R1 ; R2 = array[(flag[i] + i) % 128]
0x06, 0x02, 0x00, 0x02, // XOR R2, R0, R2 ; R2 = R2 ^ i
0x03, 0x80, 0x00, 0x02, // STORE_ADDR 0x80+R0, R2 ; flag[i] = R2
0x07, 0x00, // INC R0 ; i++
0x09, 0x03, // JMP loop ; 回到循环开始
0xFF // HALT ; 停止执行
};

int main() {
VirtualMachine vm;

uint8_t flag[25];
uint8_t pw[24]={0x28,0x79,0x17,0x4,0xc,0x73,0x26,0x36,0x50,0x39,0x7e,0x24,0x51,0x17,0x44,0x25,0x6,0x70,0x4d,0x40,0x79,0x35,0x73,0x21};

printf("\033[31mCongratulations on persevering until the fifth week of the Newstar CTF!!!\033[0m\n");
printf("\033[32mWhether or not you can create this VM, I believe it can give you something.\033[0m\n");
printf("\033[33m\033[44mPlease input your flag:\033[0m");

scanf("%24s",flag);
if (getchar() != '\n')
{
printf("no");
return 0;
}
// 将数组写入虚拟机内存
for (int i = 0; i < 128; i++) {
vm.MEMORY[i] = array[i];
}
for (int i = 0; i < 24; i++) {
vm.MEMORY[128 + i] = flag[i];
}

vm.COMMANDS = program; // 加载程序
vm.run(); // 执行程序

int jd=0;
for (int i = 0; i < 24; i++) {

if(vm.MEMORY[128 + i]==pw[i])
{
jd++;
// printf("%d验证成功\n",jd);
}
if(jd==24)
{
printf("yes");
return 0;
}
// std::cout << "flag[" << i << "]: 0x" << std::hex << static_cast<int>(vm.MEMORY[128 + i]) << std::endl;
// printf("%x,",vm.MEMORY[128 + i]);
}
printf("no");
return 0;
}

后记

​ 想要出难点的话就直接多加指令case,想要不被非预期记得别单字节加密,加密使用换表。VM这坨史,就应该老老实实写opcode才行🤯。

CATALOG
  1. 1. VM-master
    1. 1.1. 前言
    2. 1.2. 源码
    3. 1.3. 后记