网鼎2020signal
参考https://blog.csdn.net/OrientalGlass/article/details/132822924
纯手撕(太麻烦了,还容易错)基础vm逆向,没啥好说的逻辑简单,翻译下
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int _ip = 0;
int r4 = 0;
int r3 = 0;
int r2 = 0;
int r1 = 0;
int r0 = 0;
char Str[200] = {0};
void vm(int* dst){
while ( 1 ){
int n114_2 = _ip;
if ( _ip >= 114 ){
printf("Over\n");
return;
}
switch ( dst[_ip] ){
case 1:
Str[r2 + 100] = (char)r0;
++_ip;
++r2;
++r4;
printf("Str[%d + 100] = r0:%d\n", r2, r0);
break;
case 2:
r0 = dst[_ip + 1] + Str[r4];
printf("r0 = %d + Str[%d]:%d\n", dst[_ip + 1], r4, Str[r4]);
_ip += 2;
break;
case 3:
r0 = Str[r4] - (uint8_t)(dst[_ip + 1]);
printf("r0 = Str[%d] - %d\n", r4, dst[_ip + 1]);
_ip += 2;
break;
case 4:
r0 = dst[_ip + 1] ^ Str[r4];
printf("r0 = Str[%d] ^ %d\n", r4, dst[_ip + 1]);
_ip += 2;
break;
case 5:
r0 = dst[_ip + 1] * Str[r4];
printf("r0 = %d * Str[%d]:%d\n", dst[_ip + 1], r4, Str[r4]);
_ip += 2;
break;
case 6:
++_ip;
printf("nop\n");
break;
case 7:
if ( Str[r3 + 100] != (char)dst[_ip + 1] )
{
printf("what a shame...\n");
}
printf("cmp : Str[%d] != dst[%d] : %d, \n", r3+100, _ip +1, dst[_ip + 1]);
++r3;
_ip += 2;
break;
case 8:
Str[r1] = (char)r0;
printf("Str[%d] = r0:%d\n", r1, r0);
++_ip;
++r1;
break;
case 10:
printf("Input string (max %zu chars): ", sizeof(Str)-1);
if (!fgets(Str, sizeof(Str), stdin)) {
printf("Input error!\n");
return;
}
Str[strcspn(Str, "\n")] = 0;
_ip++;
printf(" -> Read: '%s'\n", Str);
break;
case 11:
r0 = Str[r4] - 1;
printf("r0 = Str[%d] -1\n", r4);
++_ip;
break;
case 12:
r0 = Str[r4] + 1;
printf("r0 = Str[%d] +1\n", r4);
++_ip;
break;
default:
printf("Unknown opcode: %d\n", dst[_ip]);
_ip++;
continue;
}
}
}
int main(){
int dst[] = {
0x0A, 0x04, 0x10, 0x08, 0x03, 0x05, 0x01, 0x04,
0x20, 0x08, 0x05, 0x03, 0x01, 0x03, 0x02, 0x08,
0x0B, 0x01, 0x0C, 0x08, 0x04, 0x04, 0x01, 0x05,
0x03, 0x08, 0x03, 0x21, 0x01, 0x0B, 0x08, 0x0B,
0x01, 0x04, 0x09, 0x08, 0x03, 0x20, 0x01, 0x02,
0x51, 0x08, 0x04, 0x24, 0x01, 0x0C, 0x08, 0x0B,
0x01, 0x05, 0x02, 0x08, 0x02, 0x25, 0x01, 0x02,
0x36, 0x08, 0x04, 0x41, 0x01, 0x02, 0x20, 0x08,
0x05, 0x01, 0x01, 0x05, 0x03, 0x08, 0x02, 0x25,
0x01, 0x04, 0x09, 0x08, 0x03, 0x20, 0x01, 0x02,
0x41, 0x08, 0x0C, 0x01, 0x07, 0x22, 0x07, 0x3F,
0x07, 0x34, 0x07, 0x32, 0x07, 0x72, 0x07, 0x33,
0x07, 0x18, 0x07, 0xA7, 0x07, 0x31, 0x07, 0xF1,
0x07, 0x28, 0x07, 0x84, 0x07, 0xC1, 0x07, 0x1E,
0x07, 0x7A
};
vm(dst);
}
//结果
Input string (max 199 chars): aaaaaaaaaaaaaaaaaaaaaa
-> Read: 'aaaaaaaaaaaaaaaaaaaaaa'
r0 = Str[0] ^ 16
Str[0] = r0:113
r0 = Str[0] - 5
Str[1 + 100] = r0:108
r0 = Str[1] ^ 32
Str[1] = r0:65
r0 = 3 * Str[1]:65
Str[2 + 100] = r0:195
r0 = Str[2] - 2
Str[2] = r0:95
r0 = Str[2] -1
Str[3 + 100] = r0:94
r0 = Str[3] +1
Str[3] = r0:98
r0 = Str[3] ^ 4
Str[4 + 100] = r0:102
r0 = 3 * Str[4]:97
Str[4] = r0:291
r0 = Str[4] - 33
Str[5 + 100] = r0:2
r0 = Str[5] -1
Str[5] = r0:96
r0 = Str[5] -1
Str[6 + 100] = r0:95
r0 = Str[6] ^ 9
Str[6] = r0:104
r0 = Str[6] - 32
Str[7 + 100] = r0:72
r0 = 81 + Str[7]:97
Str[7] = r0:178
r0 = Str[7] ^ 36
Str[8 + 100] = r0:-106
r0 = Str[8] +1
Str[8] = r0:98
r0 = Str[8] -1
Str[9 + 100] = r0:97
r0 = 2 * Str[9]:97
Str[9] = r0:194
r0 = 37 + Str[9]:-62
Str[10 + 100] = r0:-25
r0 = 54 + Str[10]:97
Str[10] = r0:151
r0 = Str[10] ^ 65
Str[11 + 100] = r0:-42
r0 = 32 + Str[11]:97
Str[11] = r0:129
r0 = 1 * Str[11]:-127
Str[12 + 100] = r0:-127
r0 = 3 * Str[12]:97
Str[12] = r0:291
r0 = 37 + Str[12]:35
Str[13 + 100] = r0:72
r0 = Str[13] ^ 9
Str[13] = r0:104
r0 = Str[13] - 32
Str[14 + 100] = r0:72
r0 = 65 + Str[14]:97
Str[14] = r0:162
r0 = Str[14] +1
Str[15 + 100] = r0:-93
what a shame...
cmp : Str[100] != dst[85] : 34,
what a shame...
cmp : Str[101] != dst[87] : 63,
what a shame...
cmp : Str[102] != dst[89] : 52,
what a shame...
cmp : Str[103] != dst[91] : 50,
what a shame...
cmp : Str[104] != dst[93] : 114,
what a shame...
cmp : Str[105] != dst[95] : 51,
what a shame...
cmp : Str[106] != dst[97] : 24,
what a shame...
cmp : Str[107] != dst[99] : 167,
what a shame...
cmp : Str[108] != dst[101] : 49,
what a shame...
cmp : Str[109] != dst[103] : 241,
what a shame...
cmp : Str[110] != dst[105] : 40,
what a shame...
cmp : Str[111] != dst[107] : 132,
what a shame...
cmp : Str[112] != dst[109] : 193,
what a shame...
cmp : Str[113] != dst[111] : 30,
what a shame...
cmp : Str[114] != dst[113] : 122,
Over
其中:enc = [34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122]逆向解密 enc = [34, 63, 52, 50, 114, 51, 24, 167, 49, 241, 40, 132, 193, 30, 122]
flag = []
# 前8位字符逆向操作
flag.append(( (enc[0] + 5) ^ 0x10 )) # 7
flag.append(( (enc[1] // 3) ^ 0x20 )) # 5
flag.append((enc[2] + 1 + 2)) # 7
flag.append(( (enc[3] ^ 0x4) - 1 )) # 5
flag.append(( (enc[4] + 33) // 3 )) # 1
flag.append(enc[5] + 1 + 1) # 5
flag.append(( (enc[6] + 32) ^ 9 )) # 1
flag.append(( (enc[7] ^ 36) - 81 )) # 2
# 后7位字符逆向操作(关键修正部分)
flag.append(enc[8]) # 1
flag.append((enc[9] - 37) // 2) # f
flag.append((enc[10] ^ 65) - 54) # 3
flag.append(enc[11] - 32) # d
flag.append((enc[12] - 37) // 3) # 4
flag.append((enc[13] + 32) ^ 9) # 7
flag.append(enc[14] - 1 - 65) # 8
# 处理字节溢出(转换为无符号字节)
flag = [chr(c & 0xff) for c in flag]
# 转换为字符串
flag_str = ''.join(flag)
print("Flag:", flag_str)
#Flag: 757515121f3d478
Angr
利用Ponce插件
羊城杯2023vm_wo
参考了https://www.nssctf.cn/note/set/3669

这题是mac的,不同IDA版本识别可能会出问题。我这是区段识别错误

解决方法很简单,修改区段大小,我这是改成了8130

成功修复。不知道为什么参考改成了8208,我这改成8130的原因纯粹是8114修改后有点问题,于是就改大了点。
接下来进行主要逻辑分析

myoperate函数分析
首先关于vm_body的理解


xor表:xorData = [0xEF, 0xBE, 0xED, 0xBE](注意小端序是反的)

接下来重点分析interpretBytecode

之后翻译(这里直接用参考的翻译了)
#include<stdio.h>
#include<stdint.h>
uint8_t target[29] = {
0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89, 0xBD,
0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3,
0x85, 0x99, 0x87, 0xBF, 0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89,
0x89, 0x8F, 0xAD
};
uint64_t opcode[2]{};
uint32_t IP = 0;
uint8_t VM_REG[8] = { 0,0,0,0xEF, 0xBE,0xED,0xBE ,0};
void Disasm(){
IP = 0;
while (true)
{
if (IP >= 15)
{
break;
}
uint8_t* code = (uint8_t*)opcode + IP;
uint32_t arg1 = *(code + 1);
uint32_t arg2 = *(code + 1);
uint32_t arg3 = *(code + 2);
uint32_t arg4 = *(code + 2);
uint32_t temp{};
switch (*code)
{
case 0:
temp = VM_REG[arg2];
VM_REG[arg2] = VM_REG[arg4];
VM_REG[arg4] = temp;
printf("xchg VM_REG[%d] : %d VM_REG[%d] : %d\n",
arg2, VM_REG[arg2], arg4, VM_REG[arg4]);
break;
case 1:
VM_REG[arg2] ^= VM_REG[arg3];
printf("xor VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, VM_REG[arg3]);
//printf("VM_REG[arg2] = 0x%X\n", VM_REG[arg2]);
break;
case 2:
VM_REG[arg2] += arg3;
printf("add VM_REG[%d] %d\n", arg2, arg3);
break;
case 3:
VM_REG[arg2] += VM_REG[arg3];
printf("add VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, VM_REG[arg3]);
break;
case 4:
VM_REG[arg2] -= arg3;
printf("sub VM_REG[%d] %d\n", arg2, arg3);
break;
case 5:
VM_REG[arg2] -= VM_REG[arg3];
printf("sub VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, VM_REG[arg3]);
break;
case 6:
VM_REG[arg2] *= (uint8_t)arg3;
printf("mul VM_REG[%d] %d\n", arg2,
((uint8_t)arg3));
break;
case 7:
VM_REG[arg2] *= VM_REG[arg3];
printf("mul VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, VM_REG[arg3]);
break;
case 8:
VM_REG[arg2] = (uint8_t)VM_REG[arg2] / arg3;
printf("div VM_REG[%d] %d\n", arg2, arg3);
break;
case 9:
VM_REG[arg2] = (uint8_t)VM_REG[arg2] /
(uint8_t)VM_REG[arg3];
printf("div VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, (uint8_t)VM_REG[arg3]);
break;
case 10:
VM_REG[arg2] = (uint8_t)VM_REG[arg2] % arg3;
printf("mod VM_REG[%d] %d\n", arg2, arg3);
break;
case 11:
VM_REG[arg2] = (uint8_t)VM_REG[arg2] %
(uint8_t)VM_REG[arg3];
printf("mod VM_REG[%d] VM_REG[%d] : %d\n", arg2,
arg3, (uint8_t)VM_REG[arg3]);
break;
case 12:
VM_REG[arg2] = (uint8_t)VM_REG[arg2] << arg3;
printf("shl VM_REG[%d] %d\n", arg2, arg3);
break;
case 13:
VM_REG[arg2] = (uint8_t)VM_REG[0] << arg3;
printf("shl VM_REG[0] %d\n",arg3);
printf("mov VM_REG[%d] VM_REG[0]\n", arg2);
break;
case 14:
printf("push VM_REG[%d] : %d\n", arg2,
VM_REG[arg2]);
break;
case 15:
break;
case 16:
break;
case 17:
if (!VM_REG[arg2]) {
IP = arg3;
printf("jz %d\n", arg3);
}
break;
case 18:
if (VM_REG[arg2]) {
IP = arg3;
printf("jnz %d\n", arg3);
}
break;
case 19:
IP = arg1;
printf("jmp %d\n", arg1);
break;
case 20:
arg2 = (uint8_t)VM_REG[arg2];
printf("mov reg2 VM_REG[%d] : %d\n", arg2,
VM_REG[arg2]);
printf("push VM_REG[%d] : %d\n", arg2,
VM_REG[arg2]);
break;
case 21:
printf("pop VM_REG[0]\n");
break;
case 23:
printf("Check\n");
break;
case 24:
VM_REG[0] = *((uint8_t*)VM_REG + 2) | *
((uint8_t*)VM_REG + 1);
printf("or VM_REG[1] VM_REG[2] : %d\n", VM_REG[2]);
printf("mov VM_REG[0] VM_REG[1]\n");
break;
case 25:
VM_REG[arg2] = (uint8_t)VM_REG[0] >> arg3;
printf("shr VM_REG[0] %d\n", arg3);
printf("mov VM_REG[%d] VM_REG[0]\n", arg2);
break;
case 26:
VM_REG[arg2] = arg3;
printf("mov VM_REG[%d] : %d\n", arg2, arg3);
break;
}
IP += 3;
}
return;
}
int main()
{
opcode[0] = 0x20D01011903001ALL;
*(uint64_t*)((uint8_t*)opcode + 7) = 0x300010201180702LL;
*((uint8_t*)opcode + 2) = 'a'; //这里的 f 是假设我输入的第一个字符是 f
Disasm();
printf("---------------------------\n");
opcode[0] = 0x20D02011903001ALL;
*(uint64_t*)((uint8_t*)opcode + 7) = 0x400010201180602LL;
*((uint8_t*)opcode + 2) = VM_REG[0];
Disasm();
printf("---------------------------\n");
opcode[0] = 0x20D03011903001ALL;
*(uint64_t*)((uint8_t*)opcode + 7) = 0x500010201180502LL;
*((uint8_t*)opcode + 2) = VM_REG[0];
Disasm();
printf("---------------------------\n");
opcode[0] = 0x20D04011903001ALL;
*(uint64_t*)((uint8_t*)opcode + 7) = 0x600010201180402LL;
*((uint8_t*)opcode + 2) = VM_REG[0];
Disasm();
return 0;
}
//结果
mov VM_REG[0] : 97 这里会随着输入而改变
shr VM_REG[0] 1
mov VM_REG[1] VM_REG[0]
shl VM_REG[0] 7
mov VM_REG[2] VM_REG[0]
or VM_REG[1] VM_REG[2] : 128
mov VM_REG[0] VM_REG[1]
xor VM_REG[0] VM_REG[3] : 239
---------------------------
mov VM_REG[0] : 95
shr VM_REG[0] 2
mov VM_REG[1] VM_REG[0]
shl VM_REG[0] 6
mov VM_REG[2] VM_REG[0]
or VM_REG[1] VM_REG[2] : 192
mov VM_REG[0] VM_REG[1]
xor VM_REG[0] VM_REG[4] : 190
---------------------------
mov VM_REG[0] : 105
shr VM_REG[0] 3
mov VM_REG[1] VM_REG[0]
shl VM_REG[0] 5
mov VM_REG[2] VM_REG[0]
or VM_REG[1] VM_REG[2] : 32
mov VM_REG[0] VM_REG[1]
xor VM_REG[0] VM_REG[5] : 237
---------------------------
mov VM_REG[0] : 192
shr VM_REG[0] 4
mov VM_REG[1] VM_REG[0]
shl VM_REG[0] 4
mov VM_REG[2] VM_REG[0]
or VM_REG[1] VM_REG[2] : 0
mov VM_REG[0] VM_REG[1]
xor VM_REG[0] VM_REG[6] : 190
//所以加密逻辑为
def operate(ch: int, i: int):
return ((ch >> i | ch << (8 - i)) & 0xFF) ^ xorData[i - 1] //&0xFF确保结果始终为单字节值(0~255)
解密爆破脚本
xorData = [0xEF, 0xBE, 0xED, 0xBE]
target = [0xDF, 0xD5, 0xF1, 0xD1, 0xFF, 0xDB, 0xA1, 0xA5, 0x89,
0xBD, 0xE9, 0x95, 0xB3, 0x9D, 0xE9, 0xB3, 0x85, 0x99,
0x87, 0xBF, 0xE9, 0xB1, 0x89, 0xE9, 0x91, 0x89, 0x89,
0x8F, 0xAD]
def operate(ch: int, i: int):
return ((ch >> i | ch << (8 - i)) & 0xFF) ^ xorData[i - 1]
def virtualMachine():
for i in range(29):
for j in range(30, 127):
val = operate(j, 1)
val = operate(val, 2)
val = operate(val, 3)
val = operate(val, 4)
if(val>>5 | val<<3) & 0xFF == target[i]:
print(chr(j), end="")
break
if __name__ == '__main__':
virtualMachine()
[HGAME2023week4]vm
初步分析
关于VM逆向主要几点
- 创建好的结构体,便于之后分析
- 对于函数的翻译尽量简单、直接
以这题为例

其中_ip是好判断的,主要是 _sp、 _zf需要在之后的函数内部判断。
所以这题先创建结构体

分析函数作用

编写脚本
#include <stdio.h>
int main(){
int r[6] = {0};
unsigned char opcode[] = {
0, 3, 2, 0, 3, 0, 2, 3, 0, 0,
0, 0, 0, 2, 1, 0, 0, 3, 2, 50,
3, 0, 2, 3, 0, 0, 0, 0, 3, 0,
1, 0, 0, 3, 2, 100, 3, 0, 2, 3,
0, 0, 0, 0, 3, 3, 1, 0, 0, 3,
0, 8, 0, 2, 2, 1, 3, 4, 1, 0,
3, 5, 2, 0, 3, 0, 1, 2, 0, 2,
0, 1, 1, 0, 0, 3, 0, 1, 3, 0,
3, 0, 0, 2, 0, 3, 0, 3, 1, 40,
4, 6, 95, 5, 0, 0, 3, 3, 0, 2,
1, 0, 3, 2, 150, 3, 0, 2, 3, 0,
0, 0, 0, 4, 7, 136, 0, 3, 0, 1,
3, 0, 3, 0, 0, 2, 0, 3, 0, 3,
1, 40, 4, 7, 99, 255, 255
};
for(int i = 0; i < sizeof(opcode);){
if(opcode[i] == 0){
printf("%d :", i);
int v2 = opcode[i + 1];
if ( v2 )
{
switch ( v2 )
{
case 1u:
printf("flag[r[2]] = r[0]\n");
break;
case 2u:
printf("r[%d] = r[%d]\n", opcode[i + 2], opcode[i + 3]);
break;
case 3u:
printf("r[%d] = %d\n", opcode[i + 2], opcode[i +3]);
break;
}
}
else
{
printf("r[0] = flag[r[2]]\n");
}
i += 4;
}else if(opcode[i] == 1){
printf("%d :", i);
int v2 = opcode[i + 1];
if(v2){
switch (v2){
case 1:
printf("stack[++sp] = r[0]\n");
break;
case 2:
printf("stack[++sp] = r[2]\n");
break;
case 3:
printf("stack[++sp] = r[3]\n");
break;
}
}else{
printf("stack[++sp] = r[0]\n");
}
i += 2;
}else if(opcode[i] == 2){
printf("%d :", i);
int v2 = opcode[i + 1];
if ( v2 )
{
switch ( v2 )
{
case 1u:
printf("r[1] = stack[sp--]\n");
break;
case 2u:
printf("r[2] = stack[sp--]\n");
break;
case 3u:
printf("r[3] = stack[sp--]\n");
break;
}
}else{
printf("r[0] = stack[sp--]\n");
}
i += 2;
}else if(opcode[i] == 3){
printf("%d :", i);
switch ( opcode[i + 1] )
{
case 0:
printf("r[%d] += r[%d]\n", opcode[i + 2], opcode[i + 3]);
break;
case 1:
printf("r[%d] -= r[%d]\n", opcode[i + 2], opcode[i + 3]);
break;
case 2:
printf("r[%d] *= r[%d]\n", opcode[i + 2], opcode[i + 3]);
break;
case 3:
printf("r[%d] ^= r[%d]\n", opcode[i + 2], opcode[i + 3]);
break;
case 4:
printf("r[%d] <<= r[%d]\n", opcode[i + 2], opcode[i + 3]);
printf("r[%d] &= 0xFF00u\n", opcode[i +2]);
break;
case 5:
printf("r[%d] = r[%d] >> r[%d]\n", opcode[i + 2], opcode[i + 2], opcode[i + 3]);
break;
default:
break;
}
i += 4;
}else if(opcode[i] == 4){
printf("%d :", i);
printf("cmp r[0], r[1]\n");
i += 1;
}else if(opcode[i] == 5){
printf("%d :", i);
printf("jmp %d\n", opcode[i + 1]);
i += 2;
}else if(opcode[i] == 6){
printf("%d :", i);
printf("jz %d\n", opcode[i + 1]);
i += 2;
}else if(opcode[i] == 7){
printf("%d :", i);
printf("jnz %d\n", opcode[i + 1]);
i += 2;
}else{
printf("%d :", i);
printf("Wrog\n");
break;
}
}
}
得到结果,经过分析
0 :r[2] = 0
4 :r[2] += r[3]
8 :r[0] = flag[r[2]]
//r[0] = input[0]
12 :r[1] = r[0]
16 :r[2] = 50
20 :r[2] += r[3]
24 :r[0] = flag[r[2]]
//r[0] = flag[50]
28 :r[1] += r[0]
//input[0] += flag[50]
//
input[] += flag[50]
//
32 :r[2] = 100
36 :r[2] += r[3]
40 :r[0] = flag[r[2]]
//r[0] = = flag[100]
44 :r[1] ^= r[0]
//input[0] ^= flag[100]
//
input[] ^= flag[100]
//
48 :r[0] = 8
52 :r[2] = r[1]
56 :r[1] <<= r[0]
r[1] &= 0xFF00u
//input[0] <<= 8
//input[0] &= 0xFF00u
60 :r[2] = r[2] >> r[0]
64 :r[1] += r[2]
//input[0] >>= 8
//input拼接
//
取高8位、低8位互换
//
68 :r[0] = r[1]
72 :stack[++sp] = r[0]
//push input[]
74 :r[0] = 1
78 :r[3] += r[0]
82 :r[0] = r[3]
86 :r[1] = 40
90 :cmp r[0], r[1]
//input[] i ?= 40
91 :jz 95
93 :jmp 0
//循环处理 ipput[]
95 :r[3] = 0
99 :r[1] = stack[sp--]
//pop input[]
101 :r[2] = 150
105 :r[2] += r[3]
109 :r[0] = flag[r[2]]
//r[0] = flag[150]
113 :cmp r[0], r[1]
//cmp flag[150] input[]
114 :jnz 136
116 :r[0] = 1
120 :r[3] += r[0]
124 :r[0] = r[3]
128 :r[1] = 40
132 :cmp r[0], r[1]
133 :jnz 99
//循环处理
135 :Wrog
功能实际是一个循环,其中循环体内对输入的flag与内存中flag数组后面偏移50的数据进行加法,后与偏移为100处的数据异或,然后执行位移操作(实际上是高8位与低8位互换),最后于偏移为150位置的数据进行比较。
解密脚本
#include <stdio.h>
#include <stdint.h>
int main() {
uint16_t flag[40] = {0x0048, 0x00F1, 0x0040, 0x0021, 0x0135, 0x0064, 0x0178, 0x00F9, 0x0118, 0x0052,
0x0025, 0x015D, 0x0047, 0x00FD, 0x0169, 0x005C, 0x01AF, 0x00B2, 0x01EC, 0x0152,
0x014F, 0x011A, 0x0050, 0x0185, 0x00CD, 0x0023, 0x00F8, 0x000C, 0x00CF, 0x013D,
0x0145, 0x0082, 0x01D2, 0x0129, 0x01D5, 0x0106, 0x01A2, 0x00DE, 0x01A6, 0x01CA};
uint16_t sub[40] = {155, 168, 2, 188, 172, 156, 206, 250, 2, 185, 255, 58, 116, 72, 25, 105, 232, 3, 203, 201,
255, 252, 128, 214, 141, 215, 114, 0, 167, 29, 61, 153, 136, 153, 191, 232, 150, 46, 93, 87};
uint16_t xor_arry[40] = {201, 169, 189, 139, 23, 194, 110, 248, 245, 110, 99, 99, 213, 70, 93, 22, 152, 56, 48, 115,
56, 193, 94, 237, 176, 41, 90, 24, 64, 167, 253, 10, 30, 120, 139, 98, 219, 15, 143, 156};
// 反转 flag 数组。为什么需要反转?因为压栈后逆序输出的。
for (int i = 0; i < 20; ++i) {
uint16_t temp = flag[i];
flag[i] = flag[39 - i];
flag[39 - i] = temp;
}
// 对 flag 数组进行异或和减法操作
for (int i = 0; i < 40; ++i) {
flag[i] ^= xor_arry[i]; // 异或操作
flag[i] -= sub[i]; // 减法操作
}
// 打印 flag 数组的内容
printf("flag 数组的内容:\n");
for (int i = 0; i < 40; ++i) {
printf("%d ", flag[i]); // 以十进制形式打印
}
printf("\n");
// 如果 flag[i] 是字符,可以按字符形式打印
printf("flag 数组的字符形式:\n");
for (int i = 0; i < 40; ++i) {
printf("%c", (char)flag[i]); // 强制转换为 char 并打印
}
printf("\n");
return 0;
}
得flag为
hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}
极客大挑战2024 baby_vm
vm逆向,先创建个结构体

_ip是很容易看出来的, _sp一开始没看出来,但是没关系可以先用个R占位,同样r中的r[4]其实是zf标志位,这里没有改了。
之后看执行部分

写个翻译脚本
#include <stdio.h>
int main(){
int r[8] = {};
int _sp = 0;
int _ip = 0;
unsigned int opcodelist[60] = {
0x00000046, 0x0000003F, 0x00000000, 0x0000003C, 0x0000003F, 0x00000019, 0x0000003D, 0x0000003A,
0x0000003E, 0x0000003F, 0x00000053, 0x00000033, 0x00000038, 0x0000003F, 0x00000059, 0x0000003E,
0x00000039, 0x00000034, 0x0000003E, 0x0000003F, 0x00000043, 0x00000037, 0x00000038, 0x00000040,
0x00000042, 0x0000003C, 0x0000003F, 0x00000063, 0x0000003E, 0x0000003A, 0x00000037, 0x0000003E,
0x0000003F, 0x00000079, 0x00000033, 0x00000038, 0x0000003F, 0x00000073, 0x0000003E, 0x00000039,
0x00000034, 0x00000038, 0x00000040, 0x00000042, 0x0000003C, 0x00000043, 0x00000026, 0x0000003F,
0x00000031, 0x0000003C, 0x0000003F, 0x00000032, 0x0000003D, 0x00000041, 0x0000003E, 0x00000039,
0x00000044, 0x00000043, 0x00000004, 0x000000FF
};
int opcode = {};
while ( 2 ){
opcode = opcodelist[_ip];
if ( opcode <= 70 ){
if ( opcode >= 51 ){
switch ( opcode ){
case '3':
printf("%d : ", _ip);
printf("r[0] += r[3]\n");
_ip++;
continue;
case '4':
printf("%d : ", _ip);
printf("r[0] -= r[3]\n");
_ip++;
continue;
case '5':
printf("%d : ", _ip);
printf("r[0] *= r[3]\n");
_ip++;
continue;
case '6':
printf("%d : ", _ip);
printf("r[0] /= r[3]\n");
_ip++;
continue;
case '7':
printf("%d : ", _ip);
printf("r[0] = ~(r[0] & r[3]) & ~(~r[3] & ~r[0])\n");
_ip++;
continue;
case '8':
printf("%d : ", _ip);
printf("v2 = r[0]\n");
printf("_sp += 1\n");
printf("stack[_sp] = v2\n");
_ip++;
continue;
case '9':
printf("%d : ", _ip);
printf("r[0] = stack[--_sp]\n");
_ip++;
continue;
case ':':
printf("%d : ", _ip);
printf("r[0] = Str[r[1]]\n");
_ip++;
continue;
case ';':
printf("%d : ", _ip);
printf("r[0] = r[3]\n");
_ip++;
continue;
case '<':
printf("%d : ", _ip);
printf("r[1] = r[0]\n");
_ip++;
continue;
case '=':
printf("%d : ", _ip);
printf("r[2] = r[0]\n");
_ip++;
continue;
case '>':
printf("%d : ", _ip);
printf("r[3] = r[0]\n");
_ip++;
continue;
case '?':
printf("%d : ", _ip);
printf("r[0] = %d\n", opcodelist[_ip + 1]);
_ip += 2;
continue;
case '@':
printf("%d : ", _ip);
printf("r[0] = r[1]\n");
_ip++;
continue;
case 'A':
printf("%d : ", _ip);
printf("r[0] = enc[r[1]]\n");
_ip++;
continue;
case 'B':
printf("%d : ", _ip);
printf("r[0] += 1\n");
_ip++;
continue;
case 'C':
printf("%d : ", _ip);
printf("jz? %d\n", opcodelist[_ip + 1]); //有点疑问
_ip +=2;
continue;
case 'D':
printf("%d : ", _ip);
printf("if r[0] != r[3] : r[4]=1 r[1]--\n");
_ip++;
continue;
case 'E':
printf("%d : ", _ip);
printf("r[0] = %d\n", opcodelist[_ip +1]);
_ip += 2;
continue;
case 'F':
printf("%d : ", _ip);
printf("Stream = (FILE *)psub_140005A90(0i64);\n");
printf("fgets(Str, 51, Stream);\n");
printf("Str[strcspn(Str, /n)] = 0;\n");
_ip++;
continue;
default:
return printf("Unknown opcode: %d\n", (unsigned int)opcode);
}
}
return printf("Unknown opcode: %d\n", (unsigned int)opcode);
}
break;
}
}
结果分析
0 : Stream = (FILE *)psub_140005A90(0i64);
fgets(Str, 51, Stream);
Str[strcspn(Str, /n)] = 0;
//
读取Str[]
//
1 : r[0] = 0
3 : r[1] = r[0]
4 : r[0] = 25
6 : r[2] = r[0]
//
r[1] = 0 r[2] = 25
//
7 : r[0] = Str[r[1]]
8 : r[3] = r[0]
9 : r[0] = 83
11 : r[0] += r[3]
12 : v2 = r[0]
_sp += 1
stack[_sp] = v2
13 : r[0] = 89
15 : r[3] = r[0]
16 : r[0] = stack[--_sp]
17 : r[0] -= r[3]
//
push Str[0] + 83 -89
//
18 : r[3] = r[0]
19 : r[0] = 67
21 : r[0] = ~(67 & r[3]) & ~(~r[3] & ~67) //等价于异或操作
22 : v2 = r[0]
_sp += 1
stack[_sp] = v2
//
push (Str[0] +83 - 89) ^ 67 #83=S 89=Y 67=C
//
23 : r[0] = r[1]
24 : r[0] += 1
25 : r[1] = r[0]
// r[1] = 1
26 : r[0] = 99
28 : r[3] = r[0]
29 : r[0] = Str[r[1]]
30 : r[0] = ~(r[0] & r[3]) & ~(~r[3] & ~r[0])
//
Str[1] ^ 99
//
31 : r[3] = r[0]
32 : r[0] = 121
34 : r[0] += r[3]
35 : v2 = r[0]
_sp += 1
stack[_sp] = v2
//
push (Str[1] ^ 99) + 121 #99=c 121=y 115=s
//
36 : r[0] = 115
38 : r[3] = r[0]
39 : r[0] = stack[--_sp]
40 : r[0] -= r[3]
41 : v2 = r[0]
_sp += 1
stack[_sp] = v2
//
push (Str[1] ^ 99) + 121 - 115
//
42 : r[0] = r[1]
43 : r[0] += 1
44 : r[1] = r[0]
// r[1]=2 处理下一位
45 : jz? 38 //?
47 : r[0] = 49
49 : r[1] = r[0]
50 : r[0] = 50
52 : r[2] = r[0]
53 : r[0] = enc[r[1]]
54 : r[3] = r[0]
55 : r[0] = stack[--_sp]
56 : if r[0] != r[3] : r[4]=1 r[1]--
//
cmp enc[49],Str[50] 其实就是比较
//
57 : jz? 4
有些地方逻辑不是很清晰,但是不影响解题。
加密为
奇数位:enc[1] = (Str[1] ^ 99) + 121 - 115
偶数位:enc[0] = (Str[0] +83 - 89) ^ 67
加密逻辑简单,写个解密脚本
#include <stdio.h>
int main() {
unsigned char enc[64] = {
0x0E, 0x40, 0x7E, 0x1E, 0x13, 0x34, 0x1A, 0x17, 0x6E, 0x1B, 0x1C, 0x17, 0x2E, 0x0C, 0x1A, 0x30,
0x69, 0x32, 0x26, 0x16, 0x1A, 0x15, 0x25, 0x0E, 0x1C, 0x42, 0x30, 0x32, 0x0B, 0x42, 0x79, 0x17,
0x6E, 0x42, 0x29, 0x17, 0x6E, 0x5A, 0x2D, 0x20, 0x1A, 0x16, 0x26, 0x10, 0x05, 0x15, 0x6E, 0x0D,
0x58, 0x24
};
char flag[51] = {0};
for(int i = 0; i < 25; ++i) {
flag[2*i] = (char)((enc[2*i] ^ 67) + 89 - 83);
flag[2*i + 1] = (char)((enc[2*i + 1] + 115 - 121) ^ 99);
}
flag[50] = '\0';
printf("%s", flag);
}
//SYC{VM_r3verse_I0Oks_llke_yON_@r3_pr37ty_skiLl3d!}
Comments NOTHING