吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5958|回复: 30
收起左侧

[CTF] 【新手】一道简单的代码解释器逆向题分析

  [复制链接]
chkds 发表于 2018-11-26 15:29
本帖最后由 chkds 于 2018-11-26 15:30 编辑

XNUCA2018  Code_Interpreter
作为一只菜鸡,第一次搞定一个解释器的题目,决定记录一下
工具:gdb、IDA pro 6.8、DIE、010editor
解压后发现有两个文件,一个code,一个Code_Interpreter,首先把Code_Interpreter拖进DIE,确定是64位程序,无壳
die.jpg
拖进IDA进行分析,main函数如下
main.JPG
main函数逻辑很简单,以 rb 的方式读取了一个文件中的数据,键盘输入三个十进制格式的数字,能通过验证就会将输入按十六进制格式组合输出作为flag,重点就在Interpreter那个函数中
根据函数的调用可以看出Interpreter函数所用的参数是来自文件(code)中的数据,反汇编出的Interpreter函数如下:
[C] 纯文本查看 复制代码
__int64 __fastcall Interpreter_400806(__int64 code){
  __int64 result; // rax@1
  int v2; // eax@4
  int v3; // ST1C_4@4
  int v4; // eax@4
  int v5; // ST1C_4@4
  int v6; // eax@4
  int v7; // ST1C_4@4
  unsigned __int8 v8; // ST1A_1@6
  unsigned __int8 v9; // ST1A_1@7
  unsigned __int8 v10; // ST1A_1@8
  unsigned __int8 v11; // ST1A_1@9
  unsigned __int8 v12; // ST1A_1@10
  unsigned __int8 v13; // ST1A_1@11
  unsigned __int8 v14; // ST1A_1@12
  unsigned __int8 v15; // ST1A_1@13
  char v16; // [sp+19h] [bp-7h]@1

  dword_6020A0[0] = dword_6024A0[11];           // first
  dword_6020A4 = dword_6024A0[8];               // second
  result = (unsigned int)dword_6024A0[10];
  dword_6020A8 = dword_6024A0[10];              // third
  dword_6024A0[7] = 0;
  dword_6024A0[6] = 2;
  dword_6024A0[5] = 0;
  v16 = 1;
  while ( v16 )
  {
    switch ( *(_BYTE *)((unsigned int)dword_6024A0[5] + code) )
    {
      case 0:
        v16 = 0;
        break;
      case 1:                                   // code[27],code[66],code[102]
        v2 = ++dword_6024A0[5];
        ++dword_6024A0[5];
        v3 = *(_BYTE *)((unsigned int)v2 + code);//0x6b
        v4 = dword_6024A0[5]++;
        v5 = (*(_BYTE *)((unsigned int)v4 + code) << 8) + v3;//0xcc << 8
        v6 = dword_6024A0[5]++;
        v7 = (*(_BYTE *)((unsigned int)dword_6024A0[5] + code) << 24) + (*(_BYTE *)((unsigned int)v6 + code) << 16) + v5;//0x7e << 16 + 0x1d << 24
        ++dword_6024A0[6];
        dword_6020A0[(unsigned __int64)(unsigned int)dword_6024A0[6]] = v7;
        break;
      case 2:                                   // code[38],code[77],code[113]
        --dword_6024A0[6];
        break;
      case 3:                                   // code[63],code[99]
        ++dword_6024A0[5];
        v8 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];
        dword_6024A0[(unsigned __int64)v8] += dword_6024A0[(unsigned __int64)*(_BYTE *)((unsigned int)dword_6024A0[5]
                                                                                      + code)];// dword_6024a0[0]=dword_6024a0[2];dword_6024a0[2]
        break;
      case 4:                                   // code[24],code[35],code[74],code[110]
        ++dword_6024A0[5];
        v9 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];
        dword_6024A0[(unsigned __int64)v9] -= dword_6024A0[(unsigned __int64)*(_BYTE *)((unsigned int)dword_6024A0[5]
                                                                                      + code)];// dword_6024a0[0]-=dword[3];[1];[1];[1]
        break;
      case 5:                                   // code[18],code[57]
        ++dword_6024A0[5];
        v10 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];
        dword_6024A0[(unsigned __int64)v10] *= *(_BYTE *)((unsigned int)dword_6024A0[5] + code);// dw[1]*=0x15;dw[3]*=3
        break;
      case 6:                                   // code[15],code[54],code[93]
        ++dword_6024A0[5];
        v11 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];                      // dw[1] = dw[1] >> 4;dw[3] = dw[3]>>8;dw[1]=dw[1]>>8
        dword_6024A0[(unsigned __int64)v11] = (unsigned int)dword_6024A0[(unsigned __int64)v11] >> *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        break;
      case 7:                                   // code[21],code[60],code[96]
        ++dword_6024A0[5];
        v12 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];                      // dw[0] = dw[1];dw[0] = dw[3];dw[0] = dw[1]
        dword_6024A0[(unsigned __int64)v12] = dword_6024A0[(unsigned __int64)*(_BYTE *)((unsigned int)dword_6024A0[5]
                                                                                      + code)];
        break;
      case 8:                                   // code[6],code[9],code[12],code[32],code[45],code[48],code[51],code[71],code[84],code[87],code[90],code[107]
        ++dword_6024A0[5];
        v13 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];                      // dw_24a0[1] = dw_20a0[0+0];dw[2] = dw[1];dw[3] = dw[2];dw[1]=dw[3];dw[1]=dw[0];dw[2]=dw[1];dw[3]=dw[2];dw[1]=dw[3];dw[1]=dw[0];dw[2]=dw[1];dw[3]=dw[2];dw[1]=dw[3]
        dword_6024A0[(unsigned __int64)v13] = dword_6020A0[(unsigned __int64)(dword_6024A0[7]
                                                                            + (unsigned int)*(_BYTE *)((unsigned int)dword_6024A0[5] + code))];
        break;
      case 9:                                   // code[0],code[3],code[42],code[81] 清零
        ++dword_6024A0[5];
        v14 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];                      // dw[4] ^= dw[4];dw[0]^=dw[0];dw[0]^=dw[0];dw[0]^=dw[0]
        dword_6024A0[(unsigned __int64)v14] ^= dword_6024A0[(unsigned __int64)*(_BYTE *)((unsigned int)dword_6024A0[5]
                                                                                       + code)];
        break;
      case 0xA:                                 // code[39],code[78],code[114]
        ++dword_6024A0[5];
        v15 = *(_BYTE *)((unsigned int)dword_6024A0[5] + code);
        ++dword_6024A0[5];                      // dw[4] |= dw[0]
        dword_6024A0[(unsigned __int64)v15] |= dword_6024A0[(unsigned __int64)*(_BYTE *)((unsigned int)dword_6024A0[5]
                                                                                       + code)];
        break;
      default:
        fprintf(stderr, "Invalid opcode. %d\n", *(_BYTE *)((unsigned int)dword_6024A0[5] + code));
        exit(1);
        return result;
    }
    result = (unsigned int)(dword_6024A0[5]++ + 1);
  }
  return result;
}

一共10个分支,进行分支跳转和运算的数据均来自code文件,稳妥起见做的时候我在switch的跳转处下了断点,追踪了一下跳转的顺序,在函数中进行了注释,然后根据跳转顺序手动(QAQ)算了一下各个分支的运算大概的结果,最后确定下来这里进行了三次运算,先是将输入的第一个数右移四位,再乘以0x15,减去第三次的输入;二是将第三个输入的数右移8位,乘以3,加第二个输入;三是将输入的第一个数右移8位,加第二个输入,三个式子分别等于case1中计算出的三个常数:0x6b + (0x1d << 24) + (0x7e << 16)  + (0xcc << 8)  =0x1d7ecc6b 、  0x7c + (0x60 << 24) + (0x79 << 16)  + (0x79 << 8) = 0x6079797c 、 0xbd + (0x5f << 24) + (0xbc << 16)  + (0xbd << 8) = 0x5fbcbdbd  PS:这三个常数可以自己看代码直接计算,也可以在gdb里下断点看,下断点比较方便一点,也不易出错,我开始就是算的时候顺序没弄对算错了几次,耽误时间
有了式子,再结合main函数里后面的判断条件,就可以写解题的脚本了,我这里使用的了z3,参考看雪论坛的一个帖子:[原创] z3 巧解CTF逆向题 写的脚本,解题脚本如下:
[Python] 纯文本查看 复制代码
from z3 import *

flag = [BitVec('u%d'%i,32) for i in xrange(3)]
slover = Solver()
slover.add((flag[0] >> 4) * 0x15 - flag[2]  == 0x1d7ecc6b)
slover.add((flag[2] >> 8) * 3 + flag[1] == 0x6079797c)
slover.add((flag[0] >> 8) + flag[1] == 0x5fbcbdbd)
slover.add(flag[0] & 0xff == 0x5e)
slover.add(flag[1] & 0xff0000 ==0x5e0000)
slover.add(flag[2] & 0xff == 0x5e)
slover.check()
result = ""
out = ""
if slover.check() == sat:
    model = slover.model()
    for i in xrange(3):
        result += str(hex(model[flag[i]].as_long().real))
    for i in xrange(len(result)):
        if result[i] == '0' or result[i] == 'x':
            continue
        else:
            out += result[i]
    print "X-NUCA{" + out + '}'
else:
    print 'false'

最后得到flag:X-NUCA{5e5f5e5e5f5e5e5f5e5e5f5e}

结语:这个代码解释器的题还是比较简单的,适合新手练习,就是需要一点耐心,顺便get一下z3的用法
原题见附件
游客通道:https://pan.baidu.com/s/1wh5lG377gwqqNaViSKS9Fg 提取码:ojlr

Code_Interpreter_86ab28978a8fa8d64499788ddcd06c0a.zip

4.53 KB, 下载次数: 5, 下载积分: 吾爱币 -1 CB

原题

免费评分

参与人数 3威望 +1 吾爱币 +9 热心值 +2 收起 理由
先生凡 + 1 我很赞同!
Hmily + 1 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
一脸不屑是傲娇 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| chkds 发表于 2018-12-5 22:07
金野喵君 发表于 2018-11-30 15:40
应该分享下你的对虚拟机分析的结果吧,比如反汇编后的内容。

每个分支的功能和调用有写在IDA那个页面的注释里的
zmr9988 发表于 2018-12-2 14:51
厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害厉害
陌小全 发表于 2018-11-26 15:41
Overcoder 发表于 2018-11-26 15:43
有理有据,感谢大佬的分享!
TuziF 发表于 2018-11-26 15:50
大佬牛啊!很详细
1360423 发表于 2018-11-26 22:23
小白,表示看不大懂
killer0 发表于 2018-11-26 23:08 来自手机
小白路过,凑热闹
大鱼爱吃猫 发表于 2018-11-26 23:22
好深奥的样子,学习啦
huazai996 发表于 2018-11-27 00:08
一脸的兴奋的进来了  , 一脸的懵逼出去了..
buxiaoquan 发表于 2018-11-27 05:38
先从模仿开始,书抄十遍,其意自见!
Hungarian 发表于 2018-11-27 10:07
先学习一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-17 06:09

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表