[MRCTF2020]EasyCpp

题目:[MRCTF2020]EasyCpp

这题是一个用g++编译的C++逆向题,第一次碰到这种类型,边做边学习吧。
因为是初次接触C++逆向,如有不足,欢迎评论区指正。

一、main

IDA64查看main函数的伪代码如下,各个反汇编后伪代码的注释也已经给出,仅供参考:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  __int64 v4; // rbx
  __int64 v5; // rax
  char v6; // bl
  __int64 v7; // rax
  __int64 v8; // rax
  __int64 v9; // rax
  __int64 v10; // rax
  __int64 v11; // rax
  __int64 v12; // rax
  char v14[40]; // [rsp+0h] [rbp-140h] BYREF
  __int64 v15; // [rsp+28h] [rbp-118h] BYREF
  __int64 v16; // [rsp+30h] [rbp-110h] BYREF
  int v17; // [rsp+3Ch] [rbp-104h] BYREF
  char v18[32]; // [rsp+40h] [rbp-100h] BYREF
  char v19[48]; // [rsp+60h] [rbp-E0h] BYREF
  char v20[31]; // [rsp+90h] [rbp-B0h] BYREF
  char v21; // [rsp+AFh] [rbp-91h] BYREF
  char v22[47]; // [rsp+B0h] [rbp-90h] BYREF
  char v23; // [rsp+DFh] [rbp-61h] BYREF
  char v24[36]; // [rsp+E0h] [rbp-60h] BYREF
  int v25; // [rsp+104h] [rbp-3Ch]
  char *v26; // [rsp+108h] [rbp-38h]
  int *v27; // [rsp+110h] [rbp-30h]
  _DWORD *v28; // [rsp+118h] [rbp-28h]
  int *v29; // [rsp+120h] [rbp-20h]
  int i; // [rsp+128h] [rbp-18h]
  unsigned int v31; // [rsp+12Ch] [rbp-14h]

  v31 = 0;
  
  /*创建一个int型容器*/
  std::vector<int>::vector(v20, argv, envp);  
  
  /*创建一个bool型容器*/  
  std::vector<bool>::vector(v19);    
             
  /*构建一个字符串变量*/
  std::allocator<char>::allocator(&v21);        
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v18, &unk_500E, &v21);
  // 构造函数,中间那个basic_string<char,std::char_traits<char>,std::allocator<char>>是类模板,后面那个basic_string函数名表明这个函数是构造函数。
  std::allocator<char>::~allocator(&v21);
  
  /*输出字符串"give me your key!\n"*/
  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "give me your key!");
  std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  
  /*输入的数据存入keys[]数组*/
  for ( i = 0; i <= 8; ++i )
  {
    std::istream::operator>>(&std::cin, &keys[i]);
    //调用string头文件里的to_string全局函数,把输入的数据转为字符的形式存到v22中
    std::__cxx11::to_string(v22, keys[i]);
    //调用string头文件里basic_string类模板中的operator+函数把输入的数据存入v18
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(v18, v22);
    //调用string头文件里basic_string类模板中的析构函数
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v22);
  }
  
  v28 = keys;
  v29 = keys;
  v27 = &unk_83E4;
  while ( v29 != v27 )
  {
    v17 = *v29;
    //将输入的压入v20容器
    std::vector<int>::push_back(v20, &v17);
    ++v29;
  }
  
  /*获取容器尾指针*/
  v4 = std::vector<int>::end(v20);
  
  /*获取容器头指针*/
  v5 = std::vector<int>::begin(v20);

  /*调用for_each函数模板对从头指针到尾指针的每一项执行lambda函数*/
  std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,main::{lambda(int &)#1}>(v5, v4);

  /*这里是一样的获取头尾指针*/
  v26 = v20;
  v16 = std::vector<int>::begin(v20);
  v15 = std::vector<int>::end(v26);

  //while循环的条件用的是gcc编译器的使用的C++库中定义的命名空间__gnu_cxx,其实是判断头尾指针是否相同。
  while ( __gnu_cxx::operator!=<int *,std::vector<int>>(&v16, &v15) )
  {
    //取v16指向的地址中的值存入v25
    v25 = *__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(&v16);
   
    /*创建字符串变量v14*/
    std::allocator<char>::allocator(&v23);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v14, &unk_500E, &v23);
    std::allocator<char>::~allocator(&v23);
  
    //加密后结果放到v14中
    depart(v25, v14);
  
    {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)#1}::operator()(&func, v14);
    
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(v24, v14);
    v6 = {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,int)#2}::operator()(
           &check,
           v24,
           v31) ^ 1;
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v24);
    
    if ( v6 )
    {
      v7 = std::operator<<<std::char_traits<char>>(&std::cout, "Wrong password!");
      std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
      system("pause");
      exit(0);
    }
    ++v31;
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v14);
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(&v16);
  }

  /*下面就是告诉我们md5加密(32位大写)后的v18就是flag*/
  v8 = std::operator<<<std::char_traits<char>>(&std::cout, "right!");
  std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
  v9 = std::operator<<<std::char_traits<char>>(&std::cout, "flag:MRCTF{md5(");
  v10 = std::operator<<<char>(v9, v18);
  v11 = std::operator<<<std::char_traits<char>>(v10, ")}");
  std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
  v12 = std::operator<<<std::char_traits<char>>(
          &std::cout,
          "md5()->{32/upper case/put the string into the function and transform into md5 hash}");
  std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
  system("pause");
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v18);
  std::vector<bool>::~vector(v19);
  std::vector<int>::~vector(v20);
  return 0;
}

二、depart

接下来看一下depart函数的伪代码。
其实看到这个函数的第一个循环我就想到了之前大一学C语言的时候学到的素数判断的算法,利用的数学原理如下:

待测试的数num只需要界于2到num的平方根之间的所有数,看它们是否可以整除num。

  • 如果可以整除,说明输入的数不是素数;
  • 如果不可整除,说明输入的数是素数。

就这个函数而言,可以发现,每找到一个因数就把除过后的数再进入depart找一次因数,可以看出是对数据的分解,把一个数分解成素因子的乘积,各因子间用空格分隔组合成字符串。

__int64 __fastcall depart(int a1, __int64 a2)
{
  char v3[32]; // [rsp+20h] [rbp-60h] BYREF
  char v4[40]; // [rsp+40h] [rbp-40h] BYREF
  int i; // [rsp+68h] [rbp-18h]
  int v6; // [rsp+6Ch] [rbp-14h]

  /*分解成素因数*/
  v6 = a1;
  for ( i = 2; std::sqrt<int>(a1) >= i; ++i )
  {
    if ( !(a1 % i) )
    {
      v6 = i;
      depart(a1 / i, a2);
      break;
    }
  }
  
  //运算后转换成字符串v4
  std::__cxx11::to_string(v4, v6);
  
  //合并&unk_500C(空格)和v4
  std::operator+<char>(v3, &unk_500C, v4);
  
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator+=(a2, v3);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v3);
  return std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(v4);
}

三、3个lambda

我们接下来对下面这三个lambda函数进行分析。
在这里插入图片描述

(一)

__int64 __fastcall std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,main::{lambda(int &)#1}>(__int64 a1, __int64 a2)
{
  unsigned int v2; // ebx
  __int64 v3; // rax
  char v5; // [rsp+Fh] [rbp-21h] BYREF
  __int64 v6; // [rsp+10h] [rbp-20h] BYREF
  __int64 v7[3]; // [rsp+18h] [rbp-18h] BYREF

  v7[0] = a1;
  v6 = a2;
  //循环条件是利用重载运算符判断两个是否相等
  while ( __gnu_cxx::operator!=<int *,std::vector<int>>(v7, &v6) )
  {
  //取头指针v7的值
    v3 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(v7);
    
    main::{lambda(int &)#1}::operator()(&v5, v3);
    
    //头指针移动
    __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(v7);
  }
  return v2;
}

里面还嵌套了一个lambda函数:

_DWORD *__fastcall main::{lambda(int &)#1}::operator()(__int64 a1, _DWORD *a2)
{
  _DWORD *result; // rax

  result = a2;
  *a2 ^= 1u;
  return result;
}

可以看出是对每一项进行与1异或的操作。

(二)

进入第二个lambda函数,看着像是替换。

__int64 __fastcall {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)#1}::operator()(__int64 a1, __int64 a2)
{
  __int64 v2; // rbx
  __int64 v3; // rax
  __int64 v4; // rbx
  __int64 v5; // rax
  __int64 v6; // rbx
  __int64 v7; // rax
  __int64 v8; // rbx
  __int64 v9; // rax
  __int64 v10; // rbx
  __int64 v11; // rax
  __int64 v12; // rbx
  __int64 v13; // rax
  __int64 v14; // rbx
  __int64 v15; // rax
  __int64 v16; // rbx
  __int64 v17; // rax
  __int64 v18; // rbx
  __int64 v19; // rax
  __int64 v20; // rbx
  __int64 v21; // rax
  __int64 v22; // rbx
  __int64 v23; // rax
  char v25; // [rsp+1Ah] [rbp-26h] BYREF
  char v26; // [rsp+1Bh] [rbp-25h] BYREF
  char v27; // [rsp+1Ch] [rbp-24h] BYREF
  char v28; // [rsp+1Dh] [rbp-23h] BYREF
  char v29; // [rsp+1Eh] [rbp-22h] BYREF
  char v30; // [rsp+1Fh] [rbp-21h] BYREF
  char v31; // [rsp+20h] [rbp-20h] BYREF
  char v32; // [rsp+21h] [rbp-1Fh] BYREF
  char v33; // [rsp+22h] [rbp-1Eh] BYREF
  char v34; // [rsp+23h] [rbp-1Dh] BYREF
  char v35; // [rsp+24h] [rbp-1Ch] BYREF
  char v36; // [rsp+25h] [rbp-1Bh] BYREF
  char v37; // [rsp+26h] [rbp-1Ah] BYREF
  char v38; // [rsp+27h] [rbp-19h] BYREF
  char v39; // [rsp+28h] [rbp-18h] BYREF
  char v40; // [rsp+29h] [rbp-17h] BYREF
  char v41; // [rsp+2Ah] [rbp-16h] BYREF
  char v42; // [rsp+2Bh] [rbp-15h] BYREF
  char v43; // [rsp+2Ch] [rbp-14h] BYREF
  char v44; // [rsp+2Dh] [rbp-13h] BYREF
  char v45; // [rsp+2Eh] [rbp-12h] BYREF
  char v46[17]; // [rsp+2Fh] [rbp-11h] BYREF

  v25 = 'O';
  v26 = '0';
  v2 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v3 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v3,
    v2,
    &v26,
    &v25);
  v27 = 'l';
  v28 = '1';
  v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v5 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v5,
    v4,
    &v28,
    &v27);
  v29 = 'z';
  v30 = '2';
  v6 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v7 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v7,
    v6,
    &v30,
    &v29);
  v31 = 69;
  v32 = 51;
  v8 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v9 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v9,
    v8,
    &v32,
    &v31);
  v33 = 'A';
  v34 = '4';
  v10 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v11 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v11,
    v10,
    &v34,
    &v33);
  v35 = 's';
  v36 = '5';
  v12 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v13 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v13,
    v12,
    &v36,
    &v35);
  v37 = 'G';
  v38 = '6';
  v14 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v15 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v15,
    v14,
    &v38,
    &v37);
  v39 = 'T';
  v40 = '7';
  v16 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v17 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v17,
    v16,
    &v40,
    &v39);
  v41 = 'B';
  v42 = '8';
  v18 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v19 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v19,
    v18,
    &v42,
    &v41);
  v43 = 'q';
  v44 = '9';
  v20 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v21 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
    v21,
    v20,
    &v44,
    &v43);
  v45 = '=';
  v46[0] = ' ';
  v22 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::end(a2);
  v23 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::begin(a2);
  return std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(
           v23,
           v22,
           v46,
           &v45);
}

看看这个replace函数是怎么替换的。如下:

__int64 __fastcall std::replace<__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>,char>(__int64 a1, __int64 a2, _BYTE *a3, _BYTE *a4)
{
  __int64 result; // rax
  __int64 v7; // [rsp+10h] [rbp-10h] BYREF
  __int64 v8; // [rsp+18h] [rbp-8h] BYREF

  v8 = a1;
  v7 = a2;
  while ( 1 )
  {
    result = __gnu_cxx::operator!=<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>(
               &v8,
               &v7);
    if ( !result )
      break;
    if ( *__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator*(&v8) == *a3 )
      *__gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator*(&v8) = *a4;
    __gnu_cxx::__normal_iterator<char *,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>::operator++(&v8);
  }
  return result;
}

原来是对应替换,比如说,如果是0就变成了O,差不多这样的意思。

(三)

紧接着的lambda是一个检查函数,把加密后的字符串与一个已知字符串ans[abi:cxx11]做对比。

_BOOL8 __fastcall {lambda(std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>,int)#2}::operator()(__int64 a1, __int64 a2, int a3)
{
  const char *v3; // rbx
  const char *v4; // rax

  //调用basic_string类模板中的c_str函数把字符串转化为C语言的字符串,目的是为了接下来用strcmp函数。【兼容性考虑】
  v3 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(&ans[abi:cxx11] + 32 * a3);
  v4 = std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(a2);
  return strcmp(v4, v3) == 0;
}

我们现在来找这个已知字符串ans[abi:cxx11]。选中那个按X查看交叉引用,双击进入到函数。
在这里插入图片描述

int __fastcall __static_initialization_and_destruction_0(int a1, int a2)
{
  int result; // eax
  char v3; // [rsp+17h] [rbp-29h] BYREF
  char v4; // [rsp+18h] [rbp-28h] BYREF
  char v5; // [rsp+19h] [rbp-27h] BYREF
  char v6; // [rsp+1Ah] [rbp-26h] BYREF
  char v7; // [rsp+1Bh] [rbp-25h] BYREF
  char v8; // [rsp+1Ch] [rbp-24h] BYREF
  char v9; // [rsp+1Dh] [rbp-23h] BYREF
  char v10; // [rsp+1Eh] [rbp-22h] BYREF
  char v11[33]; // [rsp+1Fh] [rbp-21h] BYREF

  if ( a1 == 1 && a2 == 0xFFFF )
  {
    std::ios_base::Init::Init(&std::__ioinit);
    __cxa_atexit(&std::ios_base::Init::~Init, &std::__ioinit, &_dso_handle);
    std::allocator<char>::allocator(&v3);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11],
      "=zqE=z=z=z",
      &v3);
    std::allocator<char>::~allocator(&v3);
    std::allocator<char>::allocator(&v4);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 32,
      "=lzzE",
      &v4);
    std::allocator<char>::~allocator(&v4);
    std::allocator<char>::allocator(&v5);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 64,
      "=ll=T=s=s=E",
      &v5);
    std::allocator<char>::~allocator(&v5);
    std::allocator<char>::allocator(&v6);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 96,
      "=zATT",
      &v6);
    std::allocator<char>::~allocator(&v6);
    std::allocator<char>::allocator(&v7);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 128,
      "=s=s=s=E=E=E",
      &v7);
    std::allocator<char>::~allocator(&v7);
    std::allocator<char>::allocator(&v8);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 160,
      "=EOll=E",
      &v8);
    std::allocator<char>::~allocator(&v8);
    std::allocator<char>::allocator(&v9);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 192,
      "=lE=T=E=E=E",
      &v9);
    std::allocator<char>::~allocator(&v9);
    std::allocator<char>::allocator(&v10);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 224,
      "=EsE=s=z",
      &v10);
    std::allocator<char>::~allocator(&v10);
    std::allocator<char>::allocator(v11);
    std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
      &ans[abi:cxx11] + 256,
      "=AT=lE=ll",
      v11);
    std::allocator<char>::~allocator(v11);
    result = __cxa_atexit(_tcf_0, 0LL, &_dso_handle);
  }
  return result;
}

都是很简单的初始化操作。很容易得到ans[abi:cxx11]。
分析完毕,梳理一下,对输入的9个数据先每项与1异或,然后遍历每项分解成素因子,排布,变成字符串之后(比如0 -> ‘0’)做替换然后与已有字符串作比较。
现在开始写Exp。

EXP

贴上我写的exp:

str1 = ["=zqE=z=z=z", "=lzzE", "=ll=T=s=s=E", "=zATT", "=s=s=s=E=E=E", "=EOll=E","=lE=T=E=E=E", "=EsE=s=z", "=AT=lE=ll"]
flagdata = [1,1,1,1,1,1,1,1,1]
flag=""
for i in range(len(str1)):
    k = -1
    str1[i] = str1[i].replace("O", "0")
    str1[i] = str1[i].replace("l", "1")
    str1[i] = str1[i].replace("z", "2")
    str1[i] = str1[i].replace("E", "3")
    str1[i] = str1[i].replace("A", "4")
    str1[i] = str1[i].replace("s", "5")
    str1[i] = str1[i].replace("G", "6")
    str1[i] = str1[i].replace("T", "7")
    str1[i] = str1[i].replace("B", "8")
    str1[i] = str1[i].replace("q", "9")
    str1[i] = str1[i].replace("=", " ")
    str1[i] += ' '
    a = []
    x = 0
    for j in str1[i]:
        if j == ' ':
            a.append(x)
            k += 1
            x = 0
            continue
        x = 10 * x + int(j)
    for b in range(1,len(a)):
        flagdata[i] *= a[b]
for i in range(len(flagdata)):
    flag+=str(flagdata[i] ^ 1)
print(flag)

别的博主的exp:

strs=["=zqE=z=z=z","=lzzE","=ll=T=s=s=E","=zATT","=s=s=s=E=E=E","=EOll=E","=lE=T=E=E=E","=EsE=s=z","=AT=lE=ll"]
def replacediy(str):
    str=str.replace("O","0")
    str=str.replace("l","1")
    str=str.replace("z","2")
    str=str.replace("E","3")
    str=str.replace("A","4")
    str=str.replace("s","5")
    str=str.replace("G","6")
    str=str.replace("T","7")
    str=str.replace("B","8")
    str=str.replace("q","9")
    str=str.replace("="," ")
    return str
flag=""
for i in strs:
    tmp=replacediy(i).split(" ")[1:]
    print tmp
    sum=1
    for j in range(len(tmp)):
        sum*=int(tmp[j],10)
    sum^=1
    flag+=str(sum)
print flag

用了split函数进行数值划分,这是我没想到的。。。。。

最后是md5加密,32位大写,这里我直接用模块来加密了,exp如下:

import hashlib
m=hashlib.md5()
str1="234512225774247633749032245635316720"
m.update(str1.encode('utf-8'))
print(m.hexdigest())

在这里插入图片描述
最后的flag就是

flag{4367FB5F42C6E46B2AF79BF409FB84D3}

相关参考与拓展:

1.有关注释中的知识,可以参考:

2.特别的,有关C++中c_str函数参考C++中的C_str()函数

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Em0s_Er1t

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值