伪虚拟机保护
虚拟机(英语: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;
public class JVM {
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(); }
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") 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") 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)
|