From fc4c150e28b1dec2a8b14eeae0b8286b59912351 Mon Sep 17 00:00:00 2001 From: Arnaud Viala Date: Tue, 10 Sep 2024 17:45:17 -0400 Subject: [PATCH] parse the 'hash' field after 'key' in IDX:branch block This PR is adding the ability to read the `hash` after the `key`. Nothing is being done with that `hash` for now. The immediate goal is to have a functional `extract_files` operation. Quoting the kernel's `ubifs-media.h` header file: In an authenticated UBIFS we have the hash of the referenced node after @key. This can't be added to the struct type definition because @key is a dynamically sized element already. The size of key+hash is computed dynamically by dividing the remaining length of the node by the number of children. A possible improvement could be to grab the actual hash algorithm (stored somewhere?) and verify that the hash length is the valid one. A future improvement could be to have an `--check-authentication` optional argument to the UBIFS operation what would verify that the UBIFS image has not been tampered with. --- ubireader/ubifs/nodes.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ubireader/ubifs/nodes.py b/ubireader/ubifs/nodes.py index fd2bcb7..c24e9fa 100755 --- a/ubireader/ubifs/nodes.py +++ b/ubireader/ubifs/nodes.py @@ -160,7 +160,9 @@ def __init__(self, buf): setattr(self, key, fields[key]) idxs = UBIFS_IDX_NODE_SZ - brs = UBIFS_BRANCH_SZ + # Dynamic detection of `brs` (branch size), whether a hash is present after + # the key in authenticated UBIFS. Not checking the len of the hash. + brs = int((len(buf) - UBIFS_IDX_NODE_SZ) / self.child_cnt) setattr(self, 'branches', [branch(buf[idxs+(brs*i):idxs+(brs*i)+brs]) for i in range(0, self.child_cnt)]) setattr(self, 'errors', []) @@ -183,13 +185,16 @@ class branch(object): Bin:buf -- Raw data to extract header information from. """ def __init__(self, buf): - fields = dict(list(zip(UBIFS_BRANCH_FIELDS, struct.unpack(UBIFS_BRANCH_FORMAT, buf)))) + fields = dict(list(zip(UBIFS_BRANCH_FIELDS, struct.unpack(UBIFS_BRANCH_FORMAT, buf[0:UBIFS_BRANCH_SZ])))) for key in fields: if key == 'key': setattr(self, key, parse_key(fields[key])) else: setattr(self, key, fields[key]) + if len(buf) > (UBIFS_BRANCH_SZ): + setattr(self, 'hash', buf[UBIFS_BRANCH_SZ:]) + setattr(self, 'errors', []) def __repr__(self):