C++逆向

前言

這裏不對C++進行詳細分析,側重於C++標準模板庫和string的逆向。
C++中STL提供了一組表示容器、迭代器、函數對象和算法的模板。容器是一個與數組相似的單元,能夠存儲若干個值。ios

  • 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;
}

僞代碼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循環變化很大,但仔細一看仍是有邏輯的。指針

插入迭代器front_insert與back_insert

定義在<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

能夠輕鬆看出三者區別,抓住關鍵函數便可,同上,再也不展現僞代碼

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;

能夠看到每次循環容器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容器的倒置。

總結

程序大概的加密思路爲

  1. 對input[]從第二個值開始每一個值加input[0]
  2. 對加密的input進行一個倒置
  3. 最後的input與遞歸的值相同
相關文章
相關標籤/搜索