BUUCTF-Re

CrackRTF

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
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD v3; // eax
DWORD v4; // eax
char passwd2[260]; // [esp+4Ch] [ebp-310h] BYREF
int v7; // [esp+150h] [ebp-20Ch]
char String1[260]; // [esp+154h] [ebp-208h] BYREF
char passwd1[260]; // [esp+258h] [ebp-104h] BYREF

memset(passwd1, 0, sizeof(passwd1));
memset(String1, 0, sizeof(String1));
v7 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", passwd1);
if ( strlen(passwd1) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v7 = atoi(passwd1);
if ( v7 < 100000 ) // int(passwd1)>=100000
ExitProcess(0);
strcat(passwd1, "@DBApp");
v3 = strlen(passwd1);
sub_40100A((BYTE *)passwd1, v3, String1);
if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(passwd2, 0, sizeof(passwd2));
scanf("%s", passwd2);
if ( strlen(passwd2) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(passwd2, passwd1);
memset(String1, 0, sizeof(String1));
v4 = strlen(passwd2);
sub_401019((BYTE *)passwd2, v4, String1);
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
{
if ( !(unsigned __int8)sub_40100F(passwd2) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}

passwd1转化为数字要大于等于100000,同时passwd1+@DBApp经过sub_40100A后的结果要等于6E32D0943418C2C33385BC35A1470250DD8923A9

然后passwd2+passwd1经过sub_401019后的结果等于27019e688a4e62a649fd99cadaafdb4e

sub_40100A
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
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
int result; // eax
DWORD i; // [esp+4Ch] [ebp-28h]
CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF

if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
result = 0;
}
return result;
}

注意到CryptCreateHash是windows的api函数,在windows文档中可以查看其用途,得知Algid决定了使用哪一种hash函数,对照Algid的文档可以知道0x8004对应SHA1

sub_401019
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
int __cdecl sub_401040(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
int result; // eax
DWORD i; // [esp+4Ch] [ebp-24h]
CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF

if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
result = 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
result = 0;
}
return result;
}

同样是CryptCreateHash,但是该Algid对应的hash函数为MD5

因此可以得到passwd1的爆破脚本,因为passwd2未知范围无法进行爆破

1
2
3
4
5
6
7
8
9
import hashlib

payload = ""
str1 = "@DBApp"
for i in range(100000, 1000000):
payload = str(i) + str1
if hashlib.sha1(payload.encode('UTF-8')).hexdigest().upper() == "6E32D0943418C2C33385BC35A1470250DD8923A9":
print(payload)
break

可得passwd1=123321

注意到在main函数中,进行md5的比较之后还有一个sub_40100F(passwd2)的函数

sub_40100F
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
char __cdecl sub_4014D0(LPCSTR lpString)
{
LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h] BYREF
DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
HGLOBAL hResData; // [esp+60h] [ebp-Ch]
HRSRC hResInfo; // [esp+64h] [ebp-8h]
HANDLE hFile; // [esp+68h] [ebp-4h]

hFile = 0;
hResData = 0;
nNumberOfBytesToWrite = 0;
NumberOfBytesWritten = 0;
hResInfo = FindResourceA(0, 0x65, "AAA");
if ( !hResInfo )
return 0;
nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
hResData = LoadResource(0, hResInfo);
if ( !hResData )
return 0;
lpBuffer = LockResource(hResData);
sub_401005(lpString, lpBuffer, nNumberOfBytesToWrite);
hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
if ( hFile == -1 )
return 0;
if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
return 0;
CloseHandle(hFile);
return 1;
}

有一个FindResourceA的api,用来查找资源,因此说明在程序中有一个叫做AAA的文件,将exe解压,得到该文件

sub_4014D0函数的主要功能是将AAA的内容经过sub_401005的处理后写入到一个名为dbapp.rtf的文件中

sub_401005
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
unsigned int __cdecl sub_401420(LPCSTR lpString, int a2, int a3)
{
unsigned int result; // eax
unsigned int i; // [esp+4Ch] [ebp-Ch]
unsigned int v5; // [esp+54h] [ebp-4h]

v5 = lstrlenA(lpString);
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a3 )
break;
*(i + a2) ^= lpString[i % v5];
}
return result;
}

可知v5为passwd2+passwd1的长度共18,而passwd2的长度为6,rtf文件要有一个文件头,其文件头为{\rtf17B5C72746631,因此通过passwd2与AAA中的前6个字节进行异或可以得到{\rtf1

由此可得exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib

payload = ""
str1 = "@DBApp"
for i in range(100000, 1000000):
payload = str(i) + str1
if hashlib.sha1(payload.encode('UTF-8')).hexdigest().upper() == "6E32D0943418C2C33385BC35A1470250DD8923A9":
print(payload)
break

a = [0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31]
b = [0x05, 0x7D, 0x41, 0x15, 0x26, 0x01]
for i in range(6):
print(chr(a[i] ^ b[i]), end="")

得到passwd2~!3a@0,输入到程序中,得到dbapp.rtf,flag为Flag{N0_M0re_Free_Bugs}

crackMe

这题貌似有问题

已知用户名为welcomebeijing

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
int wmain()
{
FILE *v0; // eax
FILE *v1; // eax
char v3; // [esp+3h] [ebp-405h]
char fail; // [esp+4h] [ebp-404h] BYREF
char v5[255]; // [esp+5h] [ebp-403h] BYREF
char success; // [esp+104h] [ebp-304h] BYREF
char v7[255]; // [esp+105h] [ebp-303h] BYREF
char password; // [esp+204h] [ebp-204h] BYREF
char v9[255]; // [esp+205h] [ebp-203h] BYREF
char user; // [esp+304h] [ebp-104h] BYREF
char v11[255]; // [esp+305h] [ebp-103h] BYREF

printf("Come one! Crack Me~~~\n");
user = 0;
memset(v11, 0, sizeof(v11));
password = 0;
memset(v9, 0, sizeof(v9));
while ( 1 )
{
do
{
do
{
printf("user(6-16 letters or numbers):");
scanf("%s", &user); // user=welcomebeijing
v0 = sub_4024BE();
fflush(v0);
}
while ( !check_char_or_num(&user) );
printf("password(6-16 letters or numbers):");
scanf("%s", &password);
v1 = sub_4024BE();
fflush(v1);
}
while ( !check_char_or_num(&password) );
sub_401090(&user); // 做了一次置换,因为参数中只有user,而user已经确定,因此可以直接用动态调试过掉,然后查看结果
success = 0;
memset(v7, 0, sizeof(v7));
fail = 0;
memset(v5, 0, sizeof(v5));
v3 = sub_4011A0(&success, &fail); // 给success和fail填充字符
if ( sub_401830(&user, &password) )
{
if ( v3 )
break;
}
printf(&fail);
}
printf(&success);
return 0;
}
sub_1E1830
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
69
70
71
72
73
74
75
76
77
78
79
80
81
bool __usercall sub_1E1830@<al>(int ebx0@<ebx>, int a1, const char *a2)
{
int v4; // [esp+18h] [ebp-22Ch]
signed int v5; // [esp+1Ch] [ebp-228h]
signed int v6; // [esp+28h] [ebp-21Ch]
unsigned int v7; // [esp+30h] [ebp-214h]
char v8; // [esp+36h] [ebp-20Eh]
char v9; // [esp+37h] [ebp-20Dh]
char v10; // [esp+38h] [ebp-20Ch]
unsigned __int8 v11; // [esp+39h] [ebp-20Bh]
unsigned __int8 v12; // [esp+3Ah] [ebp-20Ah]
char v13; // [esp+3Bh] [ebp-209h]
int v14; // [esp+3Ch] [ebp-208h] BYREF
char hexlist; // [esp+40h] [ebp-204h] BYREF
char v16[255]; // [esp+41h] [ebp-203h] BYREF
char v17; // [esp+140h] [ebp-104h] BYREF
char v18[255]; // [esp+141h] [ebp-103h] BYREF

v5 = 0;
v6 = 0;
v12 = 0;
v11 = 0;
v17 = 0;
memset(v18, 0, sizeof(v18));
hexlist = 0;
memset(v16, 0, sizeof(v16));
v10 = 0;
v7 = 0;
v4 = 0;
while ( v7 < strlen(a2) )
{
if ( isdigit(a2[v7]) )
{
v9 = a2[v7] - 48; // '0'->0
}
else if ( isxdigit(a2[v7]) )
{
if ( *(NtCurrentPeb()->SubSystemData + 3) != 2 )
a2[v7] = 34;
v9 = (a2[v7] | 0x20) - 87; // 'a'->10,'f'->15
}
else
{
v9 = ((a2[v7] | 0x20) - 97) % 6 + 10; // 'g'->10,以此类推
}
__rdtsc();
__rdtsc();
v10 = v9 + 16 * v10;
if ( !((v7 + 1) % 2) )
{
*(&hexlist + v4++) = v10; // 'A1'->0xA1
ebx0 = v4;
v10 = 0;
}
++v7;
}
while ( v6 < 8 )
{
v11 += byte_1F6050[++v12]; // sub_401090函数对该数组进行了置换,此处也可以通过动态调试来确认每次的v12和v7的值
v13 = byte_1F6050[v12];
v8 = byte_1F6050[v11];
byte_1F6050[v11] = v13;
byte_1F6050[v12] = v8;
if ( (NtCurrentPeb()->UnicodeCaseTableData & 0x70) != 0 )// 反调试
v13 = v11 + v12;
*(&v17 + v6) = byte_1F6050[(v8 + v13)] ^ *(&hexlist + v5);
if ( *&NtCurrentPeb()->BeingDebugged ) // 反调试
{
v11 = -83;
v12 = 43;
}
sub_1E1710(&v17, a1, v6++); // 异或,a1等于user
// result[v5]^=user[v5]
v5 = v6;
if ( v6 >= (&hexlist + strlen(&hexlist) + 1 - v16) )
v5 = 0;
}
v14 = 0;
check(ebx0, &v17, &v14); // v13为最终计算结果,应该等于0xAB94
return v14 == 0xAB94;
}

首先将将输入的密码转换成16进制数组,然后用16进制数组中的值与byte_1F6050的某些项进行异或,然后用16进制数组中的值与用户名进行异或,最终结果通过check函数进行校验,每一个字符对应一次运算,最终得到0xAB94则说明输入的密码正确

在动态调试提取byte_1F6050时要注意将反调试的代码nop掉,否则调试时会进行与直接运行不同的操作

1
2
3
4
5
6
7
8
9
import hashlib
a=[0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd]
user="welcomebeijing"
result=[100,98,97,112,112,115,101,99]#来源于sub_401470

for i in range(8):
print(str(hex(result[i]^ord(user[i])^a[i]))[2:],end="")
print()
print(hashlib.md5("39d09ffa4cfcc4cc".encode('utf-8')).hexdigest())

得到密码为39d09ffa4cfcc4cc

但是提交密码的小写md5却不对….

根据其它wp,得到的脚本应该为

1
2
3
4
5
6
7
8
9
import hashlib
a=[0x2a,0xd7,0x92,0xe9,0x53,0xe2,0xc4,0xcd]
user="welcomebeijing"
result=[100,98,97,112,112,115,101,99]#来源于sub_401470

for i in range(8):
print(str(hex(result[i]^a[i]))[2:],end="")
print()
print(hashlib.md5("4eb5f3992391a1ae".encode('utf-8')).hexdigest())

得到密码为4eb5f3992391a1ae,但是这样无法通过题目的验证

2019红帽杯

xx

Findcrypt识别出来时tea加密,但是不确定版本

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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 code_len; // rbx
__int64 input_len; // rax
__int128 *v5; // rax
__int64 keyboard; // r11
__int128 *v7; // r14
int v8; // edi
__int128 *v9; // rsi
char per_char; // r10
int v11; // edx
__int64 v12; // r8
unsigned __int64 keyboard_len0; // rcx
__int64 keyboard_len1; // rcx
unsigned __int64 v15; // rax
unsigned __int64 i; // rax
_BYTE *enc_result; // rax
size_t v18; // rsi
_BYTE *v19; // rbx
_BYTE *v20; // r9
int v21; // er11
char *v22; // r8
__int64 v23; // rcx
char v24; // al
__int64 v25; // r9
__int64 v26; // rdx
__int64 *v27; // rax
size_t Size; // [rsp+20h] [rbp-48h] BYREF
__int128 v30; // [rsp+28h] [rbp-40h] BYREF
int v31; // [rsp+38h] [rbp-30h]
int v32; // [rsp+3Ch] [rbp-2Ch]
int input[4]; // [rsp+40h] [rbp-28h] BYREF
int v34; // [rsp+50h] [rbp-18h]

*input = 0i64;
v34 = 0;
sub_7FF6EF5C18C0(std::cin, argv, input);
code_len = -1i64;
input_len = -1i64;
do
++input_len;
while ( *(input + input_len) );
if ( input_len != 19 )
{
sub_7FF6EF5C1620(std::cout, "error\n");
_exit(input);
}
v5 = operator new(5ui64);
keyboard = *&Code;
v7 = v5;
v8 = 0;
v9 = v5;
do // v9取input的前4个字符
{
per_char = *(v9 + input - v5);
v11 = 0;
*v9 = per_char;
v12 = 0i64;
keyboard_len0 = -1i64;
do
++keyboard_len0;
while ( *(keyboard + keyboard_len0) );
if ( keyboard_len0 )
{
do
{
if ( per_char == *(keyboard + v12) )
break;
++v11;
++v12;
}
while ( v11 < keyboard_len0 );
}
keyboard_len1 = -1i64;
do
++keyboard_len1;
while ( *(keyboard + keyboard_len1) );
if ( v11 == keyboard_len1 )
_exit(keyboard);
v9 = (v9 + 1);
}
while ( v9 - v5 < 4 );
*(v5 + 4) = 0;
do
++code_len;
while ( *(input + code_len) );
v15 = 0i64;
v30 = *v7;
while ( *(&v30 + v15) ) // 确定新字符串的长度?
{
if ( !*(&v30 + v15 + 1) )
{
++v15;
break;
}
if ( !*(&v30 + v15 + 2) )
{
v15 += 2i64;
break;
}
if ( !*(&v30 + v15 + 3) )
{
v15 += 3i64;
break;
}
v15 += 4i64;
if ( v15 >= 16 )
break;
}
for ( i = v15 + 1; i < 16; ++i )
*(&v30 + i) = 0; // v30=input[0]+input[1]+input[2]+input[3]+12个'\x00'
enc_result = xxTea(input, code_len, &v30, &Size);
v18 = Size;
v19 = enc_result;
v20 = operator new(Size);
v21 = 1;
*v20 = v19[2]; // 置换
v22 = v20 + 1; // v22=&v20[1]
v20[1] = *v19;
v20[2] = v19[3];
v20[3] = v19[1];
v20[4] = v19[6];
v20[5] = v19[4];
v20[6] = v19[7];
v20[7] = v19[5];
v20[8] = v19[10];
v20[9] = v19[8];
v20[10] = v19[11];
v20[11] = v19[9];
v20[12] = v19[14];
v20[13] = v19[12];
v20[14] = v19[15];
v20[15] = v19[13];
v20[16] = v19[18];
v20[17] = v19[16];
v20[18] = v19[19];
v20[19] = v19[17];
v20[20] = v19[22];
v20[21] = v19[20];
v20[22] = v19[23];
for ( v20[23] = v19[21]; v21 < v18; ++v22 )
{
v23 = 0i64;
if ( v21 / 3 > 0 )
{
v24 = *v22;
do
{
v24 ^= v20[v23++];
*v22 = v24;
}
while ( v23 < v21 / 3 );
}
++v21;
}
*&v30 = 0xC0953A7C6B40BCCEui64; // 注意小端序的转换
v25 = v20 - &v30;
*(&v30 + 1) = 0x3502F79120209BEFi64;
v26 = 0i64;
v31 = 0xC8021823;
v32 = 0xFA5656E7;
do
{
if ( *(&v30 + v26) != *(&v30 + v26 + v25) ) // 校验,v25相当于偏移量,v30+v25=v20
_exit(v8 * v8);
++v8;
++v26;
}
while ( v26 < 24 );
v27 = sub_7FF6EF5C1620(std::cout, "You win!");
std::ostream::operator<<(v27, sub_7FF6EF5C17F0);
return 0;
}

main函数的主要逻辑为

  1. 读取输入
  2. 将输入的前4个字符作为Tea加密的密钥
  3. 将加密结果进行一次置换
  4. 将结果进行异或处理
  5. 将结果与[0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B, 0x20, 0x20, 0x91, 0xF7, 0x02, 0x35, 0x23, 0x18, 0x02, 0xC8, 0xE7, 0x56, 0x56, 0xFA]进行比较

首先倒推出加密的原始结果

1
2
3
4
5
6
7
8
9
10
11
12
v30=[0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B, 
0x20, 0x20, 0x91, 0xF7, 0x02, 0x35, 0x23, 0x18, 0x02, 0xC8,
0xE7, 0x56, 0x56, 0xFA]
enc_result=[0 for i in range(0x18)]
for i in range(0x18-1,-1,-1):#注意顺序
for j in range(i//3):
v30[i]^=v30[j]
print(v30)
box=[2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21]
for i in range(0x18):
enc_result[box[i]]=v30[i]
print(enc_result)
1
2
[206, 188, 64, 165, 178, 244, 231, 178, 157, 169, 18, 18, 200, 174, 91, 16, 6, 61, 29, 215, 248, 220, 220, 112]
[188, 165, 206, 64, 244, 178, 178, 231, 169, 18, 157, 18, 174, 16, 200, 91, 61, 215, 6, 29, 220, 112, 248, 220]

然后对Tea的版本进行判断,根据题目名可以猜测版本为xxtea

同时 v11[v46] += ((v27 ^ *v11) + (v29 ^ v18[v35 ^ v37 & 3])) ^ (((4 * *v11) ^ (v29 >> 5)) + ((*v11 >> 3) ^ (16 * v29)));

根据特征<<2^>>5,>>3^<<4,可以推测其为xxtea

因此可以进行解密,使用pip install xxtea-py安装xxtea库,使用python2进行解密

1
2
3
4
5
import xxtea
key = "flag\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
encrypt_data = 'bca5ce40f4b2b2e7a9129d12ae10c85b3dd7061ddc70f8dc'.decode('hex')
decrypt_data = xxtea.decrypt(encrypt_data, key)
print decrypt_data

flag为flag{CXX_and_++tea}

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 input_len; // rax
_QWORD *v4; // rax
const CHAR *v5; // r11
__int64 v6; // r10
int v7; // er9
const CHAR *v8; // r10
__int64 outputstring_len; // rcx
__int64 v10; // rax
int result; // eax
unsigned int v12; // ecx
__int64 index; // r9
__int128 input[2]; // [rsp+20h] [rbp-38h] BYREF

input[0] = 0i64;
input[1] = 0i64;
sub_7FF7E07F1080("%s", input);
input_len = -1i64;
do
++input_len;
while ( *(input + input_len) ); // 输入长度为31
if ( input_len != 31 )
{
while ( 1 )
Sleep(1000u);
}
v4 = sub_7FF7E07F1280(input); // 应该是构建二叉树
v5 = name;
if ( v4 )
{
sub_7FF7E07F15C0(v4[1]); // 后序遍历
sub_7FF7E07F15C0(*(v6 + 16)); // 后序遍历
v7 = dword_7FF7E07F57E0;
v5[dword_7FF7E07F57E0] = *v8; // 可以通过动态调试来直接确定每个输入字符对应后序遍历后的位置
dword_7FF7E07F57E0 = v7 + 1;
}
UnDecorateSymbolName(v5, outputString, 256u, 0);// 将v5进行反修饰,outputstring为反修饰结果
// outputstring="private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)"
outputstring_len = -1i64;
do
++outputstring_len;
while ( outputString[outputstring_len] );
if ( outputstring_len == 62 )
{
v12 = 0;
index = 0i64;
do
{
if ( a1234567890Qwer[outputString[index] % 23] != *(index + 0x7FF7E07F3478i64) )// 检查,两个地址中存放着两个字符串
_exit(v12);
if ( a1234567890Qwer[outputString[index] / 23] != *(index + 0x7FF7E07F3438i64) )
_exit(v12 * v12);
++v12;
++index;
}
while ( v12 < 62 );
sub_7FF7E07F1020("flag{MD5(your input)}\n");
result = 0;
}
else
{
v10 = sub_7FF7E07F18A0(std::cout);
std::ostream::operator<<(v10, sub_7FF7E07F1A60);
result = -1;
}
return result;
}

首先根据a1234567890Qwer和两个地址中的字符串对outputstring进行倒推

1
2
3
4
5
6
7
8
9
10
11
mem1=[0x28,0x5F,0x40,0x34,0x36,0x32,0x30,0x21,0x30,0x38,0x21,0x36,0x5F,0x30,0x2A,0x30,0x34,0x34,0x32,0x21,0x40,0x31,0x38,0x36,0x25,0x25,0x30,0x40,0x33,0x3D,0x36,0x36,0x21,0x21,0x39,0x37,0x34,0x2A,0x33,0x32,0x33,0x34,0x3D,0x26,0x30,0x5E,0x33,0x26,0x31,0x40,0x3D,0x26,0x30,0x39,0x30,0x38,0x21,0x36,0x5F,0x30,0x2A,0x26,0x00]
mem2=[0x35,0x35,0x35,0x36,0x35,0x36,0x35,0x33,0x32,0x35,0x35,0x35,0x35,0x32,0x32,0x32,0x35,0x35,0x36,0x35,0x35,0x36,0x35,0x35,0x35,0x35,0x32,0x34,0x33,0x34,0x36,0x36,0x33,0x33,0x34,0x36,0x35,0x33,0x36,0x36,0x33,0x35,0x34,0x34,0x34,0x32,0x36,0x35,0x36,0x35,0x35,0x35,0x35,0x35,0x32,0x35,0x35,0x35,0x35,0x32,0x32,0x32,0x00]
a1234567890Qwer=[49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 33, 64, 35, 36, 37, 94, 38, 42, 40, 41, 95, 43, 113, 119, 101, 114, 116, 121, 117, 105, 111, 112, 91, 93, 81, 87, 69, 82, 84, 89, 85, 73, 79, 80, 123, 125, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59, 39, 65, 83, 68, 70, 71, 72, 74, 75, 76, 58, 34, 90, 88, 67, 86, 66, 78, 77, 60, 62, 63, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0]
outputString=[]
for i in range(62):
for j in range(0,128):
if a1234567890Qwer[j%23]==mem1[i] and a1234567890Qwer[j//23]==mem2[i]:
outputString.append(j)
print(outputString)
for i in outputString:
print(chr(i),end="")

得到outputstringprivate: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

然后对outputstring进行修饰

windows文档参考

wikipedia参考

根据private: char * __thiscall R0Pxx::My_Aut0_PWN(unsigned char *)来手动创建类,查看修饰名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
class R0Pxx {
public:
R0Pxx();
private:
char* My_Aut0_PWN(unsigned char*) {
printf("Function name: %s\n", __FUNCTION__);
printf("Decorated function name: %s\n", __FUNCDNAME__);
printf("Function signature: %s\n", __FUNCSIG__);
return NULL;
}
};
R0Pxx::R0Pxx() {
unsigned char a;
My_Aut0_PWN(&a);
}
int main() {
R0Pxx a;
return 0;
}
1
2
3
Function name: R0Pxx::My_Aut0_PWN
Decorated function name: ?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z
Function signature: char *__thiscall R0Pxx::My_Aut0_PWN(unsigned char *)

修饰名反修饰检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <windows.h> //如果不包含此头文件,编译DbgHelp.h时会产生大量语法错误
#include <DbgHelp.h>
#include <iostream>
#include <string>
#pragma comment(lib,"dbghelp.lib") //告诉链接器使用这个输入库

int main()
{
char a[1024] = { 0 };
std::cin >> a;
char szUndecorateName[256];
memset(szUndecorateName, 0, 256);
::UnDecorateSymbolName(a, szUndecorateName, 256, 0);
std::cout << szUndecorateName << std::endl;
return 0;
}

因此反修饰前的函数名为?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z

然后对后序遍历的位置进行确定

1
2
3
4
5
6
a="0123456789abcdefghjklmnopqrstuv"#输入
b="fg7hj83kl9mna41opbqrc5stduve620"#输出
c=[]
for i in range(31):
c.append(a.index(b[i]))
print(c)
1
[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]

最后将函数名恢复成原始输入

1
2
3
4
5
6
7
source_string="?My_Aut0_PWN@R0Pxx@@AAEPADPAE@Z"
tree=[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]
input_Str=[0 for i in range(31)]
for i in range(31):
input_Str[tree[i]]=ord(source_string[i])
for i in input_Str:
print(chr(i),end="")

原始输入为Z0@tRAEyuP@xAAA?M_A0_WNPx@@EPDP

flag为flag{63b148e750fed3a33419168ac58083f5}