Commit 4c136d1a authored by Devon Kearns's avatar Devon Kearns

Merge tag 'upstream/1.2.1'

Upstream version 1.2.1
parents d6acf069 e2e4d59b
This diff is collapsed.
This diff is collapsed.
......@@ -66,3 +66,26 @@ def get_quoted_strings(string):
return re.findall(r'\"(.*)\"', string)[0]
except:
return ''
def unique_file_name(base_name, extension=''):
'''
Creates a unique file name based on the specified base name.
@base_name - The base name to use for the unique file name.
@extension - The file extension to use for the unique file name.
Returns a unique file string.
'''
idcount = 0
if extension and not extension.startswith('.'):
extension = '.%s' % extension
fname = base_name + extension
while os.path.exists(fname):
fname = "%s-%d%s" % (base_name, idcount, extension)
idcount += 1
return fname
......@@ -27,19 +27,22 @@ class Config:
Valid file names under both the 'user' and 'system' keys are as follows:
o BINWALK_MAGIC_FILE - Path to the default binwalk magic file.
o BINCAST_MAGIC_FILE - Path to the bincast magic file (used when -C is specified with the command line binwalk script)
o BINARCH_MAGIC_FILE - Path to the binarch magic file (used when -A is specified with the command line binwalk script)
o EXTRACT_FILE - Path to the extract configuration file (used when -e is specified with the command line binwalk script)
o BINCAST_MAGIC_FILE - Path to the bincast magic file (used when -C is specified with the command line binwalk script).
o BINARCH_MAGIC_FILE - Path to the binarch magic file (used when -A is specified with the command line binwalk script).
o EXTRACT_FILE - Path to the extract configuration file (used when -e is specified with the command line binwalk script).
o PLUGINS - Path to the plugins directory.
'''
# Release version
VERSION = "1.0"
VERSION = "1.2.1"
# Sub directories
BINWALK_USER_DIR = ".binwalk"
BINWALK_MAGIC_DIR = "magic"
BINWALK_CONFIG_DIR = "config"
BINWALK_PLUGINS_DIR = "plugins"
# File names
PLUGINS = "plugins"
EXTRACT_FILE = "extract.conf"
BINWALK_MAGIC_FILE = "binwalk"
BINCAST_MAGIC_FILE = "bincast"
......@@ -61,16 +64,18 @@ class Config:
}
# Build the paths to all user-specific files
self.paths['user'][self.BINWALK_MAGIC_FILE] = self._user_file(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['user'][self.BINCAST_MAGIC_FILE] = self._user_file(self.BINWALK_MAGIC_DIR, self.BINCAST_MAGIC_FILE)
self.paths['user'][self.BINARCH_MAGIC_FILE] = self._user_file(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['user'][self.EXTRACT_FILE] = self._user_file(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE)
self.paths['user'][self.BINWALK_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['user'][self.BINCAST_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINCAST_MAGIC_FILE)
self.paths['user'][self.BINARCH_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['user'][self.EXTRACT_FILE] = self._user_path(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE)
self.paths['user'][self.PLUGINS] = self._user_path(self.BINWALK_PLUGINS_DIR)
# Build the paths to all system-wide files
self.paths['system'][self.BINWALK_MAGIC_FILE] = self._system_file(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['system'][self.BINCAST_MAGIC_FILE] = self._system_file(self.BINWALK_MAGIC_DIR, self.BINCAST_MAGIC_FILE)
self.paths['system'][self.BINARCH_MAGIC_FILE] = self._system_file(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['system'][self.EXTRACT_FILE] = self._system_file(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE)
self.paths['system'][self.BINWALK_MAGIC_FILE] = self._system_path(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['system'][self.BINCAST_MAGIC_FILE] = self._system_path(self.BINWALK_MAGIC_DIR, self.BINCAST_MAGIC_FILE)
self.paths['system'][self.BINARCH_MAGIC_FILE] = self._system_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['system'][self.EXTRACT_FILE] = self._system_path(self.BINWALK_CONFIG_DIR, self.EXTRACT_FILE)
self.paths['system'][self.PLUGINS] = self._system_path(self.BINWALK_PLUGINS_DIR)
def _get_system_dir(self):
'''
......@@ -119,7 +124,7 @@ class Config:
return fpath
def _user_file(self, subdir, basename):
def _user_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the user binwalk directory.
......@@ -130,7 +135,7 @@ class Config:
'''
return self._file_path(os.path.join(self.user_dir, self.BINWALK_USER_DIR, subdir), basename)
def _system_file(self, subdir, basename):
def _system_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the system binwalk directory.
......
......@@ -7,20 +7,23 @@
#################################################################################################################
# Assumes these utilities are installed in $PATH.
gzip compressed data:gz:gzip -d -f '%e'
lzma compressed data:7z:7zr e -y '%e'
bzip2 compressed data:bz2:bzip2 -d -f '%e'
zip archive data:zip:jar xf '%e' # jar does a better job of unzipping than unzip does...
posix tar archive:tar:tar xvf '%e'
^gzip compressed data:gz:gzip -d -f '%e'
^lzma compressed data:7z:7zr e -y '%e'
^bzip2 compressed data:bz2:bzip2 -d -f '%e'
^zip archive data:zip:jar xf '%e' # jar does a better job of unzipping than unzip does...
^posix tar archive:tar:tar xvf '%e'
^rar archive data:rar:unrar e '%e'
^arj archive data.*comment header:arj:arj e '%e'
# These assume the firmware-mod-kit is installed to /opt/firmware-mod-kit.
# If not, change the file paths appropriately.
squashfs filesystem:squashfs:/opt/firmware-mod-kit/trunk/unsquashfs_all.sh '%e'
jffs2 filesystem:jffs2:/opt/firmware-mod-kit/trunk/src/jffs2/unjffs2 '%e' # requires root
cpio archive:cpio:/opt/firmware-mod-kit/trunk/uncpio.sh '%e'
^squashfs filesystem:squashfs:/opt/firmware-mod-kit/trunk/unsquashfs_all.sh '%e'
^jffs2 filesystem:jffs2:/opt/firmware-mod-kit/trunk/src/jffs2/unjffs2 '%e' # requires root
^ascii cpio archive:cpio:/opt/firmware-mod-kit/trunk/uncpio.sh '%e'
^cramfs filesystem:cramfs:/opt/firmware-mod-kit/trunk/uncramfs_all.sh '%e'
^bff volume entry:bff:/opt/firmware-mod-kit/trunk/src/bff/bffxtractor.py '%e'
# Extract, but don't run anything
ext2 filesystem:ext2
romfs filesystem:romfs
cramfs filesystem:cramfs
private key:key
^ext2 filesystem:ext2
^romfs filesystem:romfs
^private key:key
This diff is collapsed.
This diff is collapsed.
import re
import common
from smartsig import SmartSignature
from smartsignature import SmartSignature
class MagicFilter:
'''
Class to filter libmagic results based on include/exclude rules and false positive detection.
An instance of this class is available via the Binwalk.filter object.
Note that all filter strings should be in lower case.
Example code which creates include, exclude, and grep filters before running a Binwalk scan:
Example code which creates include, exclude, and grep filters before running a binwalk scan:
import binwalk
......@@ -58,7 +60,7 @@ class MagicFilter:
Adds a new filter which explicitly includes results that contain
the specified matching text.
@match - Case insensitive text, or list of texts, to match.
@match - Regex, or list of regexs, to match.
@exclusive - If True, then results that do not explicitly contain
a FILTER_INCLUDE match will be excluded. If False,
signatures that contain the FILTER_INCLUDE match will
......@@ -67,22 +69,21 @@ class MagicFilter:
Returns None.
'''
include_filter = {
'type' : self.FILTER_INCLUDE,
'filter' : ''
}
if type(match) != type([]):
if not isinstance(match, type([])):
matches = [match]
else:
matches = match
for m in matches:
include_filter = {}
if m:
if exclusive and not self.exclusive_filter:
self.exclusive_filter = True
include_filter['filter'] = m.lower()
include_filter['type'] = self.FILTER_INCLUDE
include_filter['filter'] = m
include_filter['regex'] = re.compile(m)
self.filters.append(include_filter)
def exclude(self, match):
......@@ -90,23 +91,22 @@ class MagicFilter:
Adds a new filter which explicitly excludes results that contain
the specified matching text.
@match - Case insensitive text, or list of texts, to match.
@match - Regex, or list of regexs, to match.
Returns None.
'''
exclude_filter = {
'type' : self.FILTER_EXCLUDE,
'filter' : ''
}
if type(match) != type([]):
if not isinstance(match, type([])):
matches = [match]
else:
matches = match
for m in matches:
exclude_filter = {}
if m:
exclude_filter['filter'] = m.lower()
exclude_filter['type'] = self.FILTER_EXCLUDE
exclude_filter['filter'] = m
exclude_filter['regex'] = re.compile(m)
self.filters.append(exclude_filter)
def filter(self, data):
......@@ -124,7 +124,7 @@ class MagicFilter:
# Loop through the filters to see if any of them are a match.
# If so, return the registered type for the matching filter (FILTER_INCLUDE | FILTER_EXCLUDE).
for f in self.filters:
if f['filter'] in data:
if f['regex'].search(data):
return f['type']
# If there was not explicit match and exclusive filtering is enabled, return FILTER_EXCLUDE.
......@@ -166,7 +166,7 @@ class MagicFilter:
Add or check case-insensitive grep filters against the supplied data string.
@data - Data string to check grep filters against. Not required if filters is specified.
@filters - Filter, or list of filters, to add to the grep filters list. Not required if data is specified.
@filters - Regex, or list of regexs, to add to the grep filters list. Not required if data is specified.
Returns None if data is not specified.
If data is specified, returns True if the data contains a grep filter, or if no grep filters exist.
......@@ -174,14 +174,14 @@ class MagicFilter:
'''
# Add any specified filters to self.grep_filters
if filters:
if type(filters) != type([]):
if not isinstance(filters, type([])):
gfilters = [filters]
else:
gfilters = filters
for gfilter in gfilters:
# Filters are case insensitive
self.grep_filters.append(gfilter.lower())
self.grep_filters.append(re.compile(gfilter))
# Check the data against all grep filters until one is found
if data is not None:
......@@ -194,7 +194,7 @@ class MagicFilter:
# If a filter exists in data, return True
for gfilter in self.grep_filters:
if gfilter in data:
if gfilter.search(data):
return True
# Else, return False
......
# MIPS prologue
# addiu $sp, -XX
# 27 BD FF XX
1 string \377\275\47 MIPSEL function prologue
0 string \47\275\377 MIPS function prologue
0 string \377\275\47 MIPSEL instructions, function prologue{offset-adjust:-1}
0 string \47\275\377 MIPS instructions, function prologue
# MIPS epilogue
# jr $ra
0 belong 0x03e00008 MIPS function epilogue
0 lelong 0x03e00008 MIPSEL function epilogue
0 belong 0x03e00008 MIPS instructions, function epilogue
0 lelong 0x03e00008 MIPSEL instructions, function epilogue
# PowerPC prologue
# mflr r0
0 belong 0x7C0802A6 PowerPC big endian instructions, function prologue
0 lelong 0x7C0802A6 PowerPC little endian instructions, funciton prologue
# PowerPC epilogue
# blr
0 belong 0x4E800020 PowerPC big endian function epilogue
0 lelong 0x4E800020 PowerPC little endian function epilogue
0 belong 0x4E800020 PowerPC big endian instructions, function epilogue
0 lelong 0x4E800020 PowerPC little endian instructions, function epilogue
# ARM prologue
# STMFD SP!, {XX}
0 beshort 0xE92D ARMEB function prologue
2 leshort 0xE92D ARM function prologue
0 beshort 0xE92D ARMEB instructions, function prologue
0 leshort 0xE92D ARM instructions, function prologue{offset-adjust:-2}
# ARM epilogue
# LDMFD SP!, {XX}
0 beshort 0xE8BD ARMEB function epilogue
2 leshort 0xE8BD ARM function epilogue
0 beshort 0xE8BD ARMEB instructions, function epilogue
0 leshort 0xE8BD ARM instructions, function epilogue{offset-adjust:-2}
# x86 epilogue
# push ebp
# move ebp, esp
0 string \x55\x89\xE5 Intel x86 function epilogue
0 string \x55\x89\xE5 Intel x86 instructions, function epilogue
0 belong x Hex: 0x%.8X
#0 string x String: %s
0 lequad x Little Endian Quad: %lld
0 bequad x Big Endian Quad: %lld
0 lelong x Little Endian Long: %d
0 belong x Big Endian Long: %d
0 leshort x Little Endian Short: %d
......
This diff is collapsed.
import re
import os.path
import tempfile
from common import str2int
......@@ -23,9 +24,6 @@ class MagicParser:
All magic files generated by this class will be deleted when the class deconstructor is called.
'''
SHORT_SIZE = 2
SHORTS = ['beshort', 'leshort', 'byte']
BIG_ENDIAN = 'big'
LITTLE_ENDIAN = 'little'
......@@ -37,18 +35,6 @@ class MagicParser:
# If libmagic returns multiple results, they are delimited with this string.
RESULT_SEPERATOR = "\\012- "
# Size of the keys used in the matches set. Limited to 2
# as the key is the magic signature of a given magic file entry.
# Entries can have variable length signatures, but the lowest
# common demonitor is 2, so the first two bytes of the signature
# is used as the key. Does this result in collisions and false
# positives? Yes. But false positives are filtered out by the
# MagicFilter class. The main purpose of MagicParser.match is to
# limit the number of calls to libmagic without itself incurring
# large computational overhead. And for that purpose, this is
# quite effective.
MATCH_INDEX_SIZE = 2
def __init__(self, filter=None, smart=None):
'''
Class constructor.
......@@ -60,7 +46,6 @@ class MagicParser:
'''
self.matches = set([])
self.signatures = {}
self.sigset = {}
self.filter = filter
self.smart = smart
self.raw_fd = None
......@@ -68,10 +53,10 @@ class MagicParser:
self.fd = tempfile.NamedTemporaryFile()
def __del__(self):
'''
Class deconstructor.
'''
self.cleanup()
try:
self.cleanup()
except:
pass
def cleanup(self):
'''
......@@ -105,38 +90,34 @@ class MagicParser:
self.raw_fd.seek(0)
return self.raw_fd.name
def parse(self, file_name, filter_short_signatures=True, pre_filter_signatures=True):
def parse(self, file_name):
'''
Parses magic file(s) and contatenates them into a single temporary magic file
while simultaneously removing filtered signatures.
@file_name - Magic file, or list of magic files, to parse.
@filter_short_signatures - Set to False to include entries with short (2 byte) magic signatures.
@pre_filter_signatures - Set to False to disable smart signature keywords.
@file_name - Magic file, or list of magic files, to parse.
Returns the name of the generated temporary magic file, which will be automatically
deleted when the class deconstructor is called.
'''
if type(file_name) == type([]):
if isinstance(file_name, type([])):
files = file_name
else:
files = [file_name]
for fname in files:
if os.path.exists(fname):
self.parse_file(fname, filter_short_signatures, pre_filter_signatures)
self.parse_file(fname)
self.fd.seek(0)
return self.fd.name
def parse_file(self, file_name, filter_short_signatures=True, pre_filter_signatures=True):
def parse_file(self, file_name):
'''
Parses a magic file and appends valid signatures to the temporary magic file, as allowed
by the existing filter rules.
@file_name - Magic file to parse.
@filter_short_signatures - Set to False to include entries with short (2 byte) magic signatures.
@pre_filter_signatures - Set to False to disable smart signature keywords.
@file_name - Magic file to parse.
Returns None.
'''
......@@ -153,61 +134,29 @@ class MagicParser:
entry = self._parse_line(line)
if entry is not None:
# Once an entry is identified, default to excluding the entry
include = False
if pre_filter_signatures:
# If the smart signature include keyword is specified for this entry,
# add an include filter for this signature description.
if self.smart.include(entry['description']):
self.filter.include(entry['description'], exclusive=False)
include = True
# If we haven't already explicitly included this entry, and we are
# filtering out short signatures and this is a short signature, then
# add an exclude filter for this signature description
if not include and filter_short_signatures and self._is_short(entry):
self.filter.exclude(entry['description'])
# If this signature is marked for inclusion, include it.
if self.filter.filter(entry['description']) == self.filter.FILTER_INCLUDE:
include = True
if include:
include = True
self.signature_count += 1
if not self.signatures.has_key(entry['offset']):
self.signatures[entry['offset']] = []
if entry['condition'][:self.MATCH_INDEX_SIZE] not in self.signatures[entry['offset']]:
self.signatures[entry['offset']].append(entry['condition'][:self.MATCH_INDEX_SIZE])
if entry['condition'] not in self.signatures[entry['offset']]:
self.signatures[entry['offset']].append(entry['condition'])
else:
include = False
# Keep writing lines of the signature to the temporary magic file until
# we detect a signature that should not be included.
if include:
self.fd.write(line)
self.build_signature_set()
except Exception, e:
raise Exception("Error parsing magic file '%s' on line %d: %s" % (file_name, line_count, str(e)))
# Generate a dictionary of offsets with a set of signatures
for (offset, siglist) in self.signatures.iteritems():
self.sigset[offset] = set(siglist)
def _is_short(self, entry):
'''
Determines if a signature entry has a short (2 byte) signature or not.
@entry - Entry dictionary, as returned by self._parse_line().
Returns True if the signature is short, False if not short.
'''
if entry['type'] in self.SHORTS:
return True
elif 'string' in entry['type']:
if len(entry['condition'].decode('string_escape')) <= self.SHORT_SIZE:
return True
return False
def _parse_line(self, line):
'''
Parses a signature line into its four parts (offset, type, condition and description),
......@@ -253,7 +202,7 @@ class MagicParser:
raise Exception("%s :: %s", (str(e), line))
# If this is a string, get the length of the string
if 'string' in entry['type']:
if 'string' in entry['type'] or entry['condition'] == self.WILDCARD:
entry['length'] = len(entry['condition'])
# Else, we need to jump through a few more hoops...
else:
......@@ -267,14 +216,10 @@ class MagicParser:
# Try to convert the condition to an integer. This does not allow
# for more advanced conditions for the first line of a signature,
# but needing that is rare.
if entry['condition'] != self.WILDCARD:
try:
intval = str2int(entry['condition'].strip('L'))
except Exception, e:
raise Exception("Failed to evaluate condition for '%s' type: '%s', condition: '%s', error: %s" % (entry['description'], entry['type'], entry['condition'], str(e)))
else:
intval = 0
entry['length'] = 1
try:
intval = str2int(entry['condition'].strip('L'))
except Exception, e:
raise Exception("Failed to evaluate condition for '%s' type: '%s', condition: '%s', error: %s" % (entry['description'], entry['type'], entry['condition'], str(e)))
# How long is the field type?
if entry['type'] == 'byte':
......@@ -295,15 +240,41 @@ class MagicParser:
'''
Builds a list of signature tuples.
Returns a list of tuples in the format: [(<signature offset>, [set of 2-byte signatures])].
Returns a list of tuples in the format: [(<signature offset>, [signature regex])].
'''
signature_set = []
for (offset, sigs) in self.signatures.iteritems():
for sig in sigs:
if sig == self.WILDCARD:
sig = re.compile('.')
else:
sig = re.compile(re.escape(sig))
signature_set.append(sig)
self.signature_set = set(signature_set)
return self.signature_set
def find_signature_candidates(self, data):
'''
signatures = []
Finds candidate signatures inside of the data buffer.
Called internally by Binwalk.single_scan.
@data - Data to scan for candidate signatures.
Returns an ordered list of offsets inside of data at which candidate offsets were found.
'''
candidate_offsets = []
for regex in self.signature_set:
candidate_offsets += [match.start() for match in regex.finditer(data)]
for (offset, sigset) in self.sigset.iteritems():
signatures.append((offset, sigset))
candidate_offsets = list(set(candidate_offsets))
candidate_offsets.sort()
signatures.sort()
return signatures
return candidate_offsets
def _to_string(self, value, size, endianess):
'''
......
import os
import sys
import imp
# Valid return values for plugins
PLUGIN_CONTINUE = 0x00
PLUGIN_NO_EXTRACT = 0x01
PLUGIN_NO_DISPLAY = 0x02
PLUGIN_STOP_PLUGINS = 0x04
PLUGIN_TERMINATE = 0x08
class Plugins:
'''
Class to load and call plugin callback functions, handled automatically by Binwalk.scan / Binwalk.single_scan.
An instance of this class is available during a scan via the Binwalk.plugins object.
Each plugin must be placed in the user or system plugins directories, and must define a class named 'Plugin'.
The Plugin class constructor (__init__) is passed one argument, which is the current instance of the Binwalk class.
The Plugin class constructor is called once prior to scanning a file or set of files.
The Plugin class destructor (__del__) is called once after scanning all files.
The Plugin class can define one or all of the following callback methods:
o pre_scan(self, fd)
This method is called prior to running a scan against a file. It is passed the file object of
the file about to be scanned.
o callback(self, results)
This method is called every time a valid result is found in the file being scanned. It is passed a
dictionary of results. This dictionary is identical to that passed to Binwalk.single_scan's callback
function, and its contents may be modified as necessary by the plugin.
o post_scan(self, fd)
This method is called after running a scan against a file, but before the file has been closed.
It is passed the file object of the scanned file.
Valid return values for all plugin callbacks are (PLUGIN_* values may be OR'd together):
PLUGIN_CONTINUE - Do nothing, continue the scan normally.
PLUGIN_NO_EXTRACT - Do not preform data extraction.
PLUGIN_NO_DISPLAY - Ignore the result(s); they will not be displayed or further processed.
PLUGIN_STOP_PLUGINS - Do not call any other plugins.
PLUGIN_TERMINATE - Terminate the scan.
None - The same as PLUGIN_CONTINUE.
Values returned by pre_scan affect all results during the scan of that particular file.
Values returned by callback affect only that specific scan result.
Values returned by post_scan are ignored since the scan of that file has already been completed.
By default, all plugins are loaded during binwalk signature scans. Plugins that wish to be disabled by
default may create a class variable named 'ENABLED' and set it to False. If ENABLED is set to False, the
plugin will only be loaded if it is explicitly named in the plugins whitelist.
Simple example plugin:
from binwalk.plugins import *
class Plugin:
# Set to False to have this plugin disabled by default.
ENABLED = True
def __init__(self, binwalk):
self.binwalk = binwalk
print 'Scanning initialized!'
def __del__(self):
print 'Scanning complete!'
def pre_scan(self, fd):
print 'About to scan', fd.name
return PLUGIN_CONTINUE
def callback(self, results):
print 'Got a result:', results['description']
return PLUGIN_CONTINUE
def post_scan(self, fd):
print 'Done scanning', fd.name
return PLUGIN_CONTINUE
'''
CALLBACK = 'callback'
PRESCAN = 'pre_scan'
POSTSCAN = 'post_scan'
PLUGIN = 'Plugin'
MODULE_EXTENSION = '.py'
def __init__(self, binwalk, whitelist=[], blacklist=[]):
self.binwalk = binwalk
self.callback = []
self.pre_scan = []
self.post_scan = []
self.whitelist = whitelist
self.blacklist = blacklist
def __del__(self):
self._cleanup()
def __exit__(self, t, v, traceback):
self._cleanup()
def _cleanup(self):
try:
del self.binwalk
except:
pass
def _call_plugins(self, callback_list, arg):
retval = PLUGIN_CONTINUE
for callback in callback_list:
if (retval & PLUGIN_STOP_PLUGINS):
break
try:
val = callback(arg)
if val is not None:
retval |= val
except Exception, e:
sys.stderr.write("WARNING: %s.%s failed: %s\n" % (str(callback.im_class), callback.__name__, str(e)))
return retval
def list_plugins(self):
'''
Obtain a list of all user and system plugin modules.
Returns a dictionary of:
{
'user' : {
'modules' : [list, of, module, names],
'descriptions' : {'module_name' : 'module pydoc string'},
'enabled' : {'module_name' : True},
'path' : "path/to/module/plugin/directory"
},
'system' : {
'modules' : [list, of, module, names],
'descriptions' : {'module_name' : 'module pydoc string'},
'enabled' : {'module_name' : True},
'path' : "path/to/module/plugin/directory"
}
}
'''
plugins = {
'user' : {
'modules' : [],
'descriptions' : {},
'enabled' : {},
'path' : None,
},
'system' : {
'modules' : [],
'descriptions' : {},
'enabled' : {},
'path' : None,
}
}
for key in plugins.keys():
plugins[key]['path'] = self.binwalk.config.paths[key][self.binwalk.config.PLUGINS]