xor

分析文件

ida64打开文件,shift+F12查看字符串窗口,发现Input your flag、success、failed关键信息。查看相关部分伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  memset(v6, 0, 0x100uLL);
v3 = (char *)256;
printf("Input your flag:\n", 0LL);
get_line(v6, 256LL);
if ( strlen(v6) != 33 )
goto LABEL_12;
for ( i = 1; i < 33; ++i )
v6[i] ^= v6[i - 1];
v3 = global;
if ( !strncmp(v6, global, 0x21uLL) )
printf("Success", v3);
else
LABEL_12:
printf("Failed", v3);
result = __stack_chk_guard;
if ( __stack_chk_guard == v7 )
result = 0;
return result;

分析代码

输入v6,如果字符串长度≠33,输出failed;如果=33,那么v6从第二位起,每一位都与前一位异或,异或的结果与global的值相等,输出success。
跟进global的值:

1
2
3
__cstring:0000000100000F6E aFKWOXZUPFVMDGH db 'f',0Ah              ; DATA XREF: __data:_global↓o
__cstring:0000000100000F6E db 'k',0Ch,'w&O.@',11h,'x',0Dh,'Z;U',11h,'p',19h,'F',1Fh,'v"M#D',0Eh,'g'
__cstring:0000000100000F6E db 6,'h',0Fh,'G2O',0

写脚本

伪代码的意思就是:flag的长度是33位,每一位与前一位异或出的结果值是global。写脚本就是把这个过程逆过来。

1
2
3
4
5
6
7
8
9
10
11
12
str1 = ['f',0x0a,'k',0x0c,'w','&','O','.','@',0x11,'x',0x0d,'Z',';','U',0x11,'p',0x19,'F',0x1f,'v','"','M','#','D',0x0e,'g',0x06,'h',0x0f,'G','2','O',0x00]
str2 = []
for i in str1:
if (isinstance(i,str)):
str2.append(ord(i))
else:
str2.append(int(i))
print(str2)
flag = 'f'
for i in range(1,len(str2)):
flag = flag+chr(str2[i]^str2[i-1])
print(flag)

最后获取到flag:flag{QianQiuWanDai_YiTongJiangHu}

reverse3

分析文件

ida32打开文件,查看字符串窗口,发现wrong flag!right flag!,点进去查看伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 for ( i = 0; i < 100; ++i )
{
if ( (unsigned int)i >= 0x64 )
j____report_rangecheckfailure();
Dest[i] = 0;
}
sub_41132F("please enter the flag:");
sub_411375("%20s", &Str);
v0 = j_strlen(&Str);
v1 = (const char *)sub_4110BE(&Str, v0, &v11);
strncpy(Dest, v1, 0x28u);
v8 = j_strlen(Dest);
for ( j = 0; j < v8; ++j )
Dest[j] += j;
v2 = j_strlen(Dest);
if ( !strncmp(Dest, Str2, v2) )
sub_41132F("rigth flag!\n");
else
sub_41132F("wrong flag!\n");
HIDWORD(v4) = v3;
LODWORD(v4) = 0;
return v4;
}

分析代码

第八行输入flag,并在第十行sub_4110BE函数对输入的flag进行处理。
点进去查看sub_4110BE函数:

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
{
int v4; // STE0_4
int v5; // STE0_4
int v6; // STE0_4
int v7; // [esp+D4h] [ebp-38h]
signed int i; // [esp+E0h] [ebp-2Ch]
unsigned int v9; // [esp+ECh] [ebp-20h]
int v10; // [esp+ECh] [ebp-20h]
signed int v11; // [esp+ECh] [ebp-20h]
void *Dst; // [esp+F8h] [ebp-14h]
char *v13; // [esp+104h] [ebp-8h]

if ( !_4 || !_8 )
return 0;
v9 = _8 / 3u;
if ( (signed int)(_8 / 3u) % 3 )
++v9;
v10 = 4 * v9;
*(_DWORD *)_C = v10;
Dst = malloc(v10 + 1);
if ( !Dst )
return 0;
j_memset(Dst, 0, v10 + 1);
v13 = (char *)_4;
v11 = _8;
v7 = 0;
while ( v11 > 0 )
{
byte_41A144[2] = 0;
byte_41A144[1] = 0;
byte_41A144[0] = 0;
for ( i = 0; i < 3 && v11 >= 1; ++i )
{
byte_41A144[i] = *v13;
--v11;
++v13;
}
if ( !i )
break;
switch ( i )
{
case 1:
*((_BYTE *)Dst + v7) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
v4 = v7 + 1;
*((_BYTE *)Dst + v4++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*((_BYTE *)Dst + v4++) = aAbcdefghijklmn[64];
*((_BYTE *)Dst + v4) = aAbcdefghijklmn[64];
v7 = v4 + 1;
break;
case 2:
*((_BYTE *)Dst + v7) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
v5 = v7 + 1;
*((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*((_BYTE *)Dst + v5++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
*((_BYTE *)Dst + v5) = aAbcdefghijklmn[64];
v7 = v5 + 1;
break;
case 3:
*((_BYTE *)Dst + v7) = aAbcdefghijklmn[(signed int)(unsigned __int8)byte_41A144[0] >> 2];
v6 = v7 + 1;
*((_BYTE *)Dst + v6++) = aAbcdefghijklmn[((byte_41A144[1] & 0xF0) >> 4) | 16 * (byte_41A144[0] & 3)];
*((_BYTE *)Dst + v6++) = aAbcdefghijklmn[((byte_41A144[2] & 0xC0) >> 6) | 4 * (byte_41A144[1] & 0xF)];
*((_BYTE *)Dst + v6) = aAbcdefghijklmn[byte_41A144[2] & 0x3F];
v7 = v6 + 1;
break;
}
}
*((_BYTE *)Dst + v7) = 0;
return Dst;
}

又复杂又头疼还完全看不懂的加密······不过反复出现的aAbcdefghijklmn有必要去看一下,发现是ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
那这就好说了,这基本就是Base64加密跑不了了。
看完sub_4110BE函数,返回伪代码继续往下看(此时v1=base64加密后的flag),第十一行的strncpy函数,把v1的值赋给Dest,后面的0x28u的意思是把v1字符串的前0x28位无符号数(16进制)赋给Dest。
第十三、十四行的for循环,就是把Dest的每一位加上其对应的数字,然后与str2比较,相等就是right flag。

写脚本

1
2
3
4
5
6
7
8
9
import base64
str2 = 'e3nifIH9b_C@n@dH'
flag=''
for i in range(len(str2)):
x = chr(ord(str2[i]) - i)
flag = flag + x
print(flag)
flag = base64.b64decode(flag)
print(flag)

脚本解析

len(str2)是获取str2字符串的长度,如果不想写这么麻烦,自己又可以查准的话,直接写16也没问题。
chr(ord(str2[i]) - i)是先把str2的每一位转化成ASCII码对应的数字,然后减去相应位数的数字,再转成字符。下一行就是把这些字符拼接成字符串。
第一次print(flag)是看看最初拼接成的字符串是啥样的,然后后边再base64解密。第八、九行的作用就相当于找个base64解密的网站解密一下(真正打比赛的时候不让连外网,所以有必要知道怎么用脚本解密)。
最后跑出结果:{i_l0ve_you}

不一样的flag

打开exe,是一个需要输入1234进行上下左右操作的小东西。
不一样的flag

分析文件

ida32打开文件,查看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
{
char v3; // [esp+17h] [ebp-35h]
int v4; // [esp+30h] [ebp-1Ch]
int v5; // [esp+34h] [ebp-18h]
signed int v6; // [esp+38h] [ebp-14h]
int i; // [esp+3Ch] [ebp-10h]
int v8; // [esp+40h] [ebp-Ch]

__main();
v4 = 0;
v5 = 0;
qmemcpy(&v3, _data_start__, 0x19u);
while ( 1 )
{
puts("you can choose one action to execute");
puts("1 up");
puts("2 down");
puts("3 left");
printf("4 right\n:");
scanf("%d", &v6);
if ( v6 == 2 )
{
++v4;
}
else if ( v6 > 2 )
{
if ( v6 == 3 )
{
--v5;
}
else
{
if ( v6 != 4 )
LABEL_13:
exit(1);
++v5;
}
}
else
{
if ( v6 != 1 )
goto LABEL_13;
--v4;
}
for ( i = 0; i <= 1; ++i )
{
if ( *(&v4 + i) < 0 || *(&v4 + i) > 4 )
exit(1);
}
if ( *((_BYTE *)&v8 + 5 * v4 + v5 - 41) == `1` )
exit(1);
if ( *((_BYTE *)&v8 + 5 * v4 + v5 - 41) == `#` )
{
puts("\nok, the order you enter is the flag!");
exit(0);
}
}
}

分析代码

第12行的qmemcpy实则是memcpy()函数,其作用是内存拷贝,后面是把_data_start__的数据赋给v3,进行程序的初始化。查看_data_start__的值:.data:00402000 __data_start__ db '*11110100001010000101111#',0
15-20行就是程序的操作界面,输入v6的值开始跑程序。
从47行开始的一个循环和两个条件语句来看,这题基本是一个迷宫题,结合_data_start__的值,要从*跑到#,并且中途不能碰到1。

解题

结合_data_start__的值,可以形成一个5×5的迷宫:

1
2
3
4
5
*1111
01000
01010
00010
1111#

按照1234上下左右的步骤,最后得出flag{222441144222}

SimpleRev

分析文件

ida64打开,查看字符串窗口,发现:Please input your flagCongratulation,查看相关部分伪代码:

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
unsigned __int64 Decry()
{
char v1; // [rsp+Fh] [rbp-51h]
int v2; // [rsp+10h] [rbp-50h]
int v3; // [rsp+14h] [rbp-4Ch]
int i; // [rsp+18h] [rbp-48h]
int v5; // [rsp+1Ch] [rbp-44h]
char src[8]; // [rsp+20h] [rbp-40h]
__int64 v7; // [rsp+28h] [rbp-38h]
int v8; // [rsp+30h] [rbp-30h]
__int64 v9; // [rsp+40h] [rbp-20h]
__int64 v10; // [rsp+48h] [rbp-18h]
int v11; // [rsp+50h] [rbp-10h]
unsigned __int64 v12; // [rsp+58h] [rbp-8h]

v12 = __readfsqword(0x28u);
*(_QWORD *)src = 'SLCDN';
v7 = 0LL;
v8 = 0;
v9 = 'wodah';
v10 = 0LL;
v11 = 0;
text = join(key3, (const char *)&v9);
strcpy(key, key1);
strcat(key, src);
v2 = 0;
v3 = 0;
getchar();
v5 = strlen(key);
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 )
key[i] = key[v3 % v5] + 32;
++v3;
}
printf("Please input your flag:", src);
while ( 1 )
{
v1 = getchar();
if ( v1 == 10 )
break;
if ( v1 == 32 )
{
++v2;
}
else
{
if ( v1 <= 96 || v1 > 122 )
{
if ( v1 > 64 && v1 <= 90 )
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
else
{
str2[v2] = (v1 - 39 - key[v3++ % v5] + 97) % 26 + 97;
}
if ( !(v3 % v5) )
putchar(32);
++v2;
}
}
if ( !strcmp(text, str2) )
puts("Congratulation!\n");
else
puts("Try again!\n");
return __readfsqword(0x28u) ^ v12;
}

分析代码

第23行引入一个自定义的join函数,点进去查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char *__fastcall join(const char *a1, const char *a2)
{
size_t v2; // rbx
size_t v3; // rax
char *dest; // [rsp+18h] [rbp-18h]

v2 = strlen(a1);
v3 = strlen(a2);
dest = (char *)malloc(v2 + v3 + 1); //给dest分配一个存储空间
if ( !dest )
exit(1);
strcpy(dest, a1); //把a1的值赋给dest
strcat(dest, a2); //把a2拼接到a1后面
return dest;
}

通过strcpy、strcat函数,可以知道join函数的功能就是拼接字符串。
回到最初的伪代码段:
此时第23行的text=killshadow(v9处为小端序存储,从右向左看。)