XCTF - attack and defense world CTF platform Reverse reverse class - 53, easyCpp

Download the attachment easyCpp and check the file information first:

It is a 64 bit ELF file under Linux. Run the program:

Is a program that inputs a string and then determines whether it is correct. If there is an error, enter "You failed!"
Then open with IDA64:

main function source code and comments:

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 characters
  {
    scanf("%d", &v23[4 * i]);
    std::vector<int>::push_back(v17, &v23[4 * i]);// The entered character is inserted at the end of the v17 character array
  }
  for ( j = 0; j <= 15; ++j )
  {
    LODWORD(a1[0]) = fib(j);                    // fib is the Fibonacci sequence in c language, and the value of the j-th number of Fibonacci sequence is obtained
    std::vector<int>::push_back(v16, a1);       // Insert the first 16 numbers of Fibonacci sequence into v16 character array
  }
  std::vector<int>::push_back(v18, v23);        // v23 is the first character inserted into the v18 vector. Finally, v18 is a new address to store the calculated string
  v4 = std::back_inserter<std::vector<int>>(v18);// Iterator v4 that generates a v18 vector
  v5 = std::vector<int>::end(v17);              // Return v17 vector tail pointer to the next position of the last element of the vector, to v5
  a1[0] = std::vector<int>::begin(v17);         // Returns the v17 vector header pointer, pointing to the first element, to a1[0]
  v6 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(a1, 1LL);// Overloaded operator, v6 = a1 +1*4, that is, v6 starts from the second 64 bit value of the input string
  std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>(
    v6,                                         // The parameters are stacked in reverse order. The start address of the second value of the v6 input string is stored in RDI, and the end address of the V5 string is stored in rsi
    v5,
    v4,                                         // v4 new intermediate string
    (__int64)v23);                              // The first 64 bit of the input string
  std::vector<int>::vector((__int64)a4);
  v7 = std::vector<int>::end(v18);
  v8 = std::vector<int>::begin(v18);            // Start and end positions of intermediate string v18
  std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>(
    (__int64)a1,                                // Character to start entering
    v8,
    v7,                                         // Start and end positions of intermediate string v18
    (__int64)a4,                                // New vector
    v9,
    v10,
    v3);
  std::vector<int>::operator=(v19, a1);         // The string in reverse order is given to v19
  std::vector<int>::~vector(a1);
  std::vector<int>::~vector(a4);
  if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(v19, v16) )// Judge whether the value of v14 is equal to the first 16 numbers of Fibonacci series
  {
    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() function: add a new element to the rear of the vector, and the position is the next element of the current last element.
The logic of the program is to first save the input string to v17, and then calculate the Fibonacci number sequence. The first 16 numbers are saved in v16. Then, the main transformation process of v17 is the transform function and calculate function. After that, the result is compared with v16, and "You win!" is output through "You win!" and "You failed!" is output in case of failure.

First look at the transform function:

transform function source code and comments:

__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;                                     // Start position of input string
  v9 = a2;
  v8 = a3;
  v7 = a4;                                      // First character entered
  v12 = __readfsqword(0x28u);
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v10, &v9) )// &The address at the beginning of the V10 string is not equal to the position at the end of the &v9 string
  {
    v4 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(&v10);// Returns the pointer of v10 to v4
    v11 = main::{lambda(int)#1}::operator()(&v7, *v4);//  V11 = the value of V7 plus the value of V4
    v5 = std::back_insert_iterator<std::vector<int>>::operator*(&v8);// v5=v8
    std::back_insert_iterator<std::vector<int>>::operator=(v5, &v11);// Insert the value of v11 at the end of the v5 vector
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v10);// v10 address plus 4LL
    std::back_insert_iterator<std::vector<int>>::operator++(&v8);// v8 address plus 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);

The function of transform is to start the input string from the second value v6 and add the first value v23 one by one until the last value ends v5, and the result is saved at the new intermediate address v4.
This is followed by the calculate function:

Calculate function source code and comments

__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;                                     // Start input string
  v11 = a2;                                     // Start address of intermediate string
  v10 = a3;                                     // end
  v15 = __readfsqword(0x28u);                   // 
                                                // The function of this loop is to reverse the order of the intermediate strings. The results after the reverse order are saved and returned in v12
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v11, &v10) )// The start address and end address of the string are not equal
  {
    v7 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*((__int64)&v11);// v7 is equal to the first value at the beginning of the string
    std::vector<int>::vector(v13, a4);          // copy the value of a4 to v13. The function is to temporarily save the a4 string calculated in the previous round in the loop
    main::{lambda(std::vector<int>,int)#2}::operator()(v14, &a7, v13, v7);//  Put the current input value V7 in the first position of the result V14, and put the result V13 of the previous round behind the first position of V14.
                                                // That is, insert the second value in front of the first and the third value in front of the second every time
    std::vector<int>::operator=(a4, v14);       // move the state of v14 to a4 to obtain the calculated value
    std::vector<int>::~vector(v14);
    std::vector<int>::~vector(v13);
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v11);// Start address plus 4LL
  }
  std::vector<int>::vector(v12, a4);            // a4 result string, move to the original input v12 string
  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);
The calculate function is used to calculate the intermediate string v18 from the start address v8 to the end address v7 in the previous transform function. The conversion sequence is arranged in reverse order, and the result is saved to the address a1 of the input string.
Finally, compare whether the result string v19 and the first 16 numbers v16 of Fibonacci sequence are equal one by one.
The calculation flow of the program is:

1. Receive 16 digital inputs;
2. Calculate the first 16 items of Fibonacci series;
3. The calculation adds the first value to the 16 numbers input, starting from the second number;
4. Sort the results of step 3 in reverse order
5. Compare the result calculated in step 4 with the result calculated in step 2. If it is identical, output "You win!"

Therefore, the process of our reverse algorithm is:

1. Calculate the first 16 items of Fibonacci series;
2. Put the first 16 items of Fibonacci number series in reverse order;
3. Subtract the first value from the result obtained in step 2, starting from the second number, and the resulting array is the correct input.

python code:

#Simulated Fibonacci sequence
def fib(num):
    if not num or num == 1:
        return 1
    j = fib(num - 1)
    return j + fib(num - 2)
# 1. flag list to obtain the first 16 numbers of Fibonacci series
flag = []
for i in range(16):
    flag.append(fib(i))
print(flag)
# 2. The flag list obtains the reverse order list of the first 16 numbers of the Fibonacci sequence
flag = flag[::-1]
print(flag)
# 3. Subtract the first value from the result flag list obtained in step 2 from the second number, and the resulting array is the correct input
for i in range(1,len(flag)):
    flag[i] = flag[i] - flag[0]
print(flag)
for i in range(16):
    print(flag[i],end=" ")

Operation results:

Enter the result 987 - 377 - 610 - 754 - 843 - 898 - 932 - 953 - 966 - 974 - 979 - 982 - 984 - 985 - 986 - 986 into the program:

Get the flag: flag{987-377-843-953-979-985}

Tags: C++ Linux Algorithm

Posted on Sat, 25 Sep 2021 05:31:55 -0400 by kausee