攻防世界-re部分题解(二)

题目有点多,放在一篇里面太过臃肿,所以多分了几篇

IgniteMe

IDA打开,首先发现flag的格式,EIS{***}

找到关键函数

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
bool __cdecl sub_4011C0(char *a1)
{
size_t v2; // eax
signed int v3; // [esp+50h] [ebp-B0h]
char v4[32]; // [esp+54h] [ebp-ACh]
int v5; // [esp+74h] [ebp-8Ch]
int v6; // [esp+78h] [ebp-88h]
size_t i; // [esp+7Ch] [ebp-84h]
char v8[128]; // [esp+80h] [ebp-80h]

if ( strlen(a1) <= 4 )
return 0;
i = 4;
v6 = 0;
while ( i < strlen(a1) - 1 )
v8[v6++] = a1[i++];
v8[v6] = 0;
v5 = 0;
v3 = 0; // v8=a1
memset(v4, 0, 0x20u);
for ( i = 0; ; ++i )
{
v2 = strlen(v8);
if ( i >= v2 )
break;
if ( v8[i] >= 'a' && v8[i] <= 'z' )
{
v8[i] -= 32;
v3 = 1;
}
if ( !v3 && v8[i] >= 'A' && v8[i] <= 'Z' )
v8[i] += 32; //大小写互换
v4[i] = byte_4420B0[i] ^ sub_4013C0(v8[i]); //唯一一步有用的计算
v3 = 0;
}
return strcmp("GONDPHyGjPEKruv{{pj]X@rF", v4) == 0;

据此写脚本逆向

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
#include <iostream>
#include <cstring>
#include <string>

using namespace std;

int main() {
string v4="GONDPHyGjPEKruv{{pj]X@rF";
unsigned char byte_4420B0[] =
{
13, 19, 23, 17, 2, 1, 32, 29, 12, 2,
25, 47, 23, 43, 36, 31, 30, 22, 9, 15,
21, 39, 19, 38, 10, 47, 30, 26, 45, 12,
34, 4
};

char v8[v4.length()];
for (int i = 0;i<v4.length() ; ++i )
{
v8[i]=((v4[i]^byte_4420B0[i])-72)^0x55;
if ( v8[i] >= 'a' && v8[i] <= 'z' )
{
v8[i] -= 32;
} else if(v8[i] >= 'A' && v8[i] <= 'Z'){
v8[i] += 32;
}
else{
continue;
}
}
cout<<v8<<endl;
return 0;
}

把输出和我们已知的部分结合起来得到flag

1
EIS{wadx_tdgk_aihc_ihkn_pjlm}

hackme

通过字符串找到关键部分

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
__int64 __fastcall sub_400F8E(__int64 a1, __int64 a2)
{
__int64 v2; // rdx
__int64 v3; // rcx
__int64 v4; // r8
__int64 v5; // r9
int index; // eax
char v8[136]; // [rsp+10h] [rbp-B0h]
int v9; // [rsp+98h] [rbp-28h]
char v10; // [rsp+9Fh] [rbp-21h]
int v11; // [rsp+A0h] [rbp-20h]
unsigned __int8 v12; // [rsp+A6h] [rbp-1Ah]
char v13; // [rsp+A7h] [rbp-19h]
int v14; // [rsp+A8h] [rbp-18h]
int v15; // [rsp+ACh] [rbp-14h]
unsigned int v16; // [rsp+B0h] [rbp-10h]
int v17; // [rsp+B4h] [rbp-Ch]
_BOOL4 v18; // [rsp+B8h] [rbp-8h]
int i; // [rsp+BCh] [rbp-4h]

printf((unsigned __int64)"Give me the password: ");
scanf((__int64)"%s", v8, a2);
for ( i = 0; v8[i]; ++i )
;
v18 = i == 22;
v17 = 10;
do
{
index = rand((__int64)"%s", (__int64)v8, v2, v3, v4, v5);
v3 = (unsigned int)(index % 22);
v14 = index % 22;
v16 = 0;
v13 = byte_6B4270[index % 22];
v12 = v8[index % 22];
v11 = index % 22 + 1;
v15 = 0;
while ( v15 < v11 )
{
++v15;
v16 = 1828812941 * v16 + 12345;
}
v2 = v16;
v10 = v16 ^ v12;
if ( v13 != ((unsigned __int8)v16 ^ v12) )
v18 = 0;
--v17;
}
while ( v17 );
if ( v18 )
v9 = printf((unsigned __int64)"Congras\n");
else
v9 = printf((unsigned __int64)"Oh no!\n");
return 0LL;
}

就是个简单的运算,写个脚本逆运算一下就出来了

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
#include <iostream>
#include <cstring>
#include <string>

using namespace std;

int main() {
unsigned char byte_6B4270[] =
{
95, 242, 94, 139, 78, 14, 163, 170, 199, 147,
129, 61, 95, 116, 163, 9, 145, 43, 73, 40,
147, 103
};
char v13;
char v12;
char v8[23];
unsigned int v16=0;
for(int i=0;i<22;i++)
{
v16=0;
v13=byte_6B4270[i];
int v11=i+1;
int v15=0;
while(v15<v11){
++v15;
v16=1828812941 * v16 + 12345;
}
v8[i]=v13^v16;
}
cout<<v8<<endl;
return 0;
}

输出flag

1
flag{d826e6926098ef46}

easyre

还是通过字符串找到关键函数

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
int sub_401080()
{
unsigned int v0; // kr00_4
signed int v1; // edx
char *v2; // esi
char v3; // al
unsigned int v4; // edx
int v5; // eax
__int128 v7; // [esp+2h] [ebp-24h]
__int64 v8; // [esp+12h] [ebp-14h]
int v9; // [esp+1Ah] [ebp-Ch]
__int16 v10; // [esp+1Eh] [ebp-8h]

printf("input:", v7);
v9 = 0;
v10 = 0;
v7 = 0i64;
v8 = 0i64;
scanf((const char *)&dword_402158, (unsigned int)&v7);
v0 = strlen((const char *)&v7);
if ( v0 >= 16 && v0 == 24 ) // flag共24位
{
v1 = 0;
v2 = (char *)&v8 + 7;
do
{
v3 = *v2--;
byte_40336C[v1++] = v3; // 输入的flag倒序
}
while ( v1 < 24 );
v4 = 0;
do
{
byte_40336C[v4] = (byte_40336C[v4] + 1) ^ 6;
++v4;
}
while ( v4 < 24 );
v5 = strcmp(byte_40336C, "xIrCj~<r|2tWsv3PtI\x7Fzndka");
if ( v5 )
v5 = -(v5 < 0) | 1;
if ( !v5 )
{
printf("right\n", v7);
system("pause");
}
}
return 0;
}

里面的算法变得复杂而毫无意义,真正实现的功能就两个,首先将输入的字符串逆序,然后进行一个变换,最后得到已知的字符串

1
2
3
4
5
target='xIrCj~<r|2tWsv3PtI\x7Fzndka'
flag=''
for i in target:
flag+=chr((ord(i)^6)-1)
print(flag[::-1])

输出flag

1
flag{xNqU4otPq3ys9wkDsN}

shuffle

打开把所有的整数转换为字符就看到了flag

1
SECCON{Welcome to the SECCON 2014 CTF!}

re-for-50-plz-50

这题是MIPS,IDA直接反编译不出来,MIPS指令学的也不是很好,所以直接用retdec插件

retdec直接反编译出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(int argc, char ** argv) {
// 0x401398
for (int32_t i = 0; i < 31; i++) {
char v1 = *(char *)(i + (int32_t)"cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ"); // 0x4013d8
char v2 = *(char *)(*(int32_t *)((int32_t)argv + 4) + i); // 0x4013f0
if ((int32_t)v1 != ((int32_t)v2 ^ 55)) {
// 0x401408
print();
exit_funct();
}
}
// 0x401444
exit_funct();
return 1;
}

可以发现只有一个异或操作

1
2
3
4
5
target="cbtcqLUBChERV[[Nh@_X^D]X_YPV[CJ"
flag=''
for i in target:
flag+=chr((ord(i)^55))
print(flag)

输出flag

1
TUCTF{but_really_whoisjohngalt}

parallel-comparator-200

代码审计,直接给出了源码

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
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

#define FLAG_LEN 20

void * checking(void *arg) {
char *result = malloc(sizeof(char));
char *argument = (char *)arg;
*result = (argument[0]+argument[1]) ^ argument[2];
return result;
}

int highly_optimized_parallel_comparsion(char *user_string)
{
int initialization_number;
int i;
char generated_string[FLAG_LEN + 1];
generated_string[FLAG_LEN] = '\0';

while ((initialization_number = random()) >= 64);

int first_letter;
first_letter = (initialization_number % 26) + 97;

pthread_t thread[FLAG_LEN];
char differences[FLAG_LEN] = {0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7};
char *arguments[20];
for (i = 0; i < FLAG_LEN; i++) {
arguments[i] = (char *)malloc(3*sizeof(char));
arguments[i][0] = first_letter;
arguments[i][1] = differences[i];
arguments[i][2] = user_string[i];

pthread_create((pthread_t*)(thread+i), NULL, checking, arguments[i]);
}

void *result;
int just_a_string[FLAG_LEN] = {115, 116, 114, 97, 110, 103, 101, 95, 115, 116, 114, 105, 110, 103, 95, 105, 116, 95, 105, 115};
for (i = 0; i < FLAG_LEN; i++) {
pthread_join(*(thread+i), &result);
generated_string[i] = *(char *)result + just_a_string[i];
free(result);
free(arguments[i]);
}

int is_ok = 1;
for (i = 0; i < FLAG_LEN; i++) {
if (generated_string[i] != just_a_string[i])
return 0;
}

return 1;
}

int main()
{
char *user_string = (char *)calloc(FLAG_LEN+1, sizeof(char));
fgets(user_string, FLAG_LEN+1, stdin);
int is_ok = highly_optimized_parallel_comparsion(user_string);
if (is_ok)
printf("You win!\n");
else
printf("Wrong!\n");
return 0;
}

源码比较长,但是做的事情不多,需要注意三个函数的用法

第一个是pthread_create()用来创建一个新的线程

1
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);

几个参数分别是,指向线程的指针,线程的属性,调用的函数和参数,在这段代码里实际上就是调用checking()函数,并把之前设定好的参数传给函数

第二个是pthread_join()用来传递返回结果

1
int pthread_join(pthread_t thread, void **retval);

所以result就是指向返回结果的指针,所以最终的目的就是让checking()函数返回的值为0

第三个是random()函数,在这之前没有用随机种子初始化生成器,所以随机数生成的结果不管怎么运行都是一样的,但是需要注意linux下random()和windows下rand()生成的数据并不一样

此外还需要注意一点,在c++里面,赋值语句的返回值是赋的值而不是布尔值

所以只需要根据checking()函数来逆向运算一下就可以得到结果了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <cstring>
#include <string>

#define FLAG_LEN 20

using namespace std;

int main() {
char differences[FLAG_LEN] = {0, 9, -9, -1, 13, -13, -4, -11, -9, -1, -7, 6, -13, 13, 3, 9, -13, -11, 6, -7};


int first_letter;
first_letter = 108;

char flag[FLAG_LEN];
for (int i = 0; i < FLAG_LEN; i++) {
flag[i] = first_letter + differences[i];
}
cout << flag << endl;

return 0;
}

输出flag

1
lucky_hacker_you_are

secret-galaxy-300

题目里面只有一个结构体,运行了一遍什么都没发现,OD调试的时候查找字符串找到flag

1
aliens_are_around_us

srm-50

这题是个简单的注册机,找到关键函数

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
BOOL __stdcall DialogFunc(HWND hDlg, UINT a2, WPARAM a3, LPARAM a4)
{
HMODULE v5; // eax
HICON v6; // eax
HMODULE v7; // eax
HCURSOR v8; // ST20_4
HWND v9; // eax
CHAR String; // [esp+8h] [ebp-340h]
CHAR v11[4]; // [esp+108h] [ebp-240h]
char v12; // [esp+10Ch] [ebp-23Ch]
char v13; // [esp+10Dh] [ebp-23Bh]
char v14; // [esp+10Eh] [ebp-23Ah]
char v15; // [esp+10Fh] [ebp-239h]
char v16; // [esp+110h] [ebp-238h]
char v17; // [esp+111h] [ebp-237h]
char v18; // [esp+112h] [ebp-236h]
char v19; // [esp+113h] [ebp-235h]
char v20; // [esp+114h] [ebp-234h]
char v21; // [esp+115h] [ebp-233h]
char v22; // [esp+116h] [ebp-232h]
char v23; // [esp+117h] [ebp-231h]
CHAR Text; // [esp+208h] [ebp-140h]
char Src[16]; // [esp+308h] [ebp-40h]
__int128 v26; // [esp+318h] [ebp-30h]
int v27; // [esp+328h] [ebp-20h]
__int128 v28; // [esp+32Ch] [ebp-1Ch]
int v29; // [esp+33Ch] [ebp-Ch]
__int16 v30; // [esp+340h] [ebp-8h]

if ( a2 == 16 )
{
EndDialog(hDlg, 0);
return 0;
}
if ( a2 == 272 )
{
v5 = GetModuleHandleW(0);
v6 = LoadIconW(v5, (LPCWSTR)0x67);
SetClassLongA(hDlg, -14, (LONG)v6);
v7 = GetModuleHandleW(0);
v8 = LoadCursorW(v7, (LPCWSTR)0x66);
v9 = GetDlgItem(hDlg, 1);
SetClassLongA(v9, -12, (LONG)v8);
return 1;
}
if ( a2 != 273 || (unsigned __int16)a3 != 1 )
return 0;
memset(&String, (unsigned __int16)a3 - 1, 0x100u);
memset(v11, 0, 0x100u);
memset(&Text, 0, 0x100u);
GetDlgItemTextA(hDlg, 1001, &String, 256);
GetDlgItemTextA(hDlg, 1002, v11, 256);
if ( strstr(&String, "@") && strstr(&String, ".") && strstr(&String, ".")[1] && strstr(&String, "@")[1] != 46 )
{
v28 = xmmword_410AA0;
v29 = 1701999980;
*(_OWORD *)Src = xmmword_410A90;
v30 = 46;
v26 = xmmword_410A80;
v27 = 3830633;
if ( strlen(v11) != 16
|| v11[0] != 67
|| v23 != 88
|| v11[1] != 90
|| v11[1] + v22 != 155
|| v11[2] != 57
|| v11[2] + v21 != 155
|| v11[3] != 100
|| v20 != 55
|| v12 != 109
|| v19 != 71
|| v13 != 113
|| v13 + v18 != 170
|| v14 != 52
|| v17 != 103
|| v15 != 99
|| v16 != 56 )
{
strcpy_s(&Text, 0x100u, (const char *)&v28);
}
else
{
strcpy_s(&Text, 0x100u, Src);
strcat_s(&Text, 0x100u, v11);
}
}
else
{
strcpy_s(&Text, 0x100u, "Your E-mail address in not valid.");
}
MessageBoxA(hDlg, &Text, "Registeration", 0x40u);
return 1;
}

过程十分简单,先简单判断邮箱是不是符合格式,如果符合进入序列号的判断,序列号的判断关键也就只有一个if条件,然后就会输出注册有没有成功

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
#include <iostream>
#include <cstring>
#include <string>


using namespace std;

int main() {
char v11[17]={0};
v11[0] = 'C';
v11[15] = 'X';
v11[1] = 'Z';
v11[14] = 155-v11[1];
v11[2] = 57;
v11[13] = 155-v11[2];
v11[3] = 100;
v11[12] = 55;
v11[4] = 109;
v11[11] = 71;
v11[5] = 113;
v11[10] = 170-v11[5];
v11[6] = 52;
v11[9] = 103;
v11[7] = 99;
v11[8] = 56;
cout<<v11<<endl;

return 0;
}

输出结果为

1
CZ9dmq4c8g9G7bAX

Mysterious

通过字符串找到关键函数

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
int __stdcall sub_401090(HWND hWnd, int a2, int a3, int a4)
{
char v5; // [esp+50h] [ebp-310h]
CHAR Text[4]; // [esp+154h] [ebp-20Ch]
char v7; // [esp+159h] [ebp-207h]
__int16 v8; // [esp+255h] [ebp-10Bh]
char v9; // [esp+257h] [ebp-109h]
int v10; // [esp+258h] [ebp-108h]
CHAR String; // [esp+25Ch] [ebp-104h]
char v12; // [esp+25Fh] [ebp-101h]
char v13; // [esp+260h] [ebp-100h]
char v14; // [esp+261h] [ebp-FFh]

memset(&String, 0, 0x104u);
v10 = 0;
if ( a2 == 16 )
{
DestroyWindow(hWnd);
PostQuitMessage(0);
}
else if ( a2 == 273 )
{
if ( a3 == 1000 )
{
GetDlgItemTextA(hWnd, 1002, &String, 260);
strlen(&String);
if ( strlen(&String) > 6 )
ExitProcess(0);
v10 = atoi(&String) + 1;
if ( v10 == 123 && v12 == 'x' && v14 == 'z' && v13 == 'y' )
{
strcpy(Text, "flag");
memset(&v7, 0, 0xFCu);
v8 = 0;
v9 = 0;
_itoa(v10, &v5, 10);
strcat(Text, "{");
strcat(Text, &v5);
strcat(Text, "_");
strcat(Text, "Buff3r_0v3rf|0w");
strcat(Text, "}");
MessageBoxA(0, Text, "well done", 0);
}
SetTimer(hWnd, 1u, 0x3E8u, TimerFunc);
}
if ( a3 == 1001 )
KillTimer(hWnd, 1u);
}
return 0;
}

发现我们只要输入122xyz,就会输出flag,而且flag也可以直接拼凑出来

1
flag{123_Buff3r_0v3rf|0w}

re1-100

这题题目里面的if实在是太多了,主要是为了判断是不是有debugger,所以不能通过调试的方式获得flag,所以还是要研究一下代码

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
if ( strlen(bufParentRead) == 42 )
{
if ( !strncmp(&bufParentRead[1], "53fc275d81", 10uLL) )
{
if ( bufParentRead[strlen(bufParentRead) - 1] == '}' )
{
if ( !strncmp(&bufParentRead[31], "4938ae4efd", 10uLL) )
{
if ( !confuseKey(bufParentRead, 42) )
{
responseFalse();
}
else if ( !strncmp(bufParentRead, "{daf29f59034938ae4efd53fc275d81053ed5be8c}", 42uLL) )
{
responseTrue();
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}
}
else
{
responseFalse();
}

截取了其中一段,这里把flag分成了4个部分,每段10个字符,已经知道第一段地最后一段,但是也知道经过confuseKey()之后flag是什么,所以我们只要研究一下confusekey()就可以了

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
bool __cdecl confuseKey(char *szKey, int iKeyLength)
{
char szPart1[15]; // [rsp+10h] [rbp-50h]
char szPart2[15]; // [rsp+20h] [rbp-40h]
char szPart3[15]; // [rsp+30h] [rbp-30h]
char szPart4[15]; // [rsp+40h] [rbp-20h]
unsigned __int64 v7; // [rsp+58h] [rbp-8h]

v7 = __readfsqword(0x28u);
*(_QWORD *)szPart1 = 0LL;
*(_DWORD *)&szPart1[8] = 0;
*(_WORD *)&szPart1[12] = 0;
szPart1[14] = 0;
*(_QWORD *)szPart2 = 0LL;
*(_DWORD *)&szPart2[8] = 0;
*(_WORD *)&szPart2[12] = 0;
szPart2[14] = 0;
*(_QWORD *)szPart3 = 0LL;
*(_DWORD *)&szPart3[8] = 0;
*(_WORD *)&szPart3[12] = 0;
szPart3[14] = 0;
*(_QWORD *)szPart4 = 0LL;
*(_DWORD *)&szPart4[8] = 0;
*(_WORD *)&szPart4[12] = 0;
szPart4[14] = 0;
if ( iKeyLength != 42 )
return 0;
if ( !szKey )
return 0;
if ( strlen(szKey) != 42 )
return 0;
if ( *szKey != '{' )
return 0;
strncpy(szPart1, szKey + 1, 0xAuLL);
strncpy(szPart2, szKey + 11, 0xAuLL);
strncpy(szPart3, szKey + 21, 0xAuLL);
strncpy(szPart4, szKey + 31, 0xAuLL);
memset(szKey, 0, iKeyLength);
*szKey = 123;
strcat(szKey, szPart3);
strcat(szKey, szPart4);
strcat(szKey, szPart1);
strcat(szKey, szPart2);
szKey[41] = 125;
return 1;
}

内容挺多,实际上只实现了一件事情,把四段调换了一个顺序,变成了3421的顺序,所以调整一下顺序就出来了

1
53fc275d81053ed5be8cdaf29f59034938ae4efd

crazy

这题c++17编写,还有类,乍一看有点复杂,但实际上不是很难

还是先看关键函数,这里在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
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
__int64 v12; // rax
__int64 v13; // rax
__int64 v14; // rax
__int64 v15; // rax
__int64 v16; // rax
char v18; // [rsp+10h] [rbp-130h]
char v19; // [rsp+30h] [rbp-110h]
char v20; // [rsp+50h] [rbp-F0h]
char v21; // [rsp+70h] [rbp-D0h]
char v22; // [rsp+90h] [rbp-B0h]
char v23; // [rsp+B0h] [rbp-90h]
unsigned __int64 v24; // [rsp+128h] [rbp-18h]

v24 = __readfsqword(0x28u);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v18, argv, envp);
std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v18);
v3 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "Quote from people's champ");
std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
v6 = std::operator<<<std::char_traits<char>>(
&std::cout,
"*My goal was never to be the loudest or the craziest. It was to be the most entertaining.");
std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);
v7 = std::operator<<<std::char_traits<char>>(&std::cout, "*Wrestling was like stand-up comedy for me.");
std::ostream::operator<<(v7, &std::endl<char,std::char_traits<char>>);
v8 = std::operator<<<std::char_traits<char>>(
&std::cout,
"*I like to use the hard times in the past to motivate me today.");
std::ostream::operator<<(v8, &std::endl<char,std::char_traits<char>>);
v9 = std::operator<<<std::char_traits<char>>(&std::cout, "-------------------------------------------");
std::ostream::operator<<(v9, &std::endl<char,std::char_traits<char>>);
HighTemplar::HighTemplar((DarkTemplar *)&v23, (__int64)&v18);
v10 = std::operator<<<std::char_traits<char>>(&std::cout, "Checking....");
std::ostream::operator<<(v10, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v19, &v18);
func1((__int64)&v20, (__int64)&v19);
func2((__int64)&v21, (__int64)&v20);
func3((__int64)&v21, 0);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v21);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v20);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v19);
HighTemplar::calculate((HighTemplar *)&v23);
if ( (unsigned int)HighTemplar::getSerial((HighTemplar *)&v23) == 0 )
{
v11 = std::operator<<<std::char_traits<char>>(&std::cout, "/////////////////////////////////");
std::ostream::operator<<(v11, &std::endl<char,std::char_traits<char>>);
v12 = std::operator<<<std::char_traits<char>>(&std::cout, "Do not be angry. Happy Hacking :)");
std::ostream::operator<<(v12, &std::endl<char,std::char_traits<char>>);
v13 = std::operator<<<std::char_traits<char>>(&std::cout, "/////////////////////////////////");
std::ostream::operator<<(v13, &std::endl<char,std::char_traits<char>>);
ZN11HighTemplar7getFlagB5cxx11Ev((__int64)&v22, (__int64)&v23);
v14 = std::operator<<<std::char_traits<char>>(&std::cout, "flag{");
v15 = std::operator<<<char,std::char_traits<char>,std::allocator<char>>(v14, &v22);
v16 = std::operator<<<std::char_traits<char>>(v15, "}");
std::ostream::operator<<(v16, &std::endl<char,std::char_traits<char>>);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v22);
}
HighTemplar::~HighTemplar((HighTemplar *)&v23);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v18);
return 0;
}

乍一看很复杂,慢慢分析,发现了一个输入,被赋值给了v18,然后发现一个类的构造函数调用了我们输入的字符串,可以理解为,用我们的字符串初始化了类中的数据成员,进去仔细看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
nsigned __int64 __fastcall HighTemplar::HighTemplar(DarkTemplar *a1, __int64 a2)
{
char v3; // [rsp+17h] [rbp-19h]
unsigned __int64 v4; // [rsp+18h] [rbp-18h]

v4 = __readfsqword(0x28u);
DarkTemplar::DarkTemplar(a1);
*(_QWORD *)a1 = &off_401EA0;
*((_DWORD *)a1 + 3) = 0;
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 16, a2);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string((char *)a1 + 48, a2);
std::allocator<char>::allocator(&v3, a2);
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(
(char *)a1 + 80,
"327a6c4304ad5938eaf0efb6cc3e53dc",
&v3);
std::allocator<char>::~allocator(&v3);
return __readfsqword(0x28u) ^ v4;
}

从这里看,类里面至少应该有一个getSerial()函数在off_401EA0处,三个字符串,偏移量分别为16,48,80,长度都是32位,还有一个布尔型变量,偏移量位3,并且我们还知道,我们的数据被传入了一个变量v23里,然后返回去,跟踪v23,找到剩下的关键函数

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
bool __fastcall HighTemplar::calculate(HighTemplar *this)
{
__int64 v1; // rax
_BYTE *v2; // rbx
bool result; // al
_BYTE *v4; // rbx
int i; // [rsp+18h] [rbp-18h]
int j; // [rsp+1Ch] [rbp-14h]

if ( std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16) != 32 )
{
v1 = std::operator<<<std::char_traits<char>>(&std::cout, "Too short or too long");
std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
exit(-1);
}
for ( i = 0;
i <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
++i )
{
v2 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
i);
*v2 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
i) ^ 0x50)
+ 23;
}
for ( j = 0; ; ++j )
{
result = j <= (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
if ( !result )
break;
v4 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
j);
*v4 = (*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
j) ^ 0x13)
+ 11;
}
return result;
}

这一看就是个加密函数,并且只对偏移量位16处的字符串进行了操作,这里正好是我们输入的字符串,虽然有两个for循环,但是都是对同一个字符串的同一个位置进行操作,很好逆向,加密完之后应该还会有一个验证,找到下面的验证函数

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
__int64 __fastcall HighTemplar::getSerial(HighTemplar *this)
{
__int64 v1; // rbx
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
unsigned int i; // [rsp+1Ch] [rbp-14h]

for ( i = 0;
(signed int)i < (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length((char *)this + 16);
++i )
{
v1 = *(unsigned __int8 *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 80,
(signed int)i);
if ( (_BYTE)v1 != *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
(char *)this + 16,
(signed int)i) )
{
v4 = std::operator<<<std::char_traits<char>>(&std::cout, "You did not pass ");
v5 = std::ostream::operator<<(v4, i);
std::ostream::operator<<(v5, &std::endl<char,std::char_traits<char>>);
*((_DWORD *)this + 3) = 1;
return *((unsigned int *)this + 3);
}
v2 = std::operator<<<std::char_traits<char>>(&std::cout, "Pass ");
v3 = std::ostream::operator<<(v2, i);
std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
}
return *((unsigned int *)this + 3);
}

这个函数不长,很简单的验证了加密过后的字符串和偏移量位80的字符串是不是一样,用偏移量位3处的布尔值作为返回值,如果相同返回0,回到主函数,接下来就成功了,没有其他的变换和判断,所以这个程序实际上非常简单

1
2
3
4
5
target = "327a6c4304ad5938eaf0efb6cc3e53dc"
flag = ''
for i in target:
flag += chr((((ord(i) - 11) ^ 0x13) - 23) ^ 0x50)
print(flag)

很简单就可以逆向出,套上flag{}提交即可(这个flag长得实在不像是正确的)

1
flag{tMx~qdstOs~crvtwb~aOba}qddtbrtcd}

re4-unvm-me

pyc格式,直接uncompyle6反编译,成功,没有对pyc文件动什么手脚,反编译后的源代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import md5
md5s = [
174282896860968005525213562254350376167, 137092044126081477479435678296496849608, 126300127609096051658061491018211963916, 314989972419727999226545215739316729360, 256525866025901597224592941642385934114, 115141138810151571209618282728408211053, 8705973470942652577929336993839061582, 256697681645515528548061291580728800189, 39818552652170274340851144295913091599, 65313561977812018046200997898904313350, 230909080238053318105407334248228870753, 196125799557195268866757688147870815374, 74874145132345503095307276614727915885]
print 'Can you turn me back to python ? ...'
flag = raw_input('well as you wish.. what is the flag: ')
if len(flag) > 69:
print 'nice try'
exit()
if len(flag) % 5 != 0:
print 'nice try'
exit()
for i in range(0, len(flag), 5):
s = flag[i:i + 5]
if int('0x' + md5.new(s).hexdigest(), 16) != md5s[(i / 5)]:
print 'nice try'
exit()

print 'Congratz now you have the flag'

发现给出了很多md5,把flag每5个字符一组,算出md5要和给出的相同,所以用这些md5在线解密

组合起来就是flag

1
ALEXCTF{dv5d4s2vj8nk43s8d8l6m1n5l67ds9v41n52nv37j481h3d28n4b6v3k}

anser_to_everything

IDA打开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-4h]

printf("Gimme: ", argv, envp);
__isoc99_scanf("%d", &v4);
not_the_flag(v4);
return 0;
}

//not_the_flag
__int64 __fastcall not_the_flag(int a1)
{
if ( a1 == '*' )
puts("Cipher from Bill \nSubmit without any tags\n#kdudpeh");
else
puts("YOUSUCK");
return 0LL;
}

找到了一个字符串kdudpeh,根据题目提示,sha1加密得80ee2a3fe31da904c596d993f7f1de4827c1450a

套上flag得

1
flag{80ee2a3fe31da904c596d993f7f1de4827c1450a}

elrond32

IDA打开,main函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
int __cdecl main(int a1, char **a2)
{
if ( a1 > 1 && sub_8048414(a2[1], 0) )
{
puts(unk_80487E4);
sub_8048538((int)a2[1]);
}
else
{
puts("Access denied");
}
return 0;
}

我们得输入作为main函数得参数传入,在sub_8048414()函数中进行了一个判断,然后进入sub_8048538()函数处理后输出flag

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
signed int __cdecl sub_8048414(_BYTE *a1, int a2)
{
signed int result; // eax

switch ( a2 )
{
case 0:
if ( *a1 == 'i' )
goto LABEL_19;
result = 0;
break;
case 1:
if ( *a1 == 'e' )
goto LABEL_19;
result = 0;
break;
case 3:
if ( *a1 == 'n' )
goto LABEL_19;
result = 0;
break;
case 4:
if ( *a1 == 'd' )
goto LABEL_19;
result = 0;
break;
case 5:
if ( *a1 == 'a' )
goto LABEL_19;
result = 0;
break;
case 6:
if ( *a1 == 'g' )
goto LABEL_19;
result = 0;
break;
case 7:
if ( *a1 == 's' )
goto LABEL_19;
result = 0;
break;
case 9:
if ( *a1 == 'r' )
LABEL_19:
result = sub_8048414(a1 + 1, 7 * (a2 + 1) % 11);
else
result = 0;
break;
default:
result = 1;
break;
}
return result;
}

判断得过程是一个递归,因为返回值要为1,所以递归过程中得每一个if都需要满足,所以很好得到输入的字符串是什么

1
2
3
4
5
6
7
8
9
10
int __cdecl sub_8048538(int a1)
{
int v2[33]; // [esp+18h] [ebp-A0h]
int i; // [esp+9Ch] [ebp-1Ch]

qmemcpy(v2, &unk_8048760, sizeof(v2));
for ( i = 0; i <= 32; ++i )
putchar(v2[i] ^ *(char *)(a1 + i % 8));
return putchar(10);
}

输出部分只进行了一个很简单的处理,而且不需要逆向,直接用同样的方式处理即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
targrt = 'ie_ndags_r'
f = ''
j = 0
for i in range(8):
f += targrt[j]
j = 7 * (j + 1) % 11
# print(f)
flag = ''
a2 = [0x0F, 0x1F, 0x04, 0x09, 0x1C, 0x12, 0x42, 0x09, 0x0C, 0x44, 0x0D, 0x07, 0x09, 0x06, 0x2D,
0x37, 0x59, 0x1E, 0x00, 0x59, 0x0F, 0x08, 0x1C, 0x23, 0x36, 0x07, 0x55, 0x02, 0x0C, 0x08,
0x41, 0x0A, 0x14]

for i in range(33):
flag += chr(a2[i] ^ ord(f[i % 8]))
print(flag)

输出flag

1
flag{s0me7hing_S0me7hinG_t0lki3n}
攻防世界-re部分题解(三) 攻防世界-re部分题解(一)

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×