Skip to content

Commit f98b06a

Browse files
Antoine Pecoraroqkaiser
Antoine Pecoraro
authored andcommitted
feat(handler): Add support for HP BDL format
A BDL file contains multiple IPKG packages The header contains useful informations, such as the brand, device_id, version of the BDL file, etc, After that header, there is a table of content that has the offset and the size of each IPKG in the BDL file. This repo https://github.com/tylerwhall/hpbdl/ contains information that was useful
1 parent f58f352 commit f98b06a

File tree

7 files changed

+121
-0
lines changed

7 files changed

+121
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:240533e4188e0af859989fca729c4a0847de3d5688906c4a22d36f843afdf271
3+
size 4043
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:c99dd4e215b508395aed42ebfee1e87d64b6bb5551c165934fe1a759e40093ee
3+
size 1345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:e6478ea19e2122817e1986151847d3590e114ffd7c5d65d8d91cccea316c1cf2
3+
size 17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:c99dd4e215b508395aed42ebfee1e87d64b6bb5551c165934fe1a759e40093ee
3+
size 1345
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:e6478ea19e2122817e1986151847d3590e114ffd7c5d65d8d91cccea316c1cf2
3+
size 17

unblob/handlers/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .archive import ar, arc, arj, cab, cpio, dmg, rar, sevenzip, stuffit, tar, zip
33
from .archive.dlink import encrpted_img, shrs
44
from .archive.engeniustech import engenius
5+
from .archive.hp import bdl
56
from .archive.instar import bneg
67
from .archive.netgear import chk, trx
78
from .archive.qnap import qnap_nas
@@ -62,6 +63,7 @@
6263
hdr.HDR2Handler,
6364
qnap_nas.QnapHandler,
6465
bneg.BNEGHandler,
66+
bdl.HPBDLHandler,
6567
sparse.SparseHandler,
6668
ar.ARHandler,
6769
arc.ARCHandler,

unblob/handlers/archive/hp/bdl.py

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import io
2+
from pathlib import Path
3+
from typing import Optional
4+
5+
from dissect.cstruct import Instance
6+
from structlog import get_logger
7+
8+
from unblob.extractor import carve_chunk_to_file
9+
from unblob.file_utils import Endian, File, InvalidInputFormat, StructParser, snull
10+
from unblob.models import Chunk, Extractor, HexString, StructHandler, ValidChunk
11+
12+
logger = get_logger()
13+
14+
C_DEFINITIONS = r"""
15+
typedef struct bdl_toc_entry {
16+
uint64 offset;
17+
uint64 size;
18+
} bdl_toc_entry_t;
19+
20+
typedef struct bdl_header {
21+
char magic[4];
22+
uint16 major;
23+
uint16 minor;
24+
uint32 toc_offset;
25+
char unknown[4];
26+
uint32 toc_entries;
27+
uint32 unknowns_2[3];
28+
char release[256];
29+
char brand[256];
30+
char device_id[256];
31+
char unknown_3[9];
32+
char version[256];
33+
char revision[256];
34+
} bdl_header_t;
35+
"""
36+
37+
38+
def is_valid_header(header: Instance) -> bool:
39+
if header.toc_offset == 0 or header.toc_entries == 0:
40+
return False
41+
try:
42+
snull(header.release).decode("utf-8")
43+
snull(header.brand).decode("utf-8")
44+
snull(header.device_id).decode("utf-8")
45+
snull(header.version).decode("utf-8")
46+
snull(header.revision).decode("utf-8")
47+
except UnicodeDecodeError:
48+
return False
49+
return True
50+
51+
52+
class HPBDLExtractor(Extractor):
53+
def __init__(self):
54+
self._struct_parser = StructParser(C_DEFINITIONS)
55+
56+
def extract(self, inpath: Path, outdir: Path):
57+
entries = []
58+
with File.from_path(inpath) as file:
59+
header = self._struct_parser.parse("bdl_header_t", file, Endian.LITTLE)
60+
file.seek(header.toc_offset, io.SEEK_SET)
61+
for i in range(header.toc_entries):
62+
entry = self._struct_parser.parse(
63+
"bdl_toc_entry_t", file, Endian.LITTLE
64+
)
65+
entries.append(
66+
(
67+
outdir.joinpath(outdir.joinpath(Path(f"ipkg{i:03}"))),
68+
Chunk(
69+
start_offset=entry.offset,
70+
end_offset=entry.offset + entry.size,
71+
),
72+
)
73+
)
74+
75+
for carve_path, chunk in entries:
76+
carve_chunk_to_file(
77+
file=file,
78+
chunk=chunk,
79+
carve_path=carve_path,
80+
)
81+
82+
83+
class HPBDLHandler(StructHandler):
84+
NAME = "bdl"
85+
86+
PATTERNS = [HexString("69 62 64 6C 01 00 01 00")]
87+
88+
C_DEFINITIONS = C_DEFINITIONS
89+
HEADER_STRUCT = "bdl_header_t"
90+
EXTRACTOR = HPBDLExtractor()
91+
92+
def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
93+
header = self.parse_header(file, endian=Endian.LITTLE)
94+
95+
if not is_valid_header(header):
96+
raise InvalidInputFormat("Invalid BDL header.")
97+
98+
file.seek(start_offset + header.toc_offset, io.SEEK_SET)
99+
end_offset = -1
100+
for _ in range(header.toc_entries):
101+
entry = self._struct_parser.parse("bdl_toc_entry_t", file, Endian.LITTLE)
102+
end_offset = max(end_offset, start_offset + entry.offset + entry.size)
103+
104+
return ValidChunk(start_offset=start_offset, end_offset=end_offset)

0 commit comments

Comments
 (0)