def _dd(self, file_name, offset, size, extension, output_file_name=None): ''' Extracts a file embedded inside the target file. @file_name - Path to the target file. @offset - Offset inside the target file where the embedded file begins. @size - Number of bytes to extract. @extension - The file exension to assign to the extracted file on disk. @output_file_name - The requested name of the output file. Returns the extracted file name. ''' total_size = 0 # Default extracted file name is <hex offset>.<extension> default_bname = "%X" % offset if self.max_size and size > self.max_size: size = self.max_size if not output_file_name or output_file_name is None: bname = default_bname else: # Strip the output file name of invalid/dangerous characters (like file paths) bname = os.path.basename(output_file_name) fname = unique_file_name(bname, extension) try: # Open the target file and seek to the offset fdin = BlockFile(file_name, 'r', length=size) fdin.seek(offset) # Open the output file try: fdout = BlockFile(fname, 'w') except Exception as e: # Fall back to the default name if the requested name fails fname = unique_file_name(default_bname, extension) fdout = BlockFile(fname, 'w') while total_size < size: (data, dlen) = fdin.read_block() fdout.write(str2bytes(data[:dlen])) total_size += dlen # Cleanup fdout.close() fdin.close() except Exception as e: raise Exception("Extractor.dd failed to extract data from '%s' to '%s': %s" % (file_name, fname, str(e))) return fname
def __init__(self, x, y, title='Entropy', average=0, file_results={}, show_legend=True, save=False): ''' Plots entropy data. @x - List of graph x-coordinates (i.e., data offsets). @y - List of graph y-coordinates (i.e., entropy for each offset). @title - Graph title. @average - The average entropy. @file_results - Binwalk results, if any. @show_legend - Set to False to not generate a color-coded legend and plotted x coordinates for the graph. @save - If set to True, graph will be saved to disk rather than displayed. Returns None. ''' import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui i = 0 descriptions = {} plotted_colors = {} max_description_length = None for (offset, results) in file_results: description = results[0]['description'].split(',')[0] desc_len = len(description) if not max_description_length or desc_len > max_description_length: max_description_length = desc_len if has_key(descriptions, offset): descriptions[offset].append(description) else: descriptions[offset] = [description] #pg.setConfigOption('background', 'w') #pg.setConfigOption('foreground', 'k') plt = pg.plot(title=title, clear=True) plt.plot(x, y, pen='y') #pen='b' if file_results and show_legend: plt.addLegend(size=(max_description_length*10, 0)) # Don't really like the way pyqtgraph draws these infinite horizontal lines #if average: # plt.addLine(y=average, pen='r') if descriptions: ordered_offsets = get_keys(descriptions) ordered_offsets.sort() for offset in ordered_offsets: for description in descriptions[offset]: # If this description has already been plotted at a different offset, we need to # use the same color for the marker, but set the description to None to prevent # duplicate entries in the graph legend. # # Else, get the next color and use it to mark descriptions of this type. if has_key(plotted_colors, description): color = plotted_colors[description] description = None else: color = self.COLORS[i] plotted_colors[description] = color i += 1 if i >= len(self.COLORS): i = 0 plt.plot(x=[offset,offset], y=[0,1.1], name=description, pen=pg.mkPen(color, width=2.5)) if save: exporter = pg.exporters.ImageExporter.ImageExporter(plt.plotItem) exporter.parameters()['width'] = self.FILE_WIDTH exporter.export(common.unique_file_name(title, self.FILE_FORMAT)) else: # Only set the axis labels if we're displaying a live window (axis labels aren't well-placed when saving directly to file) plt.setLabel('left', self.YLABEL, units=self.YUNITS) plt.setLabel('bottom', self.XLABEL, units=self.XUNITS) QtGui.QApplication.instance().exec_()
def __init__(self, x, y, title='Entropy', average=0, file_results={}, show_legend=True, save=False): ''' Plots entropy data. @x - List of graph x-coordinates (i.e., data offsets). @y - List of graph y-coordinates (i.e., entropy for each offset). @title - Graph title. @average - The average entropy. @file_results - Binwalk results, if any. @show_legend - Set to False to not generate a color-coded legend and plotted x coordinates for the graph. @save - If set to True, graph will be saved to disk rather than displayed. Returns None. ''' import matplotlib.pyplot as plt import numpy as np i = 0 trigger = 0 new_ticks = [] color_mappings = {} plt.clf() if file_results: for (offset, results) in file_results: label = None description = results[0]['description'].split(',')[0] if not has_key(color_mappings, description): if show_legend: label = description color_mappings[description] = self.COLORS[i] i += 1 if i >= len(self.COLORS): i = 0 plt.axvline(x=offset, label=label, color=color_mappings[description], linewidth=self.LINE_WIDTH) new_ticks.append(offset) if show_legend: plt.legend() if new_ticks: new_ticks.sort() plt.xticks(np.array(new_ticks), new_ticks) plt.plot(x, y, linewidth=self.LINE_WIDTH) if average: plt.plot(x, [average] * len(x), linestyle='--', color='r') plt.xlabel(self.XLABEL) plt.ylabel(self.YLABEL) plt.title(title) plt.ylim(self.YLIM_MIN, self.YLIM_MAX) if save: plt.savefig(common.unique_file_name(title, self.FILE_FORMAT)) else: plt.show()
def scan(self, target_files, offset=0, length=0, show_invalid_results=False, callback=None, start_callback=None, end_callback=None, base_dir=None, matryoshka=1, plugins_whitelist=[], plugins_blacklist=[]): ''' Performs a binwalk scan on a file or list of files. @target_files - File or list of files to scan. @offset - Starting offset at which to start the scan. @length - Number of bytes to scan. Specify -1 for streams. @show_invalid_results - Set to True to display invalid results. @callback - Callback function to be invoked when matches are found. @start_callback - Callback function to be invoked prior to scanning each file. @end_callback - Callback function to be invoked after scanning each file. @base_dir - Base directory for output files. @matryoshka - Number of levels to traverse into the rabbit hole. @plugins_whitelist - A list of plugin names to load. If not empty, only these plugins will be loaded. @plugins_blacklist - A list of plugin names to not load. Returns a dictionary of : { 'target file name' : [ (0, [{description : "LZMA compressed data..."}]), (112, [{description : "gzip compressed data..."}]) ] } ''' # Prefix all directory names with an underscore. This prevents accidental deletion of the original file(s) # when the user is typing too fast and is trying to deleted the extraction directory. prefix = '_' dir_extension = 'extracted' i = 0 total_results = {} self.matryoshka = matryoshka # For backwards compatibility if not isinstance(target_files, type([])): target_files = [target_files] if base_dir is None: base_dir = '' # Instantiate the Plugins class and load all plugins, if not disabled self.plugins = Plugins(self, whitelist=plugins_whitelist, blacklist=plugins_blacklist) if self.load_plugins: self.plugins._load_plugins() # Load the default signatures if self.load_signatures has not already been invoked if self.magic is None: self.load_signatures() while i < self.matryoshka: new_target_files = [] # Scan each target file for target_file in target_files: ignore_files = [] # On the first scan, add the base_dir value to dir_prefix. Subsequent target_file values will have this value prepended already. if i == 0: dir_prefix = os.path.join(base_dir, prefix + os.path.basename(target_file)) else: dir_prefix = os.path.join(os.path.dirname(target_file), prefix + os.path.basename(target_file)) output_dir = unique_file_name(dir_prefix, dir_extension) # Set the output directory for extracted files to go to self.extractor.output_directory(output_dir) if start_callback is not None: start_callback(target_file) results = self.single_scan(target_file, offset=offset, length=length, show_invalid_results=show_invalid_results, callback=callback) if end_callback is not None: end_callback(target_file) # Get a list of extracted file names; don't scan them again. for (index, results_list) in results: for result in results_list: if result['extract']: ignore_files.append(result['extract']) # Find all newly created files and add them to new_target_files / new_target_directories for (dir_path, sub_dirs, files) in os.walk(output_dir): for fname in files: fname = os.path.join(dir_path, fname) if fname not in ignore_files: new_target_files.append(fname) # Don't worry about sub-directories break total_results[target_file] = results target_files = new_target_files i += 1 # Be sure to delete the Plugins instance so that there isn't a lingering reference to # this Binwalk class instance (lingering handles to this Binwalk instance cause the # __del__ deconstructor to not be called). if self.plugins is not None: del self.plugins self.plugins = None return total_results