攻防世界-easyCpp

题目下载:下载

这道题用到了c++里的STL标准模板库中有关向量的模板。之前没有简单,所以简单的查阅一下资料:【C++】 STL详解_c++ stl_顾城沐心的博客-CSDN博客

C++ vector 容器浅析 | 菜鸟教程

一.基本概念

STL(Standard Template Library),即标准模板库,是一个高效的C++程序库

    被容纳于C++标准程序库(C++ Standard Library)中,是ANSI/ISO C++标准中最新的也是极具革命性的一部分

    包含了诸多在计算机科学领域里常用的基本数据结构和基本算法。为广大C++程序员们提供了一个可扩展的应用框架,高度体现了软件的可复用性

    从逻辑层次来看,在STL中体现了泛型化程序设计的思想,在这种思想里,大部分基本算法被抽象,被泛化,独立于与之对应的数据结构,用于以相同或相近的方法处理各种不同情形。

二.

1.容器:容纳,包含一组元素或元素集合的对象。

2.迭代器:迭代器是面向对象版本的指针,它们提供了访问容器、序列中每个元素的方法。

3.向量(Vector)是一个封装了动态大小数组的顺序容器(Sequence Container)。跟任意其它类型容器一样,它能够存放各种类型的对象。可以简单的认为,向量是一个能够存放任意类型的动态数组。

三.部分基本函数用法:

1.vector():创建一个空vector

2.push_back():将新元素加入到尾部

3.pop_back:移出最后一个元素

4.begin 得到数组头的指针

5.end 得到数组的最后一个单元+1的指针

... ...

 知道这几个基本的函数用法,就可以分析代码了。拖入IDA。(部分变量使用了重命名)

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v3; // r15
  __int64 v4; // r12
  __int64 v5; // rbx
  __int64 v6; // rax
  __int64 v7; // rbx
  __int64 v8; // rax
  int v9; // er8
  int v10; // er9
  int v11; // er12
  int v12; // ebx
  int v13; // eax
  int v14; // ecx
  int v15; // er8
  int v16; // er9
  unsigned int *v17; // rax
  int i; // [rsp+1Ch] [rbp-174h]
  int j; // [rsp+20h] [rbp-170h]
  char _fib[32]; // [rsp+30h] [rbp-160h] BYREF
  char v22[32]; // [rsp+50h] [rbp-140h] BYREF
  char input_addr[32]; // [rsp+70h] [rbp-120h] BYREF
  char v24[32]; // [rsp+90h] [rbp-100h] BYREF
  char v25[32]; // [rsp+B0h] [rbp-E0h] BYREF
  __int64 v26[4]; // [rsp+D0h] [rbp-C0h] BYREF
  __int64 fei[4]; // [rsp+F0h] [rbp-A0h] BYREF
  _DWORD user_input[18]; // [rsp+110h] [rbp-80h] BYREF
  unsigned __int64 v29; // [rsp+158h] [rbp-38h]

  v29 = __readfsqword(0x28u);
  std::vector<int>::vector((__int64)_fib);      // 一个容器,封装动态大小的数组,可以简单的认为,向量是一个能够存放任意类型的动态数组
  std::vector<int>::vector((__int64)v22);       // 初始化向量容器
  std::vector<int>::vector((__int64)input_addr);
  std::vector<int>::vector((__int64)v24);
  std::vector<int>::vector((__int64)v25);
  for ( i = 0; i <= 15; ++i )
  {
    scanf("%d", &user_input[i]);
    std::vector<int>::push_back((__int64)v22, (__int64)&user_input[i]);// 输入地址存到v22
  }
  for ( j = 0; j <= 15; ++j )
  {
    LODWORD(fei[0]) = fib(j);
    std::vector<int>::push_back(_fib, fei);     // 斐波那契数列存入_fib
  }
  std::vector<int>::push_back((__int64)input_addr, (__int64)user_input);// 用户输入首地址赋值给input_addr
  v4 = std::back_inserter<std::vector<int>>(input_addr);// 产生一个input_addr的迭代器v4,存放变化后的字符串
  v5 = std::vector<int>::end(v22);              // v5是用户输入的末尾地址,指向数组最后元素的后一个位置
  fei[0] = std::vector<int>::begin((__int64)v22);// 用户输入的开始地址,指向第一个元素
  v6 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(fei, 1LL);// 运算符加操作,
  std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(
    v6,
    v5,
    v4,
    user_input);
  std::vector<int>::vector((__int64)v26);       // 初始化向量,存放变化后的字符串
  v7 = std::vector<int>::end(input_addr);       // 用户输入变化后的结束位置
  v8 = std::vector<int>::begin((__int64)input_addr);// 用户输入变化后的开始位置
  std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(
    (__int64)fei,
    v8,                                         // 关键操作
    v7,
    (__int64)v26,
    v9,
    v10,
    v3);
  std::vector<int>::operator=(v24, fei);        // v24=fei
  std::vector<int>::~vector(fei);               // 调用析构函数释放空间
  std::vector<int>::~vector(v26);
  if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(v24, _fib) )// 不走
  {
    puts("You failed!");
    exit(0);
  }
  v11 = std::back_inserter<std::vector<int>>(v25);
  v12 = std::vector<int>::end(v22);
  v13 = std::vector<int>::begin((__int64)v22);
  std::copy_if<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#3}>(
    v13,
    v12,
    v11,
    v14,
    v15,
    v16);
  puts("You win!");
  printf("Your flag is:flag{");
  v26[0] = std::vector<int>::begin((__int64)v25);
  fei[0] = std::vector<int>::end(v25);
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(v26, fei) )
  {
    v17 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(v26);
    std::ostream::operator<<(&std::cout, *v17);
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(v26);
  }
  putchar(125);
  std::vector<int>::~vector(v25);
  std::vector<int>::~vector(v24);
  std::vector<int>::~vector(input_addr);
  std::vector<int>::~vector(v22);
  std::vector<int>::~vector(_fib);
  return 0;
}

首先初始化向量容器,然后用户输入16个数字存放在v22中,然后第二个for循环求斐波那契数列的前16项存放在_fib数组中,最后也是用_fib与v24比较,相等就输出“You win”,所以v24数组就是用户输入变化后的数据。

看下面的操作:

 只要了解了基本函数用法就可以分析出来上面是做什么的,就像我后面写的注释那样,这里主要看这里的第一个主要操作:transform,跟进:

首先是一个while循环,条件是&v10!=&v9,通过分析知道&v10是用户输入的首地址,而&v9是用户输入的最后元素的后一个位置,所以当v10指针走到用户输入末尾时循环结束,就相当于遍历用户输入的字符串。然后把&v10位置的数据给v4,第20行也是个函数,简单跟进看一下:

是两数据相加,所以就是&v7处的数据(用户输入的第一个元素)与v4(也就是&v10)相加赋值给v11。然后存放在v5中,然后&v10++ 目的是操作用户输入的下一个字符,&v8++目的是存放下一个数据。总而言之,这个函数transform就是让用户输入的元素(下标1开始)都依次加上第一个元素。然后通过v8返回。

之后返回到主函数,看到

v8,v7又是分别指向数组的第一个元素,最后一个元素,所以猜测这是经过transform后的数组啦。看accumulate函数:

 同样是个while循环,来控制遍历次数,和上面的transform一个作用,把变化后的第一个元素给v7,然后把a4(transform后的数据)放入v13容器中,在进行一个函数:跟进

 发现其中有个copy是函数,看其他的wp,说是对transform的数组进行逆置,STL不熟真没看出来,那我们动态调试看一看规律。返回主函数,既然最后是比较v27,v24,而v24是斐波那契数列,所以v27就是accumulate后的数据,我们就动态观察他,现在虚拟机上随便输入,如1,2,3,4,5,6,7,8,9,0,11,12,13,14,15,16。

观察v27数据(十六进制):正好符合从第二位输入数据开始依次加上输入的第一个数据,在逆置。

整体代码:输入数据-->从第二个数据开始依次加上第一个数据-->逆置-->与斐波那契数列比较

所以逆向:斐波那契数列-->逆置-->从第二个数依次减去第一个数-->用户输入

所以代码如下:

fib = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987][::-1]
print(fib[0])
for i in range(1,len(fib)):
	print (fib[i]-fib[0])
#987,-377,-610,-754,-843,-898,-932,-953,-966,-974,-979,-982,-984,-985,-986,-986

 在虚拟机中运行获得flag:

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值