Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/unblob/handlers/filesystem/squashfs.py: 79%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

102 statements  

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 )