MRCTF2020 EasyCPP

两个月前做过一次 当时逆向功底太浅了 记录下以现在的视角来看这道题
main逻辑不难
大致放下分析后的代码:

for ( i = 0; i <= 8; ++i )
  {
    std::istream::operator>>(&std::cin, &INPUT[i]);
    std::to_string((std::__cxx11 *)v24, INPUT[i], (unsigned int)INPUT[i], v4, v5, v6);
    std::string::operator+=(v20, v24);
    std::string::~string(v24);
  }
  v30 = INPUT;
  v31 = INPUT;
  v29 = (int *)&unk_55B3A773E3E4;
  while ( v31 != v29 )
  {
    v19 = *v31;
    std::vector<int>::push_back(_INPUT_, &v19);
    ++v31;
  }
  v7 = std::vector<int>::end(_INPUT_);
  v8 = std::vector<int>::begin(_INPUT_);
  std::for_each<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,main::{lambda(int &)#1}>(v8, v7);// foreach  xor 0x1??? 应该是做了某种分隔 
  v28 = _INPUT_;
  v18 = std::vector<int>::begin(_INPUT_);
  v17 = std::vector<int>::end(v28);
  while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(&v18, &v17) )
  {
    v27 = *(_DWORD *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*((__int64)&v18);
    std::allocator<char>::allocator(&v25);
    std::string::basic_string(v16, &unk_55B3A773B00E, &v25);
    std::allocator<char>::~allocator(&v25);
    depart(v27, (__int64)v16);                  // !!! depart!!! ctm这看漏了...
    {lambda(std::string &)#1}::operator()((__int64)&func, (__int64)v16);// 替换某些字符
    std::string::basic_string(v26, v16);
    v9 = !{lambda(std::string,int)#2}::operator()((__int64)&check, (__int64)v26, v33);// cmp比较
    std::string::~string(v26);

与第一遍相比 这次能够独立的找到上次没发现的点
至少同第一次相比 对于c++的lamda/vector等没那么反感了 反而因为逆向逆多了有种莫名亲切感:(
这里很容易找到foreach xor 1

关键是从最后的check入手 发现里面有个没有初始赋值的ans数组 查看交叉引用就可以找到(虽然我是瞎翻翻到的 殊途同归)

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

这里看到一些奇奇怪怪的字符串
继续往前回溯
前面有个lamda1
这就找到第一次竟然没找到的点...

qmemcpy(v25, "O0", sizeof(v25));
  v2 = std::string::end(a2);
  v3 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v3, v2, &v25[1], v25);
  qmemcpy(v26, "l1", sizeof(v26));
  v4 = std::string::end(a2);
  v5 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v5, v4, &v26[1], v26);
  qmemcpy(v27, "z2", sizeof(v27));
  v6 = std::string::end(a2);
  v7 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v7, v6, &v27[1], v27);
  qmemcpy(v28, "E3", sizeof(v28));
  v8 = std::string::end(a2);
  v9 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v9, v8, &v28[1], v28);
  qmemcpy(v29, "A4", sizeof(v29));
  v10 = std::string::end(a2);
  v11 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v11, v10, &v29[1], v29);
  qmemcpy(v30, "s5", sizeof(v30));
  v12 = std::string::end(a2);
  v13 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v13, v12, &v30[1], v30);
  qmemcpy(v31, "G6", sizeof(v31));
  v14 = std::string::end(a2);
  v15 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v15, v14, &v31[1], v31);
  qmemcpy(v32, "T7", sizeof(v32));
  v16 = std::string::end(a2);
  v17 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v17, v16, &v32[1], v32);
  qmemcpy(v33, "B8", sizeof(v33));
  v18 = std::string::end(a2);
  v19 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v19, v18, &v33[1], v33);
  qmemcpy(v34, "q9", sizeof(v34));
  v20 = std::string::end(a2);
  v21 = std::string::begin(a2);
  std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v21, v20, &v34[1], v34);
  qmemcpy(v35, "= ", 2);
  v22 = std::string::end(a2);
  v23 = std::string::begin(a2);
  return std::replace<__gnu_cxx::__normal_iterator<char *,std::string>,char>(v23, v22, &v35[1], v35);

就是把0~9作了个map映射 而且'='是' '分隔符
这样替换完就是这种:

enc = [" 293 2 2 2"," 1223"," 11 7 5 5 3"," 2477"," 5 5 5 3 3 3"," 3011 3"," 13 7 3 3 3"," 353 5 2"," 47 13 11"]

开始猜测是在输入那儿作了些处理? 动调在那儿看了看 啥也没看出来
结果发现眼瞎漏掉了已经符号化的depart函数... 服了

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

  v9 = a1;
  for ( i = 2; std::sqrt<int>((unsigned int)a1) >= (double)i; ++i )// 找因子! 质因数分解
  {
    if ( !(a1 % i) )
    {
      v9 = i;
      depart(a1 / i, a2);
      break;
    }
  }
  std::to_string((std::__cxx11 *)v7, v9, (unsigned int)v9, v2, v3, v4);
  std::operator+<char>(v6, &unk_55B3A773B00C, v7);
  std::string::operator+=(a2, v6);
  std::string::~string(v6);
  return std::string::~string(v7);
}

这就是个很明显的质因数分解了 看看enc 确实是质数乘积的形式
那么我们每个里面乘回去 记得xor 1 就可以得到最初的key 运行验证正确

enc = ["=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"]
enc = [" 293 2 2 2"," 1223"," 11 7 5 5 3"," 2477"," 5 5 5 3 3 3"," 3011 3"," 13 7 3 3 3"," 353 5 2"," 47 13 11"]
key =[293*2*2*2,1223,11*7*5*5*3,2477,5*5*5*3*3*3,3011*3,13*7*3*3*3,353*5*2,47*13*11]
for x in key:
    print(x^1,end=' ')

最后大写md5(234512225774247633749032245635316720)

flag{4367FB5F42C6E46B2AF79BF409FB84D3}



总结:
发现练了两个月逆向能力还是有点提升的:(

posted @ 2023-12-25 11:24  N0zoM1z0  阅读(26)  评论(0编辑  收藏  举报