逆向入门——Re题解

前言

又是划水的最近,花了几天时间来入门逆向,二进制师傅是大爷,觉得自己会一点onlydbgida啥的,来做一做攻防世界的Re题,发现很多都是要看题解才能完成(tcl没办法),这里碰到两个有意思的题来分析分析

正文

csaw2013reversing2

拖到Exeinfo里看一看发现:
在这里插入图片描述
无壳,在拖到PEiD中看看发现:
在这里插入图片描述
话不多说,直接ida pro32打开看一看,找到main()函数后一键F5看看伪码:
在这里插入图片描述
用我菜的不能再菜的C语言基础分析一下大致就是:
先创建了一个私有堆,然后在这个私有堆上分配了指定大小的内存,分配后的内存不可移动,接着
在这里插入图片描述
将后者内存拷贝到前者分配内存的私有堆上,这里第一想法肯定是直接看后者的数据,但是结果很失望,转成字符后是这样的:
在这里插入图片描述
然后程序的逻辑接着就是进入IF判断,但是不管如何只要进入判断都会执行
ExitProcess(0xFFFFFFFF),如果是这样的话,那后面的代码就没用了,所以这里是比较可疑的地方,继续向下分析,弹出对话框函数,将Flag作为标题,窗体程序是lpMem + 1这个位置(注意并不是IpMem),在后面就是释放私有堆的内存然后退出。

分析完之后我们可以运行一下程序,和之前的分析再来进行对比:
在这里插入图片描述
Flag弹框中是乱码的,刚开始到这里就没辙了,想直接看字符串,发现并没有flag的字符串,因此肯定是存在变形函数变形后得到flag

注意:这个时候要注意到是存在弹框的,说明了什么?
说明if()条件是不成立的,那么里面的代码根本没有执行,直接跳过了,而里面的代码是什么?我们跟进看一看:
__debugbreak()这是反调试的,一调试就会退出,辛运的是它压根没执行
在这里插入图片描述
接下来这个函数,可以猜测到是对lpMen的处理
在这里插入图片描述
还比较的复杂,这里这个函数是不是对之前拷贝后的内存的值进行某种变化,而这种变化最后呈现的就是flag的值

很有可能是这样子,分析到这里开onlydbg进行动态调试:

这里我们可以在sub_40102A() || IsDebuggerPresent()任意一个函数设置断点来进行分析,这里我选择在后者进行断点
在这里插入图片描述
单步步入后我们发现
在这里插入图片描述
直接条件跳转到了MessageBoxA中而并没有执行if里面的函数,而我们的想法是在这里先条件跳转到sub_401000这个函数,在将这个函数跳转到MessageBoxA上,这样经过这个函数处理的私有堆能够呈现在弹出框中,也就是弹出真正的flag,因此现在就是找到sub_401000的地址
在这里插入图片描述
可以清楚看到在CALL了一个函数后就直接跳转到了ExitProcess中,那么我们就可以知道这个CALL就是在调用sub_401000,因此修改汇编,跳转到该函数的前一个位置
在这里插入图片描述
一直单步步入后发现最终会跳转到ExitPorocess
在这里插入图片描述
这时我们也需要修改汇编到第二个MessageBoxA,为什么不是第一个呢?
原始的程序也是跳转到第二个MessageBoxA,这里跟着原程序走就行
在这里插入图片描述
修改完成后可以单步步入继续看eax的值,也可以直接F9运行修改后的程序
在这里插入图片描述
运行到这里的时候我们发现flag已经写在了内存中,运行程序也得到了同样的结果
在这里插入图片描述

maze

这个题我确实是不会做,原谅一个菜鸡为什么如此辣鸡,看了题解之后想了很久才逐渐领悟,下面来分析一下:
先来看看文件类型elf 64bit
在这里插入图片描述
ida pro 64直接看:

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

  v9 = 0LL;
  puts("Input flag:");
  scanf("%s", &byte_6010BF[1], 0LL);
  if ( strlen(&byte_6010BF[1]) != 24 || strncmp(&byte_6010BF[1], "nctf{", 5uLL) || byte_6010BF[24] != '}' )
  {
LABEL_22:
    puts("Wrong flag!");
    exit(-1);
  }
  v3 = 5LL;
  if ( strlen(&byte_6010BF[1]) - 1 > 5 )
  {
    while ( 1 )
    {
      v4 = byte_6010BF[v3 + 1];
      v5 = 0;
      if ( v4 > 'N' )
      {
        v4 = (unsigned __int8)v4;
        if ( (unsigned __int8)v4 == 'O' )
        {
          v6 = sub_400650((_DWORD *)&v9 + 1);
          goto LABEL_14;
        }
        if ( v4 == 'o' )
        {
          v6 = sub_400660((int *)&v9 + 1);
          goto LABEL_14;
        }
      }
      else
      {
        v4 = (unsigned __int8)v4;
        if ( (unsigned __int8)v4 == '.' )
        {
          v6 = sub_400670(&v9);
          goto LABEL_14;
        }
        if ( v4 == '0' )
        {
          v6 = sub_400680((int *)&v9);
LABEL_14:
          v5 = v6;
          goto LABEL_15;
        }
      }
LABEL_15:
      if ( !(unsigned __int8)sub_400690((__int64)asc_601060, SHIDWORD(v9), v9) )
        goto LABEL_22;
      if ( ++v3 >= strlen(&byte_6010BF[1]) - 1 )
      {
        if ( v5 )
          break;
LABEL_20:
        v7 = "Wrong flag!";
        goto LABEL_21;
      }
    }
  }
  if ( asc_601060[8 * (signed int)v9 + SHIDWORD(v9)] != '#' )
    goto LABEL_20;
  v7 = "Congratulations!";
LABEL_21:
  puts(v7);
  return 0LL;
}

goto确实看的头晕,先从开始的if判断分析,因为这个if判断直接就exit()里,所以怎么样都是不能让if的条件满足

输入的长度是24位,并且前5位是nctf{,最后一位是},这个还是很容易看懂

接下来一定会进入while()的循环之中,v4就是nctf{}里面的内容逐个取出

这里分了两种情况,一种情况是v4>'N',如果v4==O,那么v9的高位(也就是(_DWORD *)&v9 + 1)会进行sub_400650,v4==o则进行sub_400660,同理另外一个情况也是类似,只是直接是v9来作为这俩个函数的参数,分析这两个关键函数:

  • sub_400650
bool __fastcall sub_400650(_DWORD *a1)
{
 int v1; // eax

 v1 = (*a1)--;
 return v1 > 0;
}
  • sub_400660
bool __fastcall sub_400660(int *a1)
{
int v1; // eax

v1 = *a1 + 1;
*a1 = v1;
return v1 < 8;
}

第一个是减一操作,第二个是加一操作,很奇怪,加一减一这意味着啥啊?
我们继续看这个:
在这里插入图片描述
里面那个函数的返回值一定要是1才行,我们看一下sub_400690:

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

  result = *(unsigned __int8 *)(a1 + a2 + 8LL * a3);
  LOBYTE(result) = (_DWORD)result == ' ' || (_DWORD)result == '#';
  return result;
}

result =a1的基地址上 + a2 + 8 * a3,如果result == ' ' || '#'返回True
而a1就是asc_601060,a2是SHIDWORD(v9),a3是v9,这里我们不清楚a2到底是啥,我们看一下汇编图
在这里插入图片描述
当执行这个函数,也就是sub_400690之前,先将[rsp+28h+var_28+4]压入寄存器,再将[rsp+28h+var_28]压入寄存器,这两个作为参数也就分别对应了SHIDWORD(v9)和v9,因此SHIDWORD(v9)就是v9的高位

因此在来看上文的result =a1的基地址上 + a2 + 8 * a3 a2就是v9的高位,a3就是v9的低位,那a1是啥?
在这里插入图片描述
其实可以大致猜测出,a1是一个8*8的数组,并且值由这些组成,a2代表列,a3代表行,a3+1也就是向下走了一个单位,对应了v4==0,因此根据这样的判断我们得出'O','o'表示左右,'.','0'表示上下

在这里插入图片描述
v5 == v6则是判断是否越界,如果越界则直接break,跳出循环后,
在这里插入图片描述
最终如果这个数组的值不是#,则也直接over
因此符合了题目名字–迷宫,我们将那个数组显示出来

00******
*000*00*
***0*0**
**00*0**
*00*#00*
**0***0*
**00000*
********

因此我们的步骤就是右下右右下下左下下下右右右右上上左左,再加上nctf{}正好24个字符,也就组成了最后的flag

str = "右下右右下下左下下下右右右右上上左左"
str = str.replace('上', '.')
str = str.replace('下', '0')
str = str.replace('左', 'O')
str = str.replace('右', 'o')

str = 'nctf{' + str + '}'

print(str)
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值