[2019红帽杯]childRE

文章详细解析了一段C++代码,涉及字符串加密(通过MD5哈希和特定替换运算),取消函数名修饰,以及二叉树结构的动态构建过程。作者通过动态调试揭示了输入字符串的处理和内存分配策略。
摘要由CSDN通过智能技术生成
int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  _QWORD *v4; // rax
  const CHAR *v5; // r11
  __int64 v6; // r10
  int v7; // r9d
  const CHAR *v8; // r10
  __int64 v9; // rcx
  __int64 *v10; // rax
  unsigned int v12; // ecx
  __int64 v13; // r9
  __int128 flag[2]; // [rsp+20h] [rbp-38h] BYREF

  memset(flag, 0, sizeof(flag));
  scanf("%s");
  v3 = -1i64;
  do
    ++v3;
  while ( *((_BYTE *)flag + v3) );
  if ( v3 != 31 )
  {
    while ( 1 )
      Sleep(0x3E8u);                            // len(flag)=31
  }
  v4 = sub_7FF766461280(flag);
  v5 = name;
  if ( v4 )
  {
    sub_7FF7664615C0((unsigned __int8 *)v4[1]);
    sub_7FF7664615C0(*(unsigned __int8 **)(v6 + 16));
    v7 = dword_7FF7664657E0;
    v5[dword_7FF7664657E0] = *v8;
    dword_7FF7664657E0 = v7 + 1;
  }
  UnDecorateSymbolName(v5, outputString, 0x100u, 0);// 将已装饰的符号名v5
                                                // 转换为未装饰的符号名outputstring
  v9 = -1i64;
  do
    ++v9;
  while ( outputString[v9] );
  if ( v9 == 62 )                               // len(outputString)=62
  {
    v12 = 0;
    v13 = 0i64;
    do
    {
      if ( a1234567890Qwer[outputString[v13] % 23] != str2[v13] )
        _exit(v12);
      if ( a1234567890Qwer[outputString[v13] / 23] != str1[v13] )
        _exit(v12 * v12);
      ++v12;
      ++v13;
    }
    while ( v12 < 62 );
    print("flag{MD5(your input)}\n");
    return 0;
  }
  else
  {
    v10 = sub_7FF7664618A0(std::cout);
    std::ostream::operator<<(v10, sub_7FF766461A60);
    return -1;
  }
}

大致只能分析出这些了。

看别人wp:

输入长度31的字符串---进行置换运算---取消修饰函数名---将未修饰函数名的商和余数与字符串比较

第一步:

根据 a/b=c......d 可以求出outputString

str1='55565653255552225565565555243466334653663544426565555525555222'
str2='(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
s='''1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;',27h,'ASDFGHJKL:"ZXCVBNM<>?zxcvbnm,.'''
p=''
for i in range(62):
    p+=chr(s.index(str2[i])+s.index(str1[i])*23)
print(p)
# private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

然后就是看那个UnDecorateSymbolName()函数

c, c++函数名编译符号修饰符说明_c函数 前缀修饰-CSDN博客

buu [2019红帽杯]childRE wr_2019红帽杯分数-CSDN博客

有点难懂。

根据 private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

那么 v5 = ?My_Aut0_PWN@R0Pxx@@AAE

也可以通过写一个函数来得到

#include <iostream>

class R0Pxx {
public:
    R0Pxx() {
        My_Aut0_PWN((unsigned char*)"hello");
    }
private:
    char* __thiscall My_Aut0_PWN(unsigned char*);
};

char* __thiscall R0Pxx::My_Aut0_PWN(unsigned char*) {
    std::cout << __FUNCDNAME__ << std::endl;

    return 0;
}

int main()
{
    R0Pxx A;

    system("PAUSE");
    return 0;
}

第二步:置换运算

发现是result给name的赋值,故找result的值怎么来的

用result来解的:

a = "?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z"
b = 0x50, 0x51, 0x48, 0x52, 0x53, 0x49, 0x44, 0x54, 0x55, 0x4a, 0x56, 0x57, 0x4b, 0x45, 0x42, 0x58, 0x59, 0x4c, 0x5a, 0x5b, 0x4d, 0x46, 0x5c, 0x5d, 0x4e, 0x5e, 0x5f, 0x4f, 0x47, 0x43, 0x41 
c = list(a)
for i in range(0,len(a)):
    c[b[i]-0x41] = a[i]
a = ''.join(c)
print(a)

/*

我传入的v14=“abcdefghijklmnopqrstuvwxyz12345”,第一次的时候它给name[0]赋值成了p,对应我输入的字符串的下标是15,我调试了多次,发现每次都是这样,而且最后的name里存放的是打乱了顺序的输入的字符串,动调它给name赋值的过程,记录下对应的下标
最后得到的赋值顺序下标是[15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]

直接说动态调试发现是固定的位置变换,直接把索引提取出来了

到这里程序逻辑就清楚了,首先输入长度为31的字符串,然后根据赋值下标的顺序打乱数据得到v2。
逆算一下输入的字符串          
原文链接:https://blog.csdn.net/mcmuyanga/article/details/115575264

*/

这个动调的思路需要学学

import hashlib
ops = '?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'
result = [''] * 31
index = [15, 16, 7, 17, 18, 8, 3, 19, 20, 9, 21, 22, 10, 4, 1, 23, 24, 11, 25, 26, 12, 5, 27, 28, 13, 29, 30, 14, 6, 2, 0]

for i in range(len(ops)):
    result[index[i]] += ops[i]
ss = ''.join(i for i in result)
print(ss)
print(hashlib.md5(ss.encode('utf-8')).hexdigest())
#63b148e750fed3a33419168ac58083f5

又细翻了一下别人wp:

_QWORD *__fastcall sub_7FF7F4FE1280(_BYTE *a1)
{
  _QWORD *v2; // r12
  _QWORD *v3; // rax
  __int64 v4; // rdx
  __int64 v5; // r8
  __int64 v6; // rcx
  char v7; // bl
  __int64 v8; // rdi
  __int64 v9; // rax
  bool v10; // zf
  __int64 v11; // r8
  int v12; // esi
  __int64 v13; // rdi
  __int64 v14; // rdx
  __int64 v15; // r8
  __int64 v16; // rcx
  char v17; // bl
  __int64 v18; // r14
  __int64 v19; // rcx
  __int64 v20; // rdx
  __int64 v21; // r8
  __int64 v22; // rcx
  char v23; // bl
  __int64 v24; // r14
  __int64 v25; // rcx
  _QWORD *v27; // [rsp+20h] [rbp-40h]
  _QWORD *v28; // [rsp+20h] [rbp-40h]
  __int128 v29; // [rsp+28h] [rbp-38h] BYREF
  __int128 v30; // [rsp+38h] [rbp-28h]
  __int64 v31; // [rsp+48h] [rbp-18h]

  v2 = operator new(0x18ui64);
  *(_BYTE *)v2 = 0;
  v2[1] = 0i64;
  v2[2] = 0i64;
  v29 = 0i64;
  v30 = 0ui64;
  v31 = 0i64;
  v3 = operator new(0x10ui64);
  v3[1] = 0i64;
  *(_QWORD *)&v29 = v3;
  *v3 = &v29;
  v4 = *((_QWORD *)&v30 + 1);
  v5 = v31;
  v6 = v30;
  if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
  {
    sub_7FF7F4FE1AE0(&v29);
    v5 = v31;
    v4 = *((_QWORD *)&v30 + 1);
    v6 = v30;
  }
  *((_QWORD *)&v30 + 1) = (2 * v6 - 1) & v4;
  v7 = BYTE8(v30) + v5;
  v8 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v5) >> 1) & (v6 - 1));
  v9 = *((_QWORD *)&v29 + 1);
  if ( !*(_QWORD *)(v8 + *((_QWORD *)&v29 + 1)) )
  {
    *(_QWORD *)(v8 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
    v9 = *((_QWORD *)&v29 + 1);
  }
  *(_QWORD *)(*(_QWORD *)(v8 + v9) + 8i64 * (v7 & 1)) = v2;
  v10 = v31 == -1;
  v11 = ++v31;
  v12 = 1;
  if ( !v10 )
  {
    do
    {
      v13 = *(_QWORD *)(*(_QWORD *)(*((_QWORD *)&v29 + 1) + 8 * ((*((_QWORD *)&v30 + 1) >> 1) & (v30 - 1)))
                      + 8i64 * (BYTE8(v30) & 1));
      v31 = v11 - 1;
      if ( v11 == 1 )
        *((_QWORD *)&v30 + 1) = 0i64;
      else
        ++*((_QWORD *)&v30 + 1);
      *(_BYTE *)v13 = *a1++;
      if ( v12 >= 31 )
      {
        v11 = v31;
      }
      else
      {
        v27 = operator new(0x18ui64);
        *(_BYTE *)v27 = 0;
        v27[1] = 0i64;
        v27[2] = 0i64;
        *(_QWORD *)(v13 + 8) = v27;
        v14 = *((_QWORD *)&v30 + 1);
        v15 = v31;
        v16 = v30;
        if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
        {
          sub_7FF7F4FE1AE0(&v29);
          v15 = v31;
          v14 = *((_QWORD *)&v30 + 1);
          v16 = v30;
        }
        *((_QWORD *)&v30 + 1) = (2 * v16 - 1) & v14;
        v17 = BYTE8(v30) + v15;
        v18 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v15) >> 1) & (v16 - 1));
        v19 = *((_QWORD *)&v29 + 1);
        if ( !*(_QWORD *)(v18 + *((_QWORD *)&v29 + 1)) )
        {
          *(_QWORD *)(v18 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
          v19 = *((_QWORD *)&v29 + 1);
        }
        *(_QWORD *)(*(_QWORD *)(v18 + v19) + 8i64 * (v17 & 1)) = *(_QWORD *)(v13 + 8);
        v11 = ++v31;
        if ( ++v12 < 31 )
        {
          v28 = operator new(0x18ui64);
          *(_BYTE *)v28 = 0;
          v28[1] = 0i64;
          v28[2] = 0i64;
          *(_QWORD *)(v13 + 16) = v28;
          v20 = *((_QWORD *)&v30 + 1);
          v21 = v31;
          v22 = v30;
          if ( (((_BYTE)v31 + BYTE8(v30)) & 1) == 0 && (unsigned __int64)v30 <= (unsigned __int64)(v31 + 2) >> 1 )
          {
            sub_7FF7F4FE1AE0(&v29);
            v21 = v31;
            v20 = *((_QWORD *)&v30 + 1);
            v22 = v30;
          }
          *((_QWORD *)&v30 + 1) = (2 * v22 - 1) & v20;
          v23 = BYTE8(v30) + v21;
          v24 = 8 * (((unsigned __int64)(*((_QWORD *)&v30 + 1) + v21) >> 1) & (v22 - 1));
          v25 = *((_QWORD *)&v29 + 1);
          if ( !*(_QWORD *)(v24 + *((_QWORD *)&v29 + 1)) )
          {
            *(_QWORD *)(v24 + *((_QWORD *)&v29 + 1)) = operator new(0x10ui64);
            v25 = *((_QWORD *)&v29 + 1);
          }
          *(_QWORD *)(*(_QWORD *)(v24 + v25) + 8i64 * (v23 & 1)) = *(_QWORD *)(v13 + 16);
          v11 = ++v31;
          ++v12;
        }
      }
    }
    while ( v11 );
  }
  sub_7FF7F4FE17D0(&v29);
  return v2;
}

注意传入的是byte,操作也是 input+8 , input+16 

 __int128 input[2]; // [rsp+20h] [rbp-38h] BYREF      16*8=128 

这是个创建二叉树的函数

动态调试会发现,开辟的地址处有三个数据,第一个是我们输入的字符,第二个是左孩子的地址,第三个是右孩子的地址。

可以识别出是二叉树结构体。

 

发现后面题都喜欢动调让猜,需要点脑洞,还有一些数据结构的

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值