攻防世界逆向刷题笔记(新手模式1-5)

总结经验:IDA Pro反编译的时候会把字符编译为ASCII码,需要注意!另外,也要多多关注循环和判断及其对应的条件。不要忽视变量类型以及数组长度!

逆向题难度不小,做了一个多小时才做了两题,还需要不断看WP。但是菜就多练,记录下做的题!

1.Reversing-x64Elf-100

发现是64位,直接丢IDA看看先。下代码经过分析,应该是当括号内容是false的时候,才会执行nice,估计就是正确的代码。接下来,我们点进去看看。有必要说明的是,sub_xxx一般指一个函数。

以下是函数

 似乎实现了一个算法,我们目的是让他返回0,也就是让if不成立。那也就是让表达式里面的值等于1就行了。

首先获取它们代表的整数到底是什么。

这串代码用到了指针的概念,但是目前我还没咋掌握指针,参考了题解后大胆认为:它是获取减号前面表达式的字符,然后在获取减号后面的字符,估计后面的字符应该比前一个少一。a1就是我们输入的字符。因为a1原本存储的是字符数组首地址,因此*(char *)实现的是对a1每一位的访问。由此,写出c++脚本。

int main()
{
  int strings[4];
  char b[20];
  strings[0] = (__int64)"Dufhbmf";
  strings[1] = (__int64)"pG`imos";
  strings[2] = (__int64)"ewUglpt";
 for(int i = 0;i<=11;i++)
 {
     b[i]=char(*(char *)(strings[i % 3] + 2 * (i / 3))-1);
     cout<<b[i];
 }
    return 0;
}

理清思路之后,也感觉是小小拿捏了一道题。

2.666 

首先拿出die,发现是一个64位程序。放到IDA老婆肚子里看看会发生什么事:

f

他妈的,给了一个假的flag,害得我提交了一下。那么,关键应该在key了,if判断的是s和enflag是否相等。strcmp函数如果两个数相等就会返回0!

复制到下面:izwhroz""w"v.K".Ni

这段代码是汇编语言中的数据定义部分,使用的是 Intel 语法。下面是对这段代码的解释:

```
data:0000000000004060 enflag          db 'izwhroz""w"v.K".Ni',0
```

1. `data`:这是一个段(segment)的名称,通常用于汇编语言中,表示数据段。数据段是存储程序中所有数据的地方,包括变量、字符串常量等。

2. `0000000000004060`:这是数据段中 `enflag` 的内存地址。在实际的程序中,这个地址可能会有所不同,因为它取决于程序的加载地址和操作系统的内存管理。

3. `enflag`:这是一个标签(label),用于标识数据段中的特定位置。在这个例子中,`enflag` 指向一个字符串。

4. `db`:这是一个数据定义伪指令,用于定义一个字节(byte)的数据。`db` 后面的内容是要定义的数据。

5. `'izwhroz""w"v.K".Ni'`:这是一个字符串常量,包含了一系列的字符。在汇编语言中,字符串通常用单引号 `' '` 包围。

6. `,0`:这部分表示在字符串的末尾添加一个字节值为 0 的字符,即空字符(null terminator),用于表示字符串的结束。在 C 语言和许多其他编程语言中,字符串以空字符结尾。

综上所述,这段代码定义了一个名为 `enflag` 的字符串,内容为 `'izwhroz""w"v.K".Ni'`,并在字符串末尾添加了一个空字符。这个字符串可能用于程序中的某个功能,例如加密或解密。具体的用途取决于程序的上下文和设计。
 

上面的应该是enflag,我们当务之急是找到s,找到S就可以推回我们应该输入什么,进而确认flag。s就是enflag里面的东西。所以关键函数应该是encode,用来解码出s的,我们需要逆向解出我们应该输入什么key,使得key编码后是s。

(整体逻辑如上图)

还不知道key的长度,双击汇编代码中的key得到:

一定要注意!这里的key就是18,而不是说key的长度是18!

接下来,就是根据算法逆推了。为了练习python,本题解题脚本我选择python来编写。

要注意,异或运算优先级小于+-,所以要在异或运算外面包裹(),否则会出现算法层面问题

剩下的没什么需要注意的,就是不要越过a2界即可!附上代码:

a2='izwhroz""w"v.K".Ni'
i=0
r=[]
while(i<18):
    r.append((chr((ord(a2[i])^18)-6)))
    r.append((chr((ord(a2[i+1])^18)+6)))
    r.append((chr(ord(a2[i+2])^18^6)))
    i+=3
print("".join(r))

3.easyRE1

压缩包有个32和64,打开后是一样的,答案就在眼前。感觉这道题反而比前两题简单多了。

4.lucknum 

查出是64位。

按它的算法,应该输入7后,出来flag。但是flag实际上已经被写出来,复制一下就行。

5.reverse_re3 

64位,linux

错误思路(因未转换为字符 )

看main函数的意思,应该是v4不要等于±1. 才会结束do...while循环。从头看main函数,首先要跑一个这个函数,返回了v1异或这个值。__readfsqword 是一个函数或宏指令,用于从特定的内存地址读取数据。后面的参数的u代表unsigned int常量。

 dword_202AB0 变量被赋值为了0,在这里需要注意一下。

shift+F12进入之后,点击ctrl+x定位到函数 。以下是代码

__int64 sub_940()
{
  int v0; // eax
  int v2; // [rsp+8h] [rbp-218h]
  int v3; // [rsp+Ch] [rbp-214h]
  char v4[520]; // [rsp+10h] [rbp-210h] BYREF
  unsigned __int64 v5; // [rsp+218h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v3 = 0;
  memset(v4, 0, 0x200uLL);
  _isoc99_scanf(&unk_1278, v4, v4);//unk_1278 = 29477
  while ( 1 )
  {
    do
    {
      v2 = 0;
      sub_86C();
      v0 = v4[v3];
      if ( v0 == 100 )
      {
        v2 = sub_E23();
      }
      else if ( v0 > 100 )
      {
        if ( v0 == 115 )
        {
          v2 = sub_C5A();
        }
        else if ( v0 == 119 )
        {
          v2 = sub_A92();
        }
      }
      else
      {
        if ( v0 == 27 )
          return 0xFFFFFFFFLL;
        if ( v0 == 97 )
          v2 = sub_FEC();
      }
      ++v3;
    }
    while ( v2 != 1 );
    if ( dword_202AB0 == 2 )
      break;
    ++dword_202AB0;
  }
  puts("success! the flag is flag{md5(your input)}");
  return 1LL;
}

memset函数原型及作用

 

memset 是 C 和 C++ 标准库中的一个函数,其功能是将一段内存空间的内容按字节设置为特定的值。通常用于初始化数组、结构体等内存区域,把指定内存范围初始化为特定字节内容,以确保内存中的数据处于预期的初始状态。

参数含义

 
  • v4:这是 memset 函数的第一个参数,它表示要进行内存设置操作的目标内存地址。一般来说,这个参数是一个指向某个内存区域起始位置的指针,比如可以是指向数组、结构体等的指针。例如,如果 v4 是一个字符数组 char arr[10];,那么传递 arr (实际上就是数组首地址)给 memset 时,操作就会从这个数组的开头位置开始对内存进行设置。
  • 0:第二个参数表示要设置的字节值,在这里值为 0,意味着将内存中的每个字节都设置为 0 这个数值对应的字节表示(在二进制下就是 00000000)。如果想把内存区域设置为其他固定的值(比如全设置为 0xFF 等),可以相应地修改这个参数。
  • 0x200uLL:第三个参数指定了要设置的内存区域的大小,单位是字节。这里 0x200uLL 是一个无符号长整型常量(u 表示无符号,LL 表示长整型后缀,在 64 位系统下通常表示 64 位的无符号整数),意味着 memset 函数会从 v4 所指向的内存起始位置开始,连续设置 0x200(也就是十进制的 512)个字节的内存内容为 0

 此函数又执行了sub_86c函数,因此点进去看看sub_86c函数。

看样子主要是给这两个变量赋值的。 v4因为用了scanf函数,大概率就是咱们所要输入的。因此我们输入的应该就是v4。

真正思路

但是做到这里就没有思路了。后来查看题解发现,IDA在反编译的时候会把字符反编译成ASCII码,因此将其中的字符变回去,得到下面的真正代码。

__int64 sub_940()
{
  int v0; // eax
  int v2; // [rsp+8h] [rbp-218h]
  int v3; // [rsp+Ch] [rbp-214h]
  char v4[520]; // [rsp+10h] [rbp-210h] BYREF
  unsigned __int64 v5; // [rsp+218h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v3 = 0;
  memset(v4, 0, 0x200uLL);
  _isoc99_scanf(&qword_1278, v4, v4);
  while ( 1 )
  {
    do
    {
      v2 = 0;
      sub_86C();
      v0 = v4[v3];
      if ( v0 == 'd' )
      {
        v2 = sub_E23();
      }
      else if ( v0 > 'd' )
      {
        if ( v0 == 's' )
        {
          v2 = sub_C5A();
        }
        else if ( v0 == 'w' )
        {
          v2 = sub_A92();
        }
      }
      else
      {
        if ( v0 == '\x1B' )
          return 0xFFFFFFFFLL;
        if ( v0 == 'a' )
          v2 = sub_FEC();
      }
      ++v3;
    }
    while ( v2 != 1 );
    if ( dword_202AB0 == 2 )
      break;
    ++dword_202AB0;
  }
  puts("success! the flag is flag{md5(your input)}");
  return 1LL;
}

每次循环都会执行下面的函数,发现就是来给B4和B8赋值的。20是一个数组应该。 

接着写,20是下面的数组。B0已经在main函数之前赋值为0,结合i和j的边界是14,推测代码应该是实现:i是行数,j是列数,3是目前的地方。我们接着往下看函数,

 

这个数组按shift+e,导出为一整列的数字,用python转换为15列的数组,方便后续操作。以下是源码。 

a = '1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,3,1,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,4,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,3,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,0,0,0,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,1,0,1,1,0,0,0,1,1,1,1,1,0,0,1,1,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,0,0,0,0,0,1,1,1,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,1,0,1,1,0,1,1,0,0,0,0,0,1,0,0,0,0,1,1,0,1,1,1,1,1,1,0,1,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,4,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,0'
a1= list(a)
b=[]
for each in a1:
    if each!= ',':
        b.append(each)

i = 0
while i < len(b):  
     print(b[i],end='')
     if((i+1)>=15 and (i+1)%15==0):
        print("\n")
     i += 1

再往下分析输入d时调用的函数。

首先这个大的do..while循环退出条件是v2=1看出来当20数组到达4的时候,就会退出循环,输入我们的flag。然后看第一个if语句。B8为列数,B8!=14说的是还在这一列之中,我们看到第一个if的意思是右边如果是1,就把右边变成3,本来的位置变成1.这就相当于3是我自己,d的操作是把3右移了一个单位长度。其实,经常玩游戏的人都知道,d本身就是向右移动的意思。假如移到4身上,游戏结束,获得flag。

AWS的原理和这个肯定一样,无非是方向的不同。我们需要从3出发,寻找到4的位置。接下来,就让我们尝试一下。

这个还有输入esc的检验,就是if ( v0 == '\x1B' )。\x1B的意思是esc。返回的0x好多 F其实就是-1,相当于退出游戏。

那么这个代码是什么意思呢。这个代码执行的时候已经 走到4的位置了,B0是乘以225的量,他要是变化了,不就溢出了?其实不是的,参考了CTF-reverse-reverse_re3(全网最详细wp,超4000字有效解析)_ctfreverse题目-优快云博客

师傅的题解,发现其实20这个数组是3个小迷宫 ,因为20的长度是675,刚好是225*3,而225=15*15。到这里一切都明白了。而且题目上说:不要走回头路,也给我们解出迷宫提示了部分信息。

最后效果大约是如此。

 里面的1是什么情况?还记得我们分析d的时候嘛?假如向右移动,就把3左边的数字赋值为1.显而易见,题目上和3靠近的1肯定是移动后的1.题目也给了我们提示,就是找1.然后,我们把对应的wasd输进去,程序就会按照算法计算,如果最后确实到达4,会输出md5加密后的wasd。

检验之后发现思路是正确的。md5加密后就可以得到答案了

不过发现少数一个s也会提示这样的弹窗,现在还不知道为啥。以后填坑。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值