Skip to content

Commit c8c32ea

Browse files
authored
Merge pull request #980 from onekey-sec/autel-ecc-firmware
feat(handler): add support for Autel ECC firmwares.
2 parents 71f37b6 + 3c897c1 commit c8c32ea

File tree

4 files changed

+342
-0
lines changed

4 files changed

+342
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:764fdaf18dec0c034b0b9e8e1dafe3a26591c4ee16041a2eca2f314a90586622
3+
size 7201
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:fdb4a54a87aa369a9edbb7309f0ee09137bff4f09c30057dadcf14ebe695531a
3+
size 7169

unblob/handlers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from ..models import DirectoryHandlers, Handlers
22
from .archive import ar, arc, arj, cab, cpio, dmg, rar, sevenzip, stuffit, tar, zip
3+
from .archive.autel import ecc
34
from .archive.dlink import encrpted_img, shrs
45
from .archive.engeniustech import engenius
56
from .archive.hp import bdl, ipkg
@@ -100,6 +101,7 @@
100101
elf.ELF64Handler,
101102
zlib.ZlibHandler,
102103
engenius.EngeniusHandler,
104+
ecc.AutelECCHandler,
103105
)
104106

105107
BUILTIN_DIR_HANDLERS: DirectoryHandlers = (
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
from pathlib import Path
2+
from typing import Optional
3+
4+
from structlog import get_logger
5+
6+
from unblob.file_utils import File, InvalidInputFormat, iterate_file
7+
from unblob.models import (
8+
Endian,
9+
Extractor,
10+
HexString,
11+
StructHandler,
12+
StructParser,
13+
ValidChunk,
14+
)
15+
16+
logger = get_logger()
17+
18+
C_DEFINITIONS = r"""
19+
20+
typedef struct autel_header {
21+
char magic[8];
22+
uint32 file_size;
23+
uint32 header_size;
24+
char copyright[16];
25+
} autel_header_t;
26+
"""
27+
28+
KEYS = [
29+
(54, 147),
30+
(96, 129),
31+
(59, 193),
32+
(191, 0),
33+
(45, 130),
34+
(96, 144),
35+
(27, 129),
36+
(152, 0),
37+
(44, 180),
38+
(118, 141),
39+
(115, 129),
40+
(210, 0),
41+
(13, 164),
42+
(27, 133),
43+
(20, 192),
44+
(139, 0),
45+
(28, 166),
46+
(17, 133),
47+
(19, 193),
48+
(224, 0),
49+
(20, 161),
50+
(145, 0),
51+
(14, 193),
52+
(12, 132),
53+
(18, 161),
54+
(17, 140),
55+
(29, 192),
56+
(246, 0),
57+
(115, 178),
58+
(28, 132),
59+
(155, 0),
60+
(12, 132),
61+
(31, 165),
62+
(20, 136),
63+
(27, 193),
64+
(142, 0),
65+
(96, 164),
66+
(18, 133),
67+
(145, 0),
68+
(23, 132),
69+
(13, 165),
70+
(13, 148),
71+
(23, 193),
72+
(19, 132),
73+
(27, 178),
74+
(83, 137),
75+
(146, 0),
76+
(145, 0),
77+
(18, 166),
78+
(96, 148),
79+
(13, 193),
80+
(159, 0),
81+
(96, 166),
82+
(20, 129),
83+
(20, 193),
84+
(27, 132),
85+
(9, 160),
86+
(96, 148),
87+
(13, 192),
88+
(159, 0),
89+
(96, 180),
90+
(142, 0),
91+
(31, 193),
92+
(155, 0),
93+
(7, 166),
94+
(224, 0),
95+
(20, 192),
96+
(27, 132),
97+
(28, 160),
98+
(17, 149),
99+
(19, 193),
100+
(96, 132),
101+
(76, 164),
102+
(208, 0),
103+
(80, 192),
104+
(78, 132),
105+
(96, 160),
106+
(27, 144),
107+
(24, 193),
108+
(140, 0),
109+
(96, 178),
110+
(17, 141),
111+
(12, 193),
112+
(224, 0),
113+
(14, 161),
114+
(17, 141),
115+
(151, 0),
116+
(14, 132),
117+
(16, 165),
118+
(96, 137),
119+
(13, 193),
120+
(155, 0),
121+
(20, 161),
122+
(29, 141),
123+
(23, 192),
124+
(24, 132),
125+
(27, 178),
126+
(10, 133),
127+
(96, 192),
128+
(140, 0),
129+
(14, 180),
130+
(17, 133),
131+
(16, 192),
132+
(144, 0),
133+
(11, 163),
134+
(13, 141),
135+
(96, 192),
136+
(17, 132),
137+
(12, 178),
138+
(96, 141),
139+
(28, 192),
140+
(27, 132),
141+
(27, 130),
142+
(18, 141),
143+
(96, 193),
144+
(31, 132),
145+
(96, 181),
146+
(13, 140),
147+
(23, 193),
148+
(224, 0),
149+
(27, 166),
150+
(142, 0),
151+
(27, 192),
152+
(24, 132),
153+
(12, 183),
154+
(96, 133),
155+
(84, 192),
156+
(14, 132),
157+
(27, 178),
158+
(10, 140),
159+
(155, 0),
160+
(9, 132),
161+
(17, 160),
162+
(56, 133),
163+
(96, 192),
164+
(82, 132),
165+
(13, 160),
166+
(27, 137),
167+
(20, 193),
168+
(139, 0),
169+
(28, 161),
170+
(145, 0),
171+
(19, 192),
172+
(118, 132),
173+
(115, 165),
174+
(20, 132),
175+
(145, 0),
176+
(14, 132),
177+
(12, 167),
178+
(146, 0),
179+
(17, 193),
180+
(29, 132),
181+
(96, 176),
182+
(28, 144),
183+
(27, 193),
184+
(140, 0),
185+
(31, 180),
186+
(148, 0),
187+
(27, 192),
188+
(14, 132),
189+
(83, 160),
190+
(18, 137),
191+
(17, 193),
192+
(23, 132),
193+
(13, 165),
194+
(13, 145),
195+
(151, 0),
196+
(147, 0),
197+
(27, 178),
198+
(96, 137),
199+
(19, 193),
200+
(159, 0),
201+
(14, 160),
202+
(25, 148),
203+
(17, 193),
204+
(142, 0),
205+
(16, 180),
206+
(27, 136),
207+
(14, 193),
208+
(224, 0),
209+
(17, 178),
210+
(12, 144),
211+
(224, 0),
212+
(28, 132),
213+
(27, 160),
214+
(13, 141),
215+
(11, 193),
216+
(96, 132),
217+
(27, 165),
218+
(30, 140),
219+
(224, 0),
220+
(146, 0),
221+
(31, 165),
222+
(29, 129),
223+
(96, 192),
224+
(140, 0),
225+
(31, 161),
226+
(24, 145),
227+
(140, 0),
228+
(96, 132),
229+
(27, 165),
230+
(29, 140),
231+
(31, 192),
232+
(154, 0),
233+
(14, 161),
234+
(27, 145),
235+
(140, 0),
236+
(18, 132),
237+
(23, 167),
238+
(96, 140),
239+
(21, 129),
240+
(14, 132),
241+
(17, 165),
242+
(9, 137),
243+
(12, 193),
244+
(155, 0),
245+
(18, 161),
246+
(96, 141),
247+
(27, 192),
248+
(148, 0),
249+
(29, 178),
250+
(23, 133),
251+
(24, 192),
252+
(155, 0),
253+
(10, 180),
254+
(96, 133),
255+
(28, 192),
256+
(14, 132),
257+
(31, 130),
258+
(28, 129),
259+
(18, 193),
260+
(31, 132),
261+
(12, 180),
262+
(13, 144),
263+
(96, 193),
264+
(31, 132),
265+
(96, 160),
266+
(13, 141),
267+
(27, 193),
268+
(18, 132),
269+
(23, 181),
270+
(26, 140),
271+
(27, 193),
272+
(156, 0),
273+
(96, 166),
274+
(79, 141),
275+
(211, 0),
276+
(76, 132),
277+
(77, 160),
278+
(75, 133),
279+
(206, 0),
280+
(182, 0),
281+
(96, 129),
282+
(59, 133),
283+
(191, 0),
284+
(173, 0),
285+
]
286+
287+
288+
def ecc_decode(data: bytes):
289+
"""Decode a 256 byte data block."""
290+
assert len(data) <= 256
291+
for i, value in enumerate(data):
292+
a, b = KEYS[i]
293+
yield ((value + a) ^ b) % 256
294+
295+
296+
class ECCExtractor(Extractor):
297+
def __init__(self):
298+
self._struct_parser = StructParser(C_DEFINITIONS)
299+
300+
def extract(self, inpath: Path, outdir: Path):
301+
outpath = outdir.joinpath(f"{inpath.name}.decrypted")
302+
with File.from_path(inpath) as file:
303+
header = self._struct_parser.parse(
304+
"autel_header_t", file, endian=Endian.LITTLE
305+
)
306+
with outpath.open("wb") as outfile:
307+
for data in iterate_file(
308+
file, header.header_size, header.file_size, 256
309+
):
310+
outfile.write(bytes(ecc_decode(data)))
311+
312+
313+
class AutelECCHandler(StructHandler):
314+
NAME = "ecc"
315+
316+
PATTERNS = [HexString("45 43 43 30 31 30 31 00")]
317+
318+
C_DEFINITIONS = C_DEFINITIONS
319+
HEADER_STRUCT = "autel_header_t"
320+
EXTRACTOR = ECCExtractor()
321+
322+
def is_valid_header(self, header) -> bool:
323+
return header.header_size == 0x20
324+
325+
def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
326+
header = self.parse_header(file, endian=Endian.LITTLE)
327+
328+
if not self.is_valid_header(header):
329+
raise InvalidInputFormat("Invalid ECC header.")
330+
331+
return ValidChunk(
332+
start_offset=start_offset,
333+
end_offset=start_offset + header.header_size + header.file_size,
334+
)

0 commit comments

Comments
 (0)