這裏不對C++進行詳細分析,側重於C++標準模板庫和string的逆向。
C++中STL提供了一組表示容器、迭代器、函數對象和算法的模板。容器是一個與數組相似的單元,能夠存儲若干個值。ios
STL使得可以構造各類容器和執行各類操做。算法
STL不是面向對象的編程,而是一種不一樣的編程模式——泛型編程(generic programming)。
要建立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();
理解迭代器是理解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; }
僞代碼spa
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循環變化很大,但仔細一看仍是有邏輯的。指針
定義在<iterator>頭文件中code
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
能夠輕鬆看出三者區別,抓住關鍵函數便可,同上,再也不展現僞代碼
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)
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;
__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;
能夠看到每次循環容器v1三、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容器的倒置。
程序大概的加密思路爲