[BUUOJ] [RE] [ACTF新生赛2020] rome1

IDA

好久没写博客了,最近在刷re,这道题是我觉得十分有意义的一道题。故AC后总结分享给大家。不足之处请指正

分析

直接导入IDA shift +F12
请添加图片描述

双击后按 ctrl + x跳转到被调用的函数中,按F5反编译,源代码如下

int func()
{
  int result; // eax
  int v1[4]; // [esp+14h] [ebp-44h]
  unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
  unsigned __int8 v3; // [esp+25h] [ebp-33h]
  unsigned __int8 v4; // [esp+26h] [ebp-32h]
  unsigned __int8 v5; // [esp+27h] [ebp-31h]
  unsigned __int8 v6; // [esp+28h] [ebp-30h]
  int v7; // [esp+29h] [ebp-2Fh]
  int v8; // [esp+2Dh] [ebp-2Bh]
  int v9; // [esp+31h] [ebp-27h]
  int v10; // [esp+35h] [ebp-23h]
  unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
  char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

  strcpy(v12, "Qsw3sj_lz4_Ujw@l");
  printf("Please input:");
  scanf("%s", &v2);
  result = v2;
  if ( v2 == 65 )
  {
    result = v3;
    if ( v3 == 67 )
    {
      result = v4;
      if ( v4 == 84 )
      {
        result = v5;
        if ( v5 == 70 )
        {
          result = v6;
          if ( v6 == 123 )
          {
            result = v11;
            if ( v11 == 125 )
            {
              v1[0] = v7;
              v1[1] = v8;
              v1[2] = v9;
              v1[3] = v10;
              *(_DWORD *)&v12[17] = 0;
              while ( *(int *)&v12[17] <= 15 )
              {
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
                ++*(_DWORD *)&v12[17];
              }
              *(_DWORD *)&v12[17] = 0;
              while ( *(int *)&v12[17] <= 15 )
              {
                result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
                if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
                  return result;
                ++*(_DWORD *)&v12[17];
              }
              result = printf("You are correct!");
            }
          }
        }
      }
    }
  }
  return result;
}

看着眼花,慢慢来讲
int8的v2-v6本质上就是字符,因为int8就是一个字节和char长度相等,
int32才是我们常说的四个字节
v2-v6和v11其实就是 ACTF{},中间的字符才是关键,也就是v7 v8 v9 v10这四个int32的变量,也就是4*4=16 个ascii字符
v7-v10赋值给了v1[0-3],后面其实就是v1和v12之间的比较,但是这个*((char *) 等指针看的眼花缭乱,下面慢慢理。

记住以下几点:
*((char *) 一个字节
*((_BYTE *) 一个字节
*(_DWORD *) 四个字节 相当于int32

我们分别看看 v1 和 v12的内存视图
v1 : 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
v12 : Qsw3sj_lz4_Ujw@l 0 | 0000 0000 0000
先看v1因为 int v1[4],所以v1有4个int32,分开来看,一个00代表了1字节数据(4个字节正好 1 int)
v1数组可以存4个int32位的整型,也可以看作4*4=16 个ascii字符。把如上反编译代码简化以下得到

int func()
{
  int result; // eax
  int v1[4]; // [esp+14h] [ebp-44h]
  unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
  unsigned __int8 v3; // [esp+25h] [ebp-33h]
  unsigned __int8 v4; // [esp+26h] [ebp-32h]
  unsigned __int8 v5; // [esp+27h] [ebp-31h]
  unsigned __int8 v6; // [esp+28h] [ebp-30h]
  int v7; // [esp+29h] [ebp-2Fh]
  int v8; // [esp+2Dh] [ebp-2Bh]
  int v9; // [esp+31h] [ebp-27h]
  int v10; // [esp+35h] [ebp-23h]
  unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
  char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

  strcpy(v12, "Qsw3sj_lz4_Ujw@l");
  printf("Please input:");
  scanf("%s", &v2);
  result = v2;
  if ( v2 == 65 )
  {
    result = v3;
    if ( v3 == 67 )
    {
      result = v4;
      if ( v4 == 84 )
      {
        result = v5;
        if ( v5 == 70 )
        {
          result = v6;
          if ( v6 == 123 )
          {
            result = v11;
            if ( v11 == 125 )
            {
              v1[0] = v7;
              v1[1] = v8;
              v1[2] = v9;
              v1[3] = v10;
              point = 0; // \*(&v12+17) int point
              //char v12[29] = "Qsw3sj_lz4_Ujw@l";
              while ( point <= 15 )
              {
                if ( *((char *)v1 + point) > 65 && *((char *)v1 + point) <= 90 )
                  *((_BYTE *)v1 + point) = (*((char *)v1 + point) - 51) % 26 + 65;
                /*
                  如果为大写字母
                  a = (a - 51) % 26 + 65
                */

                if ( *((char *)v1 + point) > 96 && *((char *)v1 + point) <= 122 )
                  *((_BYTE *)v1 + point) = (*((char *)v1 + point) - 79) % 26 + 97;
                /*
                  如果为小写字母
                  a = (a - 79) % 26 + 97
                */
                ++point;
              }
              point = 0;
              while ( point <= 15 )
              {
                result = (unsigned __int8)v12[point];
                if ( *((_BYTE *)v1 + point) != (_BYTE)result )
                  return result;
                ++point;
              }
              result = printf("You are correct!");
}

说明一:题目把v12后面没有用到的空间作为一个int整型存数字(虽然不够4个字节?这部分不是很懂,但不影响做题)即上面加粗的部分。

说明二:v1明明是一个int类型的数组,但是题目却使用了char类型指针,说明其实v1数字存放的是ascii字符。我们就应该把v1的内存分开来看,所以我在上面分开了。正好v12字符串的长度和v1的长度相等,v1其实就是我们要找的flag{}中间的那16个未知字符。每一个00 对应了一个字母。

说明三:*(_DWORD *)&v12[17]可以把他看成一个int整型的point,就是计算地址用的,就像数组中a[114]或者*a+114 这个形式

说明四:_BYTE就是char,无需在意。为什么留着这个没有简化?是因为让读者知道这时的v1不能简单地看作int类型数组。

说明五:point 从0到15长度为16,正好与v12字符串长度相等,也和我们flag长度相等。

解题

由简化后的代码可知:输入的字符经过了变换 -> 大小写字母有一个位移变换,其他字符不变。
那么我们把密码本反向构造好flag就直接出来了。

#[ACTF新生赛2020]rome1
# Author me 2023.6.6 22:03
dic = {}
'''
分别构造大小写密码本
'''
for key in range(65,90+1):
    value = ( key - 51) % 26 + 65
    dic[chr(value)] = chr(key)

for key in range(97,122+1):
    value = ( key - 79) % 26 + 97
    dic[chr(value)] = chr(key)

s = 'Qsw3sj_lz4_Ujw@l'
ans = ""
for i in s:
    if i in dic: # 是字母直接转换
        ans += dic[i]
    else:        # 不是字母的照旧
        ans += i
print(ans) # Cae3ar_th4_Gre@t
# 加上ACTF{}就得到 You are correct!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值