Created
March 14, 2019 00:53
-
-
Save x0rloser/a88b964d8c0195d879cf7ed7f77fee27 to your computer and use it in GitHub Desktop.
python3 code to extract the Dell firmware update for SKHynix SSD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# python3 code to extract the Dell firmware update for SKHynix SSD. | |
# | |
# hacked together over morning coffee by xorloser 14th march 2019 | |
# | |
# STEPS TO USE THIS | |
# This is where to download the exe packed file. | |
# https://www.dell.com/support/home/il/he/bsd/drivers/driversdetails?driverid=6p63y&lwp=rt | |
# https://downloads.dell.com/FOLDER04062662M/6/20005A00%20A03_ZPE.exe | |
# Run the exe packed file "20005A00%20A03_ZPE.exe" to get it to self extract somewhere. | |
# One of the extracted files will be "SKhynix_NVMe_Toolbox_Dell_Win_v24_20005A00.exe". | |
# Open this in 7zip and extract to ".rsrc/FIRMWARE/156". | |
# This is the main firmware file collection. | |
# I renamed this to "firmware.bin". | |
# Run this script in python3 to extract the various parts of the firmware. | |
# INFO ABOUT VARIOUS PARTS OF THE FIRMWARE | |
# The main file appears to be a collection of 4 firmwares, | |
# but has space in its headers to handle at least 8 firmwares. | |
# Each firmware then contains multiple layers of entries within entries, | |
# like a russian doll. Each extracted firmware is treated as the outer most | |
# layer of entries. | |
# Each entry that has sub entries starts with a list of entry-info for its sub entries. | |
# The first entry-info appears to always be "DSSLREVF". | |
# So an easy way to tell if an entry contains sub entries is by looking for that. | |
import struct | |
import os | |
def makedir(dirname): | |
try: | |
os.mkdir(dirname) | |
except FileExistsError: | |
pass | |
def extract_entry_info(data): | |
# 0 8 name | |
# 8 4 size | |
# C 4 offset_or_size (what is this????) | |
# 10 4 offset | |
# 14 4 zero | |
# 18 4 size2 | |
# 1C 4 crc | |
entry = struct.unpack("<8sIIIIII", data[:0x20]) | |
# if entry[1] != entry[5]: | |
# print("sizes in hdr for %s do not match: 0x%X vs 0x%X" % (entry[0], entry[1], entry[5])) | |
# if entry[2] != entry[3]: | |
# print("offsets in hdr for %s do not match: 0x%X vs 0x%X" % (entry[0], entry[2], entry[3])) | |
return entry | |
# this entry is the first on most firmware blocks, except for the outermost block | |
# probably because the outermost bvlock is actually a collection of different firmware files | |
# for the various models of the device that the firmware is for. | |
def extract_revf_info(data): | |
# 0 8 name | |
# 8 4 crc | |
# C 8 some number string (ascii) | |
# 14 4 size | |
# 18 8 magic values "EC D7 14 76 54 20 11 00" | |
entry = struct.unpack("<8sI8sIII", data[:0x20]) | |
return entry | |
def extract_entries(outputDirpath, data, level): | |
makedir(outputDirpath) | |
done_extracting = False | |
first_offset = -1 | |
for off in range(0, len(data), 0x20): | |
if done_extracting or (first_offset > 0 and off >= first_offset): | |
print("done extracting at %X" % off) | |
break | |
entry = extract_entry_info(data[off:off+0x20]) | |
if level>=2 and off==0 and entry[0] != b"DSSLREVF": | |
print("no DSSLREVF found, so must be leaf entry") # %s" % entry[0].decode("utf-8")) | |
break | |
# skip entries with 0 offset and size | |
size=entry[1] | |
offset=entry[3] | |
if size==0 and offset==0: | |
print("skipping zero entry") | |
continue | |
entry_name = entry[0].decode("utf-8") | |
# handle "DSSLREVF" entries specially | |
if entry_name[7]=="F": | |
revf_info = extract_revf_info | |
print("skipping DSSLREVF entry") | |
continue | |
# handle all other types of entries | |
if first_offset < 0: | |
first_offset = offset | |
print("setting first offset to %X" % first_offset) | |
if size + offset >= len(data): | |
done_extracting = True | |
print(entry) | |
entry_name = entry[0].decode("utf-8") | |
entry_dirname = os.path.join(outputDirpath, entry_name) | |
entry_filename = os.path.join(entry_dirname, entry_name) | |
makedir(entry_dirname) | |
entry_data = data[offset:offset+size] | |
with open(entry_filename, "wb") as entry_file: | |
entry_file.write(entry_data) | |
# extract all subentries | |
extract_entries(entry_dirname, entry_data, level+1) | |
# main entry point for this script | |
fw_filename = "firmware.bin" | |
extract_dir = "extracted" | |
fw_file_data = None | |
with open(fw_filename, "rb") as fw_file: | |
fw_file_data = fw_file.read() | |
extract_entries(extract_dir, fw_file_data, 0) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment