[python] Add module "ubi-reader"

Sources: https://github.com/onekey-sec/ubi_reader
This commit is contained in:
remittor
2023-10-16 10:34:50 +03:00
parent b284449f32
commit ba2dca32b5
30 changed files with 4680 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1,195 @@
#!/usr/bin/env python
#############################################################
# ubi_reader/scripts/ubireader_display_blocks
# (c) 2019 Jason Pruitt (jrspruitt@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
#############################################################
# Search by block parameters and display information about
# matching blocks.
#############################################################
import os
import sys
import argparse
from ubireader.ubi import ubi_base
from ubireader.ubi_io import ubi_file
from ubireader import settings
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
from ubireader.ubifs.defines import UBIFS_NODE_MAGIC
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
def main():
description = 'Search for specified blocks and display information.'
usage = """
ubireader_display_blocks "{'block.attr': value,...}" path/to/image
Search for blocks by given parameters and display information about them.
This is block only, no volume or image information is created, which can
be used to debug file and image extraction.
Example:
"{'peb_num':[0, 1] + range(100, 102), 'ec_hdr.ec': 1, 'is_valid': True}"
This matches block.peb_num 0, 1, 100, 101, and 102
with a block.ec_hdr.ec (erase count) of 1, that are valid PEB blocks.
For a full list of parameters check ubireader.ubi.block.description.
"""
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size. (UBI Only)')
parser.add_argument('-e', '--leb-size', type=int, dest='block_size',
help='Specify LEB size. (UBIFS Only)')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI/UBIFS data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI/UBIFS data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('block_search_params',
help="""
Double quoted Dict of ubi.block.description attributes, which is run through eval().
Ex. "{\'peb_num\':[0, 1], \'ec_hdr.ec\': 1, \'is_valid\': True}"
""")
parser.add_argument('filepath', help='File with blocks of interest.')
if len(sys.argv) == 1:
parser.print_help()
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
else:
parser.error('File path must be provided.')
sys.exit(1)
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if not filetype:
parser.error('Could not determine file type.')
if args.block_size:
block_size = args.block_size
else:
if filetype == UBI_EC_HDR_MAGIC:
block_size = guess_peb_size(path)
elif filetype == UBIFS_NODE_MAGIC:
block_size = guess_leb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
if args.block_search_params:
try:
search_params = eval(args.block_search_params)
if not isinstance(search_params, dict):
parser.error('Search Param Error: Params must be a Dict of block PEB object items:value pairs.')
except NameError as e:
parser.error('Search Param Error: Dict key block attrs must be single quoted.')
except Exception as e:
parser.error('Search Param Error: %s' % e)
else:
parser.error('No search parameters given, -b arg is required.')
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
ubi_obj = ubi_base(ufile_obj)
blocks = []
for block in ubi_obj.blocks:
match = True
for key in search_params:
b = ubi_obj.blocks[block]
for attr in key.split('.'):
if hasattr(b, attr):
b = getattr(b, attr)
if isinstance(search_params[key], list):
if isinstance(b, list):
for value in b:
if value in search_params[key]:
break
else:
match = False
elif b not in search_params[key]:
match = False
elif b != search_params[key]:
match = False
break
if match:
blocks.append(ubi_obj.blocks[block])
ufile_obj.close()
print('\nBlock matches: %s' % len(blocks))
for block in blocks:
print(block.display())
if __name__=='__main__':
main()
@@ -0,0 +1,192 @@
#!/usr/bin/env python
#############################################################
# ubi_reader
# (c) 2013 Jason Pruitt (jrspruitt@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
import os
import sys
import time
import argparse
from ubireader import settings
from ubireader.ubi import ubi
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
from ubireader.ubifs import ubifs
from ubireader.ubifs.defines import UBIFS_NODE_MAGIC
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
from ubireader.ubi_io import ubi_file, leb_virtual_file
def main():
start = time.time()
description = 'Show information about UBI or UBIFS image.'
usage = 'ubireader_display_info [options] filepath'
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-u', '--ubifs-info', action='store_true', dest='ubifs_info',
help='Get UBIFS information from inside a UBI image. (default: false)')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size. (UBI Only)')
parser.add_argument('-e', '--leb-size', type=int, dest='block_size',
help='Specify LEB size. (UBIFS Only)')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI/UBIFS data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI/UBIFS data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('filepath', help='File to extract contents of.')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if not filetype:
parser.error('Could not determine file type.')
ubifs_info = args.ubifs_info
if args.block_size:
block_size = args.block_size
else:
if filetype == UBI_EC_HDR_MAGIC:
block_size = guess_peb_size(path)
elif filetype == UBIFS_NODE_MAGIC:
block_size = guess_leb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
# Create file object.
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
if filetype == UBI_EC_HDR_MAGIC:
# Create UBI object
ubi_obj = ubi(ufile_obj)
# Display UBI info if not UBIFS request.
if not ubifs_info:
print(ubi_obj.display())
# Loop through found images in file.
for image in ubi_obj.images:
# Display image information if not UBIFS request.
if not ubifs_info:
print('%s' % image.display('\t'))
# Loop through volumes in each image.
for volume in image.volumes:
# Show UBI or UBIFS info.
if not ubifs_info:
# Display volume information.
print(image.volumes[volume].display('\t\t'))
else:
# Get blocks associated with this volume.
vol_blocks = image.volumes[volume].get_blocks(ubi_obj.blocks)
# Skip volume if empty.
if not len(vol_blocks):
continue
# Create LEB backed virtual file with volume blocks.
# Necessary to prevent having to load entire UBI image
# into memory.
lebv_file = leb_virtual_file(ubi_obj, vol_blocks)
# Create UBIFS object and print info.
ubifs_obj = ubifs(lebv_file)
print(ubifs_obj.display())
print(ubifs_obj.superblock_node.display('\t'))
print(ubifs_obj.master_node.display('\t'))
try:
print(ubifs_obj.master_node2.display('\t'))
except:
print('Master Node Error only one valid node.')
elif filetype == UBIFS_NODE_MAGIC:
# Create UBIFS object
ubifs_obj = ubifs(ufile_obj)
print(ubifs_obj.display())
print(ubifs_obj.superblock_node.display('\t'))
print(ubifs_obj.master_node.display('\t'))
try:
print(ubifs_obj.master_node2.display('\t'))
except:
print('Master Node Error only one valid node.')
else:
print('Something went wrong to get here.')
ufile_obj.close()
if __name__=='__main__':
main()
@@ -0,0 +1,205 @@
#!/usr/bin/env python
#############################################################
# ubi_reader
# (c) 2013 Jason Pruitt (jrspruitt@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
import os
import sys
import time
import argparse
from ubireader import settings
from ubireader.ubi import ubi
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
from ubireader.ubifs import ubifs
from ubireader.ubifs.output import extract_files
from ubireader.ubifs.defines import UBIFS_NODE_MAGIC
from ubireader.ubi_io import ubi_file, leb_virtual_file
from ubireader.debug import error, log
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
def create_output_dir(outpath):
if os.path.exists(outpath):
if os.listdir(outpath):
error(create_output_dir, 'Fatal', 'Output directory is not empty. %s' % outpath)
else:
try:
os.makedirs(outpath)
log(create_output_dir, 'Created output path: %s' % outpath)
except Exception as e:
error(create_output_dir, 'Fatal', '%s' % e)
def main():
start = time.time()
description = 'Extract contents of a UBI or UBIFS image.'
usage = 'ubireader_extract_files [options] filepath'
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-k', '--keep-permissions', action='store_true', dest='permissions',
help='Maintain file permissions, requires running as root. (default: False)')
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size. (UBI Only)')
parser.add_argument('-e', '--leb-size', type=int, dest='block_size',
help='Specify LEB size. (UBIFS Only)')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI/UBIFS data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI/UBIFS data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('-o', '--output-dir', dest='outpath',
help='Specify output directory path.')
parser.add_argument('filepath', help='File to extract contents of.')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if not filetype:
parser.error('Could not determine file type.')
if args.outpath:
outpath = args.outpath
else:
outpath = settings.output_dir
if args.block_size:
block_size = args.block_size
else:
if filetype == UBI_EC_HDR_MAGIC:
block_size = guess_peb_size(path)
elif filetype == UBIFS_NODE_MAGIC:
block_size = guess_leb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
perms = args.permissions
# Create file object.
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
if filetype == UBI_EC_HDR_MAGIC:
# Create UBI object
ubi_obj = ubi(ufile_obj)
# Loop through found images in file.
for image in ubi_obj.images:
# Create path for specific image
# In case multiple images in data
img_outpath = os.path.join(outpath, '%s' % image.image_seq)
# Loop through volumes in each image.
for volume in image.volumes:
# Get blocks associated with this volume.
vol_blocks = image.volumes[volume].get_blocks(ubi_obj.blocks)
# Create volume data output path.
vol_outpath = os.path.join(img_outpath, volume)
# Create volume output path directory.
create_output_dir(vol_outpath)
# Skip volume if empty.
if not len(vol_blocks):
continue
# Create LEB backed virtual file with volume blocks.
# Necessary to prevent having to load entire UBI image
# into memory.
lebv_file = leb_virtual_file(ubi_obj, vol_blocks)
# Extract files from UBI image.
ubifs_obj = ubifs(lebv_file)
print('Extracting files to: %s' % vol_outpath)
extract_files(ubifs_obj, vol_outpath, perms)
elif filetype == UBIFS_NODE_MAGIC:
# Create UBIFS object
ubifs_obj = ubifs(ufile_obj)
# Create directory for files.
create_output_dir(outpath)
# Extract files from UBIFS image.
print('Extracting files to: %s' % outpath)
extract_files(ubifs_obj, outpath, perms)
else:
print('Something went wrong to get here.')
ufile_obj.close()
if __name__=='__main__':
main()
@@ -0,0 +1,174 @@
#!/usr/bin/env python
#############################################################
# ubi_reader
# (c) 2013 Jason Pruitt (jrspruitt@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
import os
import sys
import time
import argparse
from ubireader import settings
from ubireader.ubi import ubi
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
from ubireader.ubi_io import ubi_file
from ubireader.debug import error, log
from ubireader.utils import guess_filetype, guess_start_offset, guess_peb_size
def create_output_dir(outpath):
if not os.path.exists(outpath):
try:
os.makedirs(outpath)
log(create_output_dir, 'Created output path: %s' % outpath)
except Exception as e:
error(create_output_dir, 'Fatal', '%s' % e)
def main():
start = time.time()
description = 'Extract UBI or UBIFS images from file containing UBI data in it.'
usage = 'ubireader_extract_images [options] filepath'
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size.')
parser.add_argument('-u', '--image-type', dest='image_type',
help='Specify image type to extract UBI or UBIFS. (default: UBIFS)')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('-o', '--output-dir', dest='outpath',
help='Specify output directory path.')
parser.add_argument('filepath', help='File to extract contents of.')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if filetype != UBI_EC_HDR_MAGIC:
parser.error('File does not look like UBI data.')
img_name = os.path.basename(path)
if args.outpath:
outpath = os.path.abspath(os.path.join(args.outpath, img_name))
else:
outpath = os.path.join(settings.output_dir, img_name)
if args.block_size:
block_size = args.block_size
else:
block_size = guess_peb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
if args.image_type:
image_type = args.image_type.upper()
else:
image_type = 'UBIFS'
# Create file object.
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
# Create UBI object
ubi_obj = ubi(ufile_obj)
# Loop through found images in file.
for image in ubi_obj.images:
if image_type == 'UBI':
# Create output path and open file.
img_outpath = os.path.join(outpath, 'img-%s.ubi' % image.image_seq)
create_output_dir(outpath)
f = open(img_outpath, 'wb')
# Loop through UBI image blocks
for block in image.get_blocks(ubi_obj.blocks):
if ubi_obj.blocks[block].is_valid:
# Write block (PEB) to file
f.write(ubi_obj.file.read_block(ubi_obj.blocks[block]))
elif image_type == 'UBIFS':
# Loop through image volumes
for volume in image.volumes:
# Create output path and open file.
vol_outpath = os.path.join(outpath, 'img-%s_vol-%s.ubifs' % (image.image_seq, volume))
create_output_dir(outpath)
f = open(vol_outpath, 'wb')
# Loop through and write volume block data (LEB) to file.
for block in image.volumes[volume].reader(ubi_obj):
f.write(block)
ufile_obj.close()
if __name__=='__main__':
main()
@@ -0,0 +1,180 @@
#!/usr/bin/env python
#############################################################
# ubi_reader
# (C) Collin Mulliner based on Jason Pruitt's ubireader_extract_images
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
import os
import sys
import time
import argparse
from ubireader import settings
from ubireader.ubi import ubi
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC
from ubireader.ubifs import ubifs
from ubireader.ubifs.list import list_files, copy_file
from ubireader.ubifs.defines import UBIFS_NODE_MAGIC
from ubireader.ubi_io import ubi_file, leb_virtual_file
from ubireader.debug import error, log
from ubireader.utils import guess_filetype, guess_start_offset, guess_leb_size, guess_peb_size
def main():
start = time.time()
description = 'List and Extract files of a UBI or UBIFS image.'
usage = 'ubireader_list_files [options] filepath'
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size. (UBI Only)')
parser.add_argument('-e', '--leb-size', type=int, dest='block_size',
help='Specify LEB size. (UBIFS Only)')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI/UBIFS data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI/UBIFS data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('-P', '--path', dest='listpath',
help='Path to list.')
parser.add_argument('-C', '--copy', dest='copyfile',
help='File to Copy.')
parser.add_argument('-D', '--copy-dest', dest='copyfiledest',
help='Copy Destination.')
parser.add_argument('filepath', help='UBI/UBIFS image file.')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if not filetype:
parser.error('Could not determine file type.')
if args.block_size:
block_size = args.block_size
else:
if filetype == UBI_EC_HDR_MAGIC:
block_size = guess_peb_size(path)
elif filetype == UBIFS_NODE_MAGIC:
block_size = guess_leb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
# Create file object.
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
if filetype == UBI_EC_HDR_MAGIC:
# Create UBI object
ubi_obj = ubi(ufile_obj)
# Loop through found images in file.
for image in ubi_obj.images:
# Loop through volumes in each image.
for volume in image.volumes:
# Get blocks associated with this volume.
vol_blocks = image.volumes[volume].get_blocks(ubi_obj.blocks)
# Skip volume if empty.
if not len(vol_blocks):
continue
# Create LEB backed virtual file with volume blocks.
# Necessary to prevent having to load entire UBI image
# into memory.
lebv_file = leb_virtual_file(ubi_obj, vol_blocks)
# Create UBIFS object.
ubifs_obj = ubifs(lebv_file)
if args.listpath:
list_files(ubifs_obj, args.listpath)
if args.copyfile and args.copyfiledest:
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)
elif filetype == UBIFS_NODE_MAGIC:
# Create UBIFS object
ubifs_obj = ubifs(ufile_obj)
if args.listpath:
list_files(ubifs_obj, args.listpath)
if args.copyfile and args.copyfiledest:
copy_file(ubifs_obj, args.copyfile, args.copyfiledest)
else:
print('Something went wrong to get here.')
ufile_obj.close()
if __name__=='__main__':
main()
@@ -0,0 +1,350 @@
#!/usr/bin/env python
#############################################################
# ubi_reader
# (c) 2013 Jason Pruitt (jrspruitt@gmail.com)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################
import os
import sys
import time
import argparse
if (sys.version_info > (3, 0)):
import configparser
else:
import ConfigParser as configparser
from ubireader import settings
from ubireader.ubi import ubi
from ubireader.ubi.defines import UBI_EC_HDR_MAGIC, PRINT_VOL_TYPE_LIST, UBI_VTBL_AUTORESIZE_FLG
from ubireader.ubifs import ubifs
from ubireader.ubifs.defines import PRINT_UBIFS_KEY_HASH, PRINT_UBIFS_COMPR
from ubireader.ubi_io import ubi_file, leb_virtual_file
from ubireader.debug import error, log
from ubireader.utils import guess_filetype, guess_start_offset, guess_peb_size
def create_output_dir(outpath):
if os.path.exists(outpath):
if os.listdir(outpath):
error(create_output_dir, 'Fatal', 'Output directory is not empty. %s' % outpath)
else:
try:
os.makedirs(outpath)
log(create_output_dir, 'Created output path: %s' % outpath)
except Exception as e:
error(create_output_dir, 'Fatal', '%s' % e)
def get_ubi_params(ubi_obj):
"""Get ubi_obj utils params
Arguments:
Obj:ubi -- UBI object.
Returns:
Dict -- Dict keyed to volume with Dict of args and flags.
"""
ubi_flags = {'min_io_size':'-m',
'max_bud_bytes':'-j',
'leb_size':'-e',
'default_compr':'-x',
'sub_page_size':'-s',
'fanout':'-f',
'key_hash':'-k',
'orph_lebs':'-p',
'log_lebs':'-l',
'max_leb_cnt': '-c',
'peb_size':'-p',
'sub_page_size':'-s',
'vid_hdr_offset':'-O',
'version':'-x',
'image_seq':'-Q',
'alignment':'-a',
'vol_id':'-n',
'name':'-N'}
ubi_params = {}
ubi_args = {}
ini_params = {}
for image in ubi_obj.images:
img_seq = image.image_seq
ubi_params[img_seq] = {}
ubi_args[img_seq] = {}
ini_params[img_seq] = {}
for volume in image.volumes:
ubi_args[img_seq][volume] = {}
ini_params[img_seq][volume] = {}
# Get ubinize.ini settings
ini_params[img_seq][volume]['vol_type'] = PRINT_VOL_TYPE_LIST[image.volumes[volume].vol_rec.vol_type]
if image.volumes[volume].vol_rec.flags == UBI_VTBL_AUTORESIZE_FLG:
ini_params[img_seq][volume]['vol_flags'] = 'autoresize'
else:
ini_params[img_seq][volume]['vol_flags'] = image.volumes[volume].vol_rec.flags
ini_params[img_seq][volume]['vol_id'] = image.volumes[volume].vol_id
ini_params[img_seq][volume]['vol_name'] = image.volumes[volume].name.rstrip(b'\x00').decode('utf-8')
ini_params[img_seq][volume]['vol_alignment'] = image.volumes[volume].vol_rec.alignment
ini_params[img_seq][volume]['vol_size'] = image.volumes[volume].vol_rec.reserved_pebs * ubi_obj.leb_size
# Create file object backed by UBI blocks.
lebv_file = leb_virtual_file(ubi_obj, image.volumes[volume].get_blocks(ubi_obj.blocks))
# Create UBIFS object
ubifs_obj = ubifs(lebv_file)
for key, value in ubifs_obj.superblock_node:
if key == 'key_hash':
value = PRINT_UBIFS_KEY_HASH[value]
elif key == 'default_compr':
value = PRINT_UBIFS_COMPR[value]
if key in ubi_flags:
ubi_args[img_seq][volume][key] = value
for key, value in image.volumes[volume].vol_rec:
if key == 'name':
value = value.rstrip(b'\x00').decode('utf-8')
if key in ubi_flags:
ubi_args[img_seq][volume][key] = value
ubi_args[img_seq][volume]['version'] = image.version
ubi_args[img_seq][volume]['vid_hdr_offset'] = image.vid_hdr_offset
ubi_args[img_seq][volume]['sub_page_size'] = ubi_args[img_seq][volume]['vid_hdr_offset']
ubi_args[img_seq][volume]['sub_page_size'] = ubi_args[img_seq][volume]['vid_hdr_offset']
ubi_args[img_seq][volume]['image_seq'] = image.image_seq
ubi_args[img_seq][volume]['peb_size'] = ubi_obj.peb_size
ubi_args[img_seq][volume]['vol_id'] = image.volumes[volume].vol_id
ubi_params[img_seq][volume] = {'flags':ubi_flags, 'args':ubi_args[img_seq][volume], 'ini':ini_params[img_seq][volume]}
return ubi_params
def print_ubi_params(ubi_obj):
ubi_params = get_ubi_params(ubi_obj)
for img_params in ubi_params:
for volume in ubi_params[img_params]:
ubi_flags = ubi_params[img_params][volume]['flags']
ubi_args = ubi_params[img_params][volume]['args']
ini_params = ubi_params[img_params][volume]['ini']
sorted_keys = sorted(ubi_params[img_params][volume]['args'])
print('\nVolume %s' % volume)
for key in sorted_keys:
if len(key)< 8:
name = '%s\t' % key
else:
name = key
print('\t%s\t%s %s' % (name, ubi_flags[key], ubi_args[key]))
print('\n\t#ubinize.ini#')
print('\t[%s]' % ini_params['vol_name'])
for key in ini_params:
if key != 'name':
print('\t%s=%s' % (key, ini_params[key]))
def make_files(ubi, outpath):
ubi_params = get_ubi_params(ubi)
for img_params in ubi_params:
config = configparser.ConfigParser()
img_outpath = os.path.join(outpath, 'img-%s' % img_params)
if not os.path.exists(img_outpath):
os.mkdir(img_outpath)
ini_path = os.path.join(img_outpath, 'img-%s.ini' % img_params)
ubi_file = os.path.join('img-%s.ubi' % img_params)
script_path = os.path.join(img_outpath, 'create_ubi_img-%s.sh' % img_params)
ubifs_files =[]
buf = '#!/bin/sh\n'
print('Writing to: %s' % script_path)
with open(script_path, 'w') as fscr:
with open(ini_path, 'w') as fini:
print('Writing to: %s' % ini_path)
vol_idx = 0
for volume in ubi_params[img_params]:
ubifs_files.append(os.path.join('img-%s_%s.ubifs' % (img_params, vol_idx)))
ini_params = ubi_params[img_params][volume]['ini']
ini_file = 'img-%s.ini' % img_params
config.add_section(volume)
config.set(volume, 'mode', 'ubi')
config.set(volume, 'image', ubifs_files[vol_idx])
for i in ini_params:
config.set(volume, i, str(ini_params[i]))
ubi_flags = ubi_params[img_params][volume]['flags']
ubi_args = ubi_params[img_params][volume]['args']
mkfs_flags = ['min_io_size',
'leb_size',
'max_leb_cnt',
'default_compr',
'fanout',
'key_hash',
'orph_lebs',
'log_lebs']
argstr = ''
for flag in mkfs_flags:
argstr += ' %s %s' % (ubi_flags[flag], ubi_args[flag])
#leb = '%s %s' % (ubi_flags['leb_size'], ubi_args['leb_size'])
peb = '%s %s' % (ubi_flags['peb_size'], ubi_args['peb_size'])
min_io = '%s %s' % (ubi_flags['min_io_size'], ubi_args['min_io_size'])
#leb_cnt = '%s %s' % (ubi_flags['max_leb_cnt'], ubi_args['max_leb_cnt'])
vid_hdr = '%s %s' % (ubi_flags['vid_hdr_offset'], ubi_args['vid_hdr_offset'])
sub_page = '%s %s' % (ubi_flags['sub_page_size'], ubi_args['sub_page_size'])
buf += '/usr/sbin/mkfs.ubifs%s -r $%s %s\n' % (argstr, (vol_idx+1), ubifs_files[vol_idx])
vol_idx += 1
config.write(fini)
ubinize_flags = ['peb_size',
'min_io_size',
'vid_hdr_offset',
'sub_page_size',
'version',
'image_seq']
argstr = ''
for flag in ubinize_flags:
argstr += ' %s %s' % (ubi_flags[flag], ubi_args[flag])
buf += '/usr/sbin/ubinize%s -o %s %s\n' % (argstr, ubi_file, ini_file)
fscr.write(buf)
os.chmod(script_path, 0o755)
def main():
start = time.time()
description = 'Determine settings for recreating UBI image.'
usage = 'ubireader_utils_info [options] filepath'
parser = argparse.ArgumentParser(usage=usage, description=description)
parser.add_argument('-r', '--show-only', action='store_true', dest='show_only',
help='Print parameters to screen only. (default: false)')
parser.add_argument('-l', '--log', action='store_true', dest='log',
help='Print extraction information to screen.')
parser.add_argument('-v', '--verbose-log', action='store_true', dest='verbose',
help='Prints nearly everything about anything to screen.')
parser.add_argument('-p', '--peb-size', type=int, dest='block_size',
help='Specify PEB size.')
parser.add_argument('-s', '--start-offset', type=int, dest='start_offset',
help='Specify offset of UBI data in file. (default: 0)')
parser.add_argument('-n', '--end-offset', type=int, dest='end_offset',
help='Specify end offset of UBI data in file.')
parser.add_argument('-g', '--guess-offset', type=int, dest='guess_offset',
help='Specify offset to start guessing where UBI data is in file. (default: 0)')
parser.add_argument('-w', '--warn-only-block-read-errors', action='store_true', dest='warn_only_block_read_errors',
help='Attempts to continue extracting files even with bad block reads. Some data will be missing or corrupted! (default: False)')
parser.add_argument('-i', '--ignore-block-header-errors', action='store_true', dest='ignore_block_header_errors',
help='Forces unused and error containing blocks to be included and also displayed with log/verbose. (default: False)')
parser.add_argument('-f', '--u-boot-fix', action='store_true', dest='uboot_fix',
help='Assume blocks with image_seq 0 are because of older U-boot implementations and include them. (default: False)')
parser.add_argument('-o', '--output-dir', dest='outpath',
help='Specify output directory path.')
parser.add_argument('filepath', help='File to extract contents of.')
if len(sys.argv) == 1:
parser.print_help()
sys.exit(1)
args = parser.parse_args()
settings.logging_on = args.log
settings.logging_on_verbose = args.verbose
settings.warn_only_block_read_errors = args.warn_only_block_read_errors
settings.ignore_block_header_errors = args.ignore_block_header_errors
settings.uboot_fix = args.uboot_fix
if args.filepath:
path = args.filepath
if not os.path.exists(path):
parser.error("File path doesn't exist.")
if args.start_offset:
start_offset = args.start_offset
elif args.guess_offset:
start_offset = guess_start_offset(path, args.guess_offset)
else:
start_offset = guess_start_offset(path)
if args.end_offset:
end_offset = args.end_offset
else:
end_offset = None
filetype = guess_filetype(path, start_offset)
if filetype != UBI_EC_HDR_MAGIC:
parser.error('File does not look like UBI data.')
img_name = os.path.basename(path)
if args.outpath:
outpath = os.path.abspath(os.path.join(args.outpath, img_name))
else:
outpath = os.path.join(settings.output_dir, img_name)
if args.block_size:
block_size = args.block_size
else:
block_size = guess_peb_size(path)
if not block_size:
parser.error('Block size could not be determined.')
# Create file object.
ufile_obj = ubi_file(path, block_size, start_offset, end_offset)
# Create UBI object
ubi_obj = ubi(ufile_obj)
# Print info.
print_ubi_params(ubi_obj)
if not args.show_only:
create_output_dir(outpath)
# Create build scripts.
make_files(ubi_obj, outpath)
ufile_obj.close()
if __name__=='__main__':
main()