base++


题目经过IDA后主函数如下:

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
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
int v4; // [esp+30h] [ebp-28h]
int v5; // [esp+34h] [ebp-24h]

sub_401790(std::cout, "please input your flag:");
sub_4019D0(std::cin, &unk_405018);
if ( (signed int)strlen((const char *)&unk_405018) < 32 )
goto LABEL_14;
sub_401100((const char *)&unk_405018);
v5 = strcmp((const char *)&unk_405018, "guvf_vf_n_snxr_synt_____________");
if ( v5 )
v5 = -(v5 < 0) | 1;
if ( v5 )
{
LABEL_14:
sub_401360();
sub_401440(strlen((const char *)&unk_405018), (int)&unk_405018, (int)a0);
v4 = strcmp(a0, "TRLT5amLBoLT5Z6Fa5LqN6mkTomqR66Da4LqX5mgBwkkP5wmTZ6D====");
if ( v4 )
v4 = -(v4 < 0) | 1;
if ( v4 )
sub_401790(std::cout, "soooooooooorry\n");
else
sub_401790(std::cout, "Congratulations!!!\n");
system("pause");
result = 0;
}
else
{
sub_401790(std::cout, "try harder!\n");
result = 0;
}
return result;
}

看一下主函数,我们可以发现在strcmp中,当经过strcmp比较后,如果a0的字符串为”TRLT5amLBoLT5Z6Fa5LqN6mkTomqR66Da4LqX5mgBwkkP5wmTZ6D====”,输出Congratulations。在strcmp之上是sub_401440()函数

sub401440函数分析

base2.png

a1是输入的字符串的长度,a2是输入的字符串的地址,a3是返回值,分别对应sub_401440(strlen((const char *)&unk_405018), (int)&unk_405018, (int)a0);中的strlen((const char *)&unk_405018),&,unk_405018,a0。在进入If判断后,通过循环对输入的字符串进行处理,每次处理的字符串为5位,得到的字符串为8位。跟入sub401170()函数

sub401170函数


sub401170

在这个函数中,关键的位置为图中标注的两个循环。第一个循环是通过一个数,经过每次移8位倍数来保存处理输入中的五个字符,第二个循环是通过移位5的倍数提取数据。每次将得到的数,取后五位作为下标,得到byte_40507c数组中相应下标的值。这一部分对应的re脚本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
for i in range(0,len(flag_int)/8):
m = 0
for j in range(0,8):
loc = i*8 + j
if flag_int[loc] != -1:
m = m + (flag_int[loc] << (5*(7-j)) )
for j in range(0,5):
letter = ( m >> (8*(4-j))) & 0xff
flag.append(chr(letter))
flag_str = ""
for i in flag:
flag_str = flag_str + i
print flag_str

在这里注意一个坑,”>>” 操作符的运算顺序是最后最后的,要注意 !!!

接下来,直接查看byte_40507c数组可以得到其值,但是在开始的时候,byte_40507c数组的值有初始赋值操作。

sub_401100函数


sub_401100(byte_40507c)中对数组byte_40507c进行了初始化的操作,对顺序进行了变换。

对应的转换代码:

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
byte_40507C = range(0x41,0x41+26)
byte_letter = []
for i in byte_40507C:
byte_letter.append(i)
def int_to_char(a):
b = []
for i in a:
b.append(chr(i))
return b
def translate_str(byte_letter):
for i in range(0,len(byte_letter)):
v3 = byte_letter[i]
if (v3 - 97) <= 0x19:
byte_letter[i] = (v3 - 84) % 26 + 97;
if (v3 - 65) <= 0x19:
byte_letter[i] = (v3 - 52)% 26 + 65;
return byte_letter
print "letter",translate_str(byte_letter)
byte_letter_2 = translate_str(byte_letter)
for i in range(0,len(byte_letter_2)):
if i % 2 == 1:
byte_letter_2[i] += 32
for i in range(55,49,-1):
byte_letter_2.append(i)
encry_str = ""
for i in byte_letter_2:
encry_str += chr(i)
#key = encry_str
key = int_to_char(byte_letter_2)

可以得到转换的字符串”NoPqRsTuVwXyZaBcDeFgHiJkLm765432”,通过比对”TRLT5amLBoLT5Z6Fa5LqN6mkTomqR66Da4LqX5mgBwkkP5wmTZ6D====”可以得到字符数组byte_40507c数组下标的排列顺序。而该下标中存储的就是变换前某步处理后的字符串。

1
2
3
4
5
6
7
for i in output_str:
if i != '=' :

m1 = key.index(i)
flag_int.append(m1)
else:
flag_int.append(-1)

这样我们就得到了下标的正确顺序值。再通过解密还原得到”10n78ppn3ro00o70r2opop5s3roqq937”,但是输入后得到的依旧是sooooooooooory….因为我们输入的字符长度为32为,不能直接goto label14

base6

还是经过sub401100()函数处理。对应的转换代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
byte_letter_l = range(96,96+26)
trans_init = int_to_char(translate_str(byte_letter_l))##函数在上文中
dict_letter = {}
j = 0
for i in range(96,96+26):
dict_letter[trans_init[j]] = i
j = j + 1
flag_new = []
for i in range(0,len(flag)):
if flag[i] <= 'z' and flag[i] >= 'a':
flag_new.append(dict_letter[flag[i]])
else:
flag_new.append(ord(flag[i]))
flag_str = ""
for i in flag_new:
flag_str += chr(i)
print flag_str

可以得到最终的flag 10a78cca3eb00b70e2bcbc5f3ebdd937

easy_tree


经过查壳后发现是UPX壳,脱壳机直接脱掉壳后,经过IDA,F5后,程序逻辑依旧很清晰。经过strncmp后输出well done。就可以得到FLAG。

v3的字符串经过sub_401794(&v6)函数得到,这个函数是base64加密函数。

sub_401794函数


aAbcdefghijklmn db ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/‘处发现该字符串,猜测可能是base64加密,再通过v4 += 3 v5 += 4大致符合base64加密。

1
2
import base64
print base64.b64decode("aWNuZXJyc2VhZXRydmVl")

得到字符串”icnerrseaetrvee”,这个时候再考虑sub_40166E(v8, v9, (int)&v6)函数

sub_40166E函数


三个参数的解析 a1 是地址,a2是第几个数,a3是复制后的地址。在第一个参数a1的传值的过程中分别是后来的*(a1+4),*(a1+8),传入的分别是左右儿子节点的地址。

解题思路


根据字符串长度,结合动态调试,比如断点设置在mov [edx],al处,通过输入不同的字符,观察每次输入的字符,后来的访问来判断树的建立,发现是满二叉树,可以得到最终的答案icanreversetree。