伪虚拟机保护

虚拟机(英语:virtual machine),在计算机科学中的体系结构里,是指一种特殊的软件,可以在计算机平台和终端用户之间建立一种环境,而终端用户则是基于虚拟机这个软件所建立的环境来操作其它软件。虚拟机(VM)是计算机系统的仿真器,通过软件模拟具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统,能提供物理计算机的功能。

很明显,ctf中的vm技术和虚拟机并不相同,却有一定关系

伪虚拟机保护是一种模拟,它将程序可执行代码转换为自定义的中间操作码(operationcode),如果操作码是一个字节,将它称为(bytecode)。中间操作码通过模拟器执行,实现程序原来的功能,同时阻碍逆向。这也是大多数跨平台语言可以成立的原因,先将代码翻译成中间码,这个代码就可以在任何有对应虚拟机的地方执行。

实例

JVM

两个文件

JVM.class

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
package p000;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

/* renamed from: JVM */
/* loaded from: JVM.class */
public class JVM {

/* renamed from: program */
static byte[] flag;

public static void main(String[] strArr) throws IOException {
File file = new File(strArr[0]);
FileInputStream fileInputStream = new FileInputStream(file);
flag = new byte[(int) file.length()];
fileInputStream.read(flag);
fileInputStream.close();
m0vm();
}

/* renamed from: vm */
private static void m0vm() throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
int i = 0;
int i2 = 0;
int[] iArr = new int[1024];
int[] iArr2 = new int[4];
while (i < flag.length) {
switch (flag[i]) {
case 0:
case 1:
case 2:
case 3:
byte b = flag[i];
byte b2 = flag[i + 1];
int i3 = iArr2[b];
iArr2[b] = iArr2[b2];
iArr2[b2] = i3;
i += 2;
break;
case 8:
byte n_next = flag[i + 1];
iArr2[n_next] = iArr2[n_next] + flag[i + 2];
i += 3;
break;
case 9:
byte b3 = flag[i + 1];
iArr2[b3] = iArr2[b3] + iArr2[flag[i + 2]];
i += 3;
break;
case 12:
byte b4 = flag[i + 1];
iArr2[b4] = iArr2[b4] - flag[i + 2];
i += 3;
break;
case 13:
byte b5 = flag[i + 1];
iArr2[b5] = iArr2[b5] - iArr2[flag[i + 2]];
i += 3;
break;
case 16:
byte b6 = flag[i + 1];
iArr2[b6] = iArr2[b6] * flag[i + 2];
i += 3;
break;
case 17:
byte b7 = flag[i + 1];
iArr2[b7] = iArr2[b7] * iArr2[flag[i + 2]];
i += 3;
break;
case 20:
byte b8 = flag[i + 1];
iArr2[b8] = iArr2[b8] / flag[i + 2];
i += 3;
break;
case 21:
byte b9 = flag[i + 1];
iArr2[b9] = iArr2[b9] / iArr2[flag[i + 2]];
i += 3;
break;
case 24:
byte b10 = flag[i + 1];
iArr2[b10] = iArr2[b10] % flag[i + 2];
i += 3;
break;
case 25:
byte b11 = flag[i + 1];
iArr2[b11] = iArr2[b11] % iArr2[flag[i + 2]];
i += 3;
break;
case 28:
byte b12 = flag[i + 1];
iArr2[b12] = iArr2[b12] << flag[i + 2];
i += 3;
break;
case 29:
byte b13 = flag[i + 1];
iArr2[b13] = iArr2[b13] << iArr2[flag[i + 2]];
i += 3;
break;
case 31:
iArr2[flag[i + 1]] = bufferedReader.read();
i += 2;
break;
case 32:
int i4 = i2;
i2++;
iArr[i4] = bufferedReader.read();
i++;
break;
case 33:
System.out.print((char) iArr2[flag[i + 1]]);
i += 2;
break;
case 34:
i2--;
System.out.print((char) iArr[i2]);
i++;
break;
case 41:
byte b14 = flag[i + 1];
byte b15 = flag[i + 2];
if (iArr2[b14] == 0) {
i = b15;
break;
} else {
i += 3;
break;
}
case 42:
byte b16 = flag[i + 1];
byte b17 = flag[i + 2];
if (iArr2[b16] != 0) {
i = b17;
break;
} else {
i += 3;
break;
}
case 43:
i = flag[i + 1];
break;
case 52:
int i5 = i2;
i2++;
iArr[i5] = iArr2[flag[i + 1]];
i += 2;
break;
case 53:
i2--;
iArr2[flag[i + 1]] = iArr[i2];
i += 2;
break;
case 54:
int i6 = i2;
i2++;
iArr[i6] = flag[i + 1];
i += 2;
break;
case Byte.MAX_VALUE:
bufferedReader.close();
return;
default:
byte b18 = flag[i];
byte b19 = flag[i + 1];
byte b20 = flag[i + 2];
flag[i] = (byte) ((flag[i] ^ b19) ^ b20);
flag[i + 1] = (byte) ((flag[i] ^ b18) ^ b20);
flag[i + 2] = (byte) ((flag[i + 1] ^ b18) ^ b19);
break;
}
}
}
}

code.jvm(二进制文件)

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
36 20 36 3E 22 22 08 00 0A 09 00 00 09 00 00 08
00 02 20 0C 00 01 2A 00 12 2B 2C 36 53 36 45 36
59 22 22 22 7F 36 4F 36 4E 22 22 7F 35 00 0C 00
7D 2A 00 25 35 00 0C 00 09 0C 00 36 2A 00 25 35
02 0C 02 0A 0C 02 63 2A 02 25 35 02 0C 02 15 0C
02 61 2A 02 25 35 00 0C 00 5F 2A 00 25 35 00 08
01 01 00 01 09 01 01 08 02 07 09 00 00 0C 02 01
2A 02 6A 08 00 02 0D 00 01 0D 01 01 2A 00 25 35
00 0C 00 5F 2A 00 25 35 01 0C 01 01 0C 01 73 2A
01 25 35 02 0C 02 03 0C 02 6B 2A 02 25 35 01 0C
01 03 0C 01 70 2A 01 25 35 00 0C 00 07 0C 00 62
2A 00 25 35 00 0C 00 5F 2A 00 25 35 00 0C 00 04
0C 00 30 2A 00 25 35 03 0C 03 0B 0C 03 6B 2A 03
25 35 03 0C 03 04 0C 03 30 2A 03 25 35 01 0C 01
13 0C 01 57 2A 01 25 35 00 0C 00 5F 2A 00 25 35
02 0C 02 1A 0C 02 54 2A 02 25 35 02 0C 02 04 0C
02 30 2A 02 25 35 01 0C 01 07 0C 01 2C 2A 01 25
35 01 0C 01 0F 0C 01 5E 2A 01 25 35 00 0C 00 5F
2A 00 25 35 01 0C 01 29 0C 01 2C 2A 01 25 35 02
0C 02 27 0C 02 48 2A 02 25 35 02 0C 02 2D 0C 02
4C 2A 02 25 35 00 0C 00 5F 2A 00 25 35 00 0C 00
07 0C 00 29 2A 00 25 35 01 0C 01 23 0C 01 41 2A
01 25 35 00 0C 00 5F 2A 00 25 35 02 0C 02 2C 0C
02 48 2A 02 25 35 03 0C 03 04 0C 03 30 2A 03 25
35 03 0C 03 00 0C 03 68 2A 03 25 35 00 0C 00 1C
0C 00 5B 2A 00 25 35 00 0C 00 7B 2A 00 25 35 00
0C 00 46 2A 00 25 35 00 0C 00 54 2A 00 25 35 00
0C 00 43 2A 00 25 35 00 0C 00 73 2A 00 25 35 00
0C 00 72 2A 00 25 35 00 0C 00 75 2A 00 25 35 00
0C 00 65 2A 00 25 35 00 0C 00 74 2A 00 25 35 00
0C 00 61 2A 00 25 35 00 0C 00 6D 2A 00 25 35 00
0C 00 61 2A 00 25 2B 1B

可以看出vm的基本特征,即:

一个switch加一堆case
这是在判断这一个字节的指令是什么,然后执行相应指令

所以,给出了程序,我们需要逆向模拟器推出每一个指令是什么意思,然后把指令还原。同时,一般还会模拟寄存器和栈结构来读取数据,所以还要找到这两个结构才好理解。

所以,化简代码

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
while (i < code.length) {
switch (code[i]) {
case 0:
case 1:
case 2:
case 3:
byte b = code[i];
byte b2 = code[i + 1];
int i3 = reg[b];
reg[b] = reg[b2];
reg[b2] = i3;
i += 2;
break;
case 8:
byte b3 = code[i + 1];
reg[b3] = reg[b3] + code[i + 2];
i += 3;
break;
case 9:
byte b4 = code[i + 1];
reg[b4] = reg[b4] + reg[code[i + 2]];
i += 3;
break;
case 12:
byte b5 = code[i + 1];
reg[b5] = reg[b5] - code[i + 2];
i += 3;
break;
case 13:
byte b6 = code[i + 1];
reg[b6] = reg[b6] - reg[code[i + 2]];
i += 3;
break;
case 16:
byte b7 = code[i + 1];
reg[b7] = reg[b7] * code[i + 2];
i += 3;
break;
case 17:
byte b8 = code[i + 1];
reg[b8] = reg[b8] * reg[code[i + 2]];
i += 3;
break;
case 20:
byte b9 = code[i + 1];
reg[b9] = reg[b9] / code[i + 2];
i += 3;
break;
case 21:
byte b10 = code[i + 1];
reg[b10] = reg[b10] / reg[code[i + 2]];
i += 3;
break;
case 24:
byte b11 = code[i + 1];
reg[b11] = reg[b11] % code[i + 2];
i += 3;
break;
case 25:
byte b12 = code[i + 1];
reg[b12] = reg[b12] % reg[code[i + 2]];
i += 3;
break;
case 28:
byte b13 = code[i + 1];
reg[b13] = reg[b13] << code[i + 2];
i += 3;
break;
case 29:
byte b14 = code[i + 1];
reg[b14] = reg[b14] << reg[code[i + 2]];
i += 3;
break;
case 31:
reg[code[i + 1]] = bufferedReader.read();
i += 2;
break;
case 32:
int i4 = i2;
i2++;
stack[i4] = bufferedReader.read();
i++;
break;
case 33:
System.out.print((char) reg[code[i + 1]]);
i += 2;
break;
case 34:
i2--;
System.out.print((char) stack[i2]);
i++;
break;
case 41:
byte b15 = code[i + 1];
byte b16 = code[i + 2];
if (reg[b15] == 0) {
i = b16;
break;
} else {
i += 3;
break;
}
case 42:
byte b17 = code[i + 1];
byte b18 = code[i + 2];
if (reg[b17] != 0) {
i = b18;
break;
} else {
i += 3;
break;
}
case 43:
i = code[i + 1];
break;
case 52:
int i5 = i2;
i2++;
stack[i5] = reg[code[i + 1]];
i += 2;
break;
case 53:
i2--;
reg[code[i + 1]] = stack[i2];
i += 2;
break;
case 54:
int i6 = i2;
i2++;
stack[i6] = code[i + 1];
i += 2;
break;
case Byte.MAX_VALUE:
bufferedReader.close();
return;
default:
byte b19 = code[i];
byte b20 = code[i + 1];
byte b21 = code[i + 2];
code[i] = (byte) ((code[i] ^ b20) ^ b21);
code[i + 1] = (byte) ((code[i] ^ b19) ^ b21);
code[i + 2] = (byte) ((code[i + 1] ^ b19) ^ b20);
break;
}

改写代码

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
while i < len(code):
b0 = code[i]
b1 = code[i + 1] if i + 1 < len(code) else 0xff
b2 = code[i + 2] if i + 2 < len(code) else 0xff
if b0 == 0 or b0 == 1 or b0 == 2 or b0 == 3:
fq.write(f"{i} swap r{b0} r{b1}\n")
i += 2
elif b0 == 8:
fq.write(f"{i} r{b1} += b2\n")
i += 3
elif b0 == 9:
fq.write(f"{i} r{b1} += r{b2}\n")
i += 3
elif b0 == 12:
fq.write(f"{i} r{b1} -= {b2}\n")
i += 3
elif b0 == 13:
fq.write(f"{i} r{b1} -= r{b2}\n")
i += 3
elif b0 == 16:
fq.write(f"{i} r{b1} *= {b2}\n")
i += 3
elif b0 == 17:
fq.write(f"{i} r{b1} *= r{b2}\n")
i += 3
elif b0 == 20:
fq.write(f"{i} r{b1} /= {b2}\n")
i += 3
elif b0 == 21:
fq.write(f"{i} r{b1} /= r{b2}\n")
i += 3
elif b0 == 24:
fq.write(f"{i} r{b1} mod= {b2}\n")
i += 3
elif b0 == 25:
fq.write(f"{i} r{b1} mod= r{b2}\n")
i += 3
elif b0 == 28:
fq.write(f"{i} shl r{b1}, {b2}\n")
i += 3
elif b0 == 29:
fq.write(f"{i} shl r{b1}, r{b2}\n")
i += 3
elif b0 == 31:
fq.write(f"{i} read r{b1}\n")
i += 2
elif b0 == 32:
fq.write(f"{i} rdpush\n")#i2为栈顶指针
i += 1
elif b0 == 33:
fq.write(f"{i} print_write r{b1}\n")#只打印不改变栈
i += 2
elif b0 == 34:
fq.write(f"{i} print_pop\n")#打印加出栈
i += 1
elif b0 == 41:
fq.write(f"{i} jmp {b2} if !r{b1}\n")#这里jmp通过修改i来跳转
i += 3
elif b0 == 42:
fq.write(f"{i} jmp {b2} if r{b1}\n")
i += 3
elif b0 == 43:
fq.write(f"{i} jmp {b1}\n")
i += 2
elif b0 == 52:
fq.write(f"{i} push r{b1}\n")
i += 2
elif b0 == 53:
fq.write(f"{i} pop r{b1}\n")
i += 2
elif b0 == 54:
fq.write(f"{i} push {b1}\n")
i += 2
elif b0 == 127:
fq.write(f"{i} quit\n")
i += 1
else:
if i + 1 < len(code) and i + 2 < len(code):#这是一个解密函数
code[i] = b0 ^ b1 ^ b2
code[i + 1] = b1
code[i + 2] = b0

根据code.jvm生成正确的代码,然后人脑模拟运行得到flag

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
0    push 32
2 push 62
4 print_pop
5 print_pop
6 r0 += b2
9 r0 += r0
12 r0 += r0
15 r0 += b2
18 rdpush
19 r0 -= 1
22 jmp 18 if r0
25 jmp 44
27 push 83
29 push 69
31 push 89
33 print_pop
34 print_pop
35 print_pop
36 quit
37 push 79
39 push 78
41 print_pop
42 print_pop
43 quit
44 pop r0
46 r0 -= 125
49 jmp 37 if r0
52 pop r0
54 r0 -= 9
57 r0 -= 54
60 jmp 37 if r0
63 pop r2
65 r2 -= 10
68 r2 -= 99
71 jmp 37 if r2
74 pop r2
76 r2 -= 21
79 r2 -= 97
82 jmp 37 if r2
85 pop r0
87 r0 -= 95
90 jmp 37 if r0
93 pop r0
95 r1 += b2
98 swap r0 r1
100 r1 += r1
103 r2 += b2
106 r0 += r0
109 r2 -= 1
112 jmp 106 if r2
115 r0 += b2
118 r0 -= r1
121 r1 -= r1
124 jmp 37 if r0
127 pop r0
129 r0 -= 95
132 jmp 37 if r0
135 pop r1
137 r1 -= 1
140 r1 -= 115
143 jmp 37 if r1
146 pop r2
148 r2 -= 3
151 r2 -= 107
154 jmp 37 if r2
157 pop r1
159 r1 -= 3
162 r1 -= 112
165 jmp 37 if r1
168 pop r0
170 r0 -= 7
173 r0 -= 98
176 jmp 37 if r0
179 pop r0
181 r0 -= 95
184 jmp 37 if r0
187 pop r0
189 r0 -= 4
192 r0 -= 48
195 jmp 37 if r0
198 pop r3
200 r3 -= 11
203 r3 -= 107
206 jmp 37 if r3
209 pop r3
211 r3 -= 4
214 r3 -= 48
217 jmp 37 if r3
220 pop r1
222 r1 -= 19
225 r1 -= 87
228 jmp 37 if r1
231 pop r0
233 r0 -= 95
236 jmp 37 if r0
239 pop r2
241 r2 -= 26
244 r2 -= 84
247 jmp 37 if r2
250 pop r2
252 r2 -= 4
255 r2 -= 48
258 jmp 37 if r2
261 pop r1
263 r1 -= 7
266 r1 -= 44
269 jmp 37 if r1
272 pop r1
274 r1 -= 15
277 r1 -= 94
280 jmp 37 if r1
283 pop r0
285 r0 -= 95
288 jmp 37 if r0
291 pop r1
293 r1 -= 41
296 r1 -= 44
299 jmp 37 if r1
302 pop r2
304 r2 -= 39
307 r2 -= 72
310 jmp 37 if r2
313 pop r2
315 r2 -= 45
318 r2 -= 76
321 jmp 37 if r2
324 pop r0
326 r0 -= 95
329 jmp 37 if r0
332 pop r0
334 r0 -= 7
337 r0 -= 41
340 jmp 37 if r0
343 pop r1
345 r1 -= 35
348 r1 -= 65
351 jmp 37 if r1
354 pop r0
356 r0 -= 95
359 jmp 37 if r0
362 pop r2
364 r2 -= 44
367 r2 -= 72
370 jmp 37 if r2
373 pop r3
375 r3 -= 4
378 r3 -= 48
381 jmp 37 if r3
384 pop r3
386 r3 -= 0
389 r3 -= 104
392 jmp 37 if r3
395 pop r0
397 r0 -= 28
400 r0 -= 91
403 jmp 37 if r0
406 pop r0
408 r0 -= 123
411 jmp 37 if r0
414 pop r0
416 r0 -= 70
419 jmp 37 if r0
422 pop r0
424 r0 -= 84
427 jmp 37 if r0
430 pop r0
432 r0 -= 67
435 jmp 37 if r0
438 pop r0
440 r0 -= 115
443 jmp 37 if r0
446 pop r0
448 r0 -= 114
451 jmp 37 if r0
454 pop r0
456 r0 -= 117
459 jmp 37 if r0
462 pop r0
464 r0 -= 101
467 jmp 37 if r0
470 pop r0
472 r0 -= 116
475 jmp 37 if r0
478 pop r0
480 r0 -= 97
483 jmp 37 if r0
486 pop r0
488 r0 -= 109
491 jmp 37 if r0
494 pop r0
496 r0 -= 97
499 jmp 37 if r0
502 jmp 27
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
def q(a):
print(chr(a),end ='')
#对应每一个循环条件

q(97)
q(109)
q(97)
q(116)
q(101)
q(117)
q(114)
q(115)
q(67)
q(84)
q(70)
q(123)
q(28+91)
q(104)
q(4+48)
q(44+72)
q(95)
q(35+65)
q(7+41)
q(95)
q(45+76)
q(39+72)
q(41+44)
q(95)
q(15+94)
q(7+44)
q(4+48)
q(26+84)
q(95)
q(19+87)
q(4+48)
q(11+107)
q(4+48)
q(95)
q(7+98)
q(115)
q(110)
q(116)
q(95)
q(65)
q(95)
q(97+21)
q(10+99)
q(9+54)
q(125)
#amateursCTF{wh4t_d0_yoU_m34n_j4v4_isnt_A_vm?}