Wp

An-ping 发布于 2025-03-30 101 次阅读


网鼎2020signal

参考https://blog.csdn.net/OrientalGlass/article/details/132822924

纯手撕(太麻烦了,还容易错)基础vm逆向,没啥好说的image-20250326182743862image-20250326182826208逻辑简单,翻译下 

#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

image-20250326095232479

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

image-20250326095606972

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

image-20250326100023244

成功修复。不知道为什么参考改成了8208,我这改成8130的原因纯粹是8114修改后有点问题,于是就改大了点。

接下来进行主要逻辑分析

image-20250326100753177

myoperate函数分析

首先关于vm_body的理解

image-20250326101205759
image-20250326101256998

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

image-20250326102151891

接下来重点分析interpretBytecode

image-20250326103715694

之后翻译(这里直接用参考的翻译了)

 #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逆向主要几点

  1. 创建好的结构体,便于之后分析
  2. 对于函数的翻译尽量简单、直接

以这题为例

image-20250322142755354

其中_ip是好判断的,主要是 _sp、 _zf需要在之后的函数内部判断。

所以这题先创建结构体

分析函数作用

image-20250322143532642

编写脚本

 #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逆向,先创建个结构体

image-20250323092826297

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

之后看执行部分

image-20250323093447560

写个翻译脚本

 #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!}