题目下载:下载
这道题用到了c++里的STL标准模板库中有关向量的模板。之前没有简单,所以简单的查阅一下资料:【C++】 STL详解_c++ stl_顾城沐心的博客-CSDN博客
一.基本概念
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: