ADworld reverse wp - crazy

Keywords: reverse


I've been doing C + + reverse in my spare time to attack and defend the world. I recently brushed master pipixia's blog. I feel that as a binary, reverse and pwn should not be separated. Moreover, I've done reverse for half a year and found that my code ability has been improved unconsciously (I found it when I wrote the code recently). It's estimated that being able to read the decompiled pseudo code can also train the code power, Moreover, it has been safe to enter the pit for one year, and I gradually realize that reverse is a very interesting thing. In the past, I was always very impatient when doing complex reverse. Now I know that patience is the most important thing to engage in technology

Analysis process

Drag directly into IDA64 (IDA32). The pop-up prompt ELFx86-64, and you should get used to checking the shell before IDA
See the main function

int __cdecl main(int argc, const char **argv, const char **envp)
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  __int64 v6; // rax
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rax
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  __int64 v13; // rax
  __int64 v14; // rax
  __int64 v15; // rax
  __int64 v16; // rax
  char v18[32]; // [rsp+10h] [rbp-130h] BYREF
  char v19[32]; // [rsp+30h] [rbp-110h] BYREF
  char v20[32]; // [rsp+50h] [rbp-F0h] BYREF
  char v21[32]; // [rsp+70h] [rbp-D0h] BYREF
  char v22[32]; // [rsp+90h] [rbp-B0h] BYREF
  char v23[120]; // [rsp+B0h] [rbp-90h] BYREF
  unsigned __int64 v24; // [rsp+128h] [rbp-18h]

  v24 = __readfsqword(0x28u);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v18, argv, envp);
  std::operator>><char>(&std::cin, v18);
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Quote from people's champ");
  std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  v5 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
  std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  v6 = std::operator<<<std::char_traits<char>>(
         "*My goal was never to be the loudest or the craziest. It was to be the most entertaining.");
  std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
  v7 = std::operator<<<std::char_traits<char>>(&std::cout, "*Wrestling was like stand-up comedy for me.");
  std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
  v8 = std::operator<<<std::char_traits<char>>(
         "*I like to use the hard times in the past to motivate me today.");
  std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
  v9 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
  std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
  HighTemplar::HighTemplar((DarkTemplar *)v23, (__int64)v18);
  v10 = std::operator<<<std::char_traits<char>>(&std::cout, "Checking....");
  std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v19, v18);
  func1(v20, v19);
  func2(v21, v20);
  func3(v21, 0LL);
  HighTemplar::calculate((HighTemplar *)v23);
  if ( !(unsigned int)HighTemplar::getSerial((HighTemplar *)v23) )
    v11 = std::operator<<<std::char_traits<char>>(&std::cout, "/");
    std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
    v12 = std::operator<<<std::char_traits<char>>(&std::cout, "Do not be angry. Happy Hacking :)");
    std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
    v13 = std::operator<<<std::char_traits<char>>(&std::cout, "/");
    std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
    HighTemplar::getFlag[abi:cxx11](v22, v23);
    v14 = std::operator<<<std::char_traits<char>>(&std::cout, "flag{");
    v15 = std::operator<<<char>(v14, v22);
    v16 = std::operator<<<std::char_traits<char>>(v15, "}");
    std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
  HighTemplar::~HighTemplar((HighTemplar *)v23);
  return 0;

From the backward analysis of flag string, there are possible key functions such as getserial, calculate and hightemple, and then locate them
STD:: operator > > < char > (& STD:: CIN, v18); know that v18 is the input string
Hightemple:: hightemple ((darktemple *) V23, (_int64) v18); the input string is processed here

Analyze hightemple

unsigned __int64 __fastcall HighTemplar::HighTemplar(DarkTemplar *a1, __int64 a2)
  char v3; // [rsp+17h] [rbp-19h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  *(_QWORD *)a1 = &off_401EA0;
  *((_DWORD *)a1 + 3) = 0;
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 16, a2);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 48, a2);
    (char *)a1 + 80,
  return __readfsqword(0x28u) ^ v4;

Guess 327a6c4304ad5938eaf0efb6cc3e53dc is a string of key strings. Although the function is not clear from a direct view, let's analyze it first

It is found that v23 appears in all possible parameter positions of key functions, and then analyze calculate

bool __fastcall HighTemplar::calculate(HighTemplar *this)
  __int64 v1; // rax
  _BYTE *v2; // rbx
  bool result; // al
  _BYTE *v4; // rbx
  int i; // [rsp+18h] [rbp-18h]
  int j; // [rsp+1Ch] [rbp-14h]

  if ( std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16) != 32 )
    v1 = std::operator<<<std::char_traits<char>>(&std::cout, "Too short or too long");
    std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
  for ( i = 0;
        i <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
        ++i )
    v2 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
    *v2 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                       (char *)this + 16,
                       i) ^ 0x50)
        + 23;
  for ( j = 0; ; ++j )
    result = j <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
    if ( !result )
    v4 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
    *v4 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                       (char *)this + 16,
                       j) ^ 0x13)
        + 11;
  return result;

The pseudo code here is ugly, but it is probably an if and two for. The string length is required to be = = 32, and then for each character ^ 0x50 + 23, and then ^ 0x13 + 11

Then look at getserial

__int64 __fastcall HighTemplar::getSerial(HighTemplar *this)
  char v1; // bl
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  __int64 v5; // rax
  unsigned int i; // [rsp+1Ch] [rbp-14h]

  for ( i = 0;
        (int)i < (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
        ++i )
    v1 = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                     (char *)this + 80,
    if ( v1 != *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                           (char *)this + 16,
                           (int)i) )
      v4 = std::operator<<<std::char_traits<char>>(&std::cout, "You did not pass ");
      v5 = std::ostream::operator<<(v4, i);
      std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
      *((_DWORD *)this + 3) = 1;
      return *((unsigned int *)this + 3);
    v2 = std::operator<<<std::char_traits<char>>(&std::cout, "Pass ");
    v3 = std::ostream::operator<<(v2, i);
    std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  return *((unsigned int *)this + 3);

Compare the string of this + 16 with that of this + 80

Problem solving script

In fact, you don't need to analyze it too clearly. You can already guess the pulse of the program. Generally speaking, it's relatively simple

serial = "327a6c4304ad5938eaf0efb6cc3e53dc"
flag = ""
for ch in serial:
    flag += chr((ord(ch) - 11 ^ 0x13) - 23 ^ 0x50)
print("flag{" + flag + "}")


The decompiled pseudo code of cpp is very ugly, but you don't need to care about a long string of function names. Just look at the key positions, such as cin, cout, etc
Reverse is still more inclined to analysis, and the problem-solving script is often much simpler than pwn
Reverse is more a top-down analysis, grasp the program logic from the macro, ignore the cumbersome code details, guess an idea, and then verify it, so as to speed up the reverse speed
Of course, unless PWN is called, PWN's reverse analysis is to dig out all the details of the vulnerability point
(in fact, there's no need to use a bunch of great principles. Brush more questions and you'll be done

There are too many things to do recently. After this period, I will continue to update the wp of BUU PWN and open a new pit of BUU reverse
About playing the game... Next year, I really don't have a long time to play the game. I can only brush the questions in fragments of time to make a living(

Posted by scotmcc on Fri, 12 Nov 2021 09:00:29 -0800