Skip to content

Instantly share code, notes, and snippets.

@craSH
Created September 3, 2015 21:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craSH/b0d7d8c9ea4f35c8fe15 to your computer and use it in GitHub Desktop.
Save craSH/b0d7d8c9ea4f35c8fe15 to your computer and use it in GitHub Desktop.
Extract chunks between chunk sizes at a given list of offsets. Handy when combined with the output of hachoir-subfile for ELF images in a filesystem, for example.
#!/usr/bin/env python
"""
Extract chunks between chunk sizes at a given list of offsets.
Handy when combined with the output of hachoir-subfile for ELF images in a filesystem, for example.
Copyleft 2015 Ian Gallagher <crash@neg9.org>
"""
import sys
import os
import logging
def setup_logging(loglevel_name):
# Setup logging
loglevel = getattr(logging, loglevel_name.upper(), logging.ERROR)
if not isinstance(loglevel, int):
raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=loglevel, format='%(asctime)s [%(levelname)-8s] (%(threadName)-12s) %(message)s',)
def debug_print(text, level=logging.DEBUG):
"""
Print a string conditionally if we're in a given loglevel or higher, but don't print using the logging facility
:param text: Text to print e.g. print(text)
:param level: Log level to print the given text at e.g. logging.DEBUG
:return: None
"""
if logging.getLogger().getEffectiveLevel() <= level:
print text
def parse_command_line():
import argparse
parser = argparse.ArgumentParser()
# (Required) positional arguments
# parser.add_argument("positional_arg", type=str,
# help="Positional parameter one")
# Required arguments
parser.add_argument("-f", "--source_path", type=str, required=True,
help="Source image path")
parser.add_argument("-o", "--offsets", type=str, required=True,
help="List of chunk offsets in source image")
parser.add_argument("-d", "--dest_dir", type=str, required=True,
help="Destination directory to write chunks to")
# Optional (default specified) arguments
parser.add_argument("-v", "--loglevel", type=str, default="ERROR",
choices=["debug", "info", "warning", "error", "critical"],
help="Debug log level")
args = parser.parse_args()
# All done, return the args object in case it's wanted
return args
def calculate_difference(sz_start, sz_end):
retval = 0
if not sz_start:
raise Exception("No start position")
if not sz_end:
retval = -1
elif sz_end > sz_start:
retval = sz_end - sz_start
return retval
def extract_chunk(source, offset, size):
debug_print("Extracting {0} bytes at offset {1}".format(size, offset))
source.seek(offset)
retval = source.read(size)
return retval
def write_chunk(dest_dir, chunk_name, chunk):
dest_path = os.path.join(dest_dir, chunk_name)
debug_print("Writing chunk: {0}".format(dest_path))
with open(dest_path, "wb") as fh:
fh.write(chunk)
def main():
args = parse_command_line()
setup_logging(args.loglevel)
# If we're at the DEBUG log level, print all the arguments provided
if logging.getLogger().getEffectiveLevel() <= logging.DEBUG:
for arg in vars(args):
logging.debug("Command line argument {name} = {val}".format(name=arg, val=getattr(args, arg)))
offsets = [int(x) for x in filter(lambda x: not x.startswith("#"), open(args.offsets, "r").readlines())]
offsets.append(0)
last_offset = 0
with open(args.source_path, "rb") as source_fh:
st_size = os.stat(source_fh.name).st_size
offset_width = len(str(st_size))
for offset in offsets:
if not last_offset:
last_offset = offset
continue
chunk_size = calculate_difference(last_offset, offset)
chunk = extract_chunk(source_fh, last_offset, chunk_size)
offset_str = str(last_offset).zfill(offset_width)
chunk_name = "{0}_{1}.bin".format(source_fh.name.rsplit(os.path.sep)[-1], offset_str)
write_chunk(args.dest_dir, chunk_name, chunk)
last_offset = offset
return(0)
if '__main__' == __name__:
sys.exit(main())
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment