Commit 850b32e4 by devttys0

Added ability to specify explicit return codes for external extractors

parent 0fa1115a
...@@ -163,7 +163,7 @@ class Extractor(Module): ...@@ -163,7 +163,7 @@ class Extractor(Module):
def append_rule(self, r): def append_rule(self, r):
self.extract_rules.append(r.copy()) self.extract_rules.append(r.copy())
def add_rule(self, txtrule=None, regex=None, extension=None, cmd=None): def add_rule(self, txtrule=None, regex=None, extension=None, cmd=None, codes=[0, None]):
''' '''
Adds a set of rules to the extraction rule list. Adds a set of rules to the extraction rule list.
...@@ -172,6 +172,7 @@ class Extractor(Module): ...@@ -172,6 +172,7 @@ class Extractor(Module):
@extension - If rule string is not specified, this is the file extension to use. @extension - If rule string is not specified, this is the file extension to use.
@cmd - If rule string is not specified, this is the command to run. @cmd - If rule string is not specified, this is the command to run.
Alternatively a callable object may be specified, which will be passed one argument: the path to the file to extract. Alternatively a callable object may be specified, which will be passed one argument: the path to the file to extract.
@codes - A list of valid return codes for the extractor.
Returns None. Returns None.
''' '''
...@@ -180,7 +181,8 @@ class Extractor(Module): ...@@ -180,7 +181,8 @@ class Extractor(Module):
r = { r = {
'extension' : '', 'extension' : '',
'cmd' : '', 'cmd' : '',
'regex' : None 'regex' : None,
'codes' : codes
} }
# Process single explicitly specified rule # Process single explicitly specified rule
...@@ -209,6 +211,7 @@ class Extractor(Module): ...@@ -209,6 +211,7 @@ class Extractor(Module):
r['regex'] = re.compile(values[0]) r['regex'] = re.compile(values[0])
r['extension'] = values[1] r['extension'] = values[1]
r['cmd'] = values[2] r['cmd'] = values[2]
r['codes'] = values[3]
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
raise e raise e
except Exception: except Exception:
...@@ -397,7 +400,7 @@ class Extractor(Module): ...@@ -397,7 +400,7 @@ class Extractor(Module):
# Execute the specified command against the extracted file # Execute the specified command against the extracted file
if self.run_extractors: if self.run_extractors:
extract_ok = self.execute(rule['cmd'], fname) extract_ok = self.execute(rule['cmd'], fname, rule['codes'])
else: else:
extract_ok = True extract_ok = True
...@@ -486,9 +489,20 @@ class Extractor(Module): ...@@ -486,9 +489,20 @@ class Extractor(Module):
@rule - Rule string. @rule - Rule string.
Returns an array of ['<case insensitive matching string>', '<file extension>', '<command to run>']. Returns an array of ['<case insensitive matching string>', '<file extension>', '<command to run>', '<comma separated return codes>'].
''' '''
return rule.strip().split(self.RULE_DELIM, 2) values = rule.strip().split(self.RULE_DELIM, 3)
if len(values) == 4:
codes = values[3].split(',')
for i in range(0, len(codes)):
try:
codes[i] = int(codes[i], 0)
except ValueError as e:
binwalk.core.common.warning("The specified return code '%s' for extractor '%s' is not a valid number!" % (codes[i], values[0]))
values[3] = codes
return values
def _dd(self, file_name, offset, size, extension, output_file_name=None): def _dd(self, file_name, offset, size, extension, output_file_name=None):
''' '''
...@@ -550,12 +564,13 @@ class Extractor(Module): ...@@ -550,12 +564,13 @@ class Extractor(Module):
binwalk.core.common.debug("Carved data block 0x%X - 0x%X from '%s' to '%s'" % (offset, offset+size, file_name, fname)) binwalk.core.common.debug("Carved data block 0x%X - 0x%X from '%s' to '%s'" % (offset, offset+size, file_name, fname))
return fname return fname
def execute(self, cmd, fname): def execute(self, cmd, fname, codes=[0, None]):
''' '''
Execute a command against the specified file. Execute a command against the specified file.
@cmd - Command to execute. @cmd - Command to execute.
@fname - File to run command against. @fname - File to run command against.
@codes - List of return codes indicating cmd success.
Returns True on success, False on failure, or None if the external extraction utility could not be found. Returns True on success, False on failure, or None if the external extraction utility could not be found.
''' '''
...@@ -592,13 +607,20 @@ class Extractor(Module): ...@@ -592,13 +607,20 @@ class Extractor(Module):
binwalk.core.common.debug("subprocess.call(%s, stdout=%s, stderr=%s)" % (command, str(tmp), str(tmp))) binwalk.core.common.debug("subprocess.call(%s, stdout=%s, stderr=%s)" % (command, str(tmp), str(tmp)))
rval = subprocess.call(shlex.split(command), stdout=tmp, stderr=tmp) rval = subprocess.call(shlex.split(command), stdout=tmp, stderr=tmp)
binwalk.core.common.debug('External extractor command "%s" completed with return code %d' % (cmd, rval))
if rval == 0: if rval in codes:
retval = True retval = True
else: else:
retval = False retval = False
break
binwalk.core.common.debug('External extractor command "%s" completed with return code %d (success: %s)' % (cmd, rval, str(retval)))
# TODO: Should errors from all commands in a command string be checked? Currently we only support
# specifying one set of error codes, so at the moment, this is not done; it is up to the
# final command to return success or failure (which presumably it will if previous necessary
# commands were not successful, but this is an assumption).
#if retval == False:
# break
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
raise e raise e
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment