mirror of
https://github.com/riptidewave93/UNVR-NAS.git
synced 2025-03-09 15:40:13 +00:00
feat: add hdds/temps/version to display
* Write a mock ustorage tool to parse disk info for ulcmd * Pass system temp info from unvr-fan-daemon to mock-ubnt-api via tmp file * add mock ubnt-tools to make ulcmd happy and report our SN * code cleanup and documentation update
This commit is contained in:
parent
a4f7f862c2
commit
9c08f287fc
7 changed files with 268 additions and 58 deletions
|
@ -1,14 +1,12 @@
|
|||
#!/usr/bin/python3
|
||||
import argparse
|
||||
import binascii
|
||||
import mmap
|
||||
import sys
|
||||
import zlib
|
||||
|
||||
from io import BytesIO
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def main(fwfile: str, savedir: str):
|
||||
# Does the file exist?
|
||||
if not Path(fwfile).is_file():
|
||||
|
@ -21,65 +19,81 @@ def main(fwfile: str, savedir: str):
|
|||
Path(savedir).mkdir(parents=True)
|
||||
|
||||
# Start parsing the OTA file
|
||||
with open(fwfile, 'rb+') as f:
|
||||
with open(fwfile, "rb+") as f:
|
||||
# Read from disk, not memory
|
||||
mm = mmap.mmap(f.fileno(), 0)
|
||||
|
||||
ubntheader = mm.read(0x104) # Read header in
|
||||
ubntheadercrc = int.from_bytes(mm.read(0x4)) # Read header CRC, convert to int
|
||||
ubntheader = mm.read(0x104) # Read header in
|
||||
ubntheadercrc = int.from_bytes(mm.read(0x4)) # Read header CRC, convert to int
|
||||
|
||||
# Do we have the UBNT header?
|
||||
if ubntheader[0:4].decode("utf-8") != "UBNT":
|
||||
print(f"Error: {fwfile} is missing the UBNT header! Is this the right firmware file?")
|
||||
print(
|
||||
f"Error: {fwfile} is missing the UBNT header! Is this the right firmware file?"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Is the header CRC valid?
|
||||
if zlib.crc32(ubntheader) != ubntheadercrc:
|
||||
print(f"Error: {fwfile} has in incorrect CRC for it's header! Please re-download the file!")
|
||||
print(
|
||||
f"Error: {fwfile} has in incorrect CRC for it's header! Please re-download the file!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# If we are here, that's a great sign :)
|
||||
# If we are here, that's a great sign :)
|
||||
ubnt_fw_ver_string = ubntheader[4:].decode("utf-8")
|
||||
print(f"Loaded in firmware file {ubnt_fw_ver_string}")
|
||||
|
||||
# Start parsing out all of the files in the OTA
|
||||
#files = []
|
||||
fcount = 1
|
||||
while True:
|
||||
file_header_offset = mm.find(b'\x46\x49\x4C\x45') # FILE in hex bye string
|
||||
file_header_offset = mm.find(b"\x46\x49\x4C\x45") # FILE in hex bye string
|
||||
# Are we done scanning the file?
|
||||
if file_header_offset == -1:
|
||||
break
|
||||
|
||||
# We found one, seek to it
|
||||
mm.seek(file_header_offset)
|
||||
file_header = mm.read(0x38) # Entire header
|
||||
file_position = file_header[39] # Increments with files read, can be used to validate this is a file for us
|
||||
file_location = file_header_offset+0x38 # header location - header = data :)
|
||||
file_header = mm.read(0x38) # Entire header
|
||||
file_position = file_header[
|
||||
39
|
||||
] # Increments with files read, can be used to validate this is a file for us
|
||||
file_location = (
|
||||
file_header_offset + 0x38
|
||||
) # header location - header = data :)
|
||||
|
||||
# Is this a VALID file?!
|
||||
if fcount == file_position:
|
||||
file_name = file_header[4:33].decode("utf-8").rstrip('\x00') # Name/type of FILE
|
||||
file_name = (
|
||||
file_header[4:33].decode("utf-8").rstrip("\x00")
|
||||
) # Name/type of FILE
|
||||
file_length = int(file_header[48:52].hex(), 16)
|
||||
print(f"{file_name} is at offset {"0x%0.2X" % file_location}, {file_length} bytes")
|
||||
print(
|
||||
f"{file_name} is at offset {"0x%0.2X" % file_location}, {file_length} bytes"
|
||||
)
|
||||
# print(int(file_header[52:56].hex(), 16)) # Maybe reserved memory or partition size? We don't use this tho
|
||||
#files.extend([(file_position, file_name, file_location, file_length)])
|
||||
fcount = fcount+1 # Increment on find!
|
||||
fcount = fcount + 1 # Increment on find!
|
||||
|
||||
fcontents = mm.read(file_length) # Read into memory
|
||||
file_footer_crc32 = mm.read(0x8)[0:4] # Read in tailing 8 bytes (crc32) footer, but we only want the first 4
|
||||
fcontents = mm.read(file_length) # Read into memory
|
||||
file_footer_crc32 = mm.read(0x8)[
|
||||
0:4
|
||||
] # Read in tailing 8 bytes (crc32) footer, but we only want the first 4
|
||||
|
||||
# Does our calculated crc32 match the unifi footer in the img?
|
||||
if hex(zlib.crc32(file_header + fcontents)).lstrip('0x') != file_footer_crc32.hex().lstrip('0'):
|
||||
print(f"Error: Contents of {file_name} does not match the Unifi CRC! Please re-download the file!")
|
||||
sys.exit(1)
|
||||
if hex(zlib.crc32(file_header + fcontents)).lstrip(
|
||||
"0x"
|
||||
) != file_footer_crc32.hex().lstrip("0"):
|
||||
print(
|
||||
f"Error: Contents of {file_name} does not match the Unifi CRC! Please re-download the file!"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Write file out since our mmap position is now AT the data, and we parsed the length
|
||||
with open(f"{savedir}/{file_name}.bin", "wb") as wf:
|
||||
wf.write(fcontents) # Write out file
|
||||
wf.write(fcontents) # Write out file
|
||||
print(f"{file_name} has been written to {savedir}/{file_name}.bin")
|
||||
|
||||
del fcontents # Cleanup memory for next run
|
||||
del fcontents # Cleanup memory for next run
|
||||
|
||||
print(f"Finished extracting the contents of {fwfile}, enjoy!")
|
||||
|
||||
|
@ -87,6 +101,8 @@ def main(fwfile: str, savedir: str):
|
|||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser("ubnt-fw-parse for Dream Machines")
|
||||
parser.add_argument("file", help="The Ubiquiti firmware file to parse", type=str)
|
||||
parser.add_argument("savedir", help="The directory to save the parsed files to", type=str)
|
||||
parser.add_argument(
|
||||
"savedir", help="The directory to save the parsed files to", type=str
|
||||
)
|
||||
args = parser.parse_args()
|
||||
main(args.file, args.savedir)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue