deedeedee crazy 寒假逆向生涯(11/100)

deedeedee

这题没说法。。。直接用notepad++打开第二个文件,然后就看到了flag。。。。人傻了。。。
deedeedee   crazy  寒假逆向生涯(11/100)
deedeedee   crazy  寒假逆向生涯(11/100)

flag{t3mplat3_met4pr0gramming_is_gr8_4_3very0n3}

crazy

这题目呢,无壳,不难,直接拖进ida,主要是费眼。。。

查看伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax@1
  __int64 v4; // rax@1
  __int64 v5; // rax@1
  __int64 v6; // rax@1
  __int64 v7; // rax@1
  __int64 v8; // rax@1
  __int64 v9; // rax@1
  __int64 v10; // rax@1
  __int64 v11; // rax@2
  __int64 v12; // rax@2
  __int64 v13; // rax@2
  __int64 v14; // rax@2
  __int64 v15; // rax@2
  __int64 v16; // rax@2
  int result; // eax@3
  __int64 v18; // rcx@3
  char v19; // [sp+10h] [bp-130h]@1
  char v20; // [sp+30h] [bp-110h]@1
  char v21; // [sp+50h] [bp-F0h]@1
  char v22; // [sp+70h] [bp-D0h]@1
  char v23; // [sp+90h] [bp-B0h]@2
  char v24; // [sp+B0h] [bp-90h]@1
  __int64 v25; // [sp+128h] [bp-18h]@1

  v25 = *MK_FP(__FS__, 40LL);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
    (__int64)&v19,
    (__int64)argv,
    (__int64)envp);
  std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v19);
  LODWORD(v3) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"-------------------------------------------");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  LODWORD(v4) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Quote from people's champ");
  std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  LODWORD(v5) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"-------------------------------------------");
  std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
  LODWORD(v6) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"*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>>);
  LODWORD(v7) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"*Wrestling was like stand-up comedy for me.");
  std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
  LODWORD(v8) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"*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>>);
  LODWORD(v9) = std::operator<<<std::char_traits<char>>(
                  (__int64)&std::cout,
                  (__int64)"-------------------------------------------");
  std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
  HighTemplar::HighTemplar(&v24, &v19);
  LODWORD(v10) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"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(
    (__int64)&v20,
    (__int64)&v19);
  func1((__int64)&v21, (__int64)&v20);
  func2((__int64)&v22, (__int64)&v21);
  func3((__int64)&v22, 0);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&v22);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&v21);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&v20);
  HighTemplar::calculate((HighTemplar *)&v24);
  if ( (unsigned int)HighTemplar::getSerial((HighTemplar *)&v24) == 0 )
  {
    LODWORD(v11) = std::operator<<<std::char_traits<char>>(
                     (__int64)&std::cout,
                     (__int64)"/");
    std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
    LODWORD(v12) = std::operator<<<std::char_traits<char>>(
                     (__int64)&std::cout,
                     (__int64)"Do not be angry. Happy Hacking :)");
    std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
    LODWORD(v13) = std::operator<<<std::char_traits<char>>(
                     (__int64)&std::cout,
                     (__int64)"/");
    std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
    ZN11HighTemplar7getFlagB5cxx11Ev((__int64)&v23, (__int64)&v24);
    LODWORD(v14) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"flag{");
    LODWORD(v15) = std::operator<<<char,std::char_traits<char>,std::allocator<char>>(v14, (__int64)&v23);
    LODWORD(v16) = std::operator<<<std::char_traits<char>>(v15, (__int64)"}");
    std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&v23);
  }
  HighTemplar::~HighTemplar((HighTemplar *)&v24);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string((__int64)&v19);
  result = 0;
  v18 = *MK_FP(__FS__, 40LL) ^ v25;
  return result;
} 

HighTemplar::HighTemplar((__int64)&v24, (__int64)&v19);
HighTemplar::calculate((HighTemplar *)&v24)
(unsigned int)HighTemplar::getSerial((HighTemplar *)&v24)

HighTemplar::HighTemplar((__int64)&v24, (__int64)&v19);

__int64 __fastcall HighTemplar::HighTemplar(__int64 a1, __int64 a2)
{
  char v3; // [sp+17h] [bp-19h]@1
  __int64 v4; // [sp+18h] [bp-18h]@1

  v4 = *MK_FP(__FS__, 40LL);
  DarkTemplar::DarkTemplar((DarkTemplar *)a1);
  *(_QWORD *)a1 = &off_401EA0;
  *(_DWORD *)(a1 + 12) = 0;
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1 + 16, a2);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1 + 48, a2);
  std::allocator<char>::allocator(&v3, a2);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
    a1 + 80,
    "327a6c4304ad5938eaf0efb6cc3e53dc",
    &v3);
  std::allocator<char>::~allocator(&v3);
  return *MK_FP(__FS__, 40LL) ^ v4;
}

这个函数作用:
初始化a1,那么它是如何初始化的呢?

  1. std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1 + 16, a2)这里的话是a2是属于输入的值,所以a1+16地址处是属于输入赋值
  2. std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1 + 48, a2)这里的话a2依然是输入的值,所以a1+48地址处属于输入赋值
  3. std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string( a1 + 80, "327a6c4304ad5938eaf0efb6cc3e53dc",&v3);这里是属于字符串赋值,字符串已知,即"327a6c4304ad5938eaf0efb6cc3e53dc",至于这个v3是干嘛的,不重要,估计是为了帮忙赋值,前面一行刚开辟,后面赋值完就立刻被析构了,所以的话,无关紧要

下面只要抓住这三条输入即可。。
然后只需要注意含有v24v19地方即可

HighTemplar::calculate((HighTemplar *)&v24)

bool __fastcall HighTemplar::calculate(HighTemplar *this)
{
  __int64 v1; // rax@1
  __int64 v2; // rax@2
  unsigned __int64 v3; // rax@5
  _BYTE *v4; // rax@6
  _BYTE *v5; // rbx@6
  _BYTE *v6; // rax@6
  unsigned __int64 v7; // rax@8
  bool result; // al@8
  _BYTE *v9; // rax@9
  _BYTE *v10; // rbx@9
  _BYTE *v11; // rax@9
  int i; // [sp+18h] [bp-18h]@4
  int j; // [sp+1Ch] [bp-14h]@7

  LODWORD(v1) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
  if ( v1 != 32 )
  {
    LODWORD(v2) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Too short or too long");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }
  for ( i = 0; ; ++i )
  {
    LODWORD(v3) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
    if ( i > v3 )
      break;
    LODWORD(v4) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    i);
    v5 = v4;
    LODWORD(v6) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    i);
    *v5 = (*v6 ^ 0x50) + 23;
  }
  for ( j = 0; ; ++j )
  {
    LODWORD(v7) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
    result = j <= v7;
    if ( !result )
      break;
    LODWORD(v9) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    j);
    v10 = v9;
    LODWORD(v11) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                     (char *)this + 16,
                     j);
    *v10 = (*v11 ^ 0x13) + 11;
  }
  return result;
}

首先,

LODWORD(v1) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
  if ( v1 != 32 )
  {
    LODWORD(v2) = std::operator<<<std::char_traits<char>>((__int64)&std::cout, (__int64)"Too short or too long");
    std::ostream::operator<<(v2, &std::endl<char,std::char_traits<char>>);
    exit(-1);
  }

来判断输入长度,是否符合,不符合的话,直接exit
下面两个for循环的话,它是把输入的第一串字符(因为它是(char *)this + 16,长度为32,即到(char *)this + 48的位置)经过一些简单的异或操作变化之后,接下来变化之后肯定得拿去比较。。。。

 LODWORD(v4) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    i);
    v5 = v4;
    LODWORD(v6) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    i);
    *v5 = (*v6 ^ 0x50) + 23;

至于为什么要这样取的话,神操作。。可能编译器有点傻,把
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[]( (char *)this + 16, i);多搞出一次了吧,别在意这些。。。

(unsigned int)HighTemplar::getSerial((HighTemplar *)&v24)

__int64 __fastcall HighTemplar::getSerial(HighTemplar *this)
{
  unsigned __int64 v1; // rax@2
  char *v2; // rax@3
  char v3; // bl@3
  _BYTE *v4; // rax@3
  __int64 v5; // rax@4
  __int64 v6; // rax@4
  __int64 v7; // rax@5
  __int64 v8; // rax@5
  unsigned int i; // [sp+1Ch] [bp-14h]@1

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

这个函数作用呢,也就是验证输入的东西对不对。。。

for ( i = 0; ; ++i )
  {
    LODWORD(v1) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
    if ( (signed int)i >= v1 )
      break;
    LODWORD(v2) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 80,
                    (signed int)i);
    v3 = *v2;
    LODWORD(v4) = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                    (char *)this + 16,
                    (signed int)i);

它把(char *)this + 16(输入且经过一系列变换)和 (char *)this + 80(已知字符串赋值)两个来相比,相同的话,这里比较就没问题了。那我们只需要把这个已知字符串经过反向的异或操作变化回去不就行了吗?是的,就是这样

额外分析

那样我们再来考虑一下,刚才是不是有一段(char *)this + 48,这段怎么没用到呢?后面就是对这段进行分析,有兴趣的可以继续看。

ZN11HighTemplar7getFlagB5cxx11Ev((__int64)&v23, (__int64)&v24);

__int64 __fastcall ZN11HighTemplar7getFlagB5cxx11Ev(__int64 a1, __int64 a2)
{
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1, a2 + 48);
  return a1;
}

这里就是把a2 + 48赋值给a1,即这里的v23
deedeedee   crazy  寒假逆向生涯(11/100)
然后再把它当flag去输出就行。。
deedeedee   crazy  寒假逆向生涯(11/100)
好家伙,三段字符,只需要输入一段
一开始数据被分为三部分,
一部分(char *)this + 16赋值输入,后面带入算法;
一部分((char *)this + 48)赋值输入,最后作为flag输出;
一部分((char *)this + 80)赋值一个字符串,作为比较对象。

GAMEOVER

#include <iostream>
using namespace std;
int main()
{
    char b[32];
    char a[] = { "327a6c4304ad5938eaf0efb6cc3e53dc" };
    for (int i = 0; i < sizeof(a)-1; i++) {
        b[i] = ((((a[i] - 11) ^ 0x13) - 23) ^ 0x50);
        cout << b[i];
    }
}

deedeedee   crazy  寒假逆向生涯(11/100)

flag{tMx~qdstOs~crvtwb~aOba}qddtbrtcd}
上一篇:2021.1.26 蓝桥杯基础练习(BASIC-3 字母图形、BASIC-2 01字串)


下一篇:程序的美颜加滤镜,和瘦脸,样式语言CSS