一:分析init函数中注册的格式化字符串回调

Untitled

例如:mov_handler

__int64 __fastcall mov_handler(FILE *stream, const struct printf_info *info, const void *const *args)
{
  int prec; // [rsp+24h] [rbp-14h]
  int width; // [rsp+28h] [rbp-10h]
  int src; // [rsp+2Ch] [rbp-Ch]
  char *dst; // [rsp+30h] [rbp-8h]

  width = info->width;
  prec = info->prec;
  if ( (*(info + 12) & 0x20) != 0 )
  {
    dst = &memory[width];
  }
  else if ( (*(info + 12) & 0x40) != 0 )
  {
    dst = &memory[regs[width]];
  }
  else
  {
    dst = &regs[width];
  }
  src = 0;
  if ( (*(info + 13) & 2) != 0 )
  {
    src = *&memory[prec];
  }
  else if ( (*(info + 12) & 2) != 0 )
  {
    src = *&memory[regs[prec]];
  }
  else if ( (*(info + 12) & 1) != 0 )
  {
    src = info->prec;
  }
  else if ( (*(info + 12) & 4) != 0 )
  {
    src = regs[prec];
  }
  *dst = src;
  return 0LL;
}

就是根据info结构体的第12字节开始的某些bit来决定src源操作数和dst目的操作数

又比如:add_handler

__int64 __fastcall add_handler(FILE *stream, const struct printf_info *info, const void *const *args)
{
  int prec; // [rsp+24h] [rbp-14h]
  int width; // [rsp+28h] [rbp-10h]
  int src; // [rsp+2Ch] [rbp-Ch]
  char *dst; // [rsp+30h] [rbp-8h]

  width = info->width;
  prec = info->prec;
  if ( (*(info + 12) & 0x20) != 0 )
  {
    dst = &memory[width];
  }
  else if ( (*(info + 12) & 0x40) != 0 )
  {
    dst = &memory[regs[width]];
  }
  else
  {
    dst = &regs[width];
  }
  src = 0;
  if ( (*(info + 13) & 2) != 0 )
  {
    src = *&memory[prec];
  }
  else if ( (*(info + 12) & 2) != 0 )
  {
    src = *&memory[regs[prec]];
  }
  else if ( (*(info + 12) & 1) != 0 )
  {
    src = info->prec;
  }
  else if ( (*(info + 12) & 4) != 0 )
  {
    src = regs[prec];
  }
  *dst += src;
  return 0LL;
}

同理,也是最后将dst += src

特殊的一个函数:call_handler

__int64 __fastcall jcc_handler(FILE *stream, printf_info *info, const void *const *args)
{
  int prec; // [rsp+24h] [rbp-Ch]
  _BOOL4 v5; // [rsp+2Ch] [rbp-4h]

  prec = info->prec;
  if ( (*(info + 12) & 0x20) != 0 )
  {
    v5 = regs[prec] < 0;
  }
  else if ( (*(info + 12) & 0x40) != 0 )
  {
    v5 = regs[prec] > 0;
  }
  else
  {
    v5 = info->pad != 48 || regs[prec] == 0;
  }
  if ( v5 )
    fprintf(stream, &memory[info->width]);
  return 0LL;
}

这个是根据寄存器的值来判断是否跳转

fprintf(stream, &memory[info->width]);就是再次触发回调,相当于goto

二:使用C++反汇编VM指令

#include <printf.h>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <iostream>
#include <sstream>          //以便下面使用stringstream类型
#include <string>

using namespace std;

char code[2000];
char* regs[32] = { "rax", "rbx", "rcx", "rdx", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26","r27","r28","r29","r30","r31" };
/*
* 定义一个函数来获取src源操作数
*/
string parse_src(const struct printf_info* info)
{
    stringstream src;
    int src_pc = info->prec;

    if ((*((char*)info + 13) & 2) != 0)                 //memory
        src << "[0x" << hex << src_pc << "]";          
    else if ((*((char*)info + 12) & 2) != 0)            //memory[regs[]]
        src << "[" << regs[src_pc] << "]";
    else if ((*((char*)info + 12) & 1) != 0)            //address(rip)
        src << "0x" << hex << src_pc;
    else if ((*((char*)info + 12) & 4) != 0)            //regs[]
        src << regs[src_pc];
    else
        src << "error src";
    return src.str();
}

/*
* 定义一个函数来获取dst目标操作数
*/
string parse_dst(const struct printf_info* info) {
    stringstream dst;
    int dst_pc = info->width;

    if ((*((char*)info + 12) & 0x20) != 0)
        dst << "[0x" << hex << dst_pc << "]";
    else if ((*((char*)info + 12) & 0x40) != 0)
        dst << "[" << regs[dst_pc] << "]";
    else
        dst << regs[dst_pc];

    return dst.str();
}

//register_printf_function('C', call_handler, sub_22F0);
int call_handler(FILE* stream, const struct printf_info* info, const void* const* args)
{
    string cond;
    int addr = info->width;
    int reg_id = info->prec;
    int eflag;
    if ((*((char*)info + 12) & 0x20) != 0)
    {
        cond = "< 0";
        eflag = 1;          //小于则跳转
    }
    else if ((*((char*)info + 12) & 0x40) != 0)
    {
        cond = "> 0"; 
        eflag = 2;          //大于则跳转
    }
    else if (info->pad == 48)
    {
        cond = "== 0";
        eflag = 3;          //je
    }
    else
    {
        eflag = 0;          // jmp
    }
    if (cond.empty() && eflag == 0)
    {
        //printf("jmp 0x%04x\\n", addr, cond.c_str());
        printf("jmp 0x%04x\\n\\n", addr, cond.c_str());     //前面的0x2000我们重新写文件的时候当成内存数据区
    }
    else if(eflag == 3)
    {
        //printf("if(%s %s) {jmp 0x%04x}\\n", regs[reg_id], cond.c_str(), addr);
        printf("test %s %s\\njmp 0x%04x\\n\\n", regs[reg_id], regs[reg_id], addr);
    }
    else if (eflag == 1)
    {
        printf("cmp %s 0\\njb 0x%04x\\n\\n", regs[reg_id], addr);
    }
    else if (eflag == 2)
    {
        printf("cmp %s 0\\nja 0x%04x\\n\\n", regs[reg_id], addr);
    }
    return 0;
}

//register_printf_function('M', mov_handler, sub_22F0);
int mov_handler(FILE* stream, const struct printf_info* info, const void* const* args)
{
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("mov %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

int add_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("add %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('O', subb_handler, sub_22F0);
int sub_handler(FILE* stream, const struct printf_info* info,const void* const* args)
{
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("sub %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

// register_printf_function('X', mul_handler, sub_22F0);
int mul_handler(FILE* stream, const struct printf_info* info,const void* const* args)
{
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("mul %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('V', div_handler, sub_22F0);
int div_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("div %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('N', mod_handler, sub_22F0);
int mod_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("mod %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('L', shl_handler, sub_22F0);
int shl_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("shl %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('R', shr_handler, sub_22F0);
int shr_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("shr %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('E', xor_handler, sub_22F0);
int xor_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("xor %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('I', and_handler, sub_22F0);
int and_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("and %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

//register_printf_function('U', or_handler, sub_22F0);
int or_handler(FILE* stream, const struct printf_info* info,
    const void* const* args) {
    string src = parse_src(info);
    string dst = parse_dst(info);
    printf("or %s, %s\\n", dst.c_str(), src.c_str());
    return 0;
}

int dummy_arginfo(const struct printf_info* info, size_t n, int* argtypes) {
    return 0;
}

//注册格式化字符串回调
void register_conv_specs()
{
    register_printf_function('C', call_handler, dummy_arginfo);
    register_printf_function('M', mov_handler, dummy_arginfo);
    register_printf_function('S', add_handler, dummy_arginfo);
    register_printf_function('O', sub_handler, dummy_arginfo);
    register_printf_function('X', mul_handler, dummy_arginfo);
    register_printf_function('V', div_handler, dummy_arginfo);
    register_printf_function('N', mod_handler, dummy_arginfo);
    register_printf_function('L', shl_handler, dummy_arginfo);
    register_printf_function('R', shr_handler, dummy_arginfo);
    register_printf_function('E', xor_handler, dummy_arginfo);
    register_printf_function('I', and_handler, dummy_arginfo);
    register_printf_function('U', or_handler, dummy_arginfo);
}

//反汇编第一段的 
void disassemble1()
{
    char temp[256];
    char* code_end = code + sizeof(code);         //获取code的结束地址
    char* addr = (char*)memchr(code, '%', sizeof(code));    //获取到第一条指令开始的标志'%'的指针

    while (addr)
    {
        char* end = strchr(addr + 1, '%');                 //找到下一条指令的开始'%s'的指针
        memset(temp, 0, sizeof(temp));
        if (end)                                          //如果可以找到说明这是一条完整的指令
            strncpy(temp, addr, end - addr);              //将指令暂时赋值给temp
        else
            strcpy(temp, addr);

        if (!strstr(temp, "%s"))                          //指令中不含%s,排除了"%52C%s"
        {
            printf("0x%04x: ", addr - code);              
            printf(temp);                                //这里触发回调
        }

        addr = (char*)memchr(addr + 1, '%', code_end - addr - 1);        //找到下一条指令开始的"%s"的指针
    }
}

//反汇编第二段的
void disassemble2()
{
    char temp[256];
    char* code_end = code + sizeof(code);                   //获取code的结束地址
    char* addr = (char*)memchr(code+0xc8, '%', sizeof(code));    //获取到第二段的第一条指令开始的标志'%'的指针

    while (addr)
    {
        char* end = strchr(addr + 1, '%');                 //找到下一条指令的开始'%s'的指针
        memset(temp, 0, sizeof(temp));
        if (end)                                          //如果可以找到说明这是一条完整的指令
            strncpy(temp, addr, end - addr);              //将指令暂时赋值给temp
        else
            strcpy(temp, addr);

        if (!strstr(temp, "%s"))                          //指令中不含%s,排除了"%52C%s"
        {
            printf("0x%04x: ", addr - code);
            printf(temp);                                //这里触发回调
        }

        addr = (char*)memchr(addr + 1, '%', code_end - addr - 1);        //找到下一条指令开始的"%s"的指针
    }
}

void decrypt()                                      //定义来decrypt code
{
    printf("%c\\n", code[0xc8] & 0xff - 0x25);     //这是第一层结尾对0xc8的一个运算,没有写回内存中,printf出来看下是哪个字符
    for (int addr = 0xc8; addr < 0x6fc; ++addr)   //解密第二层
    {
        code[addr] ^= 'T';
        printf("%c",code[addr]);                 //这里将第二层的代码打印到控制台
    }
    printf("\\n");
}

void read_code()
{
    ifstream fin("memory.bin", ios::binary);        //输入文件流
    fin.seekg(0, ios_base::end);                    //把末尾的读指针往回移0字节,获取文件末尾指针
    streampos file_size = fin.tellg();              //告诉文件的大小
    fin.seekg(0, ios_base::beg);                    //重置文件指针到开始
    fin.read(code, file_size);                      //读取文件到code全局变量
}

int main()
{
    register_conv_specs();             //注册回调
    read_code();                       //读取code                     
    disassemble1();                    //反汇编第一段的vm指令
    decrypt();
    disassemble2();                    //反汇编第二段指令 
    return 0;
}

将VM指令第一段反汇编出来如下:

Untitled

分析下:取出flag的第一个字节,进行下面这个运算之后,给rbx赋值位0xc8,rcx赋值0x6fc,然后上面循环用这个字符和memory[0xc8]-memory[0x6fc]的数据进行异或

printf("%x",(((0x61 & 0xff) << 8) | (0x61 & 0xff))&0xff);   //结果还是0x61,不变

说明这里就是一个对memory进行解密的

然后decrypt()函数对代码进行解密: