Exemple #1
0
	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
Exemple #2
0
	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_()
Exemple #3
0
	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()
Exemple #4
0
	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