0x01 py逆向

给了一个main.py和out.txt 的文件,打开分析一下

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
import random

key1 = random.choices(range(256), k=20) #0到255里取20个随机数
key2 = list(range(256)) #0到255的列表
random.shuffle(key2) #随机排列
flag = open('flag.txt', 'rb').read() #读取flag,设置成input

def enc(n): #加密函数
q = key2[n]
w = key1[q % 20]
n ^= q
return n, w

x = 0x00000000
for i, c in enumerate(flag): #枚举遍历,第一个是下标,第二个是里面的东西
x <<= 8 #x左移动8位,一个字节
n, w = enc(c) #根据密钥和flag出两个数
if i % 2: #如果是奇数下标,swap交换
n, w = w, n
x |= n
x |= (w << ((2 * i + 1) * 8) ) #按位运算的优先级高于赋值运算

print(key1)
print(key2)
print(x)

然后手动调试一下前几项,很容易发现x 的组成是由w和n交替排列的,大概的规律就是

         w34n33......w2n1w0n0w1n2......w33n34

根据output文件里的x的值,转码成十六进制

(0x)ea0149929a24de3a6ab642ebeaa16a02571f572449b67f1f57c4a4b67fc542027feb6a0eeaab4d9c42ab6a3e5124deab6a045724de244db69a16ea016ab6571f57a1a2fe4290

总共有70个字符,flag推算出是35位

根据这坨玩意写个抽象的脚本分离出w和n

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
enc="ea0149929a24de3a6ab642ebeaa16a02571f572449b67f1f57c4a4b67fc542027feb6a0eeaab4d9c42ab6a3e5124deab6a045724de244db69a16ea016ab6571f57a1a2fe4290"
enc_l=[]
enc_r=[]
print(len(enc))
for i in range(0,70):
enc_l.append(enc[i])
for i in range(70,140):
enc_r.append(enc[i])
n_o=[0xea,0x49,0x9a,0xde,0x6a,0x42,0xea,0x6a,0x57,0x57,0x49,0x7f,0x57,0xa4,0x7f,0x42,0x7f,0x6a]
n_j=[0xea,0x4d,0x42,0x6a,0x51,0xde,0x6a,0x57,0xde,0x4d,0x9a,0xea,0x6a,0x57,0x57,0xa2,0x42]
w_o=[0x0e,0xab,0x9c,0xab,0x3e,0x24,0xab,0x04,0x24,0x24,0xb6,0x16,0x01,0xb6,0x1f,0xa1,0xfe,0x90]
w_j=[0x01,0x92,0x24,0x3a,0xb6,0xeb,0xa1,0x02,0x1f,0x24,0xb6,0x1f,0xc4,0xb6,0xc5,0x02,0xeb]
#n的偶数部分,34到0
for i in range(0,70,4):
print("0x",end="")
print(enc_l[i],end="")
print(enc_l[i+1],end="")
print(",",end="")
print("\n")
#n的奇数部分,1到33
for i in range(2,70,4):
print("0x",end="")
print(enc_r[i],end="")
print(enc_r[i+1],end="")
print(",",end="")
#w的奇数部分,33到1,
print("\n")
for i in range(2,70,4):
print("0x",end="")
print(enc_l[i],end="")
print(enc_l[i+1],end="")
print(",",end="")
#w的偶数部分 ,0到34
print("\n")
for i in range(0,70,4):
print("0x",end="")
print(enc_r[i],end="")
print(enc_r[i+1],end="")
print(",",end="")
print("\n")
n=[]
w=[]
j=17
for i in range(0,17):
n.append(n_o[j])
n.append(n_j[i])
j-=1
print(n)
j=16
for i in range(18):
w.append(w_o[i])
w.append(w_j[j])
j-=1
print(w)
_n = [106, 234, 127, 77, 66, 66, 127, 106, 164, 81, 87, 222, 127,
106, 73, 87, 87, 222, 87, 77, 106,
154, 234, 234, 66, 106, 106, 87, 222, 87, 154, 162, 73, 66,234]
_w = [14, 235, 171, 2, 156, 197, 171, 182, 62, 196, 36, 31, 171,
182, 4, 36, 36, 31, 36, 2, 182,
161, 22, 235, 1, 182, 182, 58, 31, 36, 161, 146, 254, 1, 144]

代码有点乱,大概思路就是先把70个字符分成两部分,然后分别从左到右分离出下标奇数部分的n和w,下标偶数部分的w和n,然后把这些n和w排好顺序,分别交替输出得到n和w 的数组。

这个过程读者可以自行完成,我这个只是参考,而且感觉很慢。最后得到的_n和_w便是结果

(脚本里输出的n和w有些问题,分别多了一项和少了一项,笔者懒得去修正了然后手动修改了一下得到正确的n和w,然后这个_n和_w也是有些问题的,当时看题时把main.py里的下标是奇数时,n和w替换,看成了偶数时才替换,所以这里得到的_n其实是实际的w,_w是实际上的n,搞反了,不过万幸的是不影响后面的爆破,只要换一下就好了)

接下来把得到的n和w拿来爆破出c就可以组合成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
key1=[127, 81, 241, 40, 222, 128, 45, 87, 27, 154,
66,162, 73, 176, 172, 164, 106, 234, 77, 5]
key2=[155, 117, 124, 113, 104, 46, 151, 71, 144, 229, 152, 240,
199, 88, 103, 105, 245, 209, 13, 82, 166, 9, 201, 233, 228,
154, 19, 5, 30, 141, 81, 206, 246, 232, 107, 29, 208, 253,
187, 116, 98, 160, 60, 7, 220, 143, 80, 239, 52, 15, 94, 50,
149, 241, 57, 92, 230, 100, 31, 51, 36, 24, 39, 14, 25, 90, 101,
55, 194, 225, 157, 102, 2, 26, 148, 161, 180, 120, 223, 165, 32,
146, 185, 243, 119, 210, 172, 244, 1, 125, 44, 35, 169, 179, 188,
64, 207, 33, 137, 200, 142, 182, 250, 195, 28, 4, 79, 191, 86, 215,
96, 236, 91, 122, 196, 87, 118, 231, 126, 97, 147, 67, 132, 190, 234,
237, 43, 193, 252, 18, 212, 163, 56, 73, 123, 176, 162, 23, 192, 49,
21, 242, 171, 112, 153, 238, 203, 134, 167, 93, 115, 95, 8, 12, 65,
217, 248, 168, 219, 47, 211, 108, 76, 129, 145, 62, 156, 34, 218, 135,
48, 70, 75, 3, 249, 72, 202, 133, 183, 38, 37, 227, 164, 173, 159, 251,
0, 174, 54, 20, 136, 53, 138, 99, 226, 178, 42, 66, 150, 205, 204, 214,
197, 235, 110, 216, 63, 45, 184, 74, 41, 177, 27, 69, 130, 89, 61, 247,
255, 17, 254, 181, 131, 22, 224, 83, 189, 59, 114, 139, 111,
68, 6, 84,11, 127, 221, 106, 77, 109, 158,
170, 16, 121, 222, 186, 10, 58, 175, 40, 128, 198, 78, 85, 213, 140]
x = 3449711664888782790334923396354433085218951813669043815144799745483347584183883892868078716490762334737115401929391994359609927294549975954045314661787321463018287415952
w=[106, 234, 127, 77, 66, 66, 127, 106, 164, 81, 87, 222, 127,
106, 73, 87, 87, 222, 87, 77, 106,
154, 234, 234, 66, 106, 106, 87, 222, 87, 154, 162, 73, 66,234]
n=[14, 235, 171, 2, 156, 197, 171, 182, 62, 196, 36, 31, 171,
182, 4, 36, 36, 31, 36, 2, 182,
161, 22, 235, 1, 182, 182, 58, 31, 36, 161, 146, 254, 1, 144]
flag=""
def enc(n): #加密函数
q = key2[n]
w = key1[q % 20]
n ^= q
return n, w
for i in range(35):
for j in range(32, 127):
a , b = enc(j)
if a==n[i] and b==w[i]:
flag += chr(j)
break
print(flag)


nbctf{cr15s_cr0ss_str4wb3rry_s4uz3}

0x02 sands

看介绍就知道是迷宫题目,打开ida分析一下

eb2a67a911f14049a4ce7c18044a2d9f个关键的语句,和普通迷宫不同的是,这个迷宫会有‘S’,沙子没回合都会根据step移动来阻挡你的路线,如果沙子移动后正好和你的位置重合,会直接导致游戏失败(逻辑是沙子先动人后动),因为沙子撞人的判断是人移动后的,所以在这个迷宫中,只要你在沙子撞到你后移动走,也是能继续游戏的,这个是关键,知道这个小技巧后走迷宫就简单许多了。

比赛时为了节省时间可以省去不必要的东西,只保留关键的能帮助解题的判断语句,赛后我把迷宫完善了不少,已经能当做一个小游戏玩了。

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
#include<iostream>
int map[100]={38, 35, 35, 35, 48, 48, 48, 48, 48, 35,
48, 48, 35, 83, 48, 35, 35, 48, 48, 35, 35,
48, 83, 35, 48, 35, 83, 83, 48, 35, 35, 48,
48, 35, 48, 35, 48, 48, 35, 35, 48, 83, 48,
35, 48, 35, 48, 83, 83, 48, 48, 35, 35, 35,
48, 35, 48, 83, 48, 48, 48, 48, 48, 35, 48,
35, 48, 48, 48, 83, 35, 35, 48, 35, 48, 35,
35, 35, 35, 48, 48, 83, 48, 83, 48, 35, 48,
48, 83, 48, 48, 48, 83, 48, 48, 35, 76, 83,
48, 48};
int i=0,j=0,step=0;
char ch;
char step_arr[50]={0};
//i是横坐标,j是纵坐标
void move(char ch){
switch(ch){
case 'a':
if(i!=0){
if(map[10*j+i-1]=='0'||map[10*j+i-1]=='L'){
map[10*j+i-1]='&';
if(map[10*j+i]!='S'){
map[10*j+i]='0';
}
i--;
}
}
break;
case 'd':
if(i!=9){
if(map[10*j+i+1]=='0'||map[10*j+i+1]=='L'){
map[10*j+i+1]='&';
if(map[10*j+i]!='S'){
map[10*j+i]='0';
}
i++;
}
}
break;
case 's':
if(j!=9){
if(map[10*j+10+i]=='0'||map[10*j+10+i]=='L'){
map[10*j+10+i]='&';
if(map[10*j+i]!='S'){
map[10*j+i]='0';
}
j++;
}
}
break;
case 'w':
if(j!=0){
if(map[10*j-10+i]=='0'||map[10*j+i-10]=='L'){
map[10*j-10+i]='&';
if(map[10*j+i]!='S'){
map[10*j+i]='0';
}
j--;
}
}
break;
}
}
void set_map(){
int k;
for(k=0;k<100;k++){
if(k%10==0){
std::cout<<"\n";
}
printf("%c",map[k]);
}
}
void sand_move(){
int x,y;
switch(step%4){
case 3:
for(x=9;x>=0;x--){
for(y=0;y<=9;y++){
if(map[10*y+x]=='S'&&x!=9){
if(map[10*y+x+1]=='0'||map[10*y+x+1]=='&'){
map[10*y+x+1]='S';
map[10*y+x]='0';
}
}
}
}
break;
case 2:
for(y=0;y<=9;y++){
for(x=0;x<=9;x++){
if(map[10*y+x]=='S'&&y!=0){
if(map[10*y+x-10]=='0'||map[10*y+x-10]=='&'){
map[10*y+x-10]='S';
map[10*y+x]='0';
}
}
}
}
break;
case 1:
for(x=0;x<=9;x++){
for(y=0;y<=9;y++){
if(map[10*y+x]=='S'&&x!=0){
if(map[10*y+x-1]=='0'||map[10*y+x-1]=='&'){
map[10*y+x-1]='S';
map[10*y+x]='0';
}
}
}
}
break;
case 0:
for(y=9;y>=0;y--){
for(x=0;x<=9;x++){
if(map[10*y+x]=='S'&&y!=9){
if(map[10*y+x+10]=='0'||map[10/y+x+10]=='&'){
map[10*y+x+10]='S';
map[10*y+x]='0';
}
}
}
}
break;
}
}
int main(){
while(step<=49&&map[10*j+i]!='S'){
set_map();
std::cout<<"\n"<<"step:"<<step;
std::cout<<"\n"<<"sand de is "<<(step%4);
std::cout<<"\n"<<"(0是下移动,1是左移动,2是上移动,3是右移动)";
std::cout<<"\n"<<"enter your direcation:";
std::cin>>ch;
sand_move();
step_arr[step]=ch;
step++;
move(ch);
if(j==9&&i==6){
std::cout<<"yep"<<"\n";
for(int l=0;l<step;l++){
std::cout<<step_arr[l];
}
break;
}
}
return 0;
}

这样很浪费时间,不过赛后无所谓了。

然后这题还要连一下官方给的靶机,在linux虚拟机里面的命令行打入

nc 【ip】【端口】

连接成功后ls罗列一下文件,cat可以查看文件

0x03 twostep

这个题的逻辑就是反复调用两个不同的函数,然后分块加密,函数1中有几个子函数,每个子函数加密一块,函数2同理,然后调用法则是函数1到函数2,函数2再到函数1,每次调用时step(计数器)+1,然后case不同的step分块加密,最后根据一个数组word_404090[]知道这个加密块在整体flag具体的位置

850be99663a24e22ba85483570b7924e

1
2
#472036518
#123456789

下面是步数step,上面是位置。

函数1

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
__int64 __fastcall encry1(_BYTE *input)
{
__int64 result; // rax
char one; // [rsp+19h] [rbp-A7h]
__int16 v3; // [rsp+1Ah] [rbp-A6h]
__int16 v4; // [rsp+1Ch] [rbp-A4h]
__int16 v5; // [rsp+1Eh] [rbp-A2h]
unsigned __int64 i; // [rsp+20h] [rbp-A0h]
unsigned __int64 k; // [rsp+20h] [rbp-A0h]
__int64 v8; // [rsp+20h] [rbp-A0h]
unsigned __int64 m; // [rsp+20h] [rbp-A0h]
unsigned __int64 j; // [rsp+28h] [rbp-98h]
_BYTE *v11; // [rsp+30h] [rbp-90h]
_BYTE *v12; // [rsp+30h] [rbp-90h]
_BYTE *v13; // [rsp+30h] [rbp-90h]
__int16 v14[4]; // [rsp+38h] [rbp-88h]
__int64 v15[6]; // [rsp+40h] [rbp-80h]
__int64 v16[8]; // [rsp+70h] [rbp-50h]
__int16 v17; // [rsp+B6h] [rbp-Ah]
unsigned __int64 v18; // [rsp+B8h] [rbp-8h] 472036518
// 123456789

v18 = __readfsqword(0x28u);
v14[0] = 0x4808;
v14[1] = 0xC40;
v14[2] = 0x480C;
v14[3] = 0x408C;
v16[0] = 1LL;
v16[1] = 16LL;
v16[2] = 32LL;
v16[3] = 512LL;
v16[4] = 1024LL;
v16[5] = 2048LL;
v16[6] = 0x2000LL;
v16[7] = 0x4000LL;
v15[0] = 177LL;
v15[1] = 166LL;
v15[2] = 183LL;
v15[3] = 182LL;
v15[4] = 177LL;
v15[5] = 0xADLL;
v17 = 170;
switch ( step )
{
case 0LL:
if ( *input == 'n' && input[1] == 'b' && input[2] == 'c' && input[3] == 't' && input[4] == 'f' && input[5] == '{' )
result = encry2(input + 6); // step=0
else
result = 0LL;
break;
case 1LL:
v11 = step_change(input);
for ( i = 0LL; i <= 3; ++i ) // step=2
{
one = v11[i];
v4 = 0;
for ( j = 0LL; j <= 3; ++j )
{
v4 |= (one & 3) << (4 * j + 2);
one >>= 2;
}
if ( v4 != v14[i] )
return 0LL;
}
result = encry2(input);
break;
case 3LL:
v12 = step_change(input);
for ( k = 0LL; k <= 4; ++k ) // step=4
v100[k] = v12[k];
result = encry2(input);
break;
case 6LL:
v3 = *step_change(input);
v8 = 0LL; // step=7
while ( v3 )
{
v5 = v3 & -v3; // 等于1
if ( v8 == 8 )
return 0LL;
if ( v16[v8] == v5 )
++v8;
v3 ^= v5;
}
result = v8 == 8 && encry2(input);
break;
case 8LL:
v13 = step_change(input);
for ( m = 0LL; m <= 5; ++m ) // step=9
LOBYTE(v17) = v13[m] ^ v15[m]; // 去调试找找v17是多少
result = 1LL;
break;
default:
result = 0LL;
break;
}
return result;
}

函数2

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
__int64 __fastcall encry2(_BYTE *input)
{
__int64 result; // rax
unsigned __int64 i; // [rsp+18h] [rbp-E8h]
unsigned __int64 j; // [rsp+18h] [rbp-E8h]
unsigned __int64 k; // [rsp+18h] [rbp-E8h]
__int64 v5; // [rsp+20h] [rbp-E0h]
_BYTE *v6; // [rsp+28h] [rbp-D8h]
_BYTE *v7; // [rsp+28h] [rbp-D8h]
_BYTE *v8; // [rsp+28h] [rbp-D8h]
_BYTE *v9; // [rsp+28h] [rbp-D8h]
__int64 v10[6]; // [rsp+30h] [rbp-D0h]
__int64 v11[6]; // [rsp+60h] [rbp-A0h]
__int64 v12[6]; // [rsp+90h] [rbp-70h]
__int64 v13[8]; // [rsp+C0h] [rbp-40h] 472036518
// 123456789

v13[7] = __readfsqword(0x28u);
v13[0] = 54515LL;
v13[1] = 15689LL;
v13[2] = 4219LL;
v13[3] = 50297LL;
v13[4] = 43652LL;
v13[5] = 38919LL;
v10[0] = 14668LL;
v10[1] = 24063LL;
v10[2] = 37349LL;
v10[3] = 50716LL;
v10[4] = 61563LL;
v11[0] = 1843061LL;
v11[1] = 222420LL;
v11[2] = 5184810LL;
v11[3] = 4590105LL;
v11[4] = 2184197LL;
v5 = 0LL;
v12[0] = 1073741834LL;
v12[1] = 2415919110LL;
v12[2] = 939524099LL;
v12[3] = 536870913LL;
v12[4] = 1845493760LL;
switch ( step )
{
case 0LL:
v6 = step_change(input);
if ( (*v6 ^ 95) == v6[1] // step=1
&& (v6[1] ^ 117) == v6[2]
&& (v6[2] ^ 18) == v6[3]
&& (v6[3] ^ 56) == *v6
&& *v6 == 108 )
{
result = encry1(input);
}
else
{
result = 0LL;
}
break;
case 2LL:
v7 = step_change(input);
for ( i = 0LL; i <= 4; ++i ) // step=3
{
if ( v13[i] + v13[i + 1] * v7[i] != v11[i] )
return 0LL;
}
result = encry1(input);
break;
case 4LL:
v8 = step_change(input);
for ( j = 0LL; j <= 4; ++j ) // step=5
{
v5 += v100[j] + (v8[j] << 7);
if ( v5 != v10[j] )
return 0LL;
}
result = encry2(input);
break;
case 5LL:
v9 = step_change(input);
for ( k = 0LL; k <= 4; ++k ) // step=6
{
if ( v12[k] != ((v9[k] >> (k + 3)) | (v9[k] << (32 - (k + 3)))) )
return 0LL;
}
result = encry1(input);
break;
case 7LL:
qword_404308 = *step_change(input);
result = *&qword_404308 == 3.325947034342098e151 && encry1(input);// step=8
break;
default:
result = 0LL;
break;
}
return result;
}

大概思路就是这样,剩下逐个攻破加密块,按照word_404090里一一对应的关系排列组合,就能得出flag了。(调试太痛苦了,最后不调试还不知道,摆了)