-
Notifications
You must be signed in to change notification settings - Fork 1
/
upgrade_rw.py
168 lines (120 loc) · 3.76 KB
/
upgrade_rw.py
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
import sys
import crypt
import struct
import tarfile
import io
from lib import decode, Channel
from lib.box import get_box_entries, assemble_box
HELP_TEXT = '''To read file:
python3 [progname] [url] [adminpass] [file_name]
To write file:
cat [source_file] | python3 [progname] [url] [adminpass] [file_name] [upgrade_type=2]
upgrade_type 3 might allow you to write files with the executable permission
'''
if len(sys.argv) < 4:
print(HELP_TEXT.replace('[progname]', sys.argv[0]))
exit(1)
host = sys.argv[1]
uname = b'Admin'
pw = sys.argv[2].encode()
target_file_name = sys.argv[3]
write_file = not sys.stdin.isatty()
upgrade_type = 2 if len(sys.argv) < 5 else int(sys.argv[4])
conn = Channel(host)
conn.connect()
crypt_key = conn.login(uname, pw)
if not crypt_key:
print('Login failed.', file=sys.stderr)
exit(1)
print('Downloading config file...', file=sys.stderr)
to_read = target_file_name if not write_file else 'config_server.ini'
conn.send_msg(['O FATA HOINARA', 'CMD', 'DH', 'CFGFILE', 'DOWNLOAD', '1', to_read])
box_data = b''
done = False
while not done:
msgs, data = conn.recv()
if data:
box_data += data[1]
if msgs:
for msg in msgs:
if msg[4:8] == ['DH', 'CFGFILE', 'DOWNLOAD', 'FINISHED']:
done = True
break
print('Got config file', file=sys.stderr)
print('Exported type field', box_data[12:16].hex(), file=sys.stderr)
if not write_file:
for name, data in get_box_entries(io.BytesIO(box_data)):
if name == 'ProductModule':
continue
if name.endswith('.tar.gz'):
tf = tarfile.open(fileobj=io.BytesIO(data))
data = None
for file in tf:
file = tf.extractfile(file)
if file:
data = file.read()
break
if data:
sys.stdout.buffer.write(data)
break
else:
data = sys.stdin.buffer.read()
data_name = sys.argv[3]
new_box_entries = [
(target_file_name, data),
# this one is so that the operation fails
# and therefore further upgrade steps are not performed (... are there any?)
('/tmp', b'dummy')
]
new_box = assemble_box(box_data, new_box_entries)
conn.send_msg(['IP', 'CMD', 'WEBUPGRADE', 'BEGIN'])
while True:
msg = conn.recv_msg()
if len(msg) > 5 and msg[4] == 'WEBUPGRADE':
if msg[5] != 'OK':
print('web upgrade not possible', msg)
sys.exit(1)
break
# do we need to split by 0x3ec ?
num_chunks = (len(new_box) + 0x2ff) // 0x300
print(num_chunks)
conn.send_msg(['IP', 'CMD', 'WEBUPGRADE', 'LENGTH', str(num_chunks)])
while True:
msg = conn.recv_msg()
if len(msg) > 5 and msg[4] == 'WEBUPGRADE':
if msg[5] != 'FIRST':
print('web upgrade error 1')
sys.exit(1)
break
# new_box is the thing to send
offset = 0
idx = 0
# this incredibly stupid...
# first you need to split the box in chunks - that's ok
# then you send chunks and after the last one, you send the "checksum" - ok, still makes sense
# but now.. you send 100 chunks and you must send the checksum twice
# otherwise? it won't work
last_next = 0
while True:
slice = new_box[offset:offset+0x300]
to_send = b'PRO\xff' + struct.pack('<HH', idx, len(slice)+10) + slice
conn.send_data(0, to_send + struct.pack('<H', sum(to_send)))
offset += len(slice)
if idx == num_chunks + 1:
conn.send_data(0, b'PRO\xff' + struct.pack('<H2x', sum(new_box)))
if idx < last_next:
break
while True:
msg = conn.recv_msg()
if len(msg) > 4 and msg[4] == 'WEBUPGRADE':
if msg[5] == 'NEXT':
print(msg)
last_next = idx
idx = int(msg[6])
break
while True:
msg = conn.recv_msg()
if len(msg) > 5 and msg[4] == 'WEBUPGRADE':
print(msg)
break
print('Possible success', file=sys.stderr)