0%

2019红帽杯childre

childRe

main函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rax
const CHAR *v5; // r11
__int64 v6; // r10
int v7; // er9
const CHAR *v8; // r10
__int64 v9; // rcx
__int64 *v10; // rax
unsigned int v12; // ecx
__int64 v13; // r9
__int64 v14; // r8
__int64 v15; // rdx
__int128 v16[2]; // [rsp+20h] [rbp-38h] BYREF

memset(v16, 0, sizeof(v16));
sub_7FF6BF3D1080("%s", (const char *)v16); // 输入字符串存入v14
v3 = -1i64; // v3初始值为-1
do
++v3;
while ( *((_BYTE *)v16 + v3) ); // 0123456789qwertyuiopasdfghjklzx
if ( v3 != 31 ) // 限制输入字符串长度为31
{
while ( 1 )
Sleep(0x3E8u);
}
v4 = sub_7FF6BF3D1280(v16);
v5 = name;
if ( v4 )
{
sub_7FF6BF3D15C0(*(_QWORD *)(v4 + 8));
sub_7FF6BF3D15C0(*(_QWORD *)(v6 + 16));
v7 = dword_7FF6BF3D57E0;
v5[dword_7FF6BF3D57E0] = *v8;
dword_7FF6BF3D57E0 = v7 + 1;
}
UnDecorateSymbolName(v5, outputString, 0x100u, 0);
v9 = -1i64;
do
++v9;
while ( outputString[v9] );
if ( v9 == 62 )
{
v12 = 0;
v13 = 0i64;
do
{
v14 = outputString[v13] % 23;
if ( a1234567890Qwer[v14] != a46200860044218[v13] )
_exit(v12);
v15 = outputString[v13] / 23;
if ( a1234567890Qwer[v15] != a55565653255552[v13] )
_exit(v12 * v12);
++v12;
++v13;
}
while ( v12 < 62 );
sub_7FF6BF3D1020("flag{MD5(your input)}\n", v15, v14, v13);
return 0;
}
else
{
v10 = sub_7FF6BF3D18A0(std::cout);
std::ostream::operator<<(v10, sub_7FF6BF3D1A60);
return -1;
}
}

首先求出outputString

1
2
3
4
5
6
7
8
9
str1 = '1234567890-=!@#$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'+ chr(0x27) + 'A' + 'SDFGHJKL:"ZXCVBNM<>?zxcvbnm,./'
str2 = '(_@4620!08!6_0*0442!@186%%0@3=66!!974*3234=&0^3&1@=&0908!6_0*&'
str3 = '55565653255552225565565555243466334653663544426565555525555222'
input = ''
for i in range(62):
m = str1.index(str2[i])
n = str1.index(str3[i])
input += chr((n*23+m)%128)
print(input)

运行结果为一个函数名private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

关键函数为UnDecorateSymbolName,功能为让函数更容易识别

方法一

参考链接:

C++ 编译器的函数名修饰规则 - yxysuanfa - 博客园 (cnblogs.com)

  • 参考资料1
    第二个参数为未修饰的名字,第三个参数为长度,第四个参数为0表示完全修饰,第一个参数为输出地址
  • 参考材料2
    c++函数名的修饰更为复杂,提供的信息也更为丰富。
    无论 __cdecl,__fastcall还是__stdcall调用方式,函数修饰都是以一个“?”开始,后面紧跟函数的名字。再后面是参数表的开始标识和依照参数类型代号拼出的参数表。

v5 = ?My_Aut0_PWN

  • 对于C++的类成员函数(其调用方式是thiscall),函数的名字修饰与非成员的C++函数稍有不同,首先就是在函数名字和参数表之间插入以“@”字 符引导的类名

v5 = ?My_Aut0_PWN@R0Pxx

  • 其次是参数表的开始标识不同,公有(public)成员函数的标识是“@@QAE”,保护(protected)成员函数的标识是 “@@IAE”,私有(private)成员函数的标识是“@@AAE”,假设函数声明使用了constkeyword,则对应的标识应分别为“@@QBE”,“@@IBE”和“@@ABE”。

因为函数为 private,私有成员
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAE
后面就是添加参数了,先加入函数返回值参数,函数的返回值类型为char *

  • 参数表的拼写代号如下:
    X–void
    D–char
    E–unsigned char
    F–short
    H–int
    I–unsigned int
    J–long
    K–unsigned long(DWORD)
    M–float
    N–double
    _N–bool
    U–struct

    指针的方式有些特别。用PA表示指针,用PB表示const类型的指针。

char *也就是PAD
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAEPAD
然后是参数的类型 unsigned char *,也就是PAE
所以 v5 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE

  • 参数表后以“@Z”标识整个名字的结束。假设该函数无参数,则以“Z”标识结束。

所以最终v5 = ?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z

方法二

另外一种方法参考 Hk_Mayfly 师傅的博客:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#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;
}
//得到:?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z

接下来看上面的置换操作

可以采用动态调试打表的方法

输入串0123456789qwertyuiopasdfghjklzx查看输出

即可求得flag

1
2
3
4
5
6
7
8
import hashlib
str1 = '0123456789qwertyuiopasdfghjklzx'
decoded_str1 = 'yu7io83pa9sdq41fgwhje5klrzxt620'
enc = ''
str2 = '?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z'
for i in range(len(str1)):
enc += str2[decoded_str1.index(str1[i])]
print('flag{' + hashlib.md5(enc.encode("utf-8")).hexdigest() + '}')