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