[XCTF-Reverse] 62 XCTF 4th-QCTF-2018_asong

这是个比较正规的逆向题

主程序先读入flag然后打开一个文档并统计字频然后加密输出到out文件

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  void *v3; // ST00_8
  const char *v4; // ST08_8

  v3 = malloc(0xBCuLL);
  v4 = (const char *)malloc(0x50uLL);
  init_0();
  read_flag((__int64)v4);
  sub_400C02((__int64)v4);                      // 去皮
  sub_400AAA("that_girl", (__int64)v3);         // v3统计字频
  sub_400E54(v4, (__int64)v3);                  // 加密输出
  return 0LL;
}

加密输入这块先转码再重排序然后整串循环向左移3位

unsigned __int64 __fastcall sub_400E54(const char *a1, __int64 a2)
{
  int i; // [rsp+18h] [rbp-48h]
  int v4; // [rsp+1Ch] [rbp-44h]
  char v5[56]; // [rsp+20h] [rbp-40h]
  unsigned __int64 v6; // [rsp+58h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  v4 = strlen(a1);
  for ( i = 0; i < v4; ++i )
    v5[i] = *(_DWORD *)(4LL * (signed int)trancode(a1[i]) + a2);
  sub_400D33((unsigned __int8 *)v5);            // 交换顺序
  sub_400DB4(v5, v4);                           // 前3后5交错
  write_file((__int64)v5, "out", v4);
  return __readfsqword(0x28u) ^ v6;
}

转码部分,这块由于不知道flag用的字符是哪些,这个转码并不是所有转码都正确(仅有用的字符正确)但逆向时就麻烦了,所以在这里需要改一下再逆。

__int64 __fastcall trancode(char a1)
{
  __int64 result; // rax

  result = (unsigned int)(a1 - 10);
  switch ( a1 )
  {
    case 10:
      result = (unsigned int)(a1 + 35);
      break;
    case 32:
    case 33:
    case 34:
      result = (unsigned int)(a1 + 10);
      break;
    case 39:
      result = (unsigned int)(a1 + 2);
      break;
    case 44:
      result = (unsigned int)(a1 - 4);
      break;
    case 46:
      result = (unsigned int)(a1 - 7);
      break;
    case 58:
    case 59:
      result = (unsigned int)(a1 - 21);
      break;
    case 63:
      result = (unsigned int)(a1 - 27);
      break;
    case 95:
      result = (unsigned int)(a1 - 49);
      break;
    default:
      if ( a1 <= 47 || a1 > 48 )
      {
        if ( a1 <= 64 || a1 > 90 )
        {
          if ( a1 > 96 && a1 <= 122 )
            result = (unsigned int)(a1 - 87);
        }
        else
        {
          result = (unsigned int)(a1 - 55);
        }
      }
      else
      {
        result = (unsigned int)(a1 - 48);
      }
      break;
  }
  return result;
}

交换位置这块看起来先绕,不过想想快排啥的,大意就是按一个顺序表重排

__int64 __fastcall sub_400D33(unsigned __int8 *a1)
{
  __int64 result; // rax
  _BYTE v2[5]; // [rsp+13h] [rbp-5h]

  v2[4] = 0;
  *(_DWORD *)v2 = *a1;
  while ( dword_6020A0[*(signed int *)&v2[1]] )
  {
    a1[*(signed int *)&v2[1]] = a1[dword_6020A0[*(signed int *)&v2[1]]];
    *(_DWORD *)&v2[1] = dword_6020A0[*(signed int *)&v2[1]];
  }
  result = v2[0];
  a1[*(signed int *)&v2[1]] = v2[0];
  return result;
}

整体循环错3位这块,理解起来还不难

_BYTE *__fastcall sub_400DB4(_BYTE *a1, int a2)
{
  _BYTE *result; // rax
  char v3; // [rsp+17h] [rbp-5h]
  int i; // [rsp+18h] [rbp-4h]

  v3 = *a1 >> 5;
  for ( i = 0; a2 - 1 > i; ++i )
    a1[i] = 8 * a1[i] | (a1[i + 1] >> 5);
  result = &a1[i];
  *result = 8 * *result | v3;
  return result;
}

这里换顺序这块可以后作,都是单个字节处理所以后两步不影响顺序,当flag的字符都出来后换顺序可以很容易验证

第1步先循环右移3位

第2步恢复原来的字符

第3步再恢复原来的顺序,表里的序号就是字符在原串中的位置

v3的词频转码表可以gdb中得到,dword_6020a0在程序里有

转换表不区分大小写,大概可以接数字0-9,字母1-35,然后是一堆符号,不能直接用原程序转。

#v3 词频转码表
v3 = [0,0,0,0,0,0,0,0,0,0,104,30,15,29,169,19,38,67,60,0,20,39,28,118,165,26,0,61,51,133,45,7,34,0,62,0,0,0,0,0,0,40,71,0,0,66,245,0,0,0,97,0]
#dword_6020a0 顺序表
dword_6020a0 = [22,0,6,2,30,24,9,1,21,7,18,10,8,12,17,23,13,4,3,14,19,11,20,16,15,5,25,36,27,28,29,37,31,32,33,26,34,35]

#1 循环向右移3位(尾3位移到头)
a = open('out', 'rb').read()
b = ''
for i in a:
    b += bin(i)[2:].rjust(8, '0')
b = b[-3:]+b[:-3]
c = b''
for i in range(0, len(b), 8):
    c += bytes([int(b[i:i+8], 2)])
b = c
print(b)

def trancode(a):
    if a==10:
        return a+35
    elif (a==32) or (a==33) or (a==34):
        return a+10
    elif a==39:
        return a+2
    elif a==44:
        return a-4
    elif a==46:
        return a-7
    elif (a==58) or (a==59):
        return a-21
    elif a==63:
        return a-27
    elif a==95:
        return a-49
    else:
        if a <48 or a>57:   #数字
            if (a<=64) or (a>90):
                if (a>96) and (a<=122):
                    return a-87  #大写字母 不区分
                else:
                    return -1
            else:
                return a-55  #小写字母
        else:
            return a-48  #0-9
    return a-10

code = [0]*256
for i in range(256):
    code[i] = (256+trancode(i))%256
#print(code)
c = ''
for i in b:
    t1 = v3.index(i)
    t2 = code.index(t1)
    c += chr(t2)
    #print(i, t1, t2)
print(c)

a = list(c) 
for i,v in enumerate(dword_6020a0):
    a[v]=c[i]
print(''.join(a))
#THAT_GIRL_SAYING_NO_FOR_YOUR_VINDICATE
#QCTF{that_girl_saying_no_for_your_vindicate}

最后提交的时候加上包裹,由于转码程序不分大小写,大写提交不成改小写成功。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值