西湖论剑 RE easyCpp Writeup

这道题目算挺简单的,全部都是标准库函数,没有用户自己写的函数,所以程序功能比较好推测。

源程序下载:http://file.eonew.cn/re/easyCpp

主要代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v3; // r15
  __int64 v4; // rdx
  __int64 v5; // rdx
  __int64 v6; // rdx
  __int64 v7; // rdx
  __int64 v8; // r12
  __int64 end; // rbx
  __int64 v10; // rax
  __int64 v11; // rdx
  __int64 v12; // rbx
  __int64 v13; // rax
  __int64 v14; // r8
  __int64 v15; // r9
  __int64 v16; // rbx
  char v17; // al
  unsigned int *v18; // rax
  const char **v20; // [rsp+0h] [rbp-190h]
  signed int i; // [rsp+1Ch] [rbp-174h]
  signed int j; // [rsp+20h] [rbp-170h]
  char v23; // [rsp+30h] [rbp-160h]
  char v24; // [rsp+50h] [rbp-140h]
  char v25; // [rsp+70h] [rbp-120h]
  char v26; // [rsp+90h] [rbp-100h]
  char v27; // [rsp+B0h] [rbp-E0h]
  __int64 v28; // [rsp+D0h] [rbp-C0h]
  __int64 v29; // [rsp+F0h] [rbp-A0h]
  char v30[72]; // [rsp+110h] [rbp-80h]
  unsigned __int64 v31; // [rsp+158h] [rbp-38h]

  v20 = argv;
  v31 = __readfsqword(0x28u);
  std::vector<int,std::allocator<int>>::vector(&v23, argv, envp);
  std::vector<int,std::allocator<int>>::vector(&v24, argv, v4);
  std::vector<int,std::allocator<int>>::vector(&v25, argv, v5);
  std::vector<int,std::allocator<int>>::vector(&v26, argv, v6);
  std::vector<int,std::allocator<int>>::vector(&v27, argv, v7);
  for ( i = 0; i <= 15; ++i )
  {
    scanf("%d", &v30[4 * i], v20);
    std::vector<int,std::allocator<int>>::push_back(&v24, &v30[4 * i]);
  }
  for ( j = 0; j <= 15; ++j )
  {
    LODWORD(v29) = fib(j);
    std::vector<int,std::allocator<int>>::push_back(&v23, &v29);
  }
  std::vector<int,std::allocator<int>>::push_back(&v25, v30);
  v8 = std::back_inserter<std::vector<int,std::allocator<int>>>(&v25);
  end = std::vector<int,std::allocator<int>>::end(&v24);
  v29 = std::vector<int,std::allocator<int>>::begin(&v24);
  v10 = __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator+(&v29, 1LL);
  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}>(
    v10,
    end,
    v8,
    v30);
  std::vector<int,std::allocator<int>>::vector(&v28, end, v11);
  v12 = std::vector<int,std::allocator<int>>::end(&v25);
  v13 = std::vector<int,std::allocator<int>>::begin(&v25);
  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)&v29,
    v13,
    v12,
    (__int64)&v28,
    v14,
    v15,
    v3);
  std::vector<int,std::allocator<int>>::operator=(&v26, &v29);
  std::vector<int,std::allocator<int>>::~vector(&v29);
  std::vector<int,std::allocator<int>>::~vector(&v28);
  if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(&v26, &v23) )
  {
    puts("You failed!");
    exit(0);
  }
  std::back_inserter<std::vector<int,std::allocator<int>>>(&v27);
  v16 = std::vector<int,std::allocator<int>>::end(&v24);
  v17 = std::vector<int,std::allocator<int>>::begin(&v24);
  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}>(v17);
  puts("You win!");
  printf("Your flag is:flag{", v16, v20);
  v28 = std::vector<int,std::allocator<int>>::begin(&v27);
  v29 = std::vector<int,std::allocator<int>>::end(&v27);
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int,std::allocator<int>>>(&v28, &v29) )
  {
    v18 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator*(&v28);
    std::ostream::operator<<(&std::cout, *v18);
    __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator++(&v28);
  }
  putchar(125);
  std::vector<int,std::allocator<int>>::~vector(&v27);
  std::vector<int,std::allocator<int>>::~vector(&v26);
  std::vector<int,std::allocator<int>>::~vector(&v25);
  std::vector<int,std::allocator<int>>::~vector(&v24);
  std::vector<int,std::allocator<int>>::~vector(&v23);
  return 0;
}

思路

从上面的代码可以看出,程序基本使用的都是C++自带的库函数,像这种自带的库函数往往符合高内聚低耦合的编程思想,所以每个模块的功能会设计的尽可能简单。即使我们不是专业的C++编程人员,也可以通过动态调试来推测每个模块的功能。

核心代码

累加

v10 = __gnu_cxx::__normal_iterator<int *,std::vector<int,std::allocator<int>>>::operator+(&v29, 1LL);
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}>(
  v10,
  end,
  v8,
  v30);

这段代码的功能就是把我们输入的16个数,将第一个数累加到后面15个数上,然后得到新的vector,可以通过调试来进行验证该功能。

翻转数组

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)&v29,
  v13,
  v12,
  (__int64)&v28,
  v14,
  v15,
  v3);

将新的vector进行首尾倒置,得到处理好的vector。

斐波那契数列

for ( j = 0; j <= 15; ++j )
{
  LODWORD(v29) = fib(j);
  std::vector<int,std::allocator<int>>::push_back(&v23, &v29);
}

生成16个斐波那契数列,并放在一个vector中。

验证

if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(&v26, &v23) )
{
  puts("You failed!");
  exit(0);
}

只要处理好的vector和生成的 有16个斐波那契数列的vector是相同的。那么就得到了flag。

脚本

由于上面的步骤是可逆的,所以将上面的步骤反过来进行一遍即可得到要输入的数据。

#! /usr/bin/python3
# -*- coding: utf-8 -*-

def fib(n):
    if(n == 1):
        return 1
    elif(n == 2):
        return 1

    return fib(n-1) + fib(n-2)

# 生成16个斐波那契数列
inner_vector = [fib(i+1) for i in range(16)]

# 倒置
reversed_inner_vector = inner_vector[::-1]

key = []
first = reversed_inner_vector[0]
key += [first]
# 后面的15位和第一位相减
for i in range(1, len(reversed_inner_vector)):
    key += [reversed_inner_vector[i] - first]

# 输出
output = ''
for v in key:
    output += str(v) + ' '

print(output)

总结

编程思想真的很重要。