nssctf-reverse-[CISCN 2021初赛]babybc

文章讲述了如何处理.bc格式的LLVMIR位码文件,通过`clang`转换为可执行文件,然后用IDA反编译,解析fill_number和docheck函数,最终解出隐藏的数独并进行MD5加密获取flag。

babybc

.bc文件

下载附件后发现是.bc文件有点迷茫,网上查找资料后了解到.bc文件是LLVM Bitcode格式,Bitcode是LLVM IR的不可读二进制形式。

LLVM IR

在LLVM中,IR有三种表示,一种是可读的IR,类似于汇编代码,但其实它介于高等语言和汇编之间,这种表示就是给人看的,磁盘文件后缀为.ll;第二种是不可读的二进制IR,被称作位码(bitcode),磁盘文件后缀为.bc;第三种表示是一种内存格式,只保存在内存中,所以谈不上文件格式和文件后缀,这种格式是LLVM之所以编译快的一个原因,它不像gcc,每个阶段结束会生成一些中间过程文件,它编译的中间数据都是这第三种表示的IR。三种格式是完全等价的,我们可以在Clang/LLVM工具的参数中指定生成这些文件(默认不生成,对于非编译器开发人员来说,也没必要生成),可以通过llvm-asllvm-dis来在前两种文件之间做转换。

(这是我看一位大佬的文章了解到的,附上链接: LLVM基本概念入门_P2Tree的博客-优快云博客

writeup

查看文件类型发现是LLVM IR bitcode 的二进制文件,需要转换成可执行文件后再用IDA反编译。

使用命令:

clang baby.bc -o baby

将生成的可执行文件用IDA打开,查看main函数。

在这里插入图片描述

接着查看fill_number和docheck两个判断函数。

#fill_number
__int64 __fastcall fill_number(__int64 a1)
{
  char v2; // [rsp+1h] [rbp-69h]
  char v3; // [rsp+11h] [rbp-59h]
  char v4; // [rsp+21h] [rbp-49h]
  char v5; // [rsp+31h] [rbp-39h]
  char v6; // [rsp+40h] [rbp-2Ah]
  char v7; // [rsp+41h] [rbp-29h]
  __int64 v8; // [rsp+4Ah] [rbp-20h]
  __int64 v9; // [rsp+52h] [rbp-18h]
  __int64 v10; // [rsp+5Ah] [rbp-10h]

  v10 = 0LL;
  do
  {
    v9 = v10;
    v8 = 5 * v10;
    v7 = *(_BYTE *)(a1 + 5 * v10);
    if ( map[5 * v10] )
    {
      v6 = 0;
      if ( v7 != 48 )
        return v6 & 1;
    }
    else
    {
      map[5 * v10] = v7 - 48;
    }
    v5 = *(_BYTE *)(a1 + v8 + 1);
    if ( map[5 * v10 + 1] )
    {
      v6 = 0;
      if ( v5 != 48 )
        return v6 & 1;
    }
    else
    {
      map[5 * v10 + 1] = v5 - 48;
    }
    v4 = *(_BYTE *)(a1 + v8 + 2);
    if ( map[5 * v10 + 2] )
    {
      v6 = 0;
      if ( v4 != 48 )
        return v6 & 1;
    }
    else
    {
      map[5 * v10 + 2] = v4 - 48;
    }
    v3 = *(_BYTE *)(a1 + v8 + 3);
    if ( map[5 * v10 + 3] )
    {
      v6 = 0;
      if ( v3 != 48 )
        return v6 & 1;
    }
    else
    {
      map[5 * v10 + 3] = v3 - 48;
    }
    v2 = *(_BYTE *)(a1 + v8 + 4);
    if ( map[5 * v10 + 4] )
    {
      v6 = 0;
      if ( v2 != 48 )
        return v6 & 1;
    }
    else
    {
      map[5 * v10 + 4] = v2 - 48;
    }
    ++v10;
    v6 = 1;
  }
  while ( v9 + 1 < 5 );		// 5次循环,每次循环中检验一行的5个元素是否遵循
    			// (如果map数组中对应的位置不为空,我们输入的字符就必须为0,如果为空,那么就用我们输入字符-'0'之后填入对应位置)
    			//所以应该是要填写一个数独
  return v6 & 1;
}

五个字符一循环,分别判断其每一行的1~5位的数字。map[]不为0时,当前的输入字符必须是’0’;如果map[]为0,当前的map字符为输入字符的ASCII码减去48。

map[]是一个二维数组,查看它的值为:

0,0,0,0,0,
0,0,0,0,0,
0,4,0,0,0,
0,0,0,3,0,
0,0,0,0,0,
#docheck
__int64 docheck()
{
  char v1; // [rsp+2Eh] [rbp-9Ah]
  __int64 v2; // [rsp+30h] [rbp-98h]
  __int64 v3; // [rsp+40h] [rbp-88h]
  __int64 v4; // [rsp+50h] [rbp-78h]
  __int64 v5; // [rsp+58h] [rbp-70h]
  char *v6; // [rsp+68h] [rbp-60h]
  __int64 v7; // [rsp+70h] [rbp-58h]
  char v8; // [rsp+7Fh] [rbp-49h]
  char *v9; // [rsp+88h] [rbp-40h]
  __int64 v10; // [rsp+90h] [rbp-38h]
  __int64 v11; // [rsp+98h] [rbp-30h]
  __int64 v12; // [rsp+A8h] [rbp-20h]
  char v13[6]; // [rsp+BCh] [rbp-Ch] BYREF
  char v14[6]; // [rsp+C2h] [rbp-6h] BYREF

  v12 = 0LL;
  do
  {
    v10 = v12;
    memset(v14, 0, sizeof(v14));
    v9 = &v14[(unsigned __int8)map[5 * v12]];
    if ( *v9
      || (*v9 = 1, v14[(unsigned __int8)map[5 * v12 + 1]])
      || (v14[(unsigned __int8)map[5 * v12 + 1]] = 1, v14[(unsigned __int8)map[5 * v12 + 2]])
      || (v14[(unsigned __int8)map[5 * v12 + 2]] = 1, v14[(unsigned __int8)map[5 * v12 + 3]])
      || (v14[(unsigned __int8)map[5 * v12 + 3]] = 1, v14[(unsigned __int8)map[5 * v12 + 4]]) )
    {
      v8 = 0;
      return v8 & 1;
    }
    ++v12;
  }
  while ( v10 + 1 < 5 );
  v11 = 0LL;
  while ( 1 )
  {
    v7 = v11;
    memset(v13, 0, sizeof(v13));
    v6 = &v13[(unsigned __int8)map[v11]];
    if ( *v6 )
      break;
    *v6 = 1;
    if ( v13[(unsigned __int8)byte_405055[v11]] )
      break;
    v13[(unsigned __int8)byte_405055[v11]] = 1;
    if ( v13[(unsigned __int8)byte_40505A[v11]] )
      break;
    v13[(unsigned __int8)byte_40505A[v11]] = 1;
    if ( v13[(unsigned __int8)byte_40505F[v11]] )
      break;
    v13[(unsigned __int8)byte_40505F[v11]] = 1;
    if ( v13[(unsigned __int8)byte_405064[v11]] )
      break;
    ++v11;
    if ( v7 + 1 >= 5 )
    {
      v5 = 0LL;
      while ( 1 )
      {
        v4 = v5;
        if ( row[4 * v5] == 1 )
        {
          if ( (unsigned __int8)map[5 * v5] < (unsigned __int8)map[5 * v5 + 1] )
            goto LABEL_27;
        }
        else if ( row[4 * v5] == 2 && (unsigned __int8)map[5 * v5] > (unsigned __int8)map[5 * v5 + 1] )
        {
LABEL_27:
          v8 = 0;
          return v8 & 1;
        }
        if ( byte_405071[4 * v5] == 1 )
        {
          if ( (unsigned __int8)map[5 * v5 + 1] < (unsigned __int8)map[5 * v5 + 2] )
            goto LABEL_27;
        }
        else if ( byte_405071[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 1] > (unsigned __int8)map[5 * v5 + 2] )
        {
          goto LABEL_27;
        }
        if ( byte_405072[4 * v5] == 1 )
        {
          if ( (unsigned __int8)map[5 * v5 + 2] < (unsigned __int8)map[5 * v5 + 3] )
            goto LABEL_27;
        }
        else if ( byte_405072[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 2] > (unsigned __int8)map[5 * v5 + 3] )
        {
          goto LABEL_27;
        }
        if ( byte_405073[4 * v5] == 1 )
        {
          if ( (unsigned __int8)map[5 * v5 + 3] < (unsigned __int8)map[5 * v5 + 4] )
            goto LABEL_27;
        }
        else if ( byte_405073[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 3] > (unsigned __int8)map[5 * v5 + 4] )
        {
          goto LABEL_27;
        }
        ++v5;
        if ( v4 + 1 >= 5 )
        {
          v3 = 0LL;
          while ( 1 )
          {
            v2 = v3 + 1;
            if ( col[5 * v3] == 1 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3] > (unsigned __int8)map[5 * v2] )
                goto LABEL_26;
            }
            else if ( col[5 * v3] == 2 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3] < (unsigned __int8)map[5 * v2] )
              {
LABEL_26:
                v8 = v1;
                return v8 & 1;
              }
            }
            if ( byte_405091[5 * v3] == 1 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 1] > (unsigned __int8)map[5 * v2 + 1] )
                goto LABEL_26;
            }
            else if ( byte_405091[5 * v3] == 2 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 1] < (unsigned __int8)map[5 * v2 + 1] )
                goto LABEL_26;
            }
            if ( byte_405092[5 * v3] == 1 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 2] > (unsigned __int8)map[5 * v2 + 2] )
                goto LABEL_26;
            }
            else if ( byte_405092[5 * v3] == 2 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 2] < (unsigned __int8)map[5 * v2 + 2] )
                goto LABEL_26;
            }
            if ( byte_405093[5 * v3] == 1 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 3] > (unsigned __int8)map[5 * v2 + 3] )
                goto LABEL_26;
            }
            else if ( byte_405093[5 * v3] == 2 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 3] < (unsigned __int8)map[5 * v2 + 3] )
                goto LABEL_26;
            }
            if ( byte_405094[5 * v3] == 1 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 4] > (unsigned __int8)map[5 * v2 + 4] )
                goto LABEL_26;
            }
            else if ( byte_405094[5 * v3] == 2 )
            {
              v1 = 0;
              if ( (unsigned __int8)map[5 * v3 + 4] < (unsigned __int8)map[5 * v2 + 4] )
                goto LABEL_26;
            }
            ++v3;
            v1 = 1;
            if ( v2 >= 4 )
              goto LABEL_26;
          }
        }
      }
    }
  }
  v8 = 0;
  return v8 & 1;
}
row:							col:
0 0 0 1							0 0 2 0 2
1 0 0 0							0 0 0 0 0
2 0 0 1							0 0 0 1 0
0 0 0 0							0 1 0 0 1
1 0 1 0

img

所以最后的数独图应该是这样的:

在这里插入图片描述

解出数独:

在这里插入图片描述

再将红色的4和3替换为0,最后得到输入字符串为:1425353142350212150442315

再对其进行MD5加密:

在这里插入图片描述

得到最后的flag为:NSSCTF{8a04b4597ad08b83211d3adfa1f61431}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值