xctf攻防世界 REVERSE 薪手练习区(三)

11. getit

进入环境,下载附件,使用exeinfo Exe打开,发现没有加壳,如图:
在这里插入图片描述

解法1. 静态分析,得到答案

我们直接将文件丢入IDA,找到main函数,F5反编译,查看代码,如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v3; // al
  __int64 v5; // [rsp+0h] [rbp-40h]
  int i; // [rsp+4h] [rbp-3Ch]
  FILE *stream; // [rsp+8h] [rbp-38h]
  char filename[8]; // [rsp+10h] [rbp-30h]
  unsigned __int64 v9; // [rsp+28h] [rbp-18h]

  v9 = __readfsqword(0x28u);
  LODWORD(v5) = 0;							# 令V5=0
  while ( (signed int)v5 < strlen(s) )		
  {
    if ( v5 & 1 )
      v3 = 1;
    else
      v3 = -1;
    *(&t + (signed int)v5 + 10) = s[(signed int)v5] + v3;	# 对t字符的内容进行改写,与v3内容有关
    LODWORD(v5) = v5 + 1;					# v5++
  }
  strcpy(filename, "/tmp/flag.txt");		# 创建一个flag.txt文件
  stream = fopen(filename, "w");
  fprintf(stream, "%s\n", u, v5);
  for ( i = 0; i < strlen(&t); ++i )		# 依次将t字符写入文件中
  {
    fseek(stream, p[i], 0);
    fputc(*(&t + p[i]), stream);
    fseek(stream, 0LL, 0);
    fprintf(stream, "%s\n", u);
  }
  fclose(stream);
  remove(filename);
  return 0;
}

可以看到,上面的代码主要是针对v3得到的值存入t中,再对t写入文件操作,那么t就是最终的flag值!我们尝试双击t字符,查看内容,如图:
在这里插入图片描述
我们右键,将53h转成字符,如图;
在这里插入图片描述
同理查看字符s,如图:
在这里插入图片描述

那么我们可以知道,字符s和t分别为:

t = SharifCTF{????????????????????????????????}
s = c61b68366edeb7bdce3c6820314b7498

接着代码复现:

#include <stdio.h>
#include <string.h>

int main()
{
    char s[] = "c61b68366edeb7bdce3c6820314b7498";
    char t[] = "SharifCTF{????????????????????????????????}";
    int v5 = 0;
    char v3;

    while(v5 < strlen(s)){
        if (v5 & 1)
            v3 = 1;
        else
            v3 = -1;
        *(t + v5 + 10) = s[v5] + v3;
        v5++;
    }
    printf("%s\n",t);

    return 0;
}

最终flag为:SharifCTF{b70c59275fcfa8aebf2d5911223c6589}

解法2. 通过IDA的patch功能

经过上述代码分析,发现运行文件没有的原因是最后将文件通过remove命令删除了,而且在for循环里,通过fprintf函数将u,也就是一堆*******符号写入flag文件。那么我们先找到这两处地方,如图:
在这里插入图片描述
找到位置后,通过小手轻轻的触动鼠标的右键,如图:
在这里插入图片描述
将其内容替换成nop,如图:
在这里插入图片描述
将其文件导出,并扔进kali中,通过./文件,运行更改后的文件。
最后我们可以通过命令得到结果:

cat /tmp/flag.txt

结果如图:
在这里插入图片描述

12. maze

下载附件,直接用IDA打开,查看内容,找到main函数,F5反编译,得到代码,如图:
在这里插入图片描述
一大堆代码,我们逐一分析:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  const char *v3; // rsi
  signed __int64 v4; // rbx
  signed int v5; // eax
  char v6; // bp
  char v7; // al
  const char *v8; // rdi
  __int64 v10; // [rsp+0h] [rbp-28h]

  v10 = 0LL;
  puts("Input flag:");
  scanf("%s", &s1, 0LL);        // s1保存输入的字符
  if ( strlen(&s1) != 24 || (v3 = "nctf{", strncmp(&s1, "nctf{", 5uLL)) || *(&byte_6010BF + 24) != 125 )   // 可以知道s1的长度为24,且s1的前5位为nctf{
      {
        LABEL_22:
        puts("Wrong flag!");
        exit(-1);
        }
  v4 = 5LL;     // v4的值为5的长整型(也就是int64)
  if ( strlen(&s1) - 1 > 5 )    // 为什么是len(s1) - 1,其实是为了保证最后一个}符号,相当于截取nctf{?}中的?信息
  {
    while ( 1 )
    {
      v5 = *(&s1 + v4);     // 取v5 = s1[5]
      v6 = 0;
      if ( v5 > 78 )        // 78的ascii为N,(实在搞不懂这个判断逻辑有毛用)
      {
        v5 = (unsigned __int8)v5;
        if ( (unsigned __int8)v5 == 79 )    // 如果v5的值为79(也就是为'O'时候,进行左移)
        {
          v7 = sub_400650((char *)&v10 + 4, v3); // sub_400650进行操作:1. v10--,表示位置的列数-1; 2. 返回边界判断,是否越界
          goto LABEL_14;
        }
        if ( v5 == 111 )                    // 如果v5的值为111(也就是为'o'时候,进行右移)
        {
          v7 = sub_400660((char *)&v10 + 4, v3); // sub_400660进行操作:1. v10++,表示位置的列数+1; 2. 返回边界判断,是否越界
          goto LABEL_14;
        }
      }
      else
      {
        v5 = (unsigned __int8)v5;           // 如果v5的值为46(也就是为'.'时候,进行上移)
        if ( (unsigned __int8)v5 == 46 )
        {
          v7 = sub_400670(&v10, v3);        // sub_400670进行操作:1. v10--,表示位置的行数-1; 2. 返回边界判断,是否越界
          goto LABEL_14;
        }
        if ( v5 == 48 )                     // 如果v5的值为48(也就是为'0'时候,进行下移)
        {
          v7 = sub_400680(&v10, v3);        // sub_400680进行操作:1. v10++,表示位置的列数+1; 2. 返回边界判断,是否越界
LABEL_14:
          v6 = v7;                          // 将v7的值赋给v6,用于是否越界判断
          goto LABEL_15;
        }
      }
LABEL_15:
      v3 = (const char *)HIDWORD(v10);
      if ( !(unsigned __int8)sub_400690(asc_601060, HIDWORD(v10), (unsigned int)v10) )  //判断该位置的字符是' ', '#'或者'*'中的哪种,如果是'*'则,返回再次寻路(这儿代码很让我头疼)
        goto LABEL_22;
      if ( ++v4 >= strlen(&s1) - 1 ) // v4用于控制中间的24 - 5 - 1 = 18个字符(去除nctf{})
      {
        if ( v6 )
          break;
LABEL_20:
        v8 = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( asc_601060[8 * (signed int)v10 + SHIDWORD(v10)] != 35 ) // //判断是否到达'#'处,没有到达,继续返回循环,进行移动
    goto LABEL_20;
  v8 = "Congratulations!";
LABEL_21:
  puts(v8);
  return 0LL;
}

同时我们双击asc_601060变量,得到的是一串字符串,如下:

.data:0000000000601060 asc_601060      db '  *******   *  **** * ****  * ***  *#  *** *** ***     *********',0

再来看看难顶的函数:

__int64 __fastcall sub_400690(__int64 a1, int a2, int a3)
{
  __int64 result; // rax

  result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);   //a1是迷宫,a2是列,a3是行。通过行列计算迷宫对应位置的字符
  LOBYTE(result) = (_DWORD)result == 32 || (_DWORD)result == 35; //如果是' '或者'#‘返回TRUE,是'*’返回FALSE
  return result;
}
00******
*000*00*
***0*0**
**00*0**
*00*#00*
**0***0*
**00000*
********

起点(1,1),只能走0,要走到‘#’,找到路径右下右右下下左下下下右右右右上上左左

则对应的代码中字符为:o0oo00O000oooo…OO
带上前置括号,最终的字符为:nctf{o0oo00O000oooo…OO}

这个题感觉看的稀里糊涂的,后面再回头重做一下,代码还是太难阅读了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

l8947943

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值