强网杯 2025 Reverse 部分题解

强网杯 2025 Reverse 部分题解

Administrator 13 2025-10-20

强网杯 2025 Reverse 部分题解

今年又是陪跑了一次强网杯,Reverse 部分感觉挺难的,除了一个签到题其他各有各的牢点,还是自己太菜了,拼劲全力也就做出了两个题 😭

butterfly

__int64 __fastcall sub_4018D0(int n3, _QWORD *a2, __int64 a3, int a4, int a5, int a6)
{
  // [COLLAPSED LOCAL DECLARATIONS. PRESS NUMPAD "+" TO EXPAND]

  if ( n3 != 3 )
  {
    sub_4776D0(2, (unsigned int)"Usage: %s <input_file> <output_file>\n", *a2, a4, a5, a6, v39[0]);
    sub_4776D0(2, (unsigned int)"Example: %s plaintext.txt encoded.dat\n", *a2, v6, v7, v8, v39[0]);
    return 1;
  }
  v10 = a2[1];
  v11 = a2[2];
  v12 = sub_405540(v10, "rb");
  v16 = v12;
  if ( !v12 )
  {
    sub_4776D0(2, (unsigned int)"Error: Cannot open file %s\n", v10, v13, v14, v15, v39[0]);
    return 1;
  }
  sub_407480(v12, 0, 2);
  n7 = sub_405640(v16);
  sub_407480(v16, 0, 0);
  v18 = sub_412620(n7 + 8);
  v19 = (__m64 *)v18;
  if ( !v18 )
  {
    sub_405180(v16);
    sub_4774A0("Error: Memory allocation failed");
    return 1;
  }
  n7_1 = sub_41CC80(v18, n7 + 8, 1, n7, v16);
  sub_405180(v16);
  if ( n7 != n7_1 )
  {
    sub_412CF0(v19);
    sub_4774A0("Error: File read failed");
    return 1;
  }
  *(__int16 *)((char *)v19->m64_i16 + n7) = n7;
  v39[0] = _mm_loadu_si128((const __m128i *)"MMXEncode2024");
  v39[1] = _mm_loadu_si128((const __m128i *)"coding file: %s\n");
  sub_4776D0(2, (unsigned int)"Encoding file: %s\n", v10, v21, v22, v23, v39[0]);
  sub_4776D0(2, (unsigned int)"Original size: %zu bytes\n", n7, v24, v25, v26, v39[0]);
  v40[0] = *(__m64 *)&v39[0];
  if ( (int)n7 > 7 )
  {
    v27 = v19;
    do
    {
      v28 = _m_pxor((__m64)v27->m64_u64, v40[0]);
      v29 = _m_por(_m_psrlwi(v28, 8u), _m_psllwi(v28, 8u));
      v27->m64_u64 = (unsigned __int64)_m_paddb(_m_por(_m_psllqi(v29, 1u), _m_psrlqi(v29, 0x3Fu)), v40[0]);
      if ( &v19[(unsigned int)(n7 - 1) >> 3] == v27 )
        break;
      ++v27;
    }
    while ( &v19[((unsigned int)(n7 - 8) >> 3) + 1] != v27 );
  }
  _m_empty();
  if ( (unsigned int)sub_401CA0(v11, v19, n7) )
  {
    sub_4776D0(2, (unsigned int)"Successfully encoded to: %s\n", v11, v30, v31, v32, v39[0]);
    sub_4776D0(2, (unsigned int)"Encoded size: %zu bytes\n", n7, v33, v34, v35, v39[0]);
    sub_4777A0((unsigned int)v40, 256, 2, 256, (unsigned int)"%s.key", v11, v39[0]);
    if ( (unsigned int)sub_401CA0(v40, v39, 32) )
      sub_4776D0(2, (unsigned int)"Key saved to: %s\n", (unsigned int)v40, v36, v37, v38, v39[0]);
  }
  sub_412CF0(v19);
  return 0;
}

逆向签到题,很简单的加密逻辑,input 与 key 异或 -> 字节交换 -> 循环左移 -> 与 key 相加,直接写脚本就行了。

import struct

def decrypt_data(encrypted_data, key):
    key_bytes = key[:8]
    
    data_len = len(encrypted_data)
    decrypted = bytearray(encrypted_data)
    
    for i in range(0, data_len - 7, 8):
        block = encrypted_data[i:i+8]
        
        # 1. 减去密钥
        temp_block = bytearray(8)
        for j in range(8):
            temp_block[j] = (block[j] - key_bytes[j % 8]) & 0xFF
        
        # 2. 循环右移1位
        temp_val = struct.unpack('<Q', temp_block)[0]
        temp_val = ((temp_val >> 1) | ((temp_val & 1) << 63)) & 0xFFFFFFFFFFFFFFFF
        temp_block = bytearray(struct.pack('<Q', temp_val))
        
        # 3. 字节交换
        swapped_block = bytearray(8)
        for j in range(0, 8, 2):
            swapped_block[j] = temp_block[j+1]
            swapped_block[j+1] = temp_block[j]
        
        # 4. 异或
        for j in range(8):
            decrypted[i+j] = swapped_block[j] ^ key_bytes[j % 8]
    
    return bytes(decrypted)

def main():
    with open('encode.dat.key', 'rb') as f:
        key_data = f.read()
    
    print(f"key: {key_data}")
    
    with open('encode.dat', 'rb') as f:
        encrypted_data = f.read()
    
    decrypted_data = decrypt_data(encrypted_data, key_data)

    print(decrypted_data)

if __name__ == "__main__":
    main()
    
# b'flag{butter_fly_mmx_encode_7778167}\n'

tradre

main 函数如下:

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __pid_t pid; // [rsp+1Ch] [rbp-4h]

  signal(14, (__sighandler_t)((char *)sub_40113C + 3));
  alarm(0x3Cu);
  sub_402425();                                 // 初始化变量
  pid = fork();
  if ( pid )
  {
    if ( pid <= 0 )
    {
      perror("Fork.");
      return 0xFFFFFFFFLL;
    }
    sub_401FBA(pid);                            // 父进程
  }
  else
  {
    sub_401B83();                               // 子进程
  }
  return 0;
}

先分析一下父进程的代码:

unsigned __int64 __fastcall sub_401FBA(unsigned int pid)
{
  __WAIT_STATUS stat_loc; // [rsp+18h] [rbp-2A8h] BYREF
  int is_jmp; // [rsp+20h] [rbp-2A0h]
  int v4; // [rsp+24h] [rbp-29Ch]
  int i; // [rsp+28h] [rbp-298h]
  int v6; // [rsp+2Ch] [rbp-294h]
  int v7; // [rsp+30h] [rbp-290h]
  int v8; // [rsp+34h] [rbp-28Ch]
  __int64 n204; // [rsp+38h] [rbp-288h]
  user_regs_struct regs; // [rsp+40h] [rbp-280h] BYREF
  _QWORD vm_stack[51]; // [rsp+120h] [rbp-1A0h]
  unsigned __int64 v12; // [rsp+2B8h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  v6 = 0;
  HIDWORD(stat_loc.__iptr) = 0;                 // 初始化 VM 栈指针
  current_block = Block_Info;
  wait((__WAIT_STATUS)&stat_loc);
  while ( LOBYTE(stat_loc.__uptr) == 127 )
  {
    ptrace(PTRACE_GETREGS, pid, 0, &regs);
    v7 = ptrace(PTRACE_PEEKTEXT, pid, regs.rip, 0);
    n204 = (unsigned __int8)ptrace(PTRACE_PEEKDATA, pid, regs.rip - 1, 0);
    if ( n204 != 0xCC )                         // 0XCC
    {
      ptrace(PTRACE_KILL, pid, 0, 0);
      exit(0);
    }
    is_jmp = 1;
    if ( current_block->handler )
    {
      v8 = ((__int64 (__fastcall *)(user_regs_struct *))current_block->handler)(&regs);
      if ( v8 == 1 )
      {
        current_block = (_VM_INSTRUCTION *)current_block->next_vm_ip_1;// case 1: 跳转到 next_vm_ip_1, 子进程进入 child_code_address 
      }
      else if ( v8 )
      {
        switch ( v8 )
        {
          case 2:                               // case 2: pop vm_stack -> vm_code, 子进程进入 child_code_address 
            if ( SHIDWORD(stat_loc.__iptr) <= 0 )
              exit(-1);
            current_block = (_VM_INSTRUCTION *)vm_stack[--HIDWORD(stat_loc.__iptr)];
            regs.rsp += 8LL;
            break;
          case 3:                               // case 3: 子进程栈上写入当前块对应的 child_code_address, 子进程进入 next_vim_ip_1
            regs.rip = current_block->next_vm_ip_1;
            current_block = (_VM_INSTRUCTION *)current_block->next_vm_ip_0;
            regs.rsp -= 8LL;
            ptrace(PTRACE_POKEDATA, pid, regs.rsp, current_block->child_code_address);
            is_jmp = 0;
            break;
          case 4:                               // case 4: 入栈 next_vm_ip_0, 进入 next_vm_ip_1,, 子进程进入 child_code_address 
            if ( SHIDWORD(stat_loc.__iptr) > 48 )
              exit(-1);
            vm_stack[SHIDWORD(stat_loc.__iptr)] = current_block->next_vm_ip_0;
            ++HIDWORD(stat_loc.__iptr);
            regs.rsp -= 8LL;
            current_block = (_VM_INSTRUCTION *)current_block->next_vm_ip_1;
            break;
          case 5:                               // case 5: 通过子进程的 rip 找到对应的 Block 
            if ( SHIDWORD(stat_loc.__iptr) > 48 )
              exit(-1);
            vm_stack[SHIDWORD(stat_loc.__iptr)] = current_block->next_vm_ip_0;
            ++HIDWORD(stat_loc.__iptr);
            regs.rsp -= 8LL;
            v4 = 0;
            for ( i = 0; i <= 180; ++i )
            {
              if ( child_code_address[4 * i] == regs.rip )
              {
                current_block = &Block_Info[i];
                v4 = 1;
                break;
              }
            }
            if ( !v4 )
              exit(-1);
            is_jmp = 0;
            break;
        }
      }
      else
      {
        current_block = (_VM_INSTRUCTION *)current_block->next_vm_ip_0;// case 0: 进入 ip_0 的块
      }
    }
    else
    {
      current_block = (_VM_INSTRUCTION *)current_block->next_vm_ip_1;// 没有 handler 的情况下进入 ip_1 的块
    }
    if ( is_jmp )
      regs.rip = current_block->child_code_address;// 默认将子进程跳转到 vm_code 对应的 child_code_address
    ptrace(PTRACE_SETREGS, pid, 0, &regs);
    if ( ptrace(PTRACE_CONT, pid, 0, 0) < 0 )
    {
      perror("Ptrace.");
      return __readfsqword(0x28u) ^ v12;
    }
    wait((__WAIT_STATUS)&stat_loc);
  }
  return __readfsqword(0x28u) ^ v12;
}

子进程通过 0xCC 分割为若干代码块,每个代码块对应一个 blockBlock_Info 变量中存储着所有 block 的信息。

Block_Info 的结构可以用如下结构体表示:

struct _Block_Info // sizeof=0x20
{
    uint64_t next_vm_ip_0;
    uint64_t next_vm_ip_1;
    uint64_t handler;
    uint64_t child_code_address;
};

其中 next_vm_ip_0next_vm_ip_1 指向其他 block 或者子进程代码块,handler 是相应 block 的处理函数,父进程通过 handler 的返回值通过类似 VM 的操作决定子进程的执行流,child_code_address 则是该 block 对应子进程代码段的起始地址。

下面是所有的 handler 函数,有三个直接返回对应的 case,剩下的都是通过 eflags 判断。

__int64 sub_401EB4()
{
  return 4;
}

__int64 sub_401EA5()
{
  return 3;
}

_BOOL8 __fastcall sub_401CA6(user_regs_struct *user_regs_struct)
{
  unsigned __int64 eflags; // [rsp+20h] [rbp-8h]

  eflags = user_regs_struct->eflags;
  return ((eflags >> 6) & 1) == 1 || ((eflags >> 7) & 1) != ((eflags >> 11) & 1);
}

_BOOL8 __fastcall sub_401D22(user_regs_struct *user_regs_struct)
{
  return ((user_regs_struct->eflags >> 6) & 1) == 0;
}

_BOOL8 __fastcall sub_401D5B(user_regs_struct *user_regs_struct)
{
  return ((user_regs_struct->eflags >> 6) & 1) != 0;
}

__int64 sub_401E96()
{
  return 2;
}

_BOOL8 __fastcall sub_401DCD(user_regs_struct *user_regs_struct)
{
  return ((user_regs_struct->eflags >> 7) & 1) == 0;
}

_BOOL8 __fastcall sub_401F0C(user_regs_struct *user_regs_struct)
{
  unsigned __int64 eflags; // [rsp+20h] [rbp-8h]

  eflags = user_regs_struct->eflags;
  return ((eflags >> 6) & 1) == 0 && ((eflags >> 7) & 1) == ((eflags >> 11) & 1);
}

_BOOL8 __fastcall sub_401C31(user_regs_struct *user_regs_struct)
{
  unsigned __int64 eflags; // [rsp+20h] [rbp-8h]

  eflags = user_regs_struct->eflags;
  return ((eflags >> 6) & 1) == 0 && ((eflags >> 7) & 1) != ((eflags >> 11) & 1);
}

动调可以 dump 出 Block_Info:

Block_Info = [0x607160,0x607FE0,0x401EB4,0x4009F7,0x607540,0x4008D0,0x401EA5,0x400AFD,0x0,0x606F00,0x0,0x400B03,0x607DC0,0x607E00,0x401EB4,0x400B6E,0x607920,0x400870,0x401EA5,0x400B79,0x608000,0x607460,0x401CA6,0x400B7C,0x6077C0,0x607EA0,0x401D22,0x400B81,0x6071E0,0x607BA0,0x401EB4,0x400BAB,0x607140,0x608120,0x401D22,0x400BBE,0x607840,0x400830,0x401EA5,0x400BEB,0x6070A0,0x400870,0x401EA5,0x400BF8,0x607BE0,0x607120,0x401EB4,0x400BFE,0x607AC0,0x607F60,0x401EB4,0x400C10,0x607600,0x607D20,0x401D5B,0x400C25,0x607B80,0x607BA0,0x401EB4,0x400C34,0x0,0x608100,0x0,0x400C47,0x606B80,0x400900,0x401EA5,0x400C4F,0x607B60,0x606EE0,0x401CA6,0x400C74,0x6072C0,0x607A80,0x401CA6,0x400C7C,0x607CA0,0x607F40,0x401CA6,0x400C84,0x0,0x0,0x401E96,0x400C8C,0x607280,0x606FC0,0x401EB4,0x400C96,0x0,0x607A20,0x0,0x400CCF,0x6079C0,0x608060,0x401EB4,0x400CDF,0x0,0x6080C0,0x0,0x400D1F,0x607040,0x400810,0x401EA5,0x400D45,0x608020,0x607E00,0x401EB4,0x400D4D,0x0,0x0,0x401E96,0x400D58,0x0,0x607620,0x0,0x400D5B,0x606D60,0x607120,0x401EB4,0x400D90,0x0,0x607520,0x0,0x400DA3,0x0,0x0,0x401E96,0x400DDB,0x607480,0x607340,0x401D5B,0x400DE0,0x606BC0,0x400900,0x401EA5,0x400DEF,0x606CA0,0x606B00,0x401CA6,0x400E1A,0x0,0x6080C0,0x0,0x400E22,0x6077E0,0x608060,0x401EB4,0x400E3A,0x606DE0,0x400810,0x401EA5,0x400E55,0x0,0x0,0x401E96,0x400E5D,0x607100,0x606D40,0x401D5B,0x400F5C,0x607EC0,0x607860,0x401DCD,0x400F6A,0x0,0x0,0x401E96,0x400F7A,0x0,0x607960,0x0,0x400F83,0x606B20,0x6080E0,0x401CA6,0x400FB1,0x606C00,0x400810,0x401EA5,0x400FC1,0x607080,0x607C80,0x401EB4,0x400FC9,0x6079E0,0x607780,0x401EB4,0x400FDB,0x0,0x606B60,0x0,0x400FEE,0x0,0x0,0x401E96,0x400FF6,0x0,0x0,0x401E96,0x400FF9,0x606D40,0x400820,0x401EA5,0x400FFB,0x607B40,0x606FC0,0x401EB4,0x400FFC,0x0,0x608120,0x0,0x401010,0x607260,0x4008A0,0x401EA5,0x401018,0x0,0x0,0x401E96,0x401032,0x0,0x606D00,0x0,0x401034,0x607060,0x607C80,0x401EB4,0x40104D,0x6071C0,0x606C40,0x401EB4,0x401060,0x0,0x608100,0x0,0x401073,0x0,0x0,0x401E96,0x401080,0x0,0x606B60,0x0,0x40117F,0x607C40,0x4008A0,0x401EA5,0x401184,0x607CC0,0x607120,0x401EB4,0x40119E,0x608140,0x607C80,0x401EB4,0x4011B1,0x0,0x607A40,0x0,0x4011C4,0x0,0x606CE0,0x0,0x4011FF,0x0,0x607C00,0x0,0x40120A,0x0,0x606FA0,0x0,0x40120B,0x0,0x0,0x401E96,0x40120C,0x6078C0,0x608060,0x401EB4,0x401213,0x607D00,0x608060,0x401EB4,0x40122E,0x607400,0x607E00,0x401EB4,0x401249,0x607420,0x400810,0x401EA5,0x401254,0x0,0x607F80,0x0,0x40125C,0x607500,0x607220,0x401EB4,0x40129F,0x6074E0,0x400810,0x401EA5,0x4012AA,0x606DC0,0x4008D0,0x401EA5,0x4012B2,0x0,0x6078E0,0x0,0x4012B8,0x607340,0x400820,0x401EA5,0x4012C0,0x0,0x607C00,0x0,0x4012C1,0x0,0x607B00,0x0,0x4012C3,0x606F60,0x400810,0x401EA5,0x4012E7,0x6080A0,0x608060,0x401EB4,0x4012EF,0x607000,0x606E80,0x401CA6,0x40130A,0x606F20,0x6074A0,0x401D5B,0x401312,0x606F40,0x607E80,0x401EB4,0x401319,0x607C60,0x606FC0,0x401EB4,0x401324,0x607980,0x607C80,0x401EB4,0x40132A,0x0,0x6077A0,0x0,0x40133D,0x0,0x606D00,0x0,0x40137F,0x607D20,0x400820,0x401EA5,0x40138A,0x6070C0,0x606E40,0x401CA6,0x40138B,0x0,0x607A40,0x0,0x401390,0x607320,0x400810,0x401EA5,0x4013C3,0x6075A0,0x606C40,0x401EB4,0x4013CE,0x6078A0,0x606FC0,0x401EB4,0x4013E0,0x607680,0x606C40,0x401EB4,0x4013F4,0x606AE0,0x607540,0x401F0C,0x401407,0x607AE0,0x606DC0,0x401EB4,0x40140F,0x607440,0x400810,0x401EA5,0x40141C,0x6076E0,0x400860,0x401EA5,0x40142F,0x0,0x607A20,0x0,0x401441,0x607940,0x607F60,0x401EB4,0x401476,0x606E20,0x6075C0,0x401CA6,0x40148B,0x0,0x607EA0,0x0,0x401490,0x0,0x607FA0,0x0,0x401498,0x607180,0x400820,0x401EA5,0x4014A0,0x0,0x0,0x401E96,0x4014A1,0x6075E0,0x606DC0,0x401EB4,0x4014AD,0x0,0x607900,0x0,0x4014BD,0x0,0x0,0x401E96,0x4014C4,0x607BC0,0x606FC0,0x401EB4,0x4014C7,0x0,0x607020,0x0,0x4014CD,0x607240,0x607DA0,0x401CA6,0x4014D5,0x0,0x0,0x401E96,0x4014DA,0x0,0x607D60,0x0,0x4014DC,0x607F20,0x6076A0,0x401EB4,0x4014F1,0x0,0x606F00,0x0,0x4014FA,0x606C80,0x607780,0x401EB4,0x401505,0x0,0x0,0x401E96,0x401518,0x0,0x607FA0,0x0,0x40151A,0x6076C0,0x607BA0,0x401EB4,0x401525,0x607700,0x400840,0x401EA5,0x401538,0x607880,0x607760,0x401CA6,0x401561,0x607200,0x607640,0x401C31,0x401566,0x606C20,0x607120,0x401EB4,0x401576,0x6071A0,0x400900,0x401EA5,0x401589,0x6070E0,0x400820,0x401EA5,0x40158A,0x607820,0x6076A0,0x401EB4,0x40158B,0x607E40,0x4008C0,0x401EA5,0x401594,0x606EC0,0x607F00,0x401CA6,0x40159C,0x0,0x0,0x401E96,0x4015A1,0x0,0x0,0x401E96,0x4015AD,0x607660,0x607E60,0x401CA6,0x4015B2,0x607E20,0x607BA0,0x401EB4,0x4015BA,0x607D40,0x607F60,0x401EB4,0x4015CC,0x0,0x0,0x401E96,0x4015E1,0x6073E0,0x606FC0,0x401EB4,0x4015E3,0x607AA0,0x6070E0,0x401D5B,0x401612,0x607360,0x607E80,0x401EB4,0x401633,0x606BE0,0x4008A0,0x401EA5,0x40163E,0x6079A0,0x606FC0,0x401EB4,0x401658,0x606EA0,0x607F60,0x401EB4,0x40165E,0x608040,0x607960,0x401D22,0x401672,0x607A60,0x606FC0,0x401EB4,0x401684,0x0,0x607B00,0x0,0x4016BF,0x606B40,0x400900,0x401EA5,0x401702,0x0,0x0,0x401E96,0x401703,0x607B20,0x606FC0,0x401EB4,0x40170A,0x6072E0,0x606CC0,0x401CA6,0x401713,0x607C20,0x607220,0x401EB4,0x40171B,0x0,0x6078E0,0x0,0x401726,0x607380,0x607220,0x401EB4,0x4017B9,0x606E60,0x606FC0,0x401EB4,0x4017C4,0x0,0x607620,0x0,0x4017D1,0x6072A0,0x606C40,0x401EB4,0x4017E1,0x607800,0x607180,0x401D5B,0x4017F4,0x607FC0,0x400810,0x401EA5,0x401802,0x0,0x607F80,0x0,0x40180D,0x0,0x607D60,0x0,0x401831,0x0,0x607900,0x0,0x401839,0x0,0x607020,0x0,0x401843,0x606BA0,0x607780,0x401EB4,0x40184E,0x606FE0,0x606FC0,0x401EB4,0x40185B,0x0,0x606D20,0x0,0x401864,0x607580,0x606FC0,0x401EB4,0x4018A3,0x606C60,0x607DE0,0x401CA6,0x4018B7,0x6073A0,0x606E00,0x401CA6,0x4018BC,0x0,0x606FA0,0x0,0x4018CC,0x6073C0,0x400810,0x401EA5,0x4018CE,0x0,0x0,0x401E96,0x4018E2,0x607560,0x607220,0x401EB4,0x4018E5,0x0,0x607520,0x0,0x4018F0,0x0,0x6077A0,0x0,0x40193C,0x0,0x606D20,0x0,0x401953,0x607EE0,0x608060,0x401EB4,0x40195E,0x607300,0x607740,0x401C31,0x4019A2,0x607D80,0x607E00,0x401EB4,0x4019AC,0x606DA0,0x608080,0x401C31,0x4019B7,0x0,0x606CE0,0x0,0x4019CA,0x607CE0,0x607780,0x401EB4,0x4019D2]

恢复出各个 block 的信息:

block_0
# ip_0: block_53 
# ip_1: block_169 
# handler: ret 4 
# child_address: 004009F7 


block_1
# ip_0: block_84 
# ip_1: 0x4008d0 
# handler: ret 3 
# child_address: 00400AFD 


block_2
# ip_0: 0x0 
# ip_1: block_34 
# handler: NAN 
# child_address: 00400B03 


block_3
# ip_0: block_152 
# ip_1: block_154 
# handler: ret 4 
# child_address: 00400B6E 


block_4
# ip_0: block_115 
# ip_1: 0x400870 
# handler: ret 3 
# child_address: 00400B79 

……………………………………………………

其实到这里就可以通过每个 block 之间的关系对子进程的执行流进行人肉分析了,不过工作量较大,更好的方法是直接跟踪或者模拟子进程的执行流。

我的思路是用 Unicorn 加载子进程的代码,模拟父进程 VM 的操作,抓取子进程的执行流。

因为程序比较复杂,还要模拟 plt 表的调用,所以我直接拿 AI 写脚本 😈。

import struct
import sys
import os
from typing import Optional, Dict, List, TextIO, Tuple, Any
from capstone import Cs, CS_ARCH_X86, CS_MODE_64
from unicorn.unicorn import Uc, UcError
from unicorn.unicorn_const import UC_ARCH_X86, UC_MODE_64, UC_HOOK_CODE, UC_HOOK_MEM_WRITE, UC_HOOK_MEM_READ, UC_MEM_WRITE, UC_MEM_READ
from unicorn.x86_const import UC_X86_REG_RIP, UC_X86_REG_RSP, UC_X86_REG_RBP, UC_X86_REG_EFLAGS, UC_X86_REG_RAX, UC_X86_REG_FS_BASE

# VM指令结构体定义
class VMInstruction:
    def __init__(self, next_vm_ip_0, next_vm_ip_8, handler, child_code_address):
        self.next_vm_ip_0 = next_vm_ip_0
        self.next_vm_ip_8 = next_vm_ip_8
        self.handler = handler
        self.child_code_address = child_code_address
    
    def __str__(self):
        return f"VMInst(ip0={hex(self.next_vm_ip_0)}, ip8={hex(self.next_vm_ip_8)}, handler={hex(self.handler)}, child={hex(self.child_code_address)})"

class VMSimulator:
    def __init__(self, binary_path: str, vm_code_table: List[int]):
        self.binary_path = binary_path
        self.binary_data: Optional[bytes] = None
        self.uc: Optional[Uc] = None
        self.vm_instructions: List[VMInstruction] = []  # 所有VM指令列表
        self.child_addr_to_vm: Dict[int, VMInstruction] = {}  # child_code_address -> VM指令 的映射
        self.vm_stack: List[int] = []         # VM栈
        self.vm_ip: int = 0             # 当前VM指令指针
        self.trace_file: Optional[TextIO] = None
        self.instruction_count: int = 0
        self.int3_count: int = 0
        self.vm_code_table = vm_code_table  # 传入的VM指令表
        
        self.vm_block_base = 0x606AC0
        self.vm_block_end = 0x608158
        self.vm_block_size = 32
        
        self.plt_stubs = {
            0x400810: "puts",
            0x400820: "__stack_chk_fail",
            0x400830: "printf",
            0x400840: "memset",
            0x400850: "alarm",
            0x400860: "read",
            0x400870: "srand",
            0x400880: "signal",
            0x400890: "ptrace",
            0x4008A0: "setvbuf",
            0x4008B0: "perror",
            0x4008C0: "atoi",
            0x4008D0: "exit",
            0x4008E0: "wait",
            0x4008F0: "fork",
            0x400900: "rand",
        }
        
        # 初始化Unicorn引擎
        self.initialize_unicorn()
        
        # 从提供的表中加载VM指令
        self.load_vm_instructions_from_table()
        
        # 初始化反汇编器
        self.md = Cs(CS_ARCH_X86, CS_MODE_64)
        self.md.detail = True
        
    def initialize_unicorn(self):
        """初始化Unicorn模拟器"""
        print("[+] 初始化Unicorn模拟器...")
        
        # 创建Unicorn实例
        self.uc = Uc(UC_ARCH_X86, UC_MODE_64)
        
        self.uc.mem_map(0x400000, 0x210000)
        
        self.uc.mem_map(0x7ffffffde000, 0x21000)
        
        self.uc.mem_map(0x7ffff7ff6000, 0x1000) 
        
        # 加载二进制文件
        with open(self.binary_path, 'rb') as f:
            self.binary_data = f.read()
        
        print(f"[+] 二进制文件大小: {len(self.binary_data)} 字节 (0x{len(self.binary_data):x})")
        
        self.uc.mem_write(0x400000, self.binary_data[0x0:0x5288])
        self.uc.mem_write(0x605d58, self.binary_data[0x5d58:0x5d58+0xcf8])
        print(f"[+] 已按LOAD段加载: 0x400000(0x5288字节) + 0x605d58(0xcf8字节)")
        
        # 设置初始寄存器状态
        # 从反编译代码得知: sub_401B83() 调用 sub_4009F7() 作为子进程入口
        self.uc.reg_write(UC_X86_REG_RIP, 0x4009F7)  # 子进程入口点 sub_4009F7
        self.uc.reg_write(UC_X86_REG_RSP, 0x7fffffffe000)  # 栈顶指针
        self.uc.reg_write(UC_X86_REG_RBP, 0x7fffffffe000)  # 基址指针
        self.uc.reg_write(UC_X86_REG_FS_BASE, 0x7ffff7ff6000)  # FS段基址(用于TLS)
        
        # 在TLS区域写入stack canary (fs:0x28处)
        import struct
        self.uc.mem_write(0x7ffff7ff6000 + 0x28, struct.pack('<Q', 0x1234567890ABCDEF))
        
        print(f"[+] 内存映射完成:")
        print(f"    代码+数据段: 0x400000 - 0x610000")
        print(f"    栈空间: 0x7ffffffde000 - 0x7ffffffff000")
        print(f"    TLS段: 0x7ffff7ff6000 - 0x7ffff7ff7000")
        print(f"[+] 代码已加载到 0x400000")
        print(f"[+] 入口点设置为: 0x4009F7 (sub_4009F7)")
        
    def load_vm_instructions_from_table(self):
        """从提供的VM指令表加载指令
        
        注意: VM指令表的结构是每4个元素一组:
        [next_vm_ip_0, next_vm_ip_8, handler, child_code_address]
        """
        print("[+] 从VM指令表加载指令...")
        
        # 检查VM指令表长度
        if len(self.vm_code_table) % 4 != 0:
            print(f"[-] VM指令表长度不是4的倍数: {len(self.vm_code_table)}")
            return
            
        # 每4个元素组成一个VM指令
        num_instructions = len(self.vm_code_table) // 4
        print(f"[+] 发现 {num_instructions} 个VM指令")
        
        for i in range(num_instructions):
            idx = i * 4
            ip0 = self.vm_code_table[idx]
            ip8 = self.vm_code_table[idx + 1]
            handler = self.vm_code_table[idx + 2]
            child_addr = self.vm_code_table[idx + 3]
            
            # 创建VM指令对象
            vm_inst = VMInstruction(ip0, ip8, handler, child_addr)
            self.vm_instructions.append(vm_inst)
            
            # 建立child_code_address到VM指令的映射
            if child_addr != 0:
                self.child_addr_to_vm[child_addr] = vm_inst
            
            # 打印前10个指令的信息
            if i < 10:
                print(f"    VM指令{i}: {vm_inst}")
                
        print(f"[+] 成功加载 {len(self.vm_instructions)} 个VM指令")
        print(f"[+] child_addr索引数: {len(self.child_addr_to_vm)}")
    
    def find_vm_inst_by_vm_ip(self, vm_ip: int) -> Optional[VMInstruction]:
        """通过VM_IP查找VM指令
        
        VM_IP可能是两种情况:
        1. VM block地址(0x606AC0-0x608158范围内): 通过 (vm_ip - 0x606AC0) // 32 计算block索引
        2. 子进程代码地址: 通过child_code_address查找
        
        参数:
            vm_ip: VM指令指针
        返回:
            找到的VM指令对象,如果未找到则返回None
        """
        # 情况1: VM block地址
        if self.vm_block_base < vm_ip <= self.vm_block_end:
            block_index = (vm_ip - self.vm_block_base) // self.vm_block_size
            if 0 <= block_index < len(self.vm_instructions):
                print(f"[VM] VM_IP {hex(vm_ip)} -> block_{block_index}")
                return self.vm_instructions[block_index]
            else:
                print(f"[VM] block索引越界: {block_index}, vm_ip={hex(vm_ip)}")
                return None
        
        # 情况2: 子进程代码地址
        inst = self.child_addr_to_vm.get(vm_ip)
        if inst:
            print(f"[VM] VM_IP {hex(vm_ip)} -> child_code_address匹配")
        return inst
    
    def handler_dict(self):
        """Handler函数映射表
        根据父进程反编译代码,handler返回值含义:
        - 0: 跳转到 next_vm_ip_0
        - 1: 跳转到 next_vm_ip_1  
        - 2: pop栈操作
        - 3: call操作(写栈,跳转到 next_vm_ip_1)
        - 4: push栈操作
        - 5: 通过子进程rip查找VM指令
        """
        return {
            0x401EB4: ("push_op", 4),       # VM栈压入
            0x401EA5: ("call_mem_op", 3),   # 内存写入+调用
            0x401E96: ("pop_op", 2),        # VM栈弹出
            0x401CA6: ("jle", 0, 1),        # 条件跳转: a <= b,返回0或1
            0x401D22: ("jne", 0, 1),        # 条件跳转: a != b,返回0或1
            0x401D5B: ("je", 0, 1),         # 条件跳转: a == b,返回0或1
            0x401DCD: ("jns", 0, 1),        # 条件跳转: 非负,返回0或1
            0x401F0C: ("jg", 0, 1),         # 条件跳转: a > b,返回0或1
            0x401C31: ("jl", 0, 1),         # 条件跳转: a < b,返回0或1
        }
    
    def execute_vm_instruction(self, current_rip: int) -> bool:
        """执行VM指令 - 模拟父进程的VM处理逻辑
        
        参数:
            current_rip: 当前block的起始地址(child_code_address)
        """
        if self.uc is None:
            print("[-] Unicorn引擎未初始化")
            return False
            
        print(f"\n[VM] 执行VM指令,子进程RIP: {hex(current_rip)}")
        
        # 通过child_code_address查找对应的VM指令
        vm_inst = self.child_addr_to_vm.get(current_rip)
        
        if not vm_inst:
            print(f"[-] 未找到child_code_address={hex(current_rip)} 对应的VM指令")
            return False
        
        print(f"[VM] VM指令详情: {vm_inst}")
        
        is_jmp = True  # 默认要跳转到 child_code_address
        next_vm_inst = None  # 下一个要执行的VM指令
        
        # 检查是否有handler
        if vm_inst.handler == 0:
            # 没有handler的情况:进入 next_vm_ip_1 的块
            print(f"[VM] 无handler,跳转到 next_vm_ip_1: {hex(vm_inst.next_vm_ip_8)}")
            self.vm_ip = vm_inst.next_vm_ip_8
            next_vm_inst = self.find_vm_inst_by_vm_ip(vm_inst.next_vm_ip_8)
        else:
            # 获取handler信息
            handler_info = self.handler_dict().get(vm_inst.handler)
            if not handler_info:
                print(f"[-] 未知的handler: {hex(vm_inst.handler)}")
                return False
            
            handler_name = handler_info[0]
            handler_retvals = handler_info[1:]
            
            print(f"[VM] Handler: {handler_name} (可能返回值: {handler_retvals})")
            
            # 根据handler类型执行相应的VM操作
            if len(handler_retvals) == 1:
                handler_retval = handler_retvals[0]
            else:
                # 条件跳转,需要检查条件
                condition_met = self.check_condition(handler_name)
                handler_retval = 1 if condition_met else 0
                print(f"[VM] 条件跳转结果: {handler_retval}")
            
            # 根据返回值执行操作(参考父进程代码)
            if handler_retval == 0:
                # case 0: 进入 next_vm_ip_0 的块
                self.vm_ip = vm_inst.next_vm_ip_0
                next_vm_inst = self.find_vm_inst_by_vm_ip(vm_inst.next_vm_ip_0)
                print(f"[VM] Handler返回0,跳转到 next_vm_ip_0: {hex(vm_inst.next_vm_ip_0)}")
                
            elif handler_retval == 1:
                # case 1: 跳转到 next_vm_ip_1, 子进程进入 child_code_address
                self.vm_ip = vm_inst.next_vm_ip_8
                next_vm_inst = self.find_vm_inst_by_vm_ip(vm_inst.next_vm_ip_8)
                print(f"[VM] Handler返回1,跳转到 next_vm_ip_1: {hex(vm_inst.next_vm_ip_8)}")
                
            elif handler_retval == 2:
                # case 2: pop vm_stack -> vm_code, 子进程进入 child_code_address
                if not self.vm_stack:
                    print("[-] VM栈为空,无法执行pop_op, 让程序继续执行")
                    # VM栈为空时,不设置next_vm_inst,让程序自然继续执行
                    rsp = self.uc.reg_read(UC_X86_REG_RSP)
                    self.uc.reg_write(UC_X86_REG_RSP, rsp + 8)
                    is_jmp = False
                    return True
                self.vm_ip = self.vm_stack.pop()
                next_vm_inst = self.find_vm_inst_by_vm_ip(self.vm_ip)
                rsp = self.uc.reg_read(UC_X86_REG_RSP)
                self.uc.reg_write(UC_X86_REG_RSP, rsp + 8)
                print(f"[VM] 执行pop_op, 新VM_IP: {hex(self.vm_ip)}, RSP+8")
                
            elif handler_retval == 3:
                # case 3: 子进程栈上写入**下一个vm_code块**的 child_code_address, 子进程进入 next_vm_ip_1
                # 重要: 父进程先更新vm_code,然后写入新vm_code的child_code_address!
                self.vm_ip = vm_inst.next_vm_ip_0
                next_vm_inst = self.find_vm_inst_by_vm_ip(vm_inst.next_vm_ip_0)
                
                # 更新RSP和栈内容
                rsp = self.uc.reg_read(UC_X86_REG_RSP)
                self.uc.reg_write(UC_X86_REG_RSP, rsp - 8)
                
                # 关键: 写入**新vm_code**的child_code_address,不是当前的!
                if next_vm_inst:
                    ret_addr = next_vm_inst.child_code_address
                    print(f"[VM] call_mem_op准备: RSP={hex(rsp)}, 写入返回地址={hex(ret_addr)} (新vm_code的child_code_address)")
                    self.uc.mem_write(rsp - 8, struct.pack('<Q', ret_addr))
                else:
                    print(f"[VM] 警告: 找不到next_vm_inst for {hex(vm_inst.next_vm_ip_0)}")
                
                # 设置子进程新的RIP到next_vm_ip_1
                new_rip = vm_inst.next_vm_ip_8
                self.uc.reg_write(UC_X86_REG_RIP, new_rip)
                is_jmp = False  # 已经手动设置了RIP,不需要再跳转
                print(f"[VM] 执行call_mem_op, 子进程RIP设为{hex(new_rip)}")
                
            elif handler_retval == 4:
                # case 4: 入栈 next_vm_ip_0, 进入 next_vm_ip_1, 子进程进入 child_code_address
                if len(self.vm_stack) > 48:
                    print("[-] VM栈溢出")
                    return False
                self.vm_stack.append(vm_inst.next_vm_ip_0)
                self.vm_ip = vm_inst.next_vm_ip_8
                next_vm_inst = self.find_vm_inst_by_vm_ip(vm_inst.next_vm_ip_8)
                rsp = self.uc.reg_read(UC_X86_REG_RSP)
                self.uc.reg_write(UC_X86_REG_RSP, rsp - 8)
                print(f"[VM] 执行push_op, 入栈{hex(vm_inst.next_vm_ip_0)}, VM栈深度: {len(self.vm_stack)}")
                print(f"[VM] 查找next_vm_inst: key={hex(vm_inst.next_vm_ip_8)}, found={next_vm_inst is not None}")
                if next_vm_inst:
                    print(f"[VM] next_vm_inst: {next_vm_inst}")
                
            elif handler_retval == 5:
                # case 5: 通过子进程的 rip 找到对应的 VM_Instruction 块
                if len(self.vm_stack) > 48:
                    print("[-] VM栈溢出")
                    return False
                self.vm_stack.append(vm_inst.next_vm_ip_0)
                rsp = self.uc.reg_read(UC_X86_REG_RSP)
                self.uc.reg_write(UC_X86_REG_RSP, rsp - 8)
                # 读取子进程的RIP
                child_rip = self.uc.reg_read(UC_X86_REG_RIP)
                # 查找对应的VM指令
                next_vm_inst = self.child_addr_to_vm.get(child_rip)
                if not next_vm_inst:
                    print(f"[-] 未找到RIP {hex(child_rip)} 对应的VM指令")
                    return False
                # 更新VM_IP(需要找到哪个VM_IP指向这个指令)
                # 这里简化处理:使用next_vm_ip_0
                self.vm_ip = next_vm_inst.next_vm_ip_0
                print(f"[VM] 通过RIP {hex(child_rip)} 找到VM指令,VM_IP设为: {hex(self.vm_ip)}")
                is_jmp = False  # 不需要默认跳转
                
            else:
                print(f"[-] 未处理的handler返回值: {handler_retval}")
                return False
        
        # 默认将子进程跳转到 vm_code 对应的 child_code_address(如果 is_jmp=True)
        if is_jmp and next_vm_inst:
            self.uc.reg_write(UC_X86_REG_RIP, next_vm_inst.child_code_address)
            print(f"[VM] 设置子进程RIP为下一个VM指令的child_code_address: {hex(next_vm_inst.child_code_address)}")
            
        return True
    
    def find_closest_vm_instruction(self, rip):
        """查找最接近的VM指令地址"""
        closest = None
        min_diff = float('inf')
        
        for child_addr in self.child_addr_to_vm.keys():
            diff = abs(child_addr - rip)
            if diff < min_diff:
                min_diff = diff
                closest = child_addr
                
        return closest if min_diff < 0x100 else None  # 只返回在256字节范围内的
    
    def check_condition(self, condition_name: str) -> bool:
        """检查条件跳转的条件是否满足"""
        if self.uc is None:
            print("[-] Unicorn引擎未初始化")
            return False
            
        eflags = self.uc.reg_read(UC_X86_REG_EFLAGS)
        
        # 提取EFLAGS标志位
        zf = (eflags >> 6) & 1   # Zero Flag
        sf = (eflags >> 7) & 1   # Sign Flag  
        of = (eflags >> 11) & 1  # Overflow Flag
        
        print(f"[VM] EFLAGS: ZF={zf}, SF={sf}, OF={of}")
        
        if condition_name == "jle":  # a <= b
            return zf == 1 or sf != of
        elif condition_name == "jne":  # a != b
            return zf == 0
        elif condition_name == "je":   # a == b
            return zf == 1
        elif condition_name == "jns":  # 非负
            return sf == 0
        elif condition_name == "jg":   # a > b
            return zf == 0 and sf == of
        elif condition_name == "jl":   # a < b
            return zf == 0 and sf != of
        else:
            return False
    
    def hook_int3(self, uc, address, size, user_data):
        """int3指令hook函数
        
        重要: int3在每个block的结尾,而child_code_address存的是block起始地址
        所以需要通过child_addr_to_vm映射查找
        """
        self.int3_count += 1
        print(f"\n[!] 遇到int3指令 at {hex(address)} (第{self.int3_count}次)")
        
        # int3在block结尾,需要找到这个block的起始地址(child_code_address)
        # 方法: 遍历child_addr_to_vm,找到child_code_address <= address的最大值
        block_start = None
        for child_addr in self.child_addr_to_vm.keys():
            if child_addr <= address and (block_start is None or child_addr > block_start):
                block_start = child_addr
        
        if block_start is None:
            print(f"[-] 未找到int3地址 {hex(address)} 对应的block起始地址")
            uc.emu_stop()
            return
        
        print(f"[VM] int3所在block的起始地址: {hex(block_start)}")
        
        # 执行VM指令(会在内部设置新的RIP)
        if not self.execute_vm_instruction(block_start):
            print("[-] VM指令执行失败,停止模拟")
            uc.emu_stop()
        
        # 记录到跟踪文件
        if self.trace_file:
            self.trace_file.write(f"INT3 at {hex(address)}, block_start: {hex(block_start)}, VM_IP: {hex(self.vm_ip)}\n")
    
    def hook_code(self, uc, address, size, user_data):
        """代码执行hook函数"""
        from unicorn.x86_const import UC_X86_REG_RAX, UC_X86_REG_RCX, UC_X86_REG_RBP
        
        self.instruction_count += 1
        
        # 调试信息 (可选,默认关闭)
        # if address == 0x400b90:
        #     try:
        #         ptr_bytes = uc.mem_read(0x606a48, 8)
        #         ptr_value = struct.unpack('<Q', ptr_bytes)[0]
        #         print(f"[DEBUG] 0x400b90: 从0x606a48读取指针 = 0x{ptr_value:x}")
        #     except Exception as e:
        #         print(f"[DEBUG] 0x400b90: 无法读取0x606a48: {e}")
        
        # 检查是否是PLT stub调用
        if address in self.plt_stubs:
            self.handle_plt_call(uc, address)
            return
        
        # 读取指令字节
        code = uc.mem_read(address, size)
        
        # 检查是否是int3指令 (0xCC)
        if code and code[0] == 0xCC:
            self.hook_int3(uc, address, size, user_data)
            return
        
        # 反汇编指令
        for insn in self.md.disasm(code, address):
            instruction_str = f"0x{insn.address:x}:\t{insn.mnemonic}\t{insn.op_str}"
            
            # 添加函数调用的注释
            comment = ""
            if insn.mnemonic == "call":
                # 检查是否是PLT调用
                # call指令可能是相对地址或绝对地址
                if "0x" in insn.op_str:
                    try:
                        call_target = int(insn.op_str, 16)
                        func_name = self.plt_stubs.get(call_target)
                        if func_name:
                            # 读取参数
                            from unicorn.x86_const import UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX
                            rdi = uc.reg_read(UC_X86_REG_RDI)
                            rsi = uc.reg_read(UC_X86_REG_RSI)
                            rdx = uc.reg_read(UC_X86_REG_RDX)
                            
                            if func_name == "puts":
                                try:
                                    s = self.read_string(uc, rdi, max_len=50)
                                    comment = f"  ; puts(\"{s}...\")"
                                except:
                                    comment = f"  ; puts(0x{rdi:x})"
                            elif func_name == "printf":
                                try:
                                    fmt = self.read_string(uc, rdi, max_len=50)
                                    comment = f"  ; printf(\"{fmt}...\")"
                                except:
                                    comment = f"  ; printf(0x{rdi:x})"
                            elif func_name == "read":
                                comment = f"  ; read(fd={rdi}, buf=0x{rsi:x}, count={rdx})"
                            elif func_name == "atoi":
                                try:
                                    s = self.read_string(uc, rdi, max_len=20)
                                    comment = f"  ; atoi(\"{s}\")"
                                except:
                                    comment = f"  ; atoi(0x{rdi:x})"
                            elif func_name in ["srand", "alarm"]:
                                comment = f"  ; {func_name}({rdi})"
                            elif func_name == "rand":
                                comment = f"  ; rand()"
                            else:
                                comment = f"  ; {func_name}(...)"
                    except:
                        pass
            
            instruction_str_with_comment = instruction_str + comment
            
            # 输出到控制台 (每1000条指令显示一次)
            if self.instruction_count % 1000 == 0:
                print(f"[{self.instruction_count}] {instruction_str_with_comment}")
            
            # 写入跟踪文件
            if self.trace_file:
                self.trace_file.write(f"{instruction_str_with_comment}\n")
            
            break  # 只取第一个反汇编结果
    
    def handle_plt_call(self, uc, plt_address):
        """处理PLT函数调用
        
        模拟库函数的行为,避免实际调用动态链接的函数
        """
        func_name = self.plt_stubs.get(plt_address, "unknown")
        print(f"\n[PLT] 调用 {func_name}@plt (0x{plt_address:x})")
        
        # 读取函数参数 (x86-64 calling convention: rdi, rsi, rdx, rcx, r8, r9)
        from unicorn.x86_const import UC_X86_REG_RDI, UC_X86_REG_RSI, UC_X86_REG_RDX
        
        rdi = uc.reg_read(UC_X86_REG_RDI)
        rsi = uc.reg_read(UC_X86_REG_RSI)
        rsp = uc.reg_read(UC_X86_REG_RSP)
        
        # 读取返回地址(栈顶)
        ret_addr_bytes = uc.mem_read(rsp, 8)
        ret_addr = struct.unpack('<Q', ret_addr_bytes)[0]
        
        # 记录到trace文件
        if self.trace_file:
            self.trace_file.write(f"\n{'='*60}\n")
            self.trace_file.write(f"[PLT CALL] {func_name}@plt (0x{plt_address:x})\n")
        
    # 模拟不同的库函数
        if func_name == "puts":
            # puts(const char *s) - 打印字符串
            try:
                # 读取字符串(最多256字节)
                string_bytes = bytearray()
                for i in range(256):
                    b = uc.mem_read(rdi + i, 1)[0]
                    if b == 0:
                        break
                    string_bytes.append(b)
                string = string_bytes.decode('utf-8', errors='replace')
                print(f"[PLT] puts(\"{string}\")")
                if self.trace_file:
                    self.trace_file.write(f"  参数: rdi=0x{rdi:x} -> \"{string}\"\n")
                    self.trace_file.write(f"  返回值: {len(string)}\n")
                # 返回值: 非负数表示成功
                uc.reg_write(UC_X86_REG_RAX, len(string))
            except Exception as e:
                print(f"[PLT] puts() 参数读取失败: {e}")
                uc.reg_write(UC_X86_REG_RAX, -1)
                
        elif func_name == "strcmp":
            # strcmp(const char *s1, const char *s2)
            try:
                s1 = self.read_string(uc, rdi)
                s2 = self.read_string(uc, rsi)
                result = (s1 > s2) - (s1 < s2)  # 返回 -1, 0, 或 1
                print(f"[PLT] strcmp(\"{s1}\", \"{s2}\") = {result}")
                uc.reg_write(UC_X86_REG_RAX, result & 0xFFFFFFFFFFFFFFFF)
            except Exception as e:
                print(f"[PLT] strcmp() 失败: {e}")
                uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "strncmp":
            # strncmp(const char *s1, const char *s2, size_t n)
            try:
                rdx = uc.reg_read(UC_X86_REG_RDX)
                s1 = self.read_string(uc, rdi, max_len=rdx)
                s2 = self.read_string(uc, rsi, max_len=rdx)
                # 比较前n个字符
                s1_sub = s1[:rdx]
                s2_sub = s2[:rdx]
                result = (s1_sub > s2_sub) - (s1_sub < s2_sub)
                print(f"[PLT] strncmp(\"{s1}\", \"{s2}\", {rdx}) = {result}")
                uc.reg_write(UC_X86_REG_RAX, result & 0xFFFFFFFFFFFFFFFF)
            except Exception as e:
                print(f"[PLT] strncmp() 失败: {e}")
                uc.reg_write(UC_X86_REG_RAX, 0)
                
        elif func_name == "strlen":
            # strlen(const char *s)
            try:
                s = self.read_string(uc, rdi)
                print(f"[PLT] strlen(\"{s}\") = {len(s)}")
                uc.reg_write(UC_X86_REG_RAX, len(s))
            except Exception as e:
                print(f"[PLT] strlen() 失败: {e}")
                uc.reg_write(UC_X86_REG_RAX, 0)
                
        elif func_name == "atoi":
            # atoi(const char *str)
            try:
                s = self.read_string(uc, rdi)
                # 过滤非数字字符
                s_stripped = ''.join(c for c in s if c.isdigit() or c in '+-')
                result = int(s_stripped) if s_stripped and s_stripped not in ['+', '-'] else 0
                print(f"[PLT] atoi(\"{s[:20]}...\") = {result}")
                if self.trace_file:
                    self.trace_file.write(f"  参数: rdi=0x{rdi:x} -> \"{s[:50]}\"\n")
                    self.trace_file.write(f"  返回值: {result}\n")
                uc.reg_write(UC_X86_REG_RAX, result & 0xFFFFFFFFFFFFFFFF)
            except Exception as e:
                print(f"[PLT] atoi() error, returning 0")
                uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "setvbuf":
            # setvbuf(FILE *stream, char *buf, int mode, size_t size)
            # 设置缓冲区,总是返回成功
            print(f"[PLT] setvbuf(stream=0x{rdi:x}, buf=0x{rsi:x}, ...) = 0")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "alarm":
            # alarm(unsigned int seconds)
            print(f"[PLT] alarm({rdi}) = 0")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "signal":
            # signal(int signum, sighandler_t handler)
            print(f"[PLT] signal({rdi}, 0x{rsi:x}) = 0")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "ptrace":
            # ptrace(enum __ptrace_request request, ...)
            # 在模拟环境中总是返回成功(0)
            print(f"[PLT] ptrace(request={rdi}, ...) = 0")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "time":
            # time(time_t *tloc)
            # 返回一个固定的时间戳
            import time as time_module
            current_time = int(time_module.time())
            if rdi != 0:
                uc.mem_write(rdi, struct.pack('<Q', current_time))
            print(f"[PLT] time(0x{rdi:x}) = {current_time}")
            uc.reg_write(UC_X86_REG_RAX, current_time)
        
        elif func_name == "srand":
            # srand(unsigned int seed)
            print(f"[PLT] srand({rdi})")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "fork":
            # fork() - 在模拟环境中不真正fork,返回0表示子进程
            print(f"[PLT] fork() = 0 (模拟子进程)")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "printf":
            # printf(const char *format, ...) - 简化实现,只处理格式化字符串
            try:
                fmt = self.read_string(uc, rdi)
                # 简单处理,只打印格式化字符串
                print(f"[PLT] printf(\"{fmt}\")")
                if self.trace_file:
                    self.trace_file.write(f"  参数: rdi=0x{rdi:x} -> \"{fmt}\"\n")
                    self.trace_file.write(f"  返回值: {len(fmt)}\n")
                # 返回打印的字符数
                uc.reg_write(UC_X86_REG_RAX, len(fmt))
            except Exception as e:
                print(f"[PLT] printf() 失败: {e}")
                uc.reg_write(UC_X86_REG_RAX, 0)
        
        elif func_name == "read":
            # read(int fd, void *buf, size_t count)
            rdx = uc.reg_read(UC_X86_REG_RDX)

            # 准备模拟输入,带换行表示完成
            if not hasattr(self, "_mock_input"):
                self._mock_input = b"1234567890\n"
                self._mock_input_pos = 0

            remaining = self._mock_input[self._mock_input_pos:]
            data = remaining[:rdx]

            if data:
                uc.mem_write(rsi, data)
                self._mock_input_pos += len(data)
            bytes_read = len(data)

            if bytes_read:
                print(f"[PLT] read(fd={rdi}, buf=0x{rsi:x}, count={rdx}) = {bytes_read} (模拟输入: {data!r})")
                if self.trace_file:
                    self.trace_file.write(f"  参数: fd={rdi}, buf=0x{rsi:x}, count={rdx}\n")
                    self.trace_file.write(f"  模拟输入: {data!r}\n")
                    self.trace_file.write(f"  返回值: {bytes_read}\n")
            else:
                print(f"[PLT] read(fd={rdi}, buf=0x{rsi:x}, count={rdx}) = 0 (无更多模拟输入)")
                if self.trace_file:
                    self.trace_file.write(f"  参数: fd={rdi}, buf=0x{rsi:x}, count={rdx}\n")
                    self.trace_file.write(f"  返回值: 0\n")

            # read 的返回值是实际读取的字节数
            uc.reg_write(UC_X86_REG_RAX, bytes_read)
        
        elif func_name == "perror":
            # perror(const char *s) - 打印错误信息
            try:
                s = self.read_string(uc, rdi)
                print(f"[PLT] perror(\"{s}\")")
                # perror没有返回值(void)
            except Exception as e:
                print(f"[PLT] perror() 失败: {e}")
        
        elif func_name == "wait":
            # wait(int *wstatus) - 等待子进程
            # 在模拟环境中,返回-1表示没有子进程
            print(f"[PLT] wait(0x{rdi:x}) = -1 (无子进程)")
            uc.reg_write(UC_X86_REG_RAX, 0xFFFFFFFFFFFFFFFF)  # -1
        
        elif func_name == "rand":
            # rand() - 生成随机数
            # 返回固定值或简单的伪随机数
            import random
            result = random.randint(0, 0x7FFFFFFF)
            print(f"[PLT] rand() = {result}")
            uc.reg_write(UC_X86_REG_RAX, result)
        
        elif func_name == "__stack_chk_fail":
            # Stack canary检查失败,终止程序
            print(f"[PLT] __stack_chk_fail() - Stack smashing detected!")
            uc.emu_stop()
            return
                
        elif func_name == "exit":
            # exit(int status)
            print(f"[PLT] exit({rdi & 0xFFFFFFFF})")
            uc.emu_stop()
            return
            
        else:
            # 其他函数暂时返回0/success
            print(f"[PLT] {func_name}() - 未实现,返回0")
            uc.reg_write(UC_X86_REG_RAX, 0)
        
        # 模拟函数返回: 弹出返回地址
        # 直接返回到栈上的地址,让程序正常执行
        uc.reg_write(UC_X86_REG_RSP, rsp + 8)
        uc.reg_write(UC_X86_REG_RIP, ret_addr)
        print(f"[PLT] 返回到 0x{ret_addr:x}")
    
    def read_string(self, uc, addr, max_len=256):
        """从内存读取以null结尾的字符串"""
        string_bytes = bytearray()
        for i in range(max_len):
            b = uc.mem_read(addr + i, 1)[0]
            if b == 0:
                break
            string_bytes.append(b)
        return string_bytes.decode('utf-8', errors='replace')
    
    def hook_memory(self, uc, access, address, size, value, user_data):
        """内存访问hook函数"""
        if access == UC_MEM_WRITE:
            access_str = "WRITE"
            value_info = f"value: 0x{value:x}"
        elif access == UC_MEM_READ:
            access_str = "READ"
            value_info = ""
        else:
            access_str = "UNKNOWN"
            value_info = ""
        
        if self.trace_file:
            self.trace_file.write(f"MEM {access_str} at {hex(address)}, size: {size} {value_info}\n")
    
    def start_trace(self, trace_output: str = "execution_trace.log") -> None:
        """开始跟踪执行"""
        if self.uc is None:
            print("[-] Unicorn引擎未初始化")
            return
            
        print(f"[+] 开始执行跟踪,输出文件: {trace_output}")
        
        # 打开跟踪文件
        self.trace_file = open(trace_output, 'w')
        self.trace_file.write("=== VM保护程序执行跟踪 ===\n")
        self.trace_file.write(f"二进制文件: {self.binary_path}\n")
        self.trace_file.write(f"VM指令数: {len(self.vm_instructions)}\n")
        self.trace_file.write("开始执行...\n\n")
        
        try:
            # 添加hook
            self.uc.hook_add(UC_HOOK_CODE, self.hook_code)
            # 暂时禁用内存hook以减少输出
            # self.uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.hook_memory)
            
            # 开始模拟执行
            print("[+] 开始模拟执行...")
            print("[+] 从 sub_4009F7 (0x4009F7) 开始,这是子进程的入口点")
            
            # 从ELF分析和反编译得知:
            # - main() fork后,子进程执行 sub_401B83()
            # - sub_401B83() 调用 ptrace(PTRACE_TRACEME) 然后执行 sub_4009F7()
            start_address = 0x4009F7  # 子进程入口点 sub_4009F7
            end_address = 0x405288    # 代码段结束地址(从ELF分析)
            
            self.uc.emu_start(start_address, end_address, timeout=0, count=0)
            
            print(f"[+] 模拟执行完成")
            print(f"    总指令数: {self.instruction_count}")
            print(f"    int3指令数: {self.int3_count}")
            print(f"    VM指令执行: {self.int3_count} 次")
            
        except UcError as e:
            print(f"[-] 模拟执行错误: {e}")
        
        finally:
            # 关闭跟踪文件
            if self.trace_file:
                self.trace_file.write(f"\n=== 执行统计 ===\n")
                self.trace_file.write(f"总指令数: {self.instruction_count}\n")
                self.trace_file.write(f"int3指令数: {self.int3_count}\n")
                self.trace_file.write(f"VM栈大小: {len(self.vm_stack)}\n")
                self.trace_file.write(f"VM指令命中率: {self.int3_count}/{len(self.vm_instructions)}\n")
                self.trace_file.close()
    
    def analyze_results(self) -> None:
        """分析执行结果"""
        print("\n[+] 执行结果分析:")
        print(f"    VM栈状态: {[hex(addr) for addr in self.vm_stack]}")
        print(f"    当前VM_IP: {hex(self.vm_ip)}")
        
        # 读取当前寄存器状态
        if self.uc is not None:
            try:
                rip = self.uc.reg_read(UC_X86_REG_RIP)
                rsp = self.uc.reg_read(UC_X86_REG_RSP)
                rax = self.uc.reg_read(UC_X86_REG_RAX)
                print(f"    当前RIP: {hex(rip)}")
                print(f"    当前RSP: {hex(rsp)}")
                print(f"    当前RAX: {hex(rax)}")
            except:
                print("    无法读取寄存器状态")
        else:
            print("    Unicorn引擎未初始化")
            
        # 分析VM指令使用情况
        print(f"\n[+] VM指令使用统计:")
        used_instructions = []
        for vm_inst in self.vm_instructions:
            if vm_inst.child_code_address <= self.instruction_count:  # 简化统计
                used_instructions.append(vm_inst.child_code_address)
                
        print(f"    总VM指令数: {len(self.vm_instructions)}")
        print(f"    使用的VM指令数: {len(used_instructions)}")
        if len(used_instructions) > 0:
            print(f"    使用的VM指令示例: {[hex(addr) for addr in used_instructions[:5]]}")

def main():
    if len(sys.argv) != 2:
        print("用法: python vm_simulator.py <目标二进制文件>")
        sys.exit(1)
    
    binary_path = sys.argv[1]
    
    if not os.path.exists(binary_path):
        print(f"错误: 文件 '{binary_path}' 不存在")
        sys.exit(1)
    
    # 你提供的VM指令表
    vm_code_table = [0x607160,0x607FE0,0x401EB4,0x4009F7,0x607540,0x4008D0,0x401EA5,0x400AFD,0x0,0x606F00,0x0,0x400B03,0x607DC0,0x607E00,0x401EB4,0x400B6E,0x607920,0x400870,0x401EA5,0x400B79,0x608000,0x607460,0x401CA6,0x400B7C,0x6077C0,0x607EA0,0x401D22,0x400B81,0x6071E0,0x607BA0,0x401EB4,0x400BAB,0x607140,0x608120,0x401D22,0x400BBE,0x607840,0x400830,0x401EA5,0x400BEB,0x6070A0,0x400870,0x401EA5,0x400BF8,0x607BE0,0x607120,0x401EB4,0x400BFE,0x607AC0,0x607F60,0x401EB4,0x400C10,0x607600,0x607D20,0x401D5B,0x400C25,0x607B80,0x607BA0,0x401EB4,0x400C34,0x0,0x608100,0x0,0x400C47,0x606B80,0x400900,0x401EA5,0x400C4F,0x607B60,0x606EE0,0x401CA6,0x400C74,0x6072C0,0x607A80,0x401CA6,0x400C7C,0x607CA0,0x607F40,0x401CA6,0x400C84,0x0,0x0,0x401E96,0x400C8C,0x607280,0x606FC0,0x401EB4,0x400C96,0x0,0x607A20,0x0,0x400CCF,0x6079C0,0x608060,0x401EB4,0x400CDF,0x0,0x6080C0,0x0,0x400D1F,0x607040,0x400810,0x401EA5,0x400D45,0x608020,0x607E00,0x401EB4,0x400D4D,0x0,0x0,0x401E96,0x400D58,0x0,0x607620,0x0,0x400D5B,0x606D60,0x607120,0x401EB4,0x400D90,0x0,0x607520,0x0,0x400DA3,0x0,0x0,0x401E96,0x400DDB,0x607480,0x607340,0x401D5B,0x400DE0,0x606BC0,0x400900,0x401EA5,0x400DEF,0x606CA0,0x606B00,0x401CA6,0x400E1A,0x0,0x6080C0,0x0,0x400E22,0x6077E0,0x608060,0x401EB4,0x400E3A,0x606DE0,0x400810,0x401EA5,0x400E55,0x0,0x0,0x401E96,0x400E5D,0x607100,0x606D40,0x401D5B,0x400F5C,0x607EC0,0x607860,0x401DCD,0x400F6A,0x0,0x0,0x401E96,0x400F7A,0x0,0x607960,0x0,0x400F83,0x606B20,0x6080E0,0x401CA6,0x400FB1,0x606C00,0x400810,0x401EA5,0x400FC1,0x607080,0x607C80,0x401EB4,0x400FC9,0x6079E0,0x607780,0x401EB4,0x400FDB,0x0,0x606B60,0x0,0x400FEE,0x0,0x0,0x401E96,0x400FF6,0x0,0x0,0x401E96,0x400FF9,0x606D40,0x400820,0x401EA5,0x400FFB,0x607B40,0x606FC0,0x401EB4,0x400FFC,0x0,0x608120,0x0,0x401010,0x607260,0x4008A0,0x401EA5,0x401018,0x0,0x0,0x401E96,0x401032,0x0,0x606D00,0x0,0x401034,0x607060,0x607C80,0x401EB4,0x40104D,0x6071C0,0x606C40,0x401EB4,0x401060,0x0,0x608100,0x0,0x401073,0x0,0x0,0x401E96,0x401080,0x0,0x606B60,0x0,0x40117F,0x607C40,0x4008A0,0x401EA5,0x401184,0x607CC0,0x607120,0x401EB4,0x40119E,0x608140,0x607C80,0x401EB4,0x4011B1,0x0,0x607A40,0x0,0x4011C4,0x0,0x606CE0,0x0,0x4011FF,0x0,0x607C00,0x0,0x40120A,0x0,0x606FA0,0x0,0x40120B,0x0,0x0,0x401E96,0x40120C,0x6078C0,0x608060,0x401EB4,0x401213,0x607D00,0x608060,0x401EB4,0x40122E,0x607400,0x607E00,0x401EB4,0x401249,0x607420,0x400810,0x401EA5,0x401254,0x0,0x607F80,0x0,0x40125C,0x607500,0x607220,0x401EB4,0x40129F,0x6074E0,0x400810,0x401EA5,0x4012AA,0x606DC0,0x4008D0,0x401EA5,0x4012B2,0x0,0x6078E0,0x0,0x4012B8,0x607340,0x400820,0x401EA5,0x4012C0,0x0,0x607C00,0x0,0x4012C1,0x0,0x607B00,0x0,0x4012C3,0x606F60,0x400810,0x401EA5,0x4012E7,0x6080A0,0x608060,0x401EB4,0x4012EF,0x607000,0x606E80,0x401CA6,0x40130A,0x606F20,0x6074A0,0x401D5B,0x401312,0x606F40,0x607E80,0x401EB4,0x401319,0x607C60,0x606FC0,0x401EB4,0x401324,0x607980,0x607C80,0x401EB4,0x40132A,0x0,0x6077A0,0x0,0x40133D,0x0,0x606D00,0x0,0x40137F,0x607D20,0x400820,0x401EA5,0x40138A,0x6070C0,0x606E40,0x401CA6,0x40138B,0x0,0x607A40,0x0,0x401390,0x607320,0x400810,0x401EA5,0x4013C3,0x6075A0,0x606C40,0x401EB4,0x4013CE,0x6078A0,0x606FC0,0x401EB4,0x4013E0,0x607680,0x606C40,0x401EB4,0x4013F4,0x606AE0,0x607540,0x401F0C,0x401407,0x607AE0,0x606DC0,0x401EB4,0x40140F,0x607440,0x400810,0x401EA5,0x40141C,0x6076E0,0x400860,0x401EA5,0x40142F,0x0,0x607A20,0x0,0x401441,0x607940,0x607F60,0x401EB4,0x401476,0x606E20,0x6075C0,0x401CA6,0x40148B,0x0,0x607EA0,0x0,0x401490,0x0,0x607FA0,0x0,0x401498,0x607180,0x400820,0x401EA5,0x4014A0,0x0,0x0,0x401E96,0x4014A1,0x6075E0,0x606DC0,0x401EB4,0x4014AD,0x0,0x607900,0x0,0x4014BD,0x0,0x0,0x401E96,0x4014C4,0x607BC0,0x606FC0,0x401EB4,0x4014C7,0x0,0x607020,0x0,0x4014CD,0x607240,0x607DA0,0x401CA6,0x4014D5,0x0,0x0,0x401E96,0x4014DA,0x0,0x607D60,0x0,0x4014DC,0x607F20,0x6076A0,0x401EB4,0x4014F1,0x0,0x606F00,0x0,0x4014FA,0x606C80,0x607780,0x401EB4,0x401505,0x0,0x0,0x401E96,0x401518,0x0,0x607FA0,0x0,0x40151A,0x6076C0,0x607BA0,0x401EB4,0x401525,0x607700,0x400840,0x401EA5,0x401538,0x607880,0x607760,0x401CA6,0x401561,0x607200,0x607640,0x401C31,0x401566,0x606C20,0x607120,0x401EB4,0x401576,0x6071A0,0x400900,0x401EA5,0x401589,0x6070E0,0x400820,0x401EA5,0x40158A,0x607820,0x6076A0,0x401EB4,0x40158B,0x607E40,0x4008C0,0x401EA5,0x401594,0x606EC0,0x607F00,0x401CA6,0x40159C,0x0,0x0,0x401E96,0x4015A1,0x0,0x0,0x401E96,0x4015AD,0x607660,0x607E60,0x401CA6,0x4015B2,0x607E20,0x607BA0,0x401EB4,0x4015BA,0x607D40,0x607F60,0x401EB4,0x4015CC,0x0,0x0,0x401E96,0x4015E1,0x6073E0,0x606FC0,0x401EB4,0x4015E3,0x607AA0,0x6070E0,0x401D5B,0x401612,0x607360,0x607E80,0x401EB4,0x401633,0x606BE0,0x4008A0,0x401EA5,0x40163E,0x6079A0,0x606FC0,0x401EB4,0x401658,0x606EA0,0x607F60,0x401EB4,0x40165E,0x608040,0x607960,0x401D22,0x401672,0x607A60,0x606FC0,0x401EB4,0x401684,0x0,0x607B00,0x0,0x4016BF,0x606B40,0x400900,0x401EA5,0x401702,0x0,0x0,0x401E96,0x401703,0x607B20,0x606FC0,0x401EB4,0x40170A,0x6072E0,0x606CC0,0x401CA6,0x401713,0x607C20,0x607220,0x401EB4,0x40171B,0x0,0x6078E0,0x0,0x401726,0x607380,0x607220,0x401EB4,0x4017B9,0x606E60,0x606FC0,0x401EB4,0x4017C4,0x0,0x607620,0x0,0x4017D1,0x6072A0,0x606C40,0x401EB4,0x4017E1,0x607800,0x607180,0x401D5B,0x4017F4,0x607FC0,0x400810,0x401EA5,0x401802,0x0,0x607F80,0x0,0x40180D,0x0,0x607D60,0x0,0x401831,0x0,0x607900,0x0,0x401839,0x0,0x607020,0x0,0x401843,0x606BA0,0x607780,0x401EB4,0x40184E,0x606FE0,0x606FC0,0x401EB4,0x40185B,0x0,0x606D20,0x0,0x401864,0x607580,0x606FC0,0x401EB4,0x4018A3,0x606C60,0x607DE0,0x401CA6,0x4018B7,0x6073A0,0x606E00,0x401CA6,0x4018BC,0x0,0x606FA0,0x0,0x4018CC,0x6073C0,0x400810,0x401EA5,0x4018CE,0x0,0x0,0x401E96,0x4018E2,0x607560,0x607220,0x401EB4,0x4018E5,0x0,0x607520,0x0,0x4018F0,0x0,0x6077A0,0x0,0x40193C,0x0,0x606D20,0x0,0x401953,0x607EE0,0x608060,0x401EB4,0x40195E,0x607300,0x607740,0x401C31,0x4019A2,0x607D80,0x607E00,0x401EB4,0x4019AC,0x606DA0,0x608080,0x401C31,0x4019B7,0x0,0x606CE0,0x0,0x4019CA,0x607CE0,0x607780,0x401EB4,0x4019D2]
    
    # 创建模拟器
    simulator = VMSimulator(binary_path, vm_code_table)
    
    # 开始跟踪执行
    simulator.start_trace()
    
    # 分析结果
    simulator.analyze_results()

if __name__ == "__main__":
    main()

运行脚本后会得到子进程执行的所有汇编指令,我们根据这个分析逻辑就行了,虽然有五万多行汇编,不过基本上都是大循环套小循环,直接看也用不了多长时间。(因为怕他的 AES 会魔改,比赛的时候大部分时间都在看 AES 部分的汇编,没想到竟然没有魔改,输麻了)

恢复出的逻辑如下:

from Crypto.Cipher import AES

def srand(seed):
    pass

def rand() -> int: # 这里是 c 的 rand() 和 srand()
    return 0x31

xor_num1 = [
    0xe2, 0x8b, 0x55, 0x38, 0x69, 0xfa, 0x80, 0xc2,
    0x64, 0x4e, 0x7f, 0xe7, 0x13, 0x06, 0x14, 0xc5,
    0xc0, 0x13, 0xd3, 0x12, 0x6b, 0xbd, 0xf2, 0xc7,
    0x88, 0x44, 0x3e, 0x09, 0xe8, 0xa3, 0x83, 0x30
]

sbox = [0x81,0xF7,0x22,0x43,0x9B,0x91,0xEF,0x7,0x54,0x4F,0x18,0xCC,0xED,0xD1,0xBF,0xB3,0xA,0x91,0x1A,0x6F,0x91,0xE4,0xB5,0x37,0x25,0x90,0x9C,0xA6,0x74,0x7,0xF1,0xF0,0x55,0x76,0xC6,0x1E,0x5F,0xC5,0x77,0xE,0x50,0xEB,0x9A,0x16,0x62,0xDE,0x25,0xD0,0xC4,0xD4,0xF0,0xD1,0x73,0x2B,0xF7,0x5D,0x8F,0x56,0xBE,0xEB,0x3,0x84,0x31,0x45,0xEB,0x8,0x79,0x22,0x72,0x94,0xDA,0x62,0x36,0x75,0xA9,0x54,0x3A,0xE5,0x3B,0x41,0x93,0xC2,0xD3,0xFF,0x4B,0x41,0x43,0x9C,0xE2,0x8F,0x80,0x30,0xA2,0xEF,0xDB,0xFF,0x32,0x64,0xFF,0xC3,0x2A,0xB7,0xB3,0x47,0x21,0xB7,0x7D,0x98,0x43,0x3A,0x8B,0x6D,0x91,0xB0,0x93,0x9D,0xF9,0x20,0xCA,0x32,0x34,0xF2,0xE4,0x28,0xF8,0x5C,0x70,0xE2,0x2F,0x87,0x46,0xD4,0x36,0x6D,0xC4,0xD5,0xA0,0xE9,0x1,0xDA,0x77,0x5B,0xD,0xB6,0xA0,0x92,0x9C,0xCE,0x49,0x97,0x62,0x4F,0xCE,0xAA,0x86,0x1D,0x36,0xFD,0x88,0xEB,0x2,0xB9,0x6F,0x32,0x20,0xFC,0xA4,0x9E,0xA6,0x9D,0xD3,0x85,0x82,0x93,0xF0,0xBC,0x27,0xDB,0xE4,0x7F,0xE6,0x68,0xBC,0x6E,0xE4,0x12,0xCA,0xE3,0x8D,0xD9,0x2D,0x38,0x58,0xF3,0x70,0x16,0x75,0x5C,0x34,0x4,0x8C,0x93,0xB,0xF8,0x58,0xBB,0x9F,0x4F,0xB0,0x2D,0x66,0x74,0x23,0xBE,0x4,0xC9,0xE9,0x71,0x69,0xB0,0x6E,0x62,0x9E,0xAE,0x3,0x73,0xCD,0x29,0x0,0x23,0xE,0x56,0xFF,0x50,0xF8,0xE,0xDD,0x53,0x3C,0x1A,0x4C,0xB2,0x5A,0x1F,0xD4,0x5B,0xB0,0xAF,0xC9,0xDD,0x13,0x6,0x58,0xF7,0x38,0x26]

inv_sbox = [0xB0,0x82,0x3F,0xED,0x59,0xCC,0x25,0xFA,0xDB,0xE,0xDC,0x79,0x92,0xF5,0xC3,0x3E,0xBC,0xF0,0xEA,0x90,0xF0,0x92,0xD,0x40,0xBC,0xCA,0x7D,0x4D,0x2C,0x7D,0x6A,0xFB,0xB6,0xF0,0xC1,0xA,0xCF,0x38,0xA3,0xFF,0x8A,0x2,0xEA,0xEC,0x51,0xFC,0xD7,0x8B,0xC8,0x3D,0x72,0x74,0x43,0x64,0xD6,0x75,0xFE,0x1F,0x9C,0x40,0x85,0x28,0x52,0x15,0x90,0x73,0xA3,0x5C,0xEF,0x92,0x18,0xD4,0xB0,0xEA,0x23,0x2B,0x4E,0x63,0xA2,0x57,0xAC,0x63,0x9B,0x42,0x96,0x50,0x4B,0x1D,0xD6,0x51,0x78,0x5E,0x4F,0x2E,0x1E,0xB4,0x72,0x53,0xFE,0x38,0xE5,0x46,0x53,0xC8,0x93,0xAA,0x27,0xE2,0xAB,0xB5,0x51,0xC3,0x10,0x3F,0xCD,0x9D,0xA1,0x82,0xFD,0xC5,0x49,0xEB,0x83,0xA,0xE9,0xB0,0x9,0x5B,0xD8,0x1A,0x44,0x79,0x26,0x9D,0x5C,0x28,0xF3,0xBC,0xB0,0x29,0xE3,0xB2,0xF2,0xB6,0x56,0xBF,0xA7,0x30,0x8C,0x10,0xC7,0x42,0x6A,0xBD,0x9,0xE1,0xF4,0xD6,0x5C,0x5E,0xA5,0x7A,0x4F,0x49,0x74,0xD3,0x45,0x4B,0xB,0xF9,0x1D,0xE9,0xB9,0x1E,0xAA,0xDE,0x3C,0x45,0xED,0x59,0xAD,0x6F,0x8B,0xE7,0x12,0x9F,0xFE,0xF7,0x90,0x6E,0xD9,0xC4,0xFD,0x56,0xFD,0xB,0xE1,0xFD,0x47,0xF3,0xD5,0x5C,0x6F,0xBE,0x34,0x86,0xF8,0x9A,0xA0,0x42,0xAC,0xBB,0x72,0x8,0xB8,0xCA,0xA5,0xA1,0x44,0x96,0x7B,0x6A,0x1F,0xDF,0x42,0x6B,0x6E,0x75,0xC7,0xD0,0x75,0x72,0xAC,0xA5,0xC4,0xDB,0x90,0x55,0x8D,0xA4,0xD7,0x38,0xD7,0x6C,0xD1,0xCA,0x24,0xE1,0x69,0x2D,0x2A,0x6A,0xBD,0x82,0x8F,0x4D,0xDD,0xCC,0xBB,0xAA,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0]

input = [0x31] * 32 # 读取32位输入

expected = [0x43,0x6f,0x6e,0x67,0x72,0x61,0x74,0x75,0x6c,0x61,0x74,0x69,0x6f,0x6e,0x73,0x21,
            0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x6f,0x72,0x72]

# expected 从 "Congratulations! This is the correct flag!" 里面切片获得,先比较 0-15 位,再比较 17-32 位,中间跳过一位空格

srand(65536)

# 这里解出来就是标准的 sbox 和 inv_sbox
for i in range(8):
    for j in range(32):
        idx = i * 32 + j
        sbox[idx] ^= xor_num1[j]
        inv_sbox[idx] ^= xor_num1[j]
        
key = [rand() & 0xff for i in range(16)] # 十六位随机数用作 key

# 后面就是标准的 AES-ECB 了
cipher = AES.new(bytes(key), AES.MODE_ECB)
encrypted = cipher.encrypt(bytes(input))

srand(rand())
xor_num2 = [rand() & 0xff for i in range(32)] # 32位随机数序列用来异或

sum = 0
for i in range(32):
    input[i] ^= xor_num2[i] ^ xor_num1[i]
    if input[i] == expected[i]:
        sum += 1

if sum == 32:
    print("Congratulations! This is the correct flag!")
else:
    print("This is a fake flag!")

解密脚本如下:

from Crypto.Cipher import AES

xor_num1 = [
    0xe2, 0x8b, 0x55, 0x38, 0x69, 0xfa, 0x80, 0xc2,
    0x64, 0x4e, 0x7f, 0xe7, 0x13, 0x06, 0x14, 0xc5,
    0xc0, 0x13, 0xd3, 0x12, 0x6b, 0xbd, 0xf2, 0xc7,
    0x88, 0x44, 0x3e, 0x09, 0xe8, 0xa3, 0x83, 0x30
]

# linux 下跑出随机数
aes_key = [0xf4, 0x70, 0xbb, 0xc0, 0x31, 0xca, 0xee, 0x5e, 
           0x58, 0xb2, 0x72, 0xea, 0x02, 0xf3, 0xff, 0xe6]

xor_num2 = [0xf8,0x44,0xc6,0xba,0xb1,0xe5,0x0e,0x3b,
                0xa2,0x4b,0xb5,0xaa,0x4a,0x89,0xc7,0xa0,
                0x19,0xbd,0xec,0x5e,0xde,0xc1,0xc3,0x87,
                0x75,0xe6,0x12,0x71,0x61,0xea,0xf4,0x59]

expected = [0x43,0x6f,0x6e,0x67,0x72,0x61,0x74,0x75,0x6c,0x61,0x74,0x69,0x6f,0x6e,0x73,0x21,
            0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x74,0x68,0x65,0x20,0x63,0x6f,0x72,0x72]

encrypted = bytes([xor_num2[i] ^ expected[i] ^ xor_num1[i] for i in range(32)])

cipher = AES.new(bytes(aes_key), AES.MODE_ECB)
plaintext = cipher.decrypt(encrypted)

flag = plaintext.decode('ascii')
print(f"flag{{{flag}}}")

# flag{1629d52128ca395e4f6a0fc98712a3e1}

最后附一张比较清晰的执行流程图(根据代 block 在代码中的顺序依次定义为 block_xx ):

vm_cfg

另外,还有一种比较优美的做法,我们可以通过 block 之间的关系把子进程的各个代码块之间用相应的跳转指令连接起来,比如把某个 block 结尾的 int3 patch 成 jmp block_xxxjle blockxxx,这样就可以直接用 IDA 分析伪代码了,至于具体实现留给读者思考。