DDCTF2018 黑盒

351 阅读3分钟

参考: xz.aliyun.com/t/3851 www.freebuf.com/column/1746…

虚拟机组成

vm_init

vm_start

vm_dispatcher

handle

opcodes

DDCTF2018黑盒题目题解

lebel:自构opcode / 类似handle表 / vm_dispatcher / 操作新开辟空间的某偏移的值 / 搞懂opcode和handler /for嵌套

程序流程

  • 输入password,这里要输入解压出的那个txt文件名flag-xxx.txt的xxx的部分
  • 构造opcode序列,调用相关handler,使程序 PaF0!&Prv}H{ojDQ#7v= 变为Binggo 输出Binggo字样
  • 输出txt中的flag

main函数分析

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 result; // rax
  __int64 v4; // rbx
  int i; // [rsp+Ch] [rbp-84h]
  FILE *stream; // [rsp+10h] [rbp-80h]
  char s[8]; // [rsp+20h] [rbp-70h]
  int v8; // [rsp+28h] [rbp-68h]
  __int16 v9; // [rsp+2Ch] [rbp-64h]
  char v10; // [rsp+2Eh] [rbp-62h]
  char filename[8]; // [rsp+30h] [rbp-60h]
  __int64 v12; // [rsp+38h] [rbp-58h]
  __int64 v13; // [rsp+40h] [rbp-50h]
  int v14; // [rsp+48h] [rbp-48h]
  __int16 v15; // [rsp+4Ch] [rbp-44h]
  __int64 ptr; // [rsp+50h] [rbp-40h]
  __int64 v17; // [rsp+58h] [rbp-38h]
  __int64 v18; // [rsp+60h] [rbp-30h]
  __int64 v19; // [rsp+68h] [rbp-28h]
  __int64 v20; // [rsp+70h] [rbp-20h]
  unsigned __int64 v21; // [rsp+78h] [rbp-18h]

  v21 = __readfsqword(0x28u);
  *(_QWORD *)filename = 0LL;
  v12 = 0LL;
  v13 = 0LL;
  v14 = 0;
  v15 = 0;
  *(_QWORD *)s = 0LL;
  v8 = 0;
  v9 = 0;
  v10 = 0;
  ptr = 0LL;
  v17 = 0LL;
  v18 = 0LL;
  v19 = 0LL;
  v20 = 0LL;
  puts("Please input your password!");
  fgets(s, 15, stdin);
  BYTE2(v8) = 0;
  for ( i = 0; i <= 9 && isalnum(s[i]); ++i )   // 检查前十位为数字或字母
    ;
  if ( i == 10 )
  {
    sprintf(filename, "flag-%s.txt", s);        // 存flag的文件
    stream = fopen(filename, "r");
    if ( stream )
    {
      fread(&ptr, 23uLL, 1uLL, stream);         // 文件里读取23个字节
      LOBYTE(v19) = 0;
      fclose(stream);
      puts("---------------------[Welcome To ReverseMe!]---------------------");
      puts("\n\nPlease Input Your Passcode,If You See print the \"Binggo\" string,Congratulations,You Win. Good luck!");
      v4 = operator new(0xAA0uLL);
      sub_401E98(v4);                           // 初始化v4这块空间
      if ( (unsigned int)sub_4016BD(v4) == 0 )  // 程序能正常输入,这步在输入前,所以函数返回值为1
                                                // 看了下大致是一个数组的初始化
      {
        printf("Error code:%x\n", (unsigned int)dword_6038E0);
        exit(0);
      }
      fgets((char *)(v4 + 16), 100, stdin);     // 二次输入
      if ( memcmp("exit", (const void *)(v4 + 16), 4uLL) )
      {
        sub_401A48(v4);
        if ( byte_603F00 )                      // flag,交叉引用定位关键点
          printf("Success!\nYour flag is %s\n", &ptr);
        else
          puts("Failed!");
      }
      sub_401B8B(v4);
      result = 0LL;
    }
    else
    {
      puts("Could not read the flag! Please check your password.");
      result = 1LL;
    }
  }
  else
  {
    puts("Invalid password!");
    result = 1LL;
  }
  return result;
}

看见一个类似flag标志位的地方if(byte_603F00) printf(success) 交叉引用到off_603840,main没调用,看着像handler off_603840交叉引用到sub_4016BD,下断,动态调试发现在次断了9次,应是init opcode和对应的handler 在这里插入图片描述

分析opcode和handler的具体值

sub_401A48的函数指针和循环结构引起了我的注意,很像vm_dispatcher的流程

  • for ( j = 0; j <= 8; ++j ) 对应 for(i=0 ; i < OPCODE_N ; i++) ,OPCODE_N = 9
  • ( byte_603900[v2] (替换) == *(_BYTE *)(a1 + *(int *)(a1 + 4 * (j + 0x48LL) + 8) + 0x198) ) 对应 if(*cpu->eip == cpu->op_list[i].opcode) ,后者存的是所有opcode,此处下断获得具体值
  • *(_QWORD *)(a1 + 672) = *(_QWORD )(a1 + 8 * ((int *)(a1 + 4 * (j + 72LL) + 8) + 0x54LL) + 8);// 后者存的是所有handler,此处下断获得具体值
  • (*(void (__fastcall **)(__int64))(a1 + 672))(a1) 对应 cpu->op_list[i].handle(cpu); 这里一定要调用sub_40133D修改flag
//vm_dispatcher
for(i=0 ; i < OPCODE_N ; i++)
{
        if(*cpu->eip == cpu->op_list[i].opcode) 
        {
            cpu->op_list[i].handle(cpu);
            break;
        }
}
__int64 __fastcall sub_401A48(__int64 a1)
{
  char v2; // [rsp+13h] [rbp-1Dh]
  int i; // [rsp+14h] [rbp-1Ch]
  int j; // [rsp+18h] [rbp-18h]

  if ( a1 && a1 != -16 )
  {
    for ( i = 0; i < strlen((const char *)(a1 + 16)); ++i )// 循环输入长度次,相当于取所有opcode输入
    {
      v2 = *(_BYTE *)(a1 + i + 16);             // 第i个字符
      if ( i + 1 != strlen((const char *)(a1 + 16)) )
        *(_BYTE *)(a1 + 664) = *(_BYTE *)(a1 + i + 1 + 16);// i+1个字符
      for ( j = 0; j <= 8; ++j )                // 9 = OPCODE_N
      {
        if ( byte_603900[v2] == *(_BYTE *)(a1 + *(int *)(a1 + 4 * (j + 0x48LL) + 8) + 0x198) )// 后者存的是opcode
        {
          *(_QWORD *)(a1 + 672) = *(_QWORD *)(a1 + 8 * (*(int *)(a1 + 4 * (j + 72LL) + 8) + 0x54LL) + 8);// 后者存的是handler
          (*(void (__fastcall **)(__int64))(a1 + 672))(a1);// 函数指针,执行Handle,一定要执行到sub_40133D修改flag
        }
      }
    }
  }
  return 0LL;
}

下断后opcode具体值:

# 2A 27 3E 5A 3F 4E 6A 2B 28 
# *'>Z?Nj+(

因为存在一次表替换,所以可得opcode对应的输入:

import sys
begin = 0x603900
end = 0x603900+512 #数组大小
l = ['*','\'','>','Z','?','N','j','+','(']
for j in l:
    for i in range(32,127):#可见字符串范围
        if(chr(Byte(begin+i)) == j):
            sys.stdout.write(chr(i))

# $8Ct0Eu#;

下断后handler的具体值:

func1 0x0000000000400DC1 $对应
func2 0x0000000000400E7A 8对应
func3 0x0000000000400F3A C对应
func4 0x0000000000401064 t对应
func5 0x00000000004011C9
func6 0x000000000040133D  #改变了flag
func7 0x00000000004012F3
func8 0x00000000004014B9
func9 0x0000000000400CF1

分析handler的功能(分析opcode的功能)

静态分析,发现很多*(_BYTE )((_QWORD *)(a1 + 8) + *(int *)(a1 + 288))类似的变量,动态调试,确定其功能如下

  • +655 临时变量var
  • +664 下一位输入
  • +8 str (PaF0!&Prv}H{ojDQ#7v=)
  • +288 index
  • +292 0xFF,变量不能超过char范围
  • +280 0x110在func8中*(_BYTE )((_QWORD *)(a1 + 280) + *(int *)(a1 + 288) + *(_QWORD *)a1)实为str[index]
  • +16 输入的字符串 func8中*(_BYTE *)(a1 + 16+ *(int )(a1 + 288) + (__int64)(char *)(a1 + 664) - 48)实为input[index+下一位输入-48]

分析功能

func1 var = str[index] func2 str[index] = var func3 var = var+下一位输入-33 func4 var = var-下一位输入+33 func5 index++ func6 下一位输入需要为s,修改flag func7 index-- func8 index++ ; index-- ; str[index] = input[index+下一位输入-48]-49 func9 for(i=0;i<下一位输入;i++) index++; str[index] = input[index+下一位输入-48]-49

构建opcode序列

$t/80表示: var = str[0] var = 80 var = var - input[2] + 33 = 66 str[0] = var index++

类似,可构建产生Binggo的opcode序列为 $t/80$C)80$CI80$CX80Cg80\Cj8 处理多余位数 0#J1 index-- uuuuuuu 调用func6 Es 在这里插入图片描述

其它一些点

嵌套for流程图 在这里插入图片描述