155 lines
5.5 KiB
Python
155 lines
5.5 KiB
Python
import sys
|
|
import os
|
|
import re
|
|
import shutil
|
|
|
|
import utils
|
|
|
|
DEBUG = False
|
|
HEADER_SIZE = 16 * utils.KB # Header size is always 16KB
|
|
|
|
# Vars
|
|
headerScript = ""
|
|
headerScriptFound = False
|
|
counter = {}
|
|
env = {} # Environment variables, set by setenv command
|
|
|
|
# Parse args
|
|
if len(sys.argv) == 1:
|
|
print ("Usage: unpack.py <firmware> <output folder [default: ./unpacked/]>")
|
|
quit()
|
|
|
|
inputFile = sys.argv[1]
|
|
if not os.path.exists(inputFile):
|
|
print ("No such file: {}".format(inputFile))
|
|
quit()
|
|
|
|
if len(sys.argv) == 3:
|
|
outputDirectory = sys.argv[2]
|
|
else:
|
|
outputDirectory = 'unpacked'
|
|
|
|
# Create output directory
|
|
utils.createDirectory(outputDirectory)
|
|
|
|
# Find header script
|
|
# Header size is 16KB
|
|
# Non used part is filled by 0xFF
|
|
print ("[i] Analizing header ...")
|
|
header = utils.loadPart(inputFile, 0, HEADER_SIZE)
|
|
utils.copyPart(inputFile, os.path.join(outputDirectory, "~header"), 0, HEADER_SIZE)
|
|
|
|
offset = header.find('\xff'.encode(encoding='iso-8859-1'))
|
|
if offset != -1:
|
|
headerScript = header[:offset].decode()
|
|
headerScriptFound = True
|
|
|
|
if not headerScriptFound:
|
|
print ("[!] Could not find header script!")
|
|
quit()
|
|
|
|
if DEBUG:
|
|
print (headerScript)
|
|
|
|
# Save the script
|
|
print ("[i] Saving header script to " + os.path.join(outputDirectory, "~header_script") + " ...")
|
|
with open(os.path.join(outputDirectory, "~header_script"), "w") as f:
|
|
f.write(headerScript)
|
|
|
|
# Parse script
|
|
print ("[i] Parsing script ...")
|
|
# Supporting filepartload, mmc, store_secure_info, store_nuttx_config
|
|
for line in headerScript.splitlines():
|
|
|
|
if DEBUG:
|
|
print (line)
|
|
|
|
if re.match("^setenv", line):
|
|
params = utils.processSetEnv(line)
|
|
key = params["key"]
|
|
if not "value" in params:
|
|
del env[key]
|
|
else:
|
|
value = params["value"]
|
|
env[key] = value
|
|
print ("[i] Parsing setenv {} -> {}".format(key, value))
|
|
|
|
if re.match("^filepartload", line):
|
|
line = utils.applyEnv(line, env)
|
|
params = utils.processFilePartLoad(line)
|
|
offset = params["offset"]
|
|
size = params["size"]
|
|
|
|
if re.match("^store_secure_info", line):
|
|
line = utils.applyEnv(line, env)
|
|
params = utils.processStoreSecureInfo(line)
|
|
outputFile = os.path.join(outputDirectory, params["partition_name"])
|
|
utils.copyPart(inputFile, outputFile, int(offset, 16), int(size, 16))
|
|
|
|
if re.match("^store_nuttx_config", line):
|
|
line = utils.applyEnv(line, env)
|
|
params = utils.processStoreNuttxConfig(line)
|
|
outputFile = os.path.join(outputDirectory, params["partition_name"])
|
|
utils.copyPart(inputFile, outputFile, int(offset, 16), int(size, 16))
|
|
|
|
if re.match("^mmc", line):
|
|
line = utils.applyEnv(line, env)
|
|
params = utils.processMmc(line)
|
|
|
|
if params:
|
|
|
|
# if params["action"] == "create":
|
|
# nothing here
|
|
|
|
if params["action"] == "write.boot":
|
|
outputFile = utils.generateFileName(outputDirectory, params, ".img")
|
|
utils.copyPart(inputFile, outputFile, int(offset, 16), int(size, 16))
|
|
print ("[i] Partition: {}\tOffset: {}\tSize {} ({}) -> {}".format(params["partition_name"], offset, size, utils.sizeStr(int(size, 16)), outputFile))
|
|
|
|
if params["action"] == "write.p":
|
|
outputFile = os.path.join(outputDirectory, params["partition_name"] + ".img")
|
|
utils.copyPart(inputFile, outputFile, int(offset, 16), int(size, 16))
|
|
print ("[i] Partition: {}\tOffset: {}\tSize {} ({}) -> {}".format(params["partition_name"], offset, size, utils.sizeStr(int(size, 16)), outputFile))
|
|
|
|
if params["action"] == "write.p.continue":
|
|
outputFile = os.path.join(outputDirectory, params["partition_name"] + ".img")
|
|
utils.copyPart(inputFile, outputFile, int(offset, 16), int(size, 16), append = True)
|
|
print ("[i] Partition: {}\tOffset: {}\tSize {} ({}) append to {}".format(params["partition_name"], offset, size, utils.sizeStr(int(size, 16)), outputFile))
|
|
|
|
if params["action"] == "unlzo":
|
|
outputLzoFile = utils.generateFileName(outputDirectory, params, ".lzo")
|
|
outputImgFile = utils.generateFileName(outputDirectory, params, ".img")
|
|
# save .lzo
|
|
print ("[i] Partition: {}\tOffset: {}\tSize {} ({}) -> {}".format(params["partition_name"], offset, size, utils.sizeStr(int(size, 16)), outputLzoFile))
|
|
utils.copyPart(inputFile, outputLzoFile, int(offset, 16), int(size, 16))
|
|
# unpack .lzo -> .img
|
|
print ("[i] Unpacking LZO (Please be patient) {} -> {}".format(outputLzoFile, outputImgFile))
|
|
utils.unlzo(outputLzoFile, outputImgFile)
|
|
# delete .lzo
|
|
os.remove(outputLzoFile)
|
|
|
|
if params["action"] == "unlzo.continue":
|
|
if not params["partition_name"] in counter:
|
|
counter[params["partition_name"]] = 0
|
|
counter[params["partition_name"]] += 1
|
|
|
|
outputImgFile = os.path.join(outputDirectory, params["partition_name"] + ".img")
|
|
outputChunkLzoFile = os.path.join(outputDirectory, params["partition_name"] + str(counter[params["partition_name"]]) + ".lzo")
|
|
outputChunkImgFile = os.path.join(outputDirectory, params["partition_name"] + str(counter[params["partition_name"]]) + ".img")
|
|
# save .lzo
|
|
print ("[i] Partition: {}\tOffset: {}\tSize {} ({}) -> {}".format(params["partition_name"], offset, size, utils.sizeStr(int(size, 16)), outputChunkLzoFile))
|
|
utils.copyPart(inputFile, outputChunkLzoFile, int(offset, 16), int(size, 16))
|
|
# unpack chunk .lzo -> .img
|
|
print ("[i] Unpacking LZO (Please be patient) {} -> {}".format(outputChunkLzoFile, outputChunkImgFile))
|
|
utils.unlzo(outputChunkLzoFile, outputChunkImgFile)
|
|
# append the chunk to main .img
|
|
print ("[i] {} append to {}".format(outputChunkImgFile, outputImgFile))
|
|
utils.appendFile(outputChunkImgFile, outputImgFile)
|
|
# delete chunk
|
|
os.remove(outputChunkLzoFile)
|
|
os.remove(outputChunkImgFile)
|
|
|
|
|
|
|
|
|