1from pathlib import Path
2
3from structlog import get_logger
4
5from unblob.extractors import Command
6
7from ...file_utils import Endian, StructParser, get_endian, round_up
8from ...models import (
9 Extractor,
10 File,
11 HandlerDoc,
12 HandlerType,
13 HexString,
14 Reference,
15 StructHandler,
16 ValidChunk,
17)
18
19logger = get_logger()
20
21PAD_SIZES = [4_096, 1_024]
22
23
24class SquashFSExtractor(Extractor):
25 EXECUTABLE = "sasquatch"
26
27 def __init__(self, big_endian_magic: int):
28 self.big_endian_magic = big_endian_magic
29
30 def extract(self, inpath: Path, outdir: Path):
31 with File.from_path(inpath) as file:
32 endian = get_endian(file, self.big_endian_magic)
33
34 commands_args = []
35
36 if endian == Endian.BIG:
37 commands_args.append("-be")
38 else:
39 commands_args.append("-le")
40
41 commands_args.extend(
42 [
43 "-no-exit-code",
44 "-f",
45 "-d",
46 "{outdir}",
47 "{inpath}",
48 ]
49 )
50 extractor = Command(self.EXECUTABLE, *commands_args)
51 extractor.extract(inpath, outdir)
52
53 def get_dependencies(self) -> list[str]:
54 return [self.EXECUTABLE]
55
56
57class _SquashFSBase(StructHandler):
58 BIG_ENDIAN_MAGIC = 0x73_71_73_68
59
60 EXTRACTOR = SquashFSExtractor(0x73_71_73_68)
61
62 def calculate_chunk(self, file: File, start_offset: int) -> ValidChunk | None:
63 file.seek(start_offset)
64 endian = get_endian(file, self.BIG_ENDIAN_MAGIC)
65 header = self.parse_header(file, endian)
66
67 end_of_data_offset = (start_offset + header.bytes_used) & 0xFF_FF_FF_FF
68 file.seek(end_of_data_offset)
69 padding = file.read(
70 round_up(header.bytes_used, max(PAD_SIZES)) - header.bytes_used
71 )
72
73 for pad_size in sorted(PAD_SIZES, reverse=True):
74 size = round_up(header.bytes_used, pad_size)
75 padding_length = size - header.bytes_used
76
77 if padding.startswith(b"\00" * padding_length):
78 end_offset = start_offset + size
79 return ValidChunk(start_offset=start_offset, end_offset=end_offset)
80
81 return ValidChunk(
82 start_offset=start_offset, end_offset=start_offset + header.bytes_used
83 )
84
85
86class SquashFSv1Handler(_SquashFSBase):
87 NAME = "squashfs_v1"
88
89 PATTERNS = [
90 HexString(
91 """
92 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............|
93 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 |................|
94 // squashfs_v1_magic_be
95 73 71 73 68 [24] 00 01
96 """
97 ),
98 HexString(
99 """
100 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............|
101 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
102 // squashfs_v1_magic_le
103 68 73 71 73 [24] 01 00
104 """
105 ),
106 ]
107
108 C_DEFINITIONS = r"""
109 typedef struct squashfs_super_block
110 {
111 char s_magic[4];
112 uint32 inodes;
113 uint32 bytes_used;
114 uint32 uid_start;
115 uint32 guid_start;
116 uint32 inode_table_start;
117 uint32 directory_table_start;
118 uint16 s_major;
119 uint16 s_minor;
120 } squashfs_super_block_t;
121 """
122 HEADER_STRUCT = "squashfs_super_block_t"
123
124 DOC = HandlerDoc(
125 name="SquashFS (v1)",
126 description="SquashFS version 1 is a compressed, read-only file system format designed for minimal storage usage. It is commonly used in embedded systems and early Linux distributions.",
127 handler_type=HandlerType.FILESYSTEM,
128 vendor=None,
129 references=[
130 Reference(
131 title="SquashFS Documentation",
132 url="https://dr-emann.github.io/squashfs/",
133 ),
134 Reference(
135 title="SquashFS Wikipedia",
136 url="https://en.wikipedia.org/wiki/SquashFS",
137 ),
138 ],
139 limitations=[],
140 )
141
142
143class SquashFSv2Handler(SquashFSv1Handler):
144 NAME = "squashfs_v2"
145
146 PATTERNS = [
147 HexString(
148 """
149 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............|
150 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 |................|
151 // squashfs_v2_magic_be
152 73 71 73 68 [24] 00 02
153 """
154 ),
155 HexString(
156 """
157 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............|
158 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 02 00 00 00 |................|
159 // squashfs_v2_magic_le
160 68 73 71 73 [24] 02 00
161 """
162 ),
163 ]
164
165 DOC = HandlerDoc(
166 name="SquashFS (v2)",
167 description="SquashFS version 2 is a compressed, read-only file system format designed for minimal storage usage. It builds upon version 1 with additional features and improvements for embedded systems and Linux distributions.",
168 handler_type=HandlerType.FILESYSTEM,
169 vendor=None,
170 references=[
171 Reference(
172 title="SquashFS Documentation",
173 url="https://dr-emann.github.io/squashfs/",
174 ),
175 Reference(
176 title="SquashFS Wikipedia",
177 url="https://en.wikipedia.org/wiki/SquashFS",
178 ),
179 ],
180 limitations=[],
181 )
182
183
184class SquashFSv3Handler(_SquashFSBase):
185 NAME = "squashfs_v3"
186
187 PATTERNS = [
188 HexString(
189 """
190 // 00000000 73 71 73 68 00 00 00 03 00 00 00 00 00 00 00 00 |sqsh............|
191 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 |................|
192 // squashfs_v3_magic_be
193 73 71 73 68 [24] 00 03
194 """
195 ),
196 HexString(
197 """
198 // 00000000 68 73 71 73 03 00 00 00 00 00 00 00 00 00 00 00 |hsqs............|
199 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00 00 |................|
200 // squashfs_v3_magic_le
201 68 73 71 73 [24] 03 00
202 """
203 ),
204 ]
205
206 DOC = HandlerDoc(
207 name="SquashFS (v3)",
208 description="SquashFS version 3 is a compressed, read-only file system format designed for minimal storage usage. It is widely used in embedded systems and Linux distributions for efficient storage and fast access.",
209 handler_type=HandlerType.FILESYSTEM,
210 vendor=None,
211 references=[
212 Reference(
213 title="SquashFS Documentation",
214 url="https://dr-emann.github.io/squashfs/",
215 ),
216 Reference(
217 title="SquashFS Wikipedia",
218 url="https://en.wikipedia.org/wiki/SquashFS",
219 ),
220 ],
221 limitations=[],
222 )
223
224 C_DEFINITIONS = r"""
225 typedef struct squashfs3_super_block
226 {
227 char s_magic[4];
228 uint32 inodes;
229 uint32 bytes_used_2;
230 uint32 uid_start_2;
231 uint32 guid_start_2;
232 uint32 inode_table_start_2;
233 uint32 directory_table_start_2;
234 uint16 s_major;
235 uint16 s_minor;
236 uint16 block_size_1;
237 uint16 block_log;
238 uint8 flags;
239 uint8 no_uids;
240 uint8 no_guids;
241 uint32 mkfs_time /* time of filesystem creation */;
242 uint64 root_inode;
243 uint32 block_size;
244 uint32 fragments;
245 uint32 fragment_table_start_2;
246 uint64 bytes_used;
247 uint64 uid_start;
248 uint64 guid_start;
249 uint64 inode_table_start;
250 uint64 directory_table_start;
251 uint64 fragment_table_start;
252 uint64 lookup_table_start;
253 } squashfs3_super_block_t;
254 """
255 HEADER_STRUCT = "squashfs3_super_block_t"
256
257
258class SquashFSv3DDWRTHandler(SquashFSv3Handler):
259 NAME = "squashfs_v3_ddwrt"
260
261 BIG_ENDIAN_MAGIC = 0x74_71_73_68
262
263 EXTRACTOR = SquashFSExtractor(0x74_71_73_68)
264
265 PATTERNS = [
266 HexString(
267 """
268 // 00000000 68 73 71 74 21 02 00 00 00 00 00 00 00 00 00 00 |hsqt!...........|
269 // 00000010 00 00 00 00 00 00 00 00 50 02 00 00 03 00 00 00 |........P.......|
270 // squashfs_v3_magic_ddwrt_le
271 68 73 71 74 [24] 03 00
272 """
273 ),
274 HexString(
275 """
276 // 00000000 74 71 73 68 00 00 00 05 00 00 00 00 00 00 00 00 |tqsh............|
277 // 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 |................|
278 // squashfs_v3_magic_ddwrt_be
279 74 71 73 68 [24] 00 03
280 """
281 ),
282 ]
283
284 DOC = HandlerDoc(
285 name="SquashFS (v3-DDWRT)",
286 description="SquashFS version 3 DD-WRT is a variant of the SquashFS v3 format used in DD-WRT firmware. It features a unique magic number and may include specific optimizations for embedded systems.",
287 handler_type=HandlerType.FILESYSTEM,
288 vendor="DDWRT",
289 references=[
290 Reference(
291 title="SquashFS Documentation",
292 url="https://dr-emann.github.io/squashfs/",
293 ),
294 Reference(
295 title="SquashFS Wikipedia",
296 url="https://en.wikipedia.org/wiki/SquashFS",
297 ),
298 ],
299 limitations=[],
300 )
301
302
303class SquashFSv3BroadcomHandler(SquashFSv3Handler):
304 NAME = "squashfs_v3_broadcom"
305
306 BIG_ENDIAN_MAGIC = 0x71_73_68_73
307
308 EXTRACTOR = SquashFSExtractor(0x71_73_68_73)
309
310 PATTERNS = [
311 HexString(
312 """
313 // 00000000 73 68 73 71 0f 05 00 00 c8 a9 00 01 00 00 00 bc |shsq............|
314 // 00000010 1f 2d 00 a2 d0 2b 00 bf 79 2e 00 65 03 00 00 00 |.-...+..y..e....|
315 // squashfs_v3_magic_broadcom_le
316 73 68 73 71 [24] 03 00
317 """
318 ),
319 HexString(
320 """
321 // 00000000 71 73 68 73 0f 05 00 00 c8 a9 00 01 00 00 00 bc |qshs............|
322 // 00000010 1f 2d 00 a2 d0 2b 00 bf 79 2e 00 65 00 03 00 00 |.-...+..y..e....|
323 // squashfs_v3_magic_broadcom_be
324 71 73 68 73 [24] 00 03
325
326 """
327 ),
328 ]
329
330 DOC = HandlerDoc(
331 name="SquashFS (v3-Broadcom)",
332 description="SquashFS version 3 Broadcom is a variant of the SquashFS v3 format used in Broadcom firmware. It features a unique magic number and may include specific optimizations for Broadcom devices.",
333 handler_type=HandlerType.FILESYSTEM,
334 vendor="Broadcom",
335 references=[
336 Reference(
337 title="SquashFS Documentation",
338 url="https://dr-emann.github.io/squashfs/",
339 ),
340 Reference(
341 title="SquashFS Wikipedia",
342 url="https://en.wikipedia.org/wiki/SquashFS",
343 ),
344 ],
345 limitations=[],
346 )
347
348
349class SquashFSv3NSHandler(SquashFSv3Handler):
350 NAME = "squashfs_v3_nonstandard"
351
352 BIG_ENDIAN_MAGIC = 0x73_71_6C_7A
353
354 EXTRACTOR = SquashFSExtractor(0x73_71_6C_7A)
355
356 PATTERNS = [
357 HexString(
358 """
359 // 00000000 7a 6c 71 73 00 00 04 df 57 00 17 95 46 00 19 ed |zlqs....W...F...|
360 // 00000010 20 08 04 8e 02 40 01 06 c9 02 16 00 00 03 00 01 | ....@..........|
361 // squashfs_v3_magic_nonstandard_le
362 7A 6c 71 73 [24] 03 00
363 """
364 ),
365 HexString(
366 """
367 // 00000000 73 71 6c 7a 00 00 04 df 57 00 17 95 46 00 19 ed |sqlz....W...F...|
368 // 00000010 20 08 04 8e 02 40 01 06 c9 02 16 00 00 03 00 01 | ....@..........|
369 // squashfs_v3_magic_nonstandard_be
370 73 71 6c 7A [24] 00 03
371 """
372 ),
373 ]
374
375 DOC = HandlerDoc(
376 name="SquashFS (v3-non-standard)",
377 description="SquashFS version 3 is a compressed, read-only file system format designed for minimal storage usage. It is widely used in embedded systems and Linux distributions for efficient storage and fast access.",
378 handler_type=HandlerType.FILESYSTEM,
379 vendor="unknown",
380 references=[
381 Reference(
382 title="SquashFS Documentation",
383 url="https://dr-emann.github.io/squashfs/",
384 ),
385 Reference(
386 title="SquashFS Wikipedia",
387 url="https://en.wikipedia.org/wiki/SquashFS",
388 ),
389 ],
390 limitations=[],
391 )
392
393
394class SquashFSv4LEHandler(_SquashFSBase):
395 NAME = "squashfs_v4_le"
396
397 PATTERNS = [
398 HexString(
399 """
400 // 00000000 68 73 71 73 03 00 00 00 00 c1 9c 61 00 00 02 00 |hsqs.......a....|
401 // 00000010 01 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |................|
402 // squashfs_v4_magic_le
403 68 73 71 73 [24] 04 00
404 """
405 ),
406 ]
407
408 C_DEFINITIONS = r"""
409 typedef struct squashfs4_super_block
410 {
411 char s_magic[4];
412 uint32 inodes;
413 uint32 mkfs_time /* time of filesystem creation */;
414 uint32 block_size;
415 uint32 fragments;
416 uint16 compression;
417 uint16 block_log;
418 uint16 flags;
419 uint16 no_ids;
420 uint16 s_major;
421 uint16 s_minor;
422 uint64 root_inode;
423 uint64 bytes_used;
424 uint64 id_table_start;
425 uint64 xattr_id_table_start;
426 uint64 inode_table_start;
427 uint64 directory_table_start;
428 uint64 fragment_table_start;
429 uint64 lookup_table_start;
430 } squashfs4_super_block_t;
431 """
432 HEADER_STRUCT = "squashfs4_super_block_t"
433
434 DOC = HandlerDoc(
435 name="SquashFS (v4-LE)",
436 description="SquashFS version 4 is a compressed, read-only file system format designed for minimal storage usage and fast access. It is widely used in embedded systems and Linux distributions for efficient storage management.",
437 handler_type=HandlerType.FILESYSTEM,
438 vendor=None,
439 references=[
440 Reference(
441 title="SquashFS Documentation",
442 url="https://dr-emann.github.io/squashfs/",
443 ),
444 Reference(
445 title="SquashFS Wikipedia",
446 url="https://en.wikipedia.org/wiki/SquashFS",
447 ),
448 ],
449 limitations=[],
450 )
451
452
453class SquashFSv4BEExtractor(Extractor):
454 EXECUTABLE = "sasquatch-v4be"
455
456 def is_avm(self, header) -> bool:
457 # see https://raw.githubusercontent.com/Freetz/freetz/master/tools/make/squashfs4-host-be/AVM-BE-format.txt
458 return header.bytes_used == header.mkfs_time
459
460 def extract(self, inpath: Path, outdir: Path):
461 struct_parser = StructParser(SquashFSv4LEHandler.C_DEFINITIONS)
462
463 with File.from_path(inpath) as f:
464 header = struct_parser.parse(
465 SquashFSv4LEHandler.HEADER_STRUCT, f, Endian.BIG
466 )
467
468 commands_args = []
469
470 if not self.is_avm(header):
471 commands_args.append("-be")
472
473 commands_args.extend(
474 [
475 "-no-exit-code",
476 "-f",
477 "-d",
478 "{outdir}",
479 "{inpath}",
480 ]
481 )
482 extractor = Command(self.EXECUTABLE, *commands_args)
483 extractor.extract(inpath, outdir)
484
485 def get_dependencies(self) -> list[str]:
486 return [self.EXECUTABLE]
487
488
489class SquashFSv4BEHandler(SquashFSv4LEHandler):
490 NAME = "squashfs_v4_be"
491
492 PATTERNS = [
493 HexString(
494 """
495 // 00000000 73 71 73 68 03 00 00 00 00 c1 9c 61 00 00 02 00 |sqsh.......a....|
496 // 00000010 01 00 00 00 01 00 11 00 c0 00 01 00 04 00 00 00 |................|
497 // squashfs_v4_magic_be
498 73 71 73 68 [24] 00 04
499 """
500 )
501 ]
502
503 EXTRACTOR = SquashFSv4BEExtractor()
504
505 DOC = HandlerDoc(
506 name="SquashFS (v4-BE)",
507 description="SquashFS version 4 is a compressed, read-only file system format designed for minimal storage usage and fast access. It supports both big-endian and little-endian formats and is widely used in embedded systems and Linux distributions.",
508 handler_type=HandlerType.FILESYSTEM,
509 vendor=None,
510 references=[
511 Reference(
512 title="SquashFS Documentation",
513 url="https://dr-emann.github.io/squashfs/",
514 ),
515 Reference(
516 title="SquashFS Wikipedia",
517 url="https://en.wikipedia.org/wiki/SquashFS",
518 ),
519 ],
520 limitations=[],
521 )