前言
这里不对C++进行详细分析,侧重于C++标准模板库和string的逆向。
C++中STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值。
- STL容器是同质的,即存储的值的类型相同。
- 算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方。
- 迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针。
- 函数对象是类似于函数的对象,可以是类对象或函数模板。
STL使得能够构造各种容器和执行各种操作。
STL不是面向对象的编程,而是一种不同的编程模式——泛型编程(generic programming)。
模板类vector
构造函数与析构函数
要创建vector模板对象,可使用通常的<type>表示法来指出要使用的类型。另外vector模板使用动态内存分配,因此可以用初始化参数来指出需要多少矢量。
vector(): 创建一个空vector
vector(int nSize): 创建一个vector,元素个数为nSize
vector(int nSize,const t& t):创建一个vector,元素个数为nSize,且值均为t
vector(const vector&): 复制构造函数
vector(begin,end): 复制[begin,end)区间内另一个数组的元素到vector中
写一个demo,利于下一步使用IDA进行分析
#include <iostream>
#include <vector>
using namespace std;
int main(){
//创建一个int型空vector容器
vector<int> test1();
//创建一个vector,元素个数为5
vector<int> test2(5);
//创建一个vector,元素个数为5,且值均为3
vector<int> test3(5,3);
//赋值构造函数
vector<int> test4(test3);
//复制[begin,end)区间内另一个数组的元素道vector中
vector<int> test5(test4.begin(),test4.end());
return 0;
}
下面是ida f5反编译出现的代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbx
__int64 v4; // rax
char test5; // [rsp+0h] [rbp-A0h]
char test4; // [rsp+20h] [rbp-80h]
char test3; // [rsp+40h] [rbp-60h]
char test2; // [rsp+60h] [rbp-40h]
char v10; // [rsp+86h] [rbp-1Ah]
char v11; // [rsp+87h] [rbp-19h]
int v12; // [rsp+88h] [rbp-18h]
char v13; // [rsp+8Fh] [rbp-11h]
//创建的空容器test1在ida中并没有显示
////创建vector test2,元素个数为5
std::allocator<int>::allocator(&v10, argv);
std::vector<int,std::allocator<int>>::vector(&test2, 5LL, &v10);//这里可以看到直接对test2进行了size
std::allocator<int>::~allocator(&v10);
//创建vector test3,元素个数为5,值为3
std::allocator<int>::allocator(&v11, 5LL);
v12 = 3;利用变量进行传值
std::vector<int,std::allocator<int>>::vector(&test3, 5LL, &v12, &v11);
std::allocator<int>::~allocator(&v11);
//test4复制test3,与源码略有不同
std::vector<int,std::allocator<int>>::vector(&test4, &test3);
//利用区间进行传值,
std::allocator<int>::allocator(&v13, &test3);
v3 = std::vector<int,std::allocator<int>>::end(&test4);
v4 = std::vector<int,std::allocator<int>>::begin(&test4);
std::vector<int,std::allocator<int>>::vector<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,void>(
&test5,
v4,
v3,
&v13);
std::allocator<int>::~allocator(&v13);
std::vector<int,std::allocator<int>>::~vector(&test5);//对所有容器进行了析构
std::vector<int,std::allocator<int>>::~vector(&test4);
std::vector<int,std::allocator<int>>::~vector(&test3);
std::vector<int,std::allocator<int>>::~vector(&test2);
return 0;
}
总结
学会简化C++代码,括号里可以不看,只需要看他具体是什么函数即可
不同的vector构造函数的参数不同,第一个参数为容器名,第二个参数为size,第三个参数为初始值的地址,第四个参数为allocator用于分配内存
v3 = std::vector<int,std::allocator<int>>::end(&v14);
v4 = std::vector<int,std::allocator<int>>::begin(&v14);
这两句要会识别,这是常用的,他是取容器的begin和end,相当于C++的v14.begin();v14.end();
迭代器iterator
理解迭代器是理解STL的关键所在。模板使得算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。
每种容器类型都定义了自己的迭代器类型,如vector。
下面使用迭代器读取vector中的每一个元素
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector<int> ivec(10,1);
//定义一个iter变量,他的数据类型是由vector<int>定义的iterator类型
vector<int>::iterator iter;
for(iter=ivec.begin();iter!=ivec.end();++iter)
*iter = 2;//使用*访问迭代器所指向的元素
//counst_iterator 只能读取容器中的元素,而不能修改。
vector<int>::const_iterator citer;
for(citer=ivec.begin();citer!=ivec.end();++citer)
cout << *citer;
return 0;
}
伪代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int *v3; // rax
__int64 j; // [rsp+0h] [rbp-60h]
__int64 i; // [rsp+8h] [rbp-58h]
char ivec; // [rsp+10h] [rbp-50h]
char v8; // [rsp+2Bh] [rbp-35h]
int v9; // [rsp+2Ch] [rbp-34h]
__int64 v10; // [rsp+30h] [rbp-30h]
__int64 v11; // [rsp+38h] [rbp-28h]
__int64 v12; // [rsp+40h] [rbp-20h]
__int64 v13; // [rsp+48h] [rbp-18h]
std::allocator<int>::allocator(&v8, argv, envp);
v9 = 1;
std::vector<int,std::allocator<int>>::vector(&ivec, 10LL, &v9, &v8);
std::allocator<int>::~allocator(&v8);
for ( i = std::vector<int,std::allocator<int>>::begin(&ivec);
;
__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&i) )//重载了运算符++
{
v10 = std::vector<int,std::allocator<int>>::end(&ivec);
if ( !(unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&i, &v10) )
break;
*(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&i) = 2;
}
v12 = std::vector<int,std::allocator<int>>::begin(&ivec);
__gnu_cxx::__normal_iterator<int const*,std::vector<int,std::allocator<int>>>::__normal_iterator<int *>(&v11, &v12);
for ( j = v11; ; __gnu_cxx::__normal_iterator<int const*,std::vector<int,std::allocator<int>>>::operator++(&j) )
{
v13 = std::vector<int,std::allocator<int>>::end(&ivec);
if ( !(unsigned __int8)__gnu_cxx::operator!=<int const*,int *,std::vector<int,std::allocator<int>>>(&j, &v13) )
break;
v3 = (unsigned int *)__gnu_cxx::__normal_iterator<int const*,std::vector<int,std::allocator<int>>>::operator*(&j);
std::ostream::operator<<(&std::cout, *v3);
}
std::vector<int,std::allocator<int>>::~vector(&ivec);
return 0;
for循环变化很大,但仔细一看还是有逻辑的。
插入迭代器front_insert与back_insert
定义在<iterator>头文件中
back_inserter:创建一个使用push_back的迭代器
inserter:此函数接受第二个参数,这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。
front_inserter:创建一个使用push_front的迭代器(元素总是插入到容器第一个元素之前)
由于list容器类型是双向链表,支持push_front和push_back操作,因此选择list类型来试验这三个迭代器。
list<int> lst = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list<int> lst2 ={10}, lst3={10},lst4={10};
copy(lst.cbegin(), lst.cend(), back_inserter(lst2));
//lst2包含10,1,2,3,4,5,6,7,8,9
copy(lst.cbegin(), lst.cend(), inserter(lst3, lst3.begin()));
//lst3包含1,2,3,4,5,6,7,8,9,10
copy(lst.cbegin(), lst.cend(), front_inserter(lst4));
//lst4包含9,8,7,6,5,4,3,2,1,10
可以轻松看出三者区别,抓住关键函数即可,同上,不再展示伪代码
vector对象最重要的几种操作
1. v.push_back(t) 在容器的最后添加一个值为t的数据,容器的size变大。
2. v.size() 返回容器中数据的个数,size返回相应vector类定义的size_type的值。
3. v.empty() 判断vector是否为空
4. v[n] 或 v.at(n) 返回v中位置为n的元素,后者更加安全
5. v.insert(pointer,number, content) 向v中pointer指向的位置插入number个content的内容。
6. 还有v. insert(pointer, content),v.insert(pointer,a[2],a[4])将a[2]到a[4]三个元素插入。
7. v.pop_back() 删除容器的末元素,并不返回该元素。
8. v.erase(pointer1,pointer2) 删除pointer1到pointer2中间(包括pointer1所指)的元素。
9. vector中删除一个元素后,此位置以后的元素都需要往前移动一个位置,虽然当前迭代器位置没有自动加1,但是由于后续元素的顺次前移,也就相当于迭代器的自动指向下一个位置一样。
10.v1==v2 判断v1与v2是否相等。
11. !=、<、<=、>、>= 保持这些操作符惯有含义。
12. vector<typeName>::iterator p=v1.begin( ); p初始值指向v1的第一个元素。*p取所指向元素的值。
13. 对于const vector<typeName>只能用vector<typeName>::const_iterator类型的指针访问。
14.p=v1.end( ); p指向v1的最后一个元素的下一位置。
15. v.clear() 删除容器中的所有元素。
16. v.resize(2v.size)或v.resize(2v.size, 99) 将v的容量翻倍(并把新元素的值初始化为99)
西湖论剑EasyCPP
int __cdecl main(int argc, const char **argv, const char **envp)
{
//由于代码太多,我删除了变量定义部分代码
v15 = argv;
v26 = __readfsqword(0x28u);
std::vector<int,std::allocator<int>>::vector((__int64)&v18); //创建了五个vector容器
std::vector<int,std::allocator<int>>::vector((__int64)&v19);
std::vector<int,std::allocator<int>>::vector((__int64)&v20);
std::vector<int,std::allocator<int>>::vector((__int64)&v21);
std::vector<int,std::allocator<int>>::vector((__int64)&v22);
for ( i = 0; i <= 15; ++i )
{
scanf("%d", &v25[4 * i], v15);
std::vector<int,std::allocator<int>>::push_back(&v19, &v25[4 * i]); //v19.push_back[i];
}
for ( j = 0; j <= 15; ++j )
{
LODWORD(v24) = fib(j);
std::vector<int,std::allocator<int>>::push_back(&v18, &v24); //一个递归函数对容器v18赋值
}
std::vector<int,std::allocator<int>>::push_back(&v20, v25); //v20.push_back(v25[0]);
v4 = std::back_inserter<std::vector<int,std::allocator<int>>>(&v20); //创建一个使用push_back的迭代器
v5 = std::vector<int,std::allocator<int>>::end(&v19); //v5 = v19.end()
v24 = std::vector<int,std::allocator<int>>::begin(&v19); //v24 = v19.begin()
v6 = __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator+(&v24, 1LL); //v6 = v24.begin() + 1
std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#1}>(
v6, //v19.begin()+1 容器的第二个元素
v5, //v19.end()
v4, //只有输入第一个元素的容器
(__int64)v25 //输入第一个元素的值
); //函数作用为将输入从第二个数开始 每个数加第一个数的值
std::vector<int,std::allocator<int>>::vector((__int64)&v23); //创建一个新容器v23
v7 = std::vector<int,std::allocator<int>>::end(&v20); //v7 = v20.end()
v8 = std::vector<int,std::allocator<int>>::begin(&v20); //v8 = v20.begin()
std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::vector<int,std::allocator<int>>,main::{lambda(std::vector<int,std::allocator<int>>,int)#2}>(
(__int64)&v24, //v19.begin()
v8, //v20.begin()
v7, //v20.end()
(__int64)&v23, //新创建的容器
v9,
v10,
v3); //倒置函数
std::vector<int,std::allocator<int>>::operator=(&v21, &v24); //重载了运算符=,将容器v24的值赋给了容器v21
std::vector<int,std::allocator<int>>::~vector(&v24);
std::vector<int,std::allocator<int>>::~vector(&v23);
if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(&v21, &v18) ) //v21 == v18 可得flag
{
puts("You failed!");
exit(0);
}
std::back_inserter<std::vector<int,std::allocator<int>>>(&v22);
v11 = std::vector<int,std::allocator<int>>::end(&v19);
v12 = std::vector<int,std::allocator<int>>::begin(&v19);
std::copy_if<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#3}>(v12);
puts("You win!");
printf("Your flag is:flag{", v11, v15);
v23 = std::vector<int,std::allocator<int>>::begin(&v22);
v24 = std::vector<int,std::allocator<int>>::end(&v22);
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&v23, &v24) )
{
v13 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&v23);
std::ostream::operator<<(&std::cout, *v13);
__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&v23);
}
putchar(125);
std::vector<int,std::allocator<int>>::~vector(&v22);
std::vector<int,std::allocator<int>>::~vector(&v21);
std::vector<int,std::allocator<int>>::~vector(&v20);
std::vector<int,std::allocator<int>>::~vector(&v19);
std::vector<int,std::allocator<int>>::~vector(&v18);
return 0;
分析函数 std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#1}>
__int64 __fastcall std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>,main::{lambda(int)#1}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
int *v4; // rax
__int64 v5; // rax
__int64 v7; // [rsp+0h] [rbp-30h]
__int64 v8; // [rsp+8h] [rbp-28h]
__int64 v9; // [rsp+10h] [rbp-20h]
__int64 v10; // [rsp+18h] [rbp-18h]
int v11; // [rsp+24h] [rbp-Ch]
unsigned __int64 v12; // [rsp+28h] [rbp-8h]
v10 = a1;
v9 = a2;
v8 = a3;
v7 = a4;
v12 = __readfsqword(0x28u);
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&v10, &v9) )
{
v4 = (int *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&v10);
v11 = main::{lambda(int)#1}::operator() const((_DWORD **)&v7, *v4);
v5 = std::back_insert_iterator<std::vector<int,std::allocator<int>>>::operator*(&v8);
std::back_insert_iterator<std::vector<int,std::allocator<int>>>::operator=(v5, &v11);
__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&v10);
std::back_insert_iterator<std::vector<int,std::allocator<int>>>::operator++(&v8);
}
return v8;
v10指向容器v19的第二个值,并且重载了运算符++,实现了遍历功能,v19中存储了输入的所有数值
v7 中为 输入的第一个数值
下面看一下 main::{lambda(int)#1}::operator() const((_DWORD *)&v7, v4)具体是如何实现的
__int64 __fastcall main::{lambda(int)#1}::operator() const(_DWORD **a1, int a2)
{
return (unsigned int)(**a1 + a2);
}
v11 = v7 + v4;
std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::vector<int,std::allocator<int>>,main::{lambda(std::vector<int,std::allocator<int>>,int)#2}>
__int64 __fastcall std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::vector<int,std::allocator<int>>,main::{lambda(std::vector<int,std::allocator<int>>,int)#2}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, char a7)
{
int v7; // ebx
__int64 v9; // [rsp+0h] [rbp-70h]
__int64 v10; // [rsp+8h] [rbp-68h]
__int64 v11; // [rsp+10h] [rbp-60h]
__int64 v12; // [rsp+18h] [rbp-58h]
char v13; // [rsp+20h] [rbp-50h]
char v14; // [rsp+40h] [rbp-30h]
unsigned __int64 v15; // [rsp+58h] [rbp-18h]
v12 = a1; //v19.begin() v19此时已经过运算
v11 = a2; //v20.begin() v20存储的是第一个数值
v10 = a3; //v20.end()
v9 = a4; //新创建的容器
v15 = __readfsqword(0x28u);
while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&v11, &v10) )
{
v7 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&v11);
std::vector<int,std::allocator<int>>::vector(&v13, v9);
main::{lambda(std::vector<int,std::allocator<int>>,int)#2}::operator() const(
(__int64)&v14,
(__int64)&a7,
(__int64)&v13,
v7);
std::vector<int,std::allocator<int>>::operator=(v9, &v14);
std::vector<int,std::allocator<int>>::~vector(&v14);
std::vector<int,std::allocator<int>>::~vector(&v13);
__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&v11);
}
std::vector<int,std::allocator<int>>::vector(v12, v9);
return v12;
可以看到每次循环容器v13、v14都会被析构,只保留了在main中创建的新容器v9
__int64 __fastcall main::{lambda(std::vector<int,std::allocator<int>>,int)#2}::operator() const(__int64 a1, __int64 a2, __int64 a3, int a4)
{
__int64 v4; // r12
__int64 v5; // rbx
__int64 v6; // rax
int v8; // [rsp+4h] [rbp-3Ch]
__int64 v9; // [rsp+8h] [rbp-38h]
__int64 v10; // [rsp+10h] [rbp-30h]
__int64 v11; // [rsp+18h] [rbp-28h]
unsigned __int64 v12; // [rsp+28h] [rbp-18h]
v11 = a1;
v10 = a2;
v9 = a3;
v8 = a4;
v12 = __readfsqword(0x28u);
std::vector<int,std::allocator<int>>::vector(a1);
std::vector<int,std::allocator<int>>::push_back(a1, &v8);
v4 = std::back_inserter<std::vector<int,std::allocator<int>>>(v11); //定义了一个back_inserter插入迭代器
v5 = std::vector<int,std::allocator<int>>::end(v9);
v6 = std::vector<int,std::allocator<int>>::begin(v9);
std::copy<__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>,std::back_insert_iterator<std::vector<int,std::allocator<int>>>>(
v6,
v5,
v4);
return v11;
}
结合以上迭代器back_inserter使用copy函数知识可得
容器v9并没有被析构,每次循环插入一个值,完成对v19容器的倒置。
总结
程序大概的加密思路为
- 对input[]从第二个值开始每个值加input[0]
- 对加密的input进行一个倒置
- 最后的input与递归的值相同
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。