看到 vm 心里就有种不详的预感,感觉这种题目只有 ai 能做出来

附件概览

解压一下看到两个文件

估计 vm-encryptor.exe 会加载 code.bin,用自定义的指令集去解释执行里面的二进制字节码

用 010 editor 看一下 code.bin,在末尾(可能类似于 data 段)发现了一些可读的字符串

有明显的成功或失败的提示,还有 base64 的字符表,可以猜测用到了 base64 编码

逆向分析

vm-encryptor.exe

用 ida 打开 vm-encryptor.exe,通过字符串搜索定位到关键函数 sub_140001000()

// positive sp value has been detected, the output may be wrong!
__int64 __fastcall sub_140001000(int a1, __int64 a2)
{
  FILE *v4; // rax
  const char *v5; // rcx
  size_t v6; // rdx
  __int64 v7; // rsi
  FILE *v8; // rax
  size_t v9; // rax
  __int64 v10; // r8
  bool v11; // zf
  __int64 result; // rax
  FILE *v13; // rax
  FILE *v14; // rax
  _BYTE v15[4108]; // [rsp+2Ch] [rbp-54h] BYREF
  _OWORD v16[4864]; // [rsp+1038h] [rbp+FB8h] BYREF
  int v17; // [rsp+1403Ch] [rbp+13FBCh]
  char Buffer[136]; // [rsp+14040h] [rbp+13FC0h] BYREF
  _QWORD v19[3]; // [rsp+140C8h] [rbp+14048h] BYREF

  sub_140002420();
  v19[0] = 0;
  if ( a1 <= 1 )
  {
    v4 = __acrt_iob_func(2u);
    v5 = "usage: vm-encryptor.exe <program.bin>\n";
    v6 = 38;
LABEL_3:
    fwrite(v5, v6, 1u, v4);
    return 1;
  }
  v7 = *(_QWORD *)(a2 + 8);
  ((void (__fastcall *)(_BYTE *))sub_140001190)(v15);
  if ( !(unsigned int)((__int64 (__fastcall *)(_BYTE *, __int64, _QWORD *))sub_1400011B0)(v15, v7, v19) )
  {
    v14 = __acrt_iob_func(2u);
    sub_140002C70(v14, "failed to load %s\n");
    return 1;
  }
  sub_140002CC0("Input Flag: ");
  v8 = __acrt_iob_func(0);
  if ( !fgets(Buffer, 128, v8) )
  {
    v17 = 6;
    v4 = __acrt_iob_func(2u);
    v5 = "failed to read input\n";
    v6 = 21;
    goto LABEL_3;
  }
  v9 = strcspn(Buffer, "\r\n");
  v10 = 42;
  if ( v9 < 0x2A )
    v10 = v9;
  memset(v16, 0, 43);
  ((void (__fastcall *)(_OWORD *, char *, __int64))sub_1400215B0)(v16, Buffer, v10);
  v11 = (unsigned int)((__int64 (__fastcall *)(_BYTE *))sub_140001270)(v15) == 0;
  result = 0;
  if ( v11 )
  {
    v13 = __acrt_iob_func(2u);
    sub_140002C70(v13, "vm error: %d\n");
    return 1;
  }
  return result;
}

首先看到了这个程序的用法是 vm-encryptor.exe <program.bin>,来尝试一下

看来之前的猜测是对的,vm-encryptor.exe 就是一个解释器,真正的校验逻辑还是在 code.bin 中

不过要读懂 code.bin,前提是要明确解释器的机制,所以我们继续来看伪代码

一个虚拟机程序运行一般需要三个步骤

步骤解释对应函数
vm_init虚拟机初始化,为虚拟机运行开辟一块内存sub_140001190()
vm_load加载字节码,将字节码加载进刚刚开辟的内存的某个区域中sub_1400011B0()
vm_execute执行字节码,通常由一个 switch-case 分发器实现sub_140001270()

我们需要重点关注的是 vm_execute,也就是 sub_140001270() 函数

__int64 __fastcall sub_140001270(int *a1)
{
  int v2; // r14d
  __int64 v3; // rax
  unsigned int v4; // ecx
  __int64 v5; // rax
  unsigned int v6; // edx
  __int64 v7; // rcx
  BOOL v8; // r8d
  unsigned int v9; // eax
  __int64 v10; // rcx
  unsigned int v11; // edx
  __int64 v12; // r8
  unsigned int v13; // r9d
  unsigned int v14; // ecx
  unsigned int v15; // edx
  unsigned int v16; // eax
  __int64 v17; // rcx
  unsigned int v18; // edx
  __int64 v19; // rax
  int v20; // edx
  unsigned int v21; // ecx
  unsigned int v22; // edx
  unsigned int v23; // edx
  __int64 v24; // rax
  unsigned int v25; // ecx
  __int64 v26; // rdx
  unsigned int v27; // r8d
  unsigned __int64 v28; // rax
  __int64 v29; // rcx
  int v30; // ecx
  unsigned int v31; // eax
  __int64 v32; // rcx
  __int64 v33; // rcx
  unsigned int v34; // eax
  unsigned int v35; // ecx
  unsigned int v36; // edx
  unsigned int v37; // eax
  __int64 v38; // rcx
  unsigned int v39; // edx
  unsigned int v40; // eax
  unsigned int v41; // r8d
  unsigned int v42; // ecx
  __int64 v43; // r8
  unsigned int v44; // r9d
  __int64 v45; // rcx
  int v46; // eax
  unsigned int v47; // eax
  __int64 v48; // rcx
  unsigned __int64 v49; // rdx
  int v50; // edx
  unsigned __int64 v51; // rcx
  unsigned int v52; // edx
  unsigned int v53; // eax
  __int64 v54; // rax
  int v55; // ecx
  unsigned int v56; // eax
  __int64 v57; // rcx
  __int64 v58; // rcx
  unsigned int v59; // eax
  unsigned __int64 v60; // rdx
  unsigned int v61; // eax
  unsigned int v62; // edx
  unsigned int v63; // eax
  unsigned int v64; // eax
  unsigned int v65; // edx
  unsigned int v66; // edx
  unsigned int v67; // ecx
  unsigned int v68; // eax
  unsigned int v69; // edx
  unsigned int v70; // ecx
  __int64 v71; // r8
  unsigned int v72; // r9d
  unsigned int v73; // ecx
  unsigned int v74; // edx
  unsigned int v75; // eax
  unsigned int v76; // edx
  int v77; // ecx
  unsigned int v78; // ecx
  unsigned int v79; // edx
  unsigned int v80; // eax
  __int64 v81; // rax
  unsigned __int64 v82; // r15
  int v83; // ecx
  unsigned int v84; // eax
  __int64 v85; // rax
  unsigned int v86; // eax
  __int64 v87; // rax
  unsigned __int64 v88; // rdx

  a1[20483] = 1;
  while ( 2 )
  {
    v2 = 1;
    v3 = (unsigned int)*a1;
    switch ( *((_BYTE *)a1 + v3 + 12) )
    {
      case 0:
        if ( (unsigned int)(v3 + 1) < 0xFFFD )
        {
          *a1 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 2) + 12) << 8)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 3) + 12) << 16)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 4) + 12) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          *a1 = 0;
        }
        goto LABEL_145;
      case 1:
        if ( (unsigned int)(v3 + 1) < 0xFFFD )
        {
          v55 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 2) + 12) << 8)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 3) + 12) << 16)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 4) + 12) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          v55 = 0;
        }
        *a1 = v3 + 5;
        v86 = a1[1];
        if ( !v86 )
          goto LABEL_149;
        v87 = v86 - 1;
        a1[1] = v87;
        if ( !a1[v87 + 16387] )
          goto LABEL_128;
        goto LABEL_145;
      case 2:
        if ( (unsigned int)(v3 + 1) < 0xFFFD )
        {
          v55 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 2) + 12) << 8)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 3) + 12) << 16)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 4) + 12) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          v55 = 0;
        }
        *a1 = v3 + 5;
        v84 = a1[1];
        if ( !v84 )
          goto LABEL_149;
        v85 = v84 - 1;
        a1[1] = v85;
        if ( !a1[v85 + 16387] )
          goto LABEL_145;
LABEL_128:
        *a1 = v55;
        goto LABEL_145;
      case 3:
        v51 = (unsigned int)a1[1];
        if ( v51 > 0xFFF )
          goto LABEL_148;
        v52 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12);
        a1[1] = v51 + 1;
        a1[v51 + 16387] = v52;
        *a1 = v3 + 2;
        goto LABEL_145;
      case 4:
        if ( (unsigned int)(v3 + 1) < 0xFFFD )
        {
          v30 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 2) + 12) << 8)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 3) + 12) << 16)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 4) + 12) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          v30 = 0;
        }
        *a1 = v3 + 5;
        v28 = (unsigned int)a1[1];
        if ( v28 <= 0xFFF )
          goto LABEL_132;
        goto LABEL_148;
      case 5:
        *a1 = v3 + 1;
        v47 = a1[1];
        if ( !v47 )
          goto LABEL_149;
        v48 = v47 - 1;
        a1[1] = v48;
        v60 = (unsigned int)a1[v48 + 16387];
        if ( v60 > 0xFFFF )
          goto LABEL_150;
        if ( (unsigned int)v48 > 0xFFF )
          goto LABEL_148;
        v50 = *((unsigned __int8 *)a1 + v60 + 12);
        goto LABEL_138;
      case 6:
        *a1 = v3 + 1;
        v47 = a1[1];
        if ( !v47 )
          goto LABEL_149;
        v48 = v47 - 1;
        a1[1] = v48;
        v49 = (unsigned int)a1[v48 + 16387];
        if ( v49 < 0xFFFD )
        {
          v50 = *(unsigned __int16 *)((char *)a1 + v49 + 12)
              | (*((unsigned __int8 *)a1 + v49 + 14) << 16)
              | (*((unsigned __int8 *)a1 + v49 + 15) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          v50 = 0;
        }
        if ( (unsigned int)v48 <= 0xFFF )
          goto LABEL_138;
        goto LABEL_148;
      case 7:
        *a1 = v3 + 1;
        v63 = a1[1];
        if ( !v63 )
          goto LABEL_149;
        a1[1] = v63 - 1;
        goto LABEL_145;
      case 8:
        *a1 = v3 + 1;
        v31 = a1[1];
        v2 = 3;
        if ( !v31 )
          goto LABEL_151;
        v32 = v31 - 1;
        a1[1] = v32;
        if ( v31 == 1 )
          goto LABEL_151;
        v33 = (unsigned int)a1[v32 + 16387];
        v34 = v31 - 2;
        a1[1] = v34;
        if ( (unsigned int)v33 > 0xFFFF )
          goto LABEL_150;
        *((_BYTE *)a1 + v33 + 12) = a1[v34 + 16387];
        goto LABEL_145;
      case 9:
        *a1 = v3 + 1;
        v56 = a1[1];
        v2 = 3;
        if ( !v56 )
          goto LABEL_151;
        v57 = v56 - 1;
        a1[1] = v57;
        if ( v56 == 1 )
          goto LABEL_151;
        v58 = (unsigned int)a1[v57 + 16387];
        v59 = v56 - 2;
        a1[1] = v59;
        if ( (unsigned int)v58 <= 0xFFFC )
        {
          *(int *)((char *)a1 + v58 + 12) = a1[v59 + 16387];
LABEL_145:
          if ( a1[20484] )
            return 0;
          if ( !a1[20483] )
            return 1;
          continue;
        }
LABEL_150:
        v2 = 4;
LABEL_151:
        a1[20484] = v2;
        a1[20483] = 0;
        return 0;
      case 0xA:
        *a1 = v3 + 1;
        v68 = a1[1];
        v2 = 3;
        if ( !v68 )
          goto LABEL_151;
        v17 = v68 - 1;
        a1[1] = v17;
        if ( v68 == 1 )
          goto LABEL_151;
        v69 = a1[v17 + 16387];
        LODWORD(v19) = v68 - 2;
        a1[1] = v19;
        if ( (unsigned int)v19 > 0xFFF )
          goto LABEL_148;
        v19 = (unsigned int)v19;
        v20 = a1[(unsigned int)v19 + 16387] + v69;
        goto LABEL_107;
      case 0xB:
        *a1 = v3 + 1;
        v37 = a1[1];
        v2 = 3;
        if ( !v37 )
          goto LABEL_151;
        v38 = v37 - 1;
        a1[1] = v38;
        if ( v37 == 1 )
          goto LABEL_151;
        v39 = a1[v38 + 16387];
        v40 = v37 - 2;
        a1[1] = v40;
        if ( v40 > 0xFFF )
          goto LABEL_148;
        v41 = a1[v40 + 16387] - v39;
        a1[1] = v38;
        a1[v40 + 16387] = v41;
        goto LABEL_145;
      case 0xC:
        *a1 = v3 + 1;
        v61 = a1[1];
        v2 = 3;
        if ( !v61 )
          goto LABEL_151;
        v17 = v61 - 1;
        a1[1] = v17;
        if ( v61 == 1 )
          goto LABEL_151;
        v62 = a1[v17 + 16387];
        LODWORD(v19) = v61 - 2;
        a1[1] = v19;
        if ( (unsigned int)v19 > 0xFFF )
          goto LABEL_148;
        v19 = (unsigned int)v19;
        v20 = a1[(unsigned int)v19 + 16387] * v62;
        goto LABEL_107;
      case 0xD:
        *a1 = v3 + 1;
        v42 = a1[1];
        v2 = 3;
        if ( !v42 )
          goto LABEL_151;
        v43 = v42 - 1;
        a1[1] = v43;
        if ( v42 == 1 )
          goto LABEL_151;
        v44 = a1[v43 + 16387];
        v45 = v42 - 2;
        a1[1] = v45;
        if ( v44 )
        {
          v46 = a1[v45 + 16387] / v44;
          if ( (unsigned int)v45 > 0xFFF )
            goto LABEL_148;
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x500000000LL;
          v46 = 0;
          if ( (unsigned int)v45 > 0xFFF )
            goto LABEL_148;
        }
        a1[1] = v43;
        a1[v45 + 16387] = v46;
        goto LABEL_145;
      case 0xE:
        *a1 = v3 + 1;
        v70 = a1[1];
        v2 = 3;
        if ( !v70 )
          goto LABEL_151;
        v71 = v70 - 1;
        a1[1] = v71;
        if ( v70 == 1 )
          goto LABEL_151;
        v72 = a1[v71 + 16387];
        v48 = v70 - 2;
        a1[1] = v48;
        if ( v72 )
        {
          v50 = a1[v48 + 16387] % v72;
          if ( (unsigned int)v48 > 0xFFF )
            goto LABEL_148;
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x500000000LL;
          v50 = 0;
          if ( (unsigned int)v48 > 0xFFF )
          {
LABEL_148:
            v2 = 2;
            goto LABEL_151;
          }
        }
        a1[1] = v71;
LABEL_144:
        a1[v48 + 16387] = v50;
        goto LABEL_145;
      case 0xF:
        *a1 = v3 + 1;
        v64 = a1[1];
        v2 = 3;
        if ( !v64 )
          goto LABEL_151;
        v17 = v64 - 1;
        a1[1] = v17;
        if ( v64 == 1 )
          goto LABEL_151;
        v65 = a1[v17 + 16387];
        LODWORD(v19) = v64 - 2;
        a1[1] = v19;
        if ( (unsigned int)v19 > 0xFFF )
          goto LABEL_148;
        v19 = (unsigned int)v19;
        v20 = a1[(unsigned int)v19 + 16387] & v65;
        goto LABEL_107;
      case 0x10:
        *a1 = v3 + 1;
        v75 = a1[1];
        v2 = 3;
        if ( !v75 )
          goto LABEL_151;
        v17 = v75 - 1;
        a1[1] = v17;
        if ( v75 == 1 )
          goto LABEL_151;
        v76 = a1[v17 + 16387];
        LODWORD(v19) = v75 - 2;
        a1[1] = v19;
        if ( (unsigned int)v19 > 0xFFF )
          goto LABEL_148;
        v19 = (unsigned int)v19;
        v20 = a1[(unsigned int)v19 + 16387] | v76;
        goto LABEL_107;
      case 0x11:
        *a1 = v3 + 1;
        v47 = a1[1];
        if ( !v47 )
          goto LABEL_149;
        LODWORD(v48) = v47 - 1;
        a1[1] = v47 - 1;
        if ( v47 - 1 > 0xFFF )
          goto LABEL_148;
        v48 = (unsigned int)v48;
        v50 = ~a1[(unsigned int)v48 + 16387];
LABEL_138:
        a1[1] = v47;
        goto LABEL_144;
      case 0x12:
        *a1 = v3 + 1;
        v16 = a1[1];
        v2 = 3;
        if ( !v16 )
          goto LABEL_151;
        v17 = v16 - 1;
        a1[1] = v17;
        if ( v16 == 1 )
          goto LABEL_151;
        v18 = a1[v17 + 16387];
        LODWORD(v19) = v16 - 2;
        a1[1] = v19;
        if ( (unsigned int)v19 > 0xFFF )
          goto LABEL_148;
        v19 = (unsigned int)v19;
        v20 = a1[(unsigned int)v19 + 16387] ^ v18;
LABEL_107:
        a1[1] = v17;
        a1[v19 + 16387] = v20;
        goto LABEL_145;
      case 0x13:
        *a1 = v3 + 1;
        v66 = a1[1];
        v2 = 3;
        if ( !v66 )
          goto LABEL_151;
        v24 = v66 - 1;
        a1[1] = v24;
        if ( v66 == 1 )
          goto LABEL_151;
        v67 = a1[v24 + 16387];
        LODWORD(v26) = v66 - 2;
        a1[1] = v26;
        if ( (unsigned int)v26 > 0xFFF )
          goto LABEL_148;
        v26 = (unsigned int)v26;
        v27 = a1[(unsigned int)v26 + 16387] << v67;
        goto LABEL_83;
      case 0x14:
        *a1 = v3 + 1;
        v23 = a1[1];
        v2 = 3;
        if ( !v23 )
          goto LABEL_151;
        v24 = v23 - 1;
        a1[1] = v24;
        if ( v23 == 1 )
          goto LABEL_151;
        v25 = a1[v24 + 16387];
        LODWORD(v26) = v23 - 2;
        a1[1] = v26;
        if ( (unsigned int)v26 > 0xFFF )
          goto LABEL_148;
        v26 = (unsigned int)v26;
        v27 = (unsigned int)a1[(unsigned int)v26 + 16387] >> v25;
LABEL_83:
        a1[1] = v24;
        a1[v26 + 16387] = v27;
        goto LABEL_145;
      case 0x15:
      case 0x16:
        *a1 = v3 + 1;
        v4 = a1[1];
        v2 = 3;
        if ( !v4 )
          goto LABEL_151;
        v5 = v4 - 1;
        a1[1] = v5;
        if ( v4 == 1 )
          goto LABEL_151;
        v6 = a1[v5 + 16387];
        LODWORD(v7) = v4 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] == v6;
        goto LABEL_114;
      case 0x17:
        *a1 = v3 + 1;
        v73 = a1[1];
        v2 = 3;
        if ( !v73 )
          goto LABEL_151;
        v5 = v73 - 1;
        a1[1] = v5;
        if ( v73 == 1 )
          goto LABEL_151;
        v74 = a1[v5 + 16387];
        LODWORD(v7) = v73 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] != v74;
        goto LABEL_114;
      case 0x18:
        *a1 = v3 + 1;
        v14 = a1[1];
        v2 = 3;
        if ( !v14 )
          goto LABEL_151;
        v5 = v14 - 1;
        a1[1] = v5;
        if ( v14 == 1 )
          goto LABEL_151;
        v15 = a1[v5 + 16387];
        LODWORD(v7) = v14 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] < v15;
        goto LABEL_114;
      case 0x19:
        *a1 = v3 + 1;
        v21 = a1[1];
        v2 = 3;
        if ( !v21 )
          goto LABEL_151;
        v5 = v21 - 1;
        a1[1] = v5;
        if ( v21 == 1 )
          goto LABEL_151;
        v22 = a1[v5 + 16387];
        LODWORD(v7) = v21 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] > v22;
        goto LABEL_114;
      case 0x1A:
        *a1 = v3 + 1;
        v35 = a1[1];
        v2 = 3;
        if ( !v35 )
          goto LABEL_151;
        v5 = v35 - 1;
        a1[1] = v5;
        if ( v35 == 1 )
          goto LABEL_151;
        v36 = a1[v5 + 16387];
        LODWORD(v7) = v35 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] <= v36;
        goto LABEL_114;
      case 0x1B:
        *a1 = v3 + 1;
        v78 = a1[1];
        v2 = 3;
        if ( !v78 )
          goto LABEL_151;
        v5 = v78 - 1;
        a1[1] = v5;
        if ( v78 == 1 )
          goto LABEL_151;
        v79 = a1[v5 + 16387];
        LODWORD(v7) = v78 - 2;
        a1[1] = v7;
        if ( (unsigned int)v7 > 0xFFF )
          goto LABEL_148;
        v7 = (unsigned int)v7;
        v8 = a1[(unsigned int)v7 + 16387] >= v79;
LABEL_114:
        a1[1] = v5;
        a1[v7 + 16387] = v8;
        goto LABEL_145;
      case 0x1C:
        *a1 = v3 + 1;
        v28 = (unsigned int)a1[1];
        if ( !a1[1] )
          goto LABEL_149;
        v29 = (unsigned int)(v28 - 1);
        a1[1] = v29;
        v2 = 2;
        if ( (unsigned int)v29 > 0xFFF )
          goto LABEL_151;
        v30 = a1[v29 + 16387];
        a1[1] = v28;
        if ( (unsigned int)v28 > 0xFFF )
          goto LABEL_151;
LABEL_132:
        a1[1] = v28 + 1;
        a1[v28 + 16387] = v30;
        goto LABEL_145;
      case 0x1D:
        *a1 = v3 + 1;
        v9 = a1[1];
        v2 = 3;
        if ( !v9 )
          goto LABEL_151;
        v10 = v9 - 1;
        a1[1] = v10;
        if ( v9 == 1 )
          goto LABEL_151;
        v11 = a1[v10 + 16387];
        v12 = v9 - 2;
        a1[1] = v12;
        v2 = 2;
        if ( (unsigned int)v12 > 0xFFF )
          goto LABEL_151;
        v13 = a1[v12 + 16387];
        a1[1] = v10;
        a1[v12 + 16387] = v11;
        if ( (unsigned int)v10 > 0xFFF )
          goto LABEL_151;
        a1[1] = v9;
        a1[v10 + 16387] = v13;
        goto LABEL_145;
      case 0x1E:
        if ( (unsigned int)(v3 + 1) < 0xFFFD )
        {
          v77 = *((unsigned __int8 *)a1 + (unsigned int)(v3 + 1) + 12)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 2) + 12) << 8)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 3) + 12) << 16)
              | (*((unsigned __int8 *)a1 + (unsigned int)(v3 + 4) + 12) << 24);
        }
        else
        {
          *(_QWORD *)(a1 + 20483) = 0x400000000LL;
          v77 = 0;
        }
        v88 = (unsigned int)a1[1];
        if ( v88 > 0xFFF )
          goto LABEL_148;
        a1[1] = v88 + 1;
        a1[v88 + 16387] = v3 + 5;
        *a1 = v77;
        goto LABEL_145;
      case 0x1F:
        v53 = a1[1];
        if ( !v53 )
          goto LABEL_149;
        v54 = v53 - 1;
        a1[1] = v54;
        *a1 = a1[v54 + 16387];
        goto LABEL_145;
      case 0x20:
        *a1 = v3 + 1;
        v80 = a1[1];
        if ( !v80 )
        {
LABEL_149:
          v2 = 3;
          goto LABEL_151;
        }
        v81 = v80 - 1;
        a1[1] = v81;
        v82 = (unsigned int)a1[v81 + 16387];
        v2 = 4;
        if ( v82 > 0xFFFF )
          goto LABEL_151;
        while ( 1 )
        {
          v83 = *((char *)a1 + v82 + 12);
          if ( !*((_BYTE *)a1 + v82 + 12) )
            break;
          ++v82;
          putchar(v83);
          if ( (_DWORD)v82 == 0x10000 )
            goto LABEL_151;
        }
        putchar(10);
        goto LABEL_145;
      case 0x21:
      case 0x22:
      case 0x23:
      case 0x24:
      case 0x25:
      case 0x26:
      case 0x27:
      case 0x28:
      case 0x29:
      case 0x2A:
      case 0x2B:
      case 0x2C:
      case 0x2D:
      case 0x2E:
      case 0x2F:
      case 0x30:
      case 0x31:
      case 0x32:
      case 0x33:
      case 0x34:
      case 0x35:
      case 0x36:
      case 0x37:
      case 0x38:
      case 0x39:
      case 0x3A:
      case 0x3B:
      case 0x3C:
      case 0x3D:
      case 0x3E:
      case 0x3F:
      case 0x40:
      case 0x41:
      case 0x42:
      case 0x43:
      case 0x44:
      case 0x45:
      case 0x46:
      case 0x47:
      case 0x48:
      case 0x49:
      case 0x4A:
      case 0x4B:
      case 0x4C:
      case 0x4D:
      case 0x4E:
      case 0x4F:
      case 0x50:
      case 0x51:
      case 0x52:
      case 0x53:
      case 0x54:
      case 0x55:
      case 0x56:
      case 0x57:
      case 0x58:
      case 0x59:
      case 0x5A:
      case 0x5B:
      case 0x5C:
      case 0x5D:
      case 0x5E:
      case 0x5F:
      case 0x60:
      case 0x61:
      case 0x62:
      case 0x63:
      case 0x64:
      case 0x65:
      case 0x66:
      case 0x67:
      case 0x68:
      case 0x69:
      case 0x6A:
      case 0x6B:
      case 0x6C:
      case 0x6D:
      case 0x6E:
      case 0x6F:
      case 0x70:
      case 0x71:
      case 0x72:
      case 0x73:
      case 0x74:
      case 0x75:
      case 0x76:
      case 0x77:
      case 0x78:
      case 0x79:
      case 0x7A:
      case 0x7B:
      case 0x7C:
      case 0x7D:
      case 0x7E:
      case 0x7F:
      case 0x80:
      case 0x81:
      case 0x82:
      case 0x83:
      case 0x84:
      case 0x85:
      case 0x86:
      case 0x87:
      case 0x88:
      case 0x89:
      case 0x8A:
      case 0x8B:
      case 0x8C:
      case 0x8D:
      case 0x8E:
      case 0x8F:
      case 0x90:
      case 0x91:
      case 0x92:
      case 0x93:
      case 0x94:
      case 0x95:
      case 0x96:
      case 0x97:
      case 0x98:
      case 0x99:
      case 0x9A:
      case 0x9B:
      case 0x9C:
      case 0x9D:
      case 0x9E:
      case 0x9F:
      case 0xA0:
      case 0xA1:
      case 0xA2:
      case 0xA3:
      case 0xA4:
      case 0xA5:
      case 0xA6:
      case 0xA7:
      case 0xA8:
      case 0xA9:
      case 0xAA:
      case 0xAB:
      case 0xAC:
      case 0xAD:
      case 0xAE:
      case 0xAF:
      case 0xB0:
      case 0xB1:
      case 0xB2:
      case 0xB3:
      case 0xB4:
      case 0xB5:
      case 0xB6:
      case 0xB7:
      case 0xB8:
      case 0xB9:
      case 0xBA:
      case 0xBB:
      case 0xBC:
      case 0xBD:
      case 0xBE:
      case 0xBF:
      case 0xC0:
      case 0xC1:
      case 0xC2:
      case 0xC3:
      case 0xC4:
      case 0xC5:
      case 0xC6:
      case 0xC7:
      case 0xC8:
      case 0xC9:
      case 0xCA:
      case 0xCB:
      case 0xCC:
      case 0xCD:
      case 0xCE:
      case 0xCF:
      case 0xD0:
      case 0xD1:
      case 0xD2:
      case 0xD3:
      case 0xD4:
      case 0xD5:
      case 0xD6:
      case 0xD7:
      case 0xD8:
      case 0xD9:
      case 0xDA:
      case 0xDB:
      case 0xDC:
      case 0xDD:
      case 0xDE:
      case 0xDF:
      case 0xE0:
      case 0xE1:
      case 0xE2:
      case 0xE3:
      case 0xE4:
      case 0xE5:
      case 0xE6:
      case 0xE7:
      case 0xE8:
      case 0xE9:
      case 0xEA:
      case 0xEB:
      case 0xEC:
      case 0xED:
      case 0xEE:
      case 0xEF:
      case 0xF0:
      case 0xF1:
      case 0xF2:
      case 0xF3:
      case 0xF4:
      case 0xF5:
      case 0xF6:
      case 0xF7:
      case 0xF8:
      case 0xF9:
      case 0xFA:
      case 0xFB:
      case 0xFC:
      case 0xFD:
      case 0xFE:
        goto LABEL_151;
      case 0xFF:
        *a1 = v3 + 1;
        a1[20483] = 0;
        goto LABEL_145;
    }
  }
}

这个程序定义了 33 条指令,总结如下

Opcode助记符操作编码
0x00JMP跳转到立即数地址00 <target:u32>
0x01JNZ弹栈cond,cond≠0则跳转到目标地址01 <target:u32>
0x02JZ弹栈cond,cond=0则跳转到目标地址02 <target:u32>
0x03PUSH8压入8位立即数03 <imm:u8>
0x04PUSH32压入32位立即数04 <imm:u32>
0x05LOAD8弹地址,压入该地址的1字节值05
0x06LOAD32弹地址,压入该地址的4字节值06
0x07POP弹出栈顶并丢弃07
0x08STORE8弹地址,再弹值,将值低8位写入地址08
0x09STORE32弹地址,再弹值,将32位值写入地址09
0x0AADD弹b,弹a,压入a+b0A
0x0BSUB弹b,弹a,压入a-b0B
0x0CMUL弹b,弹a,压入a*b0C
0x0DDIV弹b,弹a,压入a/b0D
0x0EMOD弹b,弹a,压入a%b0E
0x0FAND弹b,弹a,压入a&b0F
0x10OR弹b,弹a,压入a|b10
0x11NOT弹a,压入~a11
0x12XOR弹b,弹a,压入a^b12
0x13SHL弹b,弹a,压入a<<b13
0x14SHR弹b,弹a,压入a>>b14
0x15-0x16EQ弹b,弹a,压入a==b15 或 16
0x17NE弹b,弹a,压入a!=b17
0x18LT弹b,弹a,压入a<b18
0x19GT弹b,弹a,压入a>b19
0x1ALE弹b,弹a,压入a<=b1A
0x1BGE弹b,弹a,压入a>=b1B
0x1CDUP复制栈顶元素并压入1C
0x1DSWAP交换栈顶两个元素1D
0x1ECALL压入返回地址,跳转到目标地址1E <target:u32>
0x1FRET弹出返回地址并跳转1F
0x20PRINT弹字符串地址,输出以0结尾字符串并换行20
0x21-0xFEINVALID非法/未实现指令21-FE
0xFFHALT停止VM执行FF

通过上面这张映射表我们可以将 code.bin 中的字节码反汇编为汇编语言

code.bin

主程序入口

代码很长,就不完整展示了,我们只看开头的主程序入口

| ADDR | RAW | INSTRUCTION | ASCII |
|------|-----|-------------|-------|
| `0000` | `04 00 10 00 00` | PUSH32 0x00001000 | `.....` |
| `0005` | `04 2B 10 00 00` | PUSH32 0x0000102B | `.+...` |
| `000A` | `04 2A 00 00 00` | PUSH32 0x0000002A | `.*...` |
| `000F` | `1E 4F 00 00 00` | CALL 0x0000004F | `.O...` |            调用加密函数1:自定义 Base64
| `0014` | `04 2B 10 00 00` | PUSH32 0x0000102B | `.+...` |
| `0019` | `04 64 10 00 00` | PUSH32 0x00001064 | `.d...` |
| `001E` | `04 38 00 00 00` | PUSH32 0x00000038 | `.8...` |
| `0023` | `1E 68 03 00 00` | CALL 0x00000368 | `.h...` |            调用加密函数2:XOR 变换
| `0028` | `04 64 10 00 00` | PUSH32 0x00001064 | `.d...` |          加密结果存放地址
| `002D` | `04 9D 10 00 00` | PUSH32 0x0000109D | `.....` |          目标字符串地址
| `0032` | `04 38 00 00 00` | PUSH32 0x00000038 | `.8...` |          目标字符串长度 = 0x38
| `0037` | `1E 71 04 00 00` | CALL 0x00000471 | `.q...` |            将加密结果与目标字符串比较
| `003C` | `01 48 00 00 00` | JNZ 0x00000048 | `.H...` |             结果≠0 -> 失败
| `0041` | `04 D5 10 00 00` | PUSH32 0x000010D5 | `.....` |          "Right!"
| `0046` | `20` | PRINT | ` ` |
| `0047` | `FF` | HALT | `.` |
| `0048` | `04 DC 10 00 00` | PUSH32 0x000010DC | `.....` |          "Wrong!"
| `004D` | `20` | PRINT | ` ` |
| `004E` | `FF` | HALT | `.` |

首先就看到后面俩 PRINT,前面 PUSH 的地址分别是 0x10D5 和 0x10DC,分别对应 Right! 和 Wrong!

前面 0x3c 地址处的 JNZ 跳转说明若上面 CALL 调用的 0x471 地址处的函数返回值为 0 则失败

因此可以猜测 0x471 地址处的函数负责将密文与内置的目标字符串作比较

进而推测上面两个 PUSH 的参数 0x109D 和 0x38 分别是目标字符串的地址和长度

进一步猜测上面两个 CALL 调用的两个地址 0x4F 和 0x368 就是加密函数 1 和 2

加密函数 1(地址:0x4F)

| ADDR | RAW | INSTRUCTION | ASCII |
|------|-----|-------------|-------|
| `004F` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `0054` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0059` | `09` | STORE32 | `.` |
| `005A` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `005F` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0064` | `06` | LOAD32 | `.` |
| `0065` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `006A` | `15` | EQ | `.` |
| `006B` | `02 DB 00 00 00` | JZ 0x000000DB | `.....` |
| `0070` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0075` | `06` | LOAD32 | `.` |
| `0076` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `007B` | `15` | EQ | `.` |
| `007C` | `02 20 01 00 00` | JZ 0x00000120 | `. ...` |
| `0081` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0086` | `06` | LOAD32 | `.` |
| `0087` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `008C` | `15` | EQ | `.` |
| `008D` | `02 52 01 00 00` | JZ 0x00000152 | `.R...` |
| `0092` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0097` | `06` | LOAD32 | `.` |
| `0098` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `009D` | `15` | EQ | `.` |
| `009E` | `02 7D 01 00 00` | JZ 0x0000017D | `.}...` |
| `00A3` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `00A8` | `06` | LOAD32 | `.` |
| `00A9` | `04 04 00 00 00` | PUSH32 0x00000004 | `.....` |
| `00AE` | `15` | EQ | `.` |
| `00AF` | `02 A2 01 00 00` | JZ 0x000001A2 | `.....` |
| `00B4` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `00B9` | `06` | LOAD32 | `.` |
| `00BA` | `04 05 00 00 00` | PUSH32 0x00000005 | `.....` |
| `00BF` | `15` | EQ | `.` |
| `00C0` | `02 B3 01 00 00` | JZ 0x000001B3 | `.....` |
| `00C5` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `00CA` | `06` | LOAD32 | `.` |
| `00CB` | `04 06 00 00 00` | PUSH32 0x00000006 | `.....` |
| `00D0` | `15` | EQ | `.` |
| `00D1` | `02 61 03 00 00` | JZ 0x00000361 | `.a...` |
| `00D6` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `00DB` | `04 23 11 00 00` | PUSH32 0x00001123 | `.#...` |
| `00E0` | `09` | STORE32 | `.` |
| `00E1` | `04 2F 11 00 00` | PUSH32 0x0000112F | `./...` |
| `00E6` | `09` | STORE32 | `.` |
| `00E7` | `04 2B 11 00 00` | PUSH32 0x0000112B | `.+...` |
| `00EC` | `09` | STORE32 | `.` |
| `00ED` | `04 27 11 00 00` | PUSH32 0x00001127 | `.'...` |
| `00F2` | `09` | STORE32 | `.` |
| `00F3` | `04 2F 11 00 00` | PUSH32 0x0000112F | `./...` |
| `00F8` | `06` | LOAD32 | `.` |
| `00F9` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `00FE` | `0D` | DIV | `.` |
| `00FF` | `04 37 11 00 00` | PUSH32 0x00001137 | `.7...` |
| `0104` | `09` | STORE32 | `.` |
| `0105` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `010A` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `010F` | `09` | STORE32 | `.` |
| `0110` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0115` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `011A` | `09` | STORE32 | `.` |
| `011B` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `0120` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0125` | `06` | LOAD32 | `.` |
| `0126` | `04 37 11 00 00` | PUSH32 0x00001137 | `.7...` |
| `012B` | `06` | LOAD32 | `.` |
| `012C` | `15` | EQ | `.` |
| `012D` | `02 42 01 00 00` | JZ 0x00000142 | `.B...` |
| `0132` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `0137` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `013C` | `09` | STORE32 | `.` |
| `013D` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `0142` | `04 06 00 00 00` | PUSH32 0x00000006 | `.....` |
| `0147` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `014C` | `09` | STORE32 | `.` |
| `014D` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `0152` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0157` | `1C` | DUP | `.` |
| `0158` | `01 6D 01 00 00` | JNZ 0x0000016D | `.m...` |
| `015D` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `0162` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0167` | `09` | STORE32 | `.` |
| `0168` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `016D` | `04 05 00 00 00` | PUSH32 0x00000005 | `.....` |
| `0172` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `0177` | `09` | STORE32 | `.` |
| `0178` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `017D` | `02 92 01 00 00` | JZ 0x00000192 | `.....` |
| `0182` | `04 04 00 00 00` | PUSH32 0x00000004 | `.....` |
| `0187` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `018C` | `09` | STORE32 | `.` |
| `018D` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `0192` | `04 05 00 00 00` | PUSH32 0x00000005 | `.....` |
| `0197` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `019C` | `09` | STORE32 | `.` |
| `019D` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `01A2` | `03 04` | PUSH8 0x04 | `..` |
| `01A4` | `05` | LOAD8 | `.` |
| `01A5` | `00 00 00 04 47` | JMP 0x47040000 | `....G` |
| `01AA` | `11` | NOT | `.` |
| `01AB` | `00 00 09 00 5F` | JMP 0x5F000900 | `...._` |
| `01B0` | `00 00 00 04 27` | JMP 0x27040000 | `....'` |
| `01B5` | `11` | NOT | `.` |
| `01B6` | `00 00 06 04 33` | JMP 0x33040600 | `....3` |
| `01BB` | `11` | NOT | `.` |
| `01BC` | `00 00 06 04 03` | JMP 0x03040600 | `.....` |
| `01C1` | `00 00 00 0C 0A` | JMP 0x0A0C0000 | `.....` |
| `01C6` | `04 3B 11 00 00` | PUSH32 0x0000113B | `.;...` |
| `01CB` | `09` | STORE32 | `.` |
| `01CC` | `04 2B 11 00 00` | PUSH32 0x0000112B | `.+...` |
| `01D1` | `06` | LOAD32 | `.` |
| `01D2` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `01D7` | `06` | LOAD32 | `.` |
| `01D8` | `04 04 00 00 00` | PUSH32 0x00000004 | `.....` |
| `01DD` | `0C` | MUL | `.` |
| `01DE` | `0A` | ADD | `.` |
| `01DF` | `04 3F 11 00 00` | PUSH32 0x0000113F | `.?...` |
| `01E4` | `09` | STORE32 | `.` |
| `01E5` | `04 3B 11 00 00` | PUSH32 0x0000113B | `.;...` |
| `01EA` | `06` | LOAD32 | `.` |
| `01EB` | `05` | LOAD8 | `.` |
| `01EC` | `04 10 00 00 00` | PUSH32 0x00000010 | `.....` |
| `01F1` | `13` | SHL | `.` |
| `01F2` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `01F7` | `09` | STORE32 | `.` |
| `01F8` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `01FD` | `06` | LOAD32 | `.` |
| `01FE` | `04 3B 11 00 00` | PUSH32 0x0000113B | `.;...` |
| `0203` | `06` | LOAD32 | `.` |
| `0204` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0209` | `0A` | ADD | `.` |
| `020A` | `05` | LOAD8 | `.` |
| `020B` | `04 08 00 00 00` | PUSH32 0x00000008 | `.....` |
| `0210` | `13` | SHL | `.` |
| `0211` | `10` | OR | `.` |
| `0212` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0217` | `09` | STORE32 | `.` |
| `0218` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `021D` | `06` | LOAD32 | `.` |
| `021E` | `04 3B 11 00 00` | PUSH32 0x0000113B | `.;...` |
| `0223` | `06` | LOAD32 | `.` |
| `0224` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `0229` | `0A` | ADD | `.` |
| `022A` | `05` | LOAD8 | `.` |
| `022B` | `10` | OR | `.` |
| `022C` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0231` | `09` | STORE32 | `.` |
| `0232` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0237` | `06` | LOAD32 | `.` |
| `0238` | `04 05 00 00 00` | PUSH32 0x00000005 | `.....` |
| `023D` | `13` | SHL | `.` |
| `023E` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0243` | `06` | LOAD32 | `.` |
| `0244` | `04 13 00 00 00` | PUSH32 0x00000013 | `.....` |
| `0249` | `14` | SHR | `.` |
| `024A` | `10` | OR | `.` |
| `024B` | `04 FF FF FF 00` | PUSH32 0x00FFFFFF | `.....` |
| `0250` | `0F` | AND | `.` |
| `0251` | `04 7D 75 55 00` | PUSH32 0x0055757D | `.}uU.` |
| `0256` | `12` | XOR | `.` |
| `0257` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `025C` | `09` | STORE32 | `.` |
| `025D` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0262` | `06` | LOAD32 | `.` |
| `0263` | `04 0B 00 00 00` | PUSH32 0x0000000B | `.....` |
| `0268` | `13` | SHL | `.` |
| `0269` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `026E` | `06` | LOAD32 | `.` |
| `026F` | `04 0D 00 00 00` | PUSH32 0x0000000D | `.....` |
| `0274` | `14` | SHR | `.` |
| `0275` | `10` | OR | `.` |
| `0276` | `04 FF FF FF 00` | PUSH32 0x00FFFFFF | `.....` |
| `027B` | `0F` | AND | `.` |
| `027C` | `04 7D 75 55 00` | PUSH32 0x0055757D | `.}uU.` |
| `0281` | `12` | XOR | `.` |
| `0282` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0287` | `09` | STORE32 | `.` |
| `0288` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `028D` | `06` | LOAD32 | `.` |
| `028E` | `04 14 00 00 00` | PUSH32 0x00000014 | `.....` |
| `0293` | `13` | SHL | `.` |
| `0294` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0299` | `06` | LOAD32 | `.` |
| `029A` | `04 04 00 00 00` | PUSH32 0x00000004 | `.....` |
| `029F` | `14` | SHR | `.` |
| `02A0` | `10` | OR | `.` |
| `02A1` | `04 FF FF FF 00` | PUSH32 0x00FFFFFF | `.....` |
| `02A6` | `0F` | AND | `.` |
| `02A7` | `04 7D 75 55 00` | PUSH32 0x0055757D | `.}uU.` |
| `02AC` | `12` | XOR | `.` |
| `02AD` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `02B2` | `09` | STORE32 | `.` |
| `02B3` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `02B8` | `06` | LOAD32 | `.` |
| `02B9` | `04 12 00 00 00` | PUSH32 0x00000012 | `.....` |
| `02BE` | `14` | SHR | `.` |
| `02BF` | `04 3F 00 00 00` | PUSH32 0x0000003F | `.?...` |
| `02C4` | `0F` | AND | `.` |
| `02C5` | `04 E3 10 00 00` | PUSH32 0x000010E3 | `.....` |
| `02CA` | `0A` | ADD | `.` |
| `02CB` | `05` | LOAD8 | `.` |
| `02CC` | `04 3F 11 00 00` | PUSH32 0x0000113F | `.?...` |
| `02D1` | `06` | LOAD32 | `.` |
| `02D2` | `08` | STORE8 | `.` |
| `02D3` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `02D8` | `06` | LOAD32 | `.` |
| `02D9` | `04 0C 00 00 00` | PUSH32 0x0000000C | `.....` |
| `02DE` | `14` | SHR | `.` |
| `02DF` | `04 3F 00 00 00` | PUSH32 0x0000003F | `.?...` |
| `02E4` | `0F` | AND | `.` |
| `02E5` | `04 E3 10 00 00` | PUSH32 0x000010E3 | `.....` |
| `02EA` | `0A` | ADD | `.` |
| `02EB` | `05` | LOAD8 | `.` |
| `02EC` | `04 3F 11 00 00` | PUSH32 0x0000113F | `.?...` |
| `02F1` | `06` | LOAD32 | `.` |
| `02F2` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `02F7` | `0A` | ADD | `.` |
| `02F8` | `08` | STORE8 | `.` |
| `02F9` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `02FE` | `06` | LOAD32 | `.` |
| `02FF` | `04 06 00 00 00` | PUSH32 0x00000006 | `.....` |
| `0304` | `14` | SHR | `.` |
| `0305` | `04 3F 00 00 00` | PUSH32 0x0000003F | `.?...` |
| `030A` | `0F` | AND | `.` |
| `030B` | `04 E3 10 00 00` | PUSH32 0x000010E3 | `.....` |
| `0310` | `0A` | ADD | `.` |
| `0311` | `05` | LOAD8 | `.` |
| `0312` | `04 3F 11 00 00` | PUSH32 0x0000113F | `.?...` |
| `0317` | `06` | LOAD32 | `.` |
| `0318` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `031D` | `0A` | ADD | `.` |
| `031E` | `08` | STORE8 | `.` |
| `031F` | `04 43 11 00 00` | PUSH32 0x00001143 | `.C...` |
| `0324` | `06` | LOAD32 | `.` |
| `0325` | `04 3F 00 00 00` | PUSH32 0x0000003F | `.?...` |
| `032A` | `0F` | AND | `.` |
| `032B` | `04 E3 10 00 00` | PUSH32 0x000010E3 | `.....` |
| `0330` | `0A` | ADD | `.` |
| `0331` | `05` | LOAD8 | `.` |
| `0332` | `04 3F 11 00 00` | PUSH32 0x0000113F | `.?...` |
| `0337` | `06` | LOAD32 | `.` |
| `0338` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `033D` | `0A` | ADD | `.` |
| `033E` | `08` | STORE8 | `.` |
| `033F` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0344` | `06` | LOAD32 | `.` |
| `0345` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `034A` | `0A` | ADD | `.` |
| `034B` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0350` | `09` | STORE32 | `.` |
| `0351` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0356` | `04 47 11 00 00` | PUSH32 0x00001147 | `.G...` |
| `035B` | `09` | STORE32 | `.` |
| `035C` | `00 5F 00 00 00` | JMP 0x0000005F | `._...` |
| `0361` | `04 23 11 00 00` | PUSH32 0x00001123 | `.#...` |
| `0366` | `06` | LOAD32 | `.` |
| `0367` | `1F` | RET | `.` |

是个自定义的 base64 编码

注意:ROL() 是 24 位循环左移

加密函数 2(地址:0x368)

| ADDR | RAW | INSTRUCTION | ASCII |
|------|-----|-------------|-------|
| `0368` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `036D` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `0372` | `09` | STORE32 | `.` |
| `0373` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `0378` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `037D` | `06` | LOAD32 | `.` |
| `037E` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `0383` | `15` | EQ | `.` |
| `0384` | `02 C1 03 00 00` | JZ 0x000003C1 | `.....` |
| `0389` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `038E` | `06` | LOAD32 | `.` |
| `038F` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0394` | `15` | EQ | `.` |
| `0395` | `02 F4 03 00 00` | JZ 0x000003F4 | `.....` |
| `039A` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `039F` | `06` | LOAD32 | `.` |
| `03A0` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `03A5` | `15` | EQ | `.` |
| `03A6` | `02 26 04 00 00` | JZ 0x00000426 | `.&...` |
| `03AB` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `03B0` | `06` | LOAD32 | `.` |
| `03B1` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `03B6` | `15` | EQ | `.` |
| `03B7` | `02 6A 04 00 00` | JZ 0x0000046A | `.j...` |
| `03BC` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `03C1` | `04 23 11 00 00` | PUSH32 0x00001123 | `.#...` |
| `03C6` | `09` | STORE32 | `.` |
| `03C7` | `04 2F 11 00 00` | PUSH32 0x0000112F | `./...` |
| `03CC` | `09` | STORE32 | `.` |
| `03CD` | `04 2B 11 00 00` | PUSH32 0x0000112B | `.+...` |
| `03D2` | `09` | STORE32 | `.` |
| `03D3` | `04 27 11 00 00` | PUSH32 0x00001127 | `.'...` |
| `03D8` | `09` | STORE32 | `.` |
| `03D9` | `04 00 00 00 00` | PUSH32 0x00000000 | `.....` |
| `03DE` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `03E3` | `09` | STORE32 | `.` |
| `03E4` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `03E9` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `03EE` | `09` | STORE32 | `.` |
| `03EF` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `03F4` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `03F9` | `06` | LOAD32 | `.` |
| `03FA` | `04 2F 11 00 00` | PUSH32 0x0000112F | `./...` |
| `03FF` | `06` | LOAD32 | `.` |
| `0400` | `15` | EQ | `.` |
| `0401` | `02 16 04 00 00` | JZ 0x00000416 | `.....` |
| `0406` | `04 02 00 00 00` | PUSH32 0x00000002 | `.....` |
| `040B` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `0410` | `09` | STORE32 | `.` |
| `0411` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `0416` | `04 03 00 00 00` | PUSH32 0x00000003 | `.....` |
| `041B` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `0420` | `09` | STORE32 | `.` |
| `0421` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `0426` | `04 27 11 00 00` | PUSH32 0x00001127 | `.'...` |
| `042B` | `06` | LOAD32 | `.` |
| `042C` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0431` | `06` | LOAD32 | `.` |
| `0432` | `0A` | ADD | `.` |
| `0433` | `05` | LOAD8 | `.` |
| `0434` | `04 63 00 00 00` | PUSH32 0x00000063 | `.c...` |
| `0439` | `12` | XOR | `.` |
| `043A` | `04 2B 11 00 00` | PUSH32 0x0000112B | `.+...` |
| `043F` | `06` | LOAD32 | `.` |
| `0440` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0445` | `06` | LOAD32 | `.` |
| `0446` | `0A` | ADD | `.` |
| `0447` | `08` | STORE8 | `.` |
| `0448` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `044D` | `06` | LOAD32 | `.` |
| `044E` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `0453` | `0A` | ADD | `.` |
| `0454` | `04 33 11 00 00` | PUSH32 0x00001133 | `.3...` |
| `0459` | `09` | STORE32 | `.` |
| `045A` | `04 01 00 00 00` | PUSH32 0x00000001 | `.....` |
| `045F` | `04 4F 11 00 00` | PUSH32 0x0000114F | `.O...` |
| `0464` | `09` | STORE32 | `.` |
| `0465` | `00 78 03 00 00` | JMP 0x00000378 | `.x...` |
| `046A` | `04 23 11 00 00` | PUSH32 0x00001123 | `.#...` |
| `046F` | `06` | LOAD32 | `.` |
| `0470` | `1F` | RET | `.` |

就是一个简单的逐字节异或变换

分析完加密算法就可以写脚本解密了

解密脚本

脚本可以自动从 code.bin 中提取密文(地址:0x109D:0x109D+56)

import base64

with open('code.bin', 'rb') as f:
    data = f.read()

# 56字节预期密文
expected = bytes(data[0x109d:0x109d+56])

# 逆 XOR 0x63 → 得到自定义Base64
b64_bytes = bytes(b ^ 0x63 for b in expected)

# 标准 Base64 字母表
alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

# 转为6位索引
indices = [alphabet.index(chr(b)) for b in b64_bytes]

# 24位循环移位辅助函数
def rol24(val, n):
    val &= 0xFFFFFF
    return ((val << n) | (val >> (24 - n))) & 0xFFFFFF

def ror24(val, n):
    val &= 0xFFFFFF
    return ((val >> n) | (val << (24 - n))) & 0xFFFFFF

# 分组解密
flag_bytes = []
for i in range(0, len(indices), 4):
    # 4个6位索引 → 24位值
    val = (indices[i] << 18) | (indices[i+1] << 12) | \
          (indices[i+2] << 6) | indices[i+3]
    
    # 逆推3轮混淆 (反向顺序)
    val = ror24(val ^ 0x55757d, 20)
    val = ror24(val ^ 0x55757d, 11)
    val = ror24(val ^ 0x55757d, 5)
    
    # 24位 → 3字节
    flag_bytes.append((val >> 16) & 0xFF)
    flag_bytes.append((val >> 8) & 0xFF)
    flag_bytes.append(val & 0xFF)

flag = bytes(flag_bytes).decode('ascii')
print(f'Flag: {flag}')

'''
输出内容:
Flag: NCTF{1578be15-ad09-4859-9193-5d52585eb485}
'''