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