Commit 84e83d0f by devttys0

Added --cast option

parent 5c4291ec
......@@ -2,32 +2,35 @@ import binwalk.core.C
from binwalk.core.compat import *
class Magic(object):
'''
Minimalist Python wrapper around libmagic.
'''
LIBMAGIC_FUNCTIONS = [
binwalk.core.C.Function(name="magic_open", type=int),
binwalk.core.C.Function(name="magic_load", type=int),
binwalk.core.C.Function(name="magic_buffer", type=str),
]
MAGIC_NO_CHECK_TEXT = 0x020000
MAGIC_NO_CHECK_APPTYPE = 0x008000
MAGIC_NO_CHECK_TOKENS = 0x100000
MAGIC_NO_CHECK_ENCODING = 0x200000
MAGIC_FLAGS = MAGIC_NO_CHECK_TEXT | MAGIC_NO_CHECK_ENCODING | MAGIC_NO_CHECK_APPTYPE | MAGIC_NO_CHECK_TOKENS
def __init__(self, magic_file=None):
if magic_file:
self.magic_file = str2bytes(magic_file)
self.libmagic = binwalk.core.C.Library("magic", self.LIBMAGIC_FUNCTIONS)
self.magic_cookie = self.libmagic.magic_open(self.MAGIC_FLAGS)
self.libmagic.magic_load(self.magic_cookie, self.magic_file)
def buffer(self, data):
return self.libmagic.magic_buffer(self.magic_cookie, str2bytes(data), len(data))
'''
Minimalist Python wrapper around libmagic.
'''
LIBMAGIC_FUNCTIONS = [
binwalk.core.C.Function(name="magic_open", type=int),
binwalk.core.C.Function(name="magic_load", type=int),
binwalk.core.C.Function(name="magic_buffer", type=str),
]
MAGIC_CONTINUE = 0x000020
MAGIC_NO_CHECK_TEXT = 0x020000
MAGIC_NO_CHECK_APPTYPE = 0x008000
MAGIC_NO_CHECK_TOKENS = 0x100000
MAGIC_NO_CHECK_ENCODING = 0x200000
MAGIC_FLAGS = MAGIC_NO_CHECK_TEXT | MAGIC_NO_CHECK_ENCODING | MAGIC_NO_CHECK_APPTYPE | MAGIC_NO_CHECK_TOKENS
def __init__(self, magic_file=None, flags=0):
if magic_file:
self.magic_file = str2bytes(magic_file)
else:
self.magic_file = None
self.libmagic = binwalk.core.C.Library("magic", self.LIBMAGIC_FUNCTIONS)
self.magic_cookie = self.libmagic.magic_open(self.MAGIC_FLAGS | flags)
self.libmagic.magic_load(self.magic_cookie, self.magic_file)
def buffer(self, data):
return self.libmagic.magic_buffer(self.magic_cookie, str2bytes(data), len(data))
......@@ -3,159 +3,162 @@ import binwalk.core.common as common
from binwalk.core.compat import *
class Settings:
'''
Binwalk settings class, used for accessing user and system file paths and general configuration settings.
After instatiating the class, file paths can be accessed via the self.paths dictionary.
System file paths are listed under the 'system' key, user file paths under the 'user' key.
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 PLUGINS - Path to the plugins directory.
'''
# Release version
VERSION = "2.0.0 alpha"
# 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"
BINARCH_MAGIC_FILE = "binarch"
def __init__(self):
'''
Class constructor. Enumerates file paths and populates self.paths.
'''
# Path to the user binwalk directory
self.user_dir = self._get_user_dir()
# Path to the system wide binwalk directory
self.system_dir = self._get_system_dir()
# Dictionary of all absolute user/system file paths
self.paths = {
'user' : {},
'system' : {},
}
# Build the paths to all user-specific files
self.paths['user'][self.BINWALK_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINWALK_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_path(self.BINWALK_MAGIC_DIR, self.BINWALK_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 find_magic_file(self, fname, system_only=False, user_only=False):
'''
Finds the specified magic file name in the system / user magic file directories.
@fname - The name of the magic file.
@system_only - If True, only the system magic file directory will be searched.
@user_only - If True, only the user magic file directory will be searched.
If system_only and user_only are not set, the user directory is always searched first.
Returns the path to the file on success; returns None on failure.
'''
loc = None
if not system_only:
fpath = self._user_path(self.BINWALK_MAGIC_DIR, fname)
if os.path.exists(fpath) and common.file_size(fpath) > 0:
loc = fpath
if loc is None and not user_only:
fpath = self._system_path(self.BINWALK_MAGIC_DIR, fname)
if os.path.exists(fpath) and common.file_size(fpath) > 0:
loc = fpath
return fpath
def _get_system_dir(self):
'''
Find the directory where the binwalk module is installed on the system.
'''
try:
root = __file__
if os.path.islink(root):
root = os.path.realpath(root)
return os.path.dirname(os.path.dirname(os.path.abspath(root)))
except KeyboardInterrupt as e:
raise e
except Exception:
return ''
def _get_user_dir(self):
'''
Get the user's home directory.
'''
try:
# This should work in both Windows and Unix environments
return os.getenv('USERPROFILE') or os.getenv('HOME')
except KeyboardInterrupt as e:
raise e
except Exception:
return ''
def _file_path(self, dirname, filename):
'''
Builds an absolute path and creates the directory and file if they don't already exist.
@dirname - Directory path.
@filename - File name.
Returns a full path of 'dirname/filename'.
'''
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except KeyboardInterrupt as e:
raise e
except Exception:
pass
fpath = os.path.join(dirname, filename)
if not os.path.exists(fpath):
try:
open(fpath, "w").close()
except KeyboardInterrupt as e:
raise e
except Exception:
pass
return fpath
def _user_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the user binwalk directory.
@subdir - Subdirectory inside the user binwalk directory.
@basename - File name inside the subdirectory.
Returns the full path to the 'subdir/basename' file.
'''
return self._file_path(os.path.join(self.user_dir, self.BINWALK_USER_DIR, subdir), basename)
def _system_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the system binwalk directory.
@subdir - Subdirectory inside the system binwalk directory.
@basename - File name inside the subdirectory.
Returns the full path to the 'subdir/basename' file.
'''
return self._file_path(os.path.join(self.system_dir, subdir), basename)
'''
Binwalk settings class, used for accessing user and system file paths and general configuration settings.
After instatiating the class, file paths can be accessed via the self.paths dictionary.
System file paths are listed under the 'system' key, user file paths under the 'user' key.
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 PLUGINS - Path to the plugins directory.
'''
# Release version
VERSION = "2.0.0 alpha"
# 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"
BINARCH_MAGIC_FILE = "binarch"
BINCAST_MAGIC_FILE = "bincast"
def __init__(self):
'''
Class constructor. Enumerates file paths and populates self.paths.
'''
# Path to the user binwalk directory
self.user_dir = self._get_user_dir()
# Path to the system wide binwalk directory
self.system_dir = self._get_system_dir()
# Dictionary of all absolute user/system file paths
self.paths = {
'user' : {},
'system' : {},
}
# Build the paths to all user-specific files
self.paths['user'][self.BINWALK_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['user'][self.BINARCH_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['user'][self.BINCAST_MAGIC_FILE] = self._user_path(self.BINWALK_MAGIC_DIR, self.BINCAST_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_path(self.BINWALK_MAGIC_DIR, self.BINWALK_MAGIC_FILE)
self.paths['system'][self.BINARCH_MAGIC_FILE] = self._system_path(self.BINWALK_MAGIC_DIR, self.BINARCH_MAGIC_FILE)
self.paths['system'][self.BINCAST_MAGIC_FILE] = self._system_path(self.BINWALK_MAGIC_DIR, self.BINCAST_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 find_magic_file(self, fname, system_only=False, user_only=False):
'''
Finds the specified magic file name in the system / user magic file directories.
@fname - The name of the magic file.
@system_only - If True, only the system magic file directory will be searched.
@user_only - If True, only the user magic file directory will be searched.
If system_only and user_only are not set, the user directory is always searched first.
Returns the path to the file on success; returns None on failure.
'''
loc = None
if not system_only:
fpath = self._user_path(self.BINWALK_MAGIC_DIR, fname)
if os.path.exists(fpath) and common.file_size(fpath) > 0:
loc = fpath
if loc is None and not user_only:
fpath = self._system_path(self.BINWALK_MAGIC_DIR, fname)
if os.path.exists(fpath) and common.file_size(fpath) > 0:
loc = fpath
return fpath
def _get_system_dir(self):
'''
Find the directory where the binwalk module is installed on the system.
'''
try:
root = __file__
if os.path.islink(root):
root = os.path.realpath(root)
return os.path.dirname(os.path.dirname(os.path.abspath(root)))
except KeyboardInterrupt as e:
raise e
except Exception:
return ''
def _get_user_dir(self):
'''
Get the user's home directory.
'''
try:
# This should work in both Windows and Unix environments
return os.getenv('USERPROFILE') or os.getenv('HOME')
except KeyboardInterrupt as e:
raise e
except Exception:
return ''
def _file_path(self, dirname, filename):
'''
Builds an absolute path and creates the directory and file if they don't already exist.
@dirname - Directory path.
@filename - File name.
Returns a full path of 'dirname/filename'.
'''
if not os.path.exists(dirname):
try:
os.makedirs(dirname)
except KeyboardInterrupt as e:
raise e
except Exception:
pass
fpath = os.path.join(dirname, filename)
if not os.path.exists(fpath):
try:
open(fpath, "w").close()
except KeyboardInterrupt as e:
raise e
except Exception:
pass
return fpath
def _user_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the user binwalk directory.
@subdir - Subdirectory inside the user binwalk directory.
@basename - File name inside the subdirectory.
Returns the full path to the 'subdir/basename' file.
'''
return self._file_path(os.path.join(self.user_dir, self.BINWALK_USER_DIR, subdir), basename)
def _system_path(self, subdir, basename=''):
'''
Gets the full path to the 'subdir/basename' file in the system binwalk directory.
@subdir - Subdirectory inside the system binwalk directory.
@basename - File name inside the subdirectory.
Returns the full path to the 'subdir/basename' file.
'''
return self._file_path(os.path.join(self.system_dir, subdir), basename)
......@@ -5,143 +5,158 @@ from binwalk.core.module import Module, Option, Kwarg
class Signature(Module):
TITLE = "Signature Scan"
ORDER = 10
CLI = [
Option(short='B',
long='signature',
kwargs={'enabled' : True},
description='Scan target file(s) for file signatures'),
Option(short='R',
long='raw-bytes',
kwargs={'raw_bytes' : None},
type=str,
description='Scan target file(s) for the specified sequence of bytes'),
Option(short='A',
long='opcodes',
kwargs={'enabled' : True, 'search_for_opcodes' : True},
description='Scan target file(s) for common executable opcodes'),
Option(short='m',
long='magic',
kwargs={'magic_files' : []},
type=list,
dtype='file',
description='Specify a custom magic file to use'),
Option(short='b',
long='dumb',
kwargs={'dumb_scan' : True},
description='Disable smart signature keywords'),
]
KWARGS = [
Kwarg(name='enabled', default=False),
Kwarg(name='raw_bytes', default=None),
Kwarg(name='search_for_opcodes', default=False),
Kwarg(name='dumb_scan', default=False),
Kwarg(name='magic_files', default=[]),
]
VERBOSE_FORMAT = "%s %d"
def init(self):
# Create Signature and MagicParser class instances. These are mostly for internal use.
self.smart = binwalk.core.smart.Signature(self.config.filter, ignore_smart_signatures=self.dumb_scan)
self.parser = binwalk.core.parser.MagicParser(self.config.filter, self.smart)
# If a raw byte sequence was specified, build a magic file from that instead of using the default magic files
if self.raw_bytes is not None:
self.magic_files = [self.parser.file_from_string(self.raw_bytes)]
# Use the system default magic file if no other was specified
if not self.magic_files:
if self.search_for_opcodes:
self.magic_files = [
self.config.settings.paths['user'][self.config.settings.BINARCH_MAGIC_FILE],
self.config.settings.paths['system'][self.config.settings.BINARCH_MAGIC_FILE],
]
else:
# Append the user's magic file first so that those signatures take precedence
self.magic_files = [
self.config.settings.paths['user'][self.config.settings.BINWALK_MAGIC_FILE],
self.config.settings.paths['system'][self.config.settings.BINWALK_MAGIC_FILE],
]
# Parse the magic file(s) and initialize libmagic
self.mfile = self.parser.parse(self.magic_files)
self.magic = binwalk.core.magic.Magic(self.mfile)
# Once the temporary magic files are loaded into libmagic, we don't need them anymore; delete the temp files
self.parser.rm_magic_files()
self.VERBOSE = ["Signatures:", self.parser.signature_count]
def validate(self, r):
'''
Called automatically by self.result.
'''
if not r.description:
r.valid = False
if r.size and (r.size + r.offset) > r.file.size:
r.valid = False
if r.jump and (r.jump + r.offset) > r.file.size:
r.valid = False
def scan_file(self, fp):
current_file_offset = 0
while True:
(data, dlen) = fp.read_block()
if not data:
break
current_block_offset = 0
block_start = fp.tell() - dlen
self.status.completed = block_start - fp.offset
for candidate_offset in self.parser.find_signature_candidates(data, dlen):
# current_block_offset is set when a jump-to-offset keyword is encountered while
# processing signatures. This points to an offset inside the current data block
# that scanning should jump to, so ignore any subsequent candidate signatures that
# occurr before this offset inside the current data block.
if candidate_offset < current_block_offset:
continue
# Pass the data to libmagic for parsing
magic_result = self.magic.buffer(data[candidate_offset:candidate_offset+fp.block_peek_size])
if not magic_result:
continue
# The smart filter parser returns a binwalk.core.module.Result object
r = self.smart.parse(magic_result)
if self.config.filter.valid_result(r.description):
# Set the absolute offset inside the target file
r.offset = block_start + candidate_offset + r.adjust
# Provide an instance of the current file object
r.file = fp
# Register the result for futher processing/display
self.result(r=r)
# Is this a valid result and did it specify a jump-to-offset keyword?
if r.valid and r.jump > 0:
absolute_jump_offset = r.offset + r.jump
current_block_offset = candidate_offset + r.jump
# If the jump-to-offset is beyond the confines of the current block, seek the file to
# that offset and quit processing this block of data.
if absolute_jump_offset >= fp.tell():
fp.seek(r.offset + r.jump)
break
def run(self):
for fp in iter(self.next_file, None):
self.header()
self.scan_file(fp)
self.footer()
TITLE = "Signature Scan"
ORDER = 10
CLI = [
Option(short='B',
long='signature',
kwargs={'enabled' : True},
description='Scan target file(s) for file signatures'),
Option(short='R',
long='raw-bytes',
kwargs={'raw_bytes' : None},
type=str,
description='Scan target file(s) for the specified sequence of bytes'),
Option(short='A',
long='opcodes',
kwargs={'enabled' : True, 'search_for_opcodes' : True},
description='Scan target file(s) for common executable opcodes'),
Option(short='C',
long='cast',
kwargs={'enabled' : True, 'cast_data_types' : True},
description='Cast offsets as various data types'),
Option(short='m',
long='magic',
kwargs={'magic_files' : []},
type=list,
dtype='file',
description='Specify a custom magic file to use'),
Option(short='b',
long='dumb',
kwargs={'dumb_scan' : True},
description='Disable smart signature keywords'),
]
KWARGS = [
Kwarg(name='enabled', default=False),
Kwarg(name='raw_bytes', default=None),
Kwarg(name='search_for_opcodes', default=False),
Kwarg(name='cast_data_types', default=False),
Kwarg(name='dumb_scan', default=False),
Kwarg(name='magic_files', default=[]),
]
VERBOSE_FORMAT = "%s %d"
def init(self):
flags = 0
# Create Signature and MagicParser class instances. These are mostly for internal use.
self.smart = binwalk.core.smart.Signature(self.config.filter, ignore_smart_signatures=self.dumb_scan)
self.parser = binwalk.core.parser.MagicParser(self.config.filter, self.smart)
# If a raw byte sequence was specified, build a magic file from that instead of using the default magic files
if self.raw_bytes is not None:
self.magic_files = [self.parser.file_from_string(self.raw_bytes)]
# Use the system default magic file if no other was specified
# Append the user's magic file first so that those signatures take precedence
if not self.magic_files:
if self.search_for_opcodes:
flags |= binwalk.core.magic.Magic.MAGIC_CONTINUE
self.magic_files = [
self.config.settings.paths['user'][self.config.settings.BINARCH_MAGIC_FILE],
self.config.settings.paths['system'][self.config.settings.BINARCH_MAGIC_FILE],
]
elif self.cast_data_types:
self.magic_files = [
self.config.settings.paths['user'][self.config.settings.BINCAST_MAGIC_FILE],
self.config.settings.paths['system'][self.config.settings.BINCAST_MAGIC_FILE],
]
else:
self.magic_files = [
self.config.settings.paths['user'][self.config.settings.BINWALK_MAGIC_FILE],
self.config.settings.paths['system'][self.config.settings.BINWALK_MAGIC_FILE],
]
# Parse the magic file(s) and initialize libmagic
self.mfile = self.parser.parse(self.magic_files)
self.magic = binwalk.core.magic.Magic(self.mfile, flags)
# Once the temporary magic files are loaded into libmagic, we don't need them anymore; delete the temp files
self.parser.rm_magic_files()
self.VERBOSE = ["Signatures:", self.parser.signature_count]
def validate(self, r):
'''
Called automatically by self.result.
'''
if not r.description:
r.valid = False
if r.size and (r.size + r.offset) > r.file.size:
r.valid = False
if r.jump and (r.jump + r.offset) > r.file.size:
r.valid = False
def scan_file(self, fp):
current_file_offset = 0
while True:
(data, dlen) = fp.read_block()
if not data:
break
current_block_offset = 0
block_start = fp.tell() - dlen
self.status.completed = block_start - fp.offset
for candidate_offset in self.parser.find_signature_candidates(data, dlen):
# current_block_offset is set when a jump-to-offset keyword is encountered while
# processing signatures. This points to an offset inside the current data block
# that scanning should jump to, so ignore any subsequent candidate signatures that
# occurr before this offset inside the current data block.
if candidate_offset < current_block_offset:
continue
# Pass the data to libmagic for parsing
magic_result = self.magic.buffer(data[candidate_offset:candidate_offset+fp.block_peek_size])
if not magic_result:
continue
# The smart filter parser returns a binwalk.core.module.Result object
r = self.smart.parse(magic_result)
if self.config.filter.valid_result(r.description):
# Set the absolute offset inside the target file
r.offset = block_start + candidate_offset + r.adjust
# Provide an instance of the current file object
r.file = fp
# Register the result for futher processing/display
self.result(r=r)
# Is this a valid result and did it specify a jump-to-offset keyword?
if r.valid and r.jump > 0:
absolute_jump_offset = r.offset + r.jump
current_block_offset = candidate_offset + r.jump
# If the jump-to-offset is beyond the confines of the current block, seek the file to
# that offset and quit processing this block of data.
if absolute_jump_offset >= fp.tell():
fp.seek(r.offset + r.jump)
break
def run(self):
for fp in iter(self.next_file, None):
self.header()
self.scan_file(fp)
self.footer()
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