Skip to content

Commit c9eeab0

Browse files
committed
feat(handler): add support for Autel ECC firmwares.
1 parent 647025d commit c9eeab0

File tree

2 files changed

+335
-0
lines changed

2 files changed

+335
-0
lines changed

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

0 commit comments

Comments
 (0)