XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp

Posted 大灬白

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp相关的知识,希望对你有一定的参考价值。

下载附件easyCpp,先查看文件信息:

是Linux下的64位ELF文件,运行程序:

是一个输入字符串然后判断是否正确的程序,错误就输入“You failed!”
之后用IDA64打开:

main函数源代码及注释:

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
  __int64 v9; // r8
  __int64 v10; // r9
  char v11; // al
  unsigned int *v12; // rax
  int i; // [rsp+1Ch] [rbp-174h]
  int j; // [rsp+20h] [rbp-170h]
  char v16[32]; // [rsp+30h] [rbp-160h] BYREF
  char v17[32]; // [rsp+50h] [rbp-140h] BYREF
  char v18[32]; // [rsp+70h] [rbp-120h] BYREF
  char v19[32]; // [rsp+90h] [rbp-100h] BYREF
  char v20[32]; // [rsp+B0h] [rbp-E0h] BYREF
  __int64 a4[4]; // [rsp+D0h] [rbp-C0h] BYREF
  __int64 a1[4]; // [rsp+F0h] [rbp-A0h] BYREF
  char v23[72]; // [rsp+110h] [rbp-80h] BYREF
  unsigned __int64 v24; // [rsp+158h] [rbp-38h]

  v24 = __readfsqword(0x28u);
  std::vector<int>::vector((__int64)v16);
  std::vector<int>::vector((__int64)v17);
  std::vector<int>::vector((__int64)v18);
  std::vector<int>::vector((__int64)v19);
  std::vector<int>::vector((__int64)v20);
  for ( i = 0; i <= 15; ++i )                   // 16个字符
  {
    scanf("%d", &v23[4 * i]);
    std::vector<int>::push_back(v17, &v23[4 * i]);// 输入的字符插入到v17字符数组最后
  }
  for ( j = 0; j <= 15; ++j )
  {
    LODWORD(a1[0]) = fib(j);                    // fib在c语言中为斐波那契数列,获得斐波那契数列第j个数的值
    std::vector<int>::push_back(v16, a1);       // 把斐波那契数列前16个数插入到v16字符数组
  }
  std::vector<int>::push_back(v18, v23);        // v23就是第一个字符插入v18向量最后,v18是一个新的地址存储计算后的字符串
  v4 = std::back_inserter<std::vector<int>>(v18);// 产生一个v18向量的迭代器v4
  v5 = std::vector<int>::end(v17);              // 返回v17向量尾指针指向向量最后一个元素的下一个位置,给v5
  a1[0] = std::vector<int>::begin(v17);         // 返回v17向量头指针,指向第一个元素,给a1[0]
  v6 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(a1, 1LL);// 重载操作符,v6 = a1 +1*4,也就是v6是从输入字符串的第二个64位值开始
  std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(
    v6,                                         // 参数逆序入栈,v6输入字符串的第二个值的开始地址存放在rdi,v5字符串结束地址存放在rsi
    v5,
    v4,                                         // v4新的中间字符串
    (__int64)v23);                              // 输入字符串的第一个64位
  std::vector<int>::vector((__int64)a4);
  v7 = std::vector<int>::end(v18);
  v8 = std::vector<int>::begin(v18);            // 中间字符串v18的起始、结束位置
  std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(
    (__int64)a1,                                // 开始输入的字符
    v8,
    v7,                                         // 中间字符串v18的起始、结束位置
    (__int64)a4,                                // 新的向量
    v9,
    v10,
    v3);
  std::vector<int>::operator=(v19, a1);         // 逆序后的字符串给v19
  std::vector<int>::~vector(a1);
  std::vector<int>::~vector(a4);
  if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(v19, v16) )// 判断v14的值是否等于斐波那契数列前16个数
  {
    puts("You failed!");
    exit(0);
  }
  std::back_inserter<std::vector<int>>(v20);
  std::vector<int>::end(v17);
  v11 = std::vector<int>::begin(v17);
  std::copy_if<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#3}>(v11);
  puts("You win!");
  printf("Your flag is:flag{");
  a4[0] = std::vector<int>::begin(v20);
  a1[0] = std::vector<int>::end(v20);
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(a4, a1) )
  {
    v12 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(a4);
    std::ostream::operator<<(&std::cout, *v12);
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(a4);
  }
  putchar(125);
  std::vector<int>::~vector(v20);
  std::vector<int>::~vector(v19);
  std::vector<int>::~vector(v18);
  std::vector<int>::~vector(v17);
  std::vector<int>::~vector(v16);
  return 0;
}

push_back()函数:将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素。
程序的逻辑就是首先保存输入的字符串到v17,然后计算斐波那契数列前16个数保存在v16,之后将v17经过主要的变换过程就是transform函数和accumulate函数,之后得到的结果和v16比较,通过就输出“You win!”,失败就输出“You failed!”。

首先查看transform函数:

transform函数源代码及注释:

__int64 __fastcall std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  unsigned int *v4; // rax
  __int64 v5; // rax
  __int64 v7; // [rsp+0h] [rbp-30h] BYREF
  __int64 v8; // [rsp+8h] [rbp-28h] BYREF
  __int64 v9; // [rsp+10h] [rbp-20h] BYREF
  __int64 v10; // [rsp+18h] [rbp-18h] BYREF
  int v11; // [rsp+24h] [rbp-Ch] BYREF
  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>>(&v10, &v9) )// &v10字符串开始的地址,不等于,&v9字符串结束的位置
  {
    v4 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(&v10);// 返回v10的指针给v4
    v11 = main::{lambda(int)#1}::operator()(&v7, *v4);// v11 = v7的值 加上 v4的值
    v5 = std::back_insert_iterator<std::vector<int>>::operator*(&v8);// v5=v8
    std::back_insert_iterator<std::vector<int>>::operator=(v5, &v11);// 把v11的值插入到v5向量的最后
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v10);// v10地址加4LL
    std::back_insert_iterator<std::vector<int>>::operator++(&v8);// v8地址加4LL
  }
  return v8;
}

std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(v6,v5,v4,(__int64)v23);

transform函数函数的作用就是把输入的字符串从第二个值开始v6逐个加上第一个值v23,直到最后一个值结束v5,结果保存在新的中间地址v4。
之后是accumulate函数:

accumulate函数源代码及注释

__int64 __fastcall std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, char a7)
{
  unsigned int v7; // ebx
  __int64 v10; // [rsp+8h] [rbp-68h] BYREF
  __int64 v11; // [rsp+10h] [rbp-60h] BYREF
  __int64 v12; // [rsp+18h] [rbp-58h]
  char v13[32]; // [rsp+20h] [rbp-50h] BYREF
  char v14[24]; // [rsp+40h] [rbp-30h] BYREF
  unsigned __int64 v15; // [rsp+58h] [rbp-18h]

  v12 = a1;                                     // 开始输入的字符串
  v11 = a2;                                     // 中间字符串的开始地址
  v10 = a3;                                     // 结束
  v15 = __readfsqword(0x28u);                   // 
                                                // 这个循环的作用是把中间字符串逆序,逆序后的结果保存在v12返回
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v11, &v10) )// 字符串的开始地址和结束地址不相等
  {
    v7 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*((__int64)&v11);// v7等于字符串开始的第一个值
    std::vector<int>::vector(v13, a4);          // 把a4的值copy给v13。作用是在循环中暂时保存上一轮计算得到的a4字符串
    main::{lambda(std::vector<int>,int)#2}::operator()(v14, &a7, v13, v7);// 把当前输入的值v7放在结果v14的第一个位置,把上一轮的结果v13放在v14第一个位置后面。
                                                // 也就是每次把第二个值插在第一个前面,第三个之插在第二个前面
    std::vector<int>::operator=(a4, v14);       // 把v14的状态move给a4,获得计算之后值
    std::vector<int>::~vector(v14);
    std::vector<int>::~vector(v13);
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v11);// 开始地址加4LL
  }
  std::vector<int>::vector(v12, a4);            // a4的结果字符串,move给原来的输入的v12字符串
  return v12;
}

std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>((__int64)a1,v8,v7,(__int64)a4,v9,v10,v3);
accumulate函数函数的作用就是前面transform函数计算得到的中间字符串v18,从起始地址v8到结束地址v7,转换顺序逆序排列,将结果再保存到输入字符串的地址a1中。
最后比较结果字符串v19和斐波那契数列前16个数v16是否逐个数相等。
程序的计算流程就是:

1、接收16个数字输入;
2、计算斐波那契数列前16项;
3、计算把输入的16个数字,从第二个数字开始,都加上第一个值;
4、将步骤3的计算的结果顺序,逆向排序
5、将步骤4计算的结果和步骤2的结果比较,完全相同则输出“You win!”

所以我们的逆向算法的过程就是:

1、计算斐波那契数列前16项;
2、将斐波那契数列前16项顺序,逆向排序 ;
3、把步骤2中得到的结果,从第二个数字开始,都减去第一个值,得到的数组就是正确的输入。

python代码:

#模拟斐波那契数列
def fib(num):
    if not num or num == 1:
        return 1
    j = fib(num - 1)
    return j + fib(num - 2)
# 1、flag列表获得斐波那契数列前16个数
flag = []
for i in range(16):
    flag.append(fib(i))
print(flag)
# 2、flag列表获得斐波那契数列前16个数的逆序列表
flag = flag[::-1]
print(flag)
# 3、把步骤2中得到的结果flag列表,从第二个数字开始,都减去第一个值,得到的数组就是正确的输入
for i in range(1,len(flag)):
    flag[i] = flag[i] - flag[0]
print(flag)
for i in range(16):
    print(flag[i],end=" ")

运行结果:

将结果987 -377 -610 -754 -843 -898 -932 -953 -966 -974 -979 -982 -984 -985 -986 -986输入程序:

得到flag:flag{987-377-843-953-979-985}

以上是关于XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp的主要内容,如果未能解决你的问题,请参考以下文章

XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp

XCTF-攻防世界CTF平台-Reverse逆向类——65reverse-box

XCTF-攻防世界CTF平台-Reverse逆向类——59mfc逆向-200

XCTF-攻防世界CTF平台-Reverse逆向类——52handcrafted-pyc

XCTF-攻防世界CTF平台-Reverse逆向类——56tar-tar-binks

XCTF-攻防世界CTF平台-Reverse逆向类——54echo-server