Commit 0a988379 by devttys0

Added --lzma and --stop options to raw compression module

parent 45474ebd
...@@ -4,12 +4,17 @@ try: ...@@ -4,12 +4,17 @@ try:
except ImportError: except ImportError:
pass pass
# Don't load the compression module if the lzma module can't be found
try:
from binwalk.modules.compression import RawCompression
except ImportError:
pass
from binwalk.modules.signature import Signature from binwalk.modules.signature import Signature
from binwalk.modules.hexdiff import HexDiff from binwalk.modules.hexdiff import HexDiff
from binwalk.modules.general import General from binwalk.modules.general import General
from binwalk.modules.extractor import Extractor from binwalk.modules.extractor import Extractor
from binwalk.modules.entropy import Entropy from binwalk.modules.entropy import Entropy
from binwalk.modules.compression import RawCompression
# These are depreciated. # These are depreciated.
#from binwalk.modules.binvis import Plotter #from binwalk.modules.binvis import Plotter
......
# Performs raw decompression of various compression algorithms (currently, only deflate). # Performs raw decompression of various compression algorithms (currently, only deflate).
import os import os
import lzma
import struct
import binwalk.core.C import binwalk.core.C
import binwalk.core.compat
from binwalk.core.module import Option, Kwarg, Module from binwalk.core.module import Option, Kwarg, Module
class LZMAHeader(object):
def __init__(self, **kwargs):
for (k,v) in binwalk.core.compat.iterator(kwargs):
setattr(self, k, v)
class LZMA(object):
DESCRIPTION = "Raw LZMA compression stream"
FAKE_SIZE = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
MAX_PROP = ((4 * 5 + 4) * 9 + 8)
BLOCK_SIZE = 32*1024
def __init__(self, module):
self.module = module
self.build_properties()
self.build_dictionaries()
self.build_headers()
def build_property(self, pb, lp, lc):
prop = (((pb * 5) + lp) * 9) + lc
if prop > self.MAX_PROP:
prop = None
return prop
def parse_property(self, prop):
prop = int(ord(prop))
if prop > self.MAX_PROP:
return None
pb = prop / (9 * 5);
prop -= pb * 9 * 5;
lp = prop / 9;
lc = prop - lp * 9;
return (pb, lp, lc)
def parse_header(self, header):
(pb, lp, lc) = self.parse_property(header[0])
dictionary = struct.unpack("<I", binwalk.core.compat.str2bytes(header[1:5]))[0]
return LZMAHeader(pb=pb, lp=lp, lc=lc, dictionary=dictionary)
def build_properties(self):
self.properties = set()
for pb in range(0, 9):
for lp in range(0, 5):
for lc in range(0, 5):
prop = self.build_property(pb, lp, lc)
if prop is not None:
self.properties.add(chr(prop))
def build_dictionaries(self):
self.dictionaries = set()
for n in range(16, 26):
self.dictionaries.add(binwalk.core.compat.bytes2str(struct.pack("<I", 2**n)))
def build_headers(self):
self.headers = set()
for prop in self.properties:
for dictionary in self.dictionaries:
self.headers.add(prop + dictionary + self.FAKE_SIZE)
def decompress(self, data):
result = None
description = None
for header in self.headers:
# The only acceptable exceptions are those indicating that the input data was truncated.
try:
lzma.decompress(binwalk.core.compat.str2bytes(header + data))
result = self.parse_header(header)
break
except IOError as e:
# The Python2 module gives this error on truncated input data.
if str(e) == "unknown BUF error":
result = self.parse_header(header)
break
except Exception as e:
# The Python3 module gives this error on truncated input data.
# The inconsistency between modules is a bit worrisome.
if str(e) == "Compressed data ended before the end-of-stream marker was reached":
result = self.parse_header(header)
break
if result is not None:
description = "%s, pb: %d, lp: %d, lc: %d, dictionary size: %d" % (self.DESCRIPTION,
result.pb,
result.lp,
result.lc,
result.dictionary)
return description
class Deflate(object): class Deflate(object):
''' '''
Finds and extracts raw deflate compression streams. Finds and extracts raw deflate compression streams.
...@@ -49,6 +149,7 @@ class RawCompression(Module): ...@@ -49,6 +149,7 @@ class RawCompression(Module):
DECOMPRESSORS = { DECOMPRESSORS = {
'deflate' : Deflate, 'deflate' : Deflate,
'lzma' : LZMA,
} }
TITLE = 'Raw Compression' TITLE = 'Raw Compression'
...@@ -58,10 +159,19 @@ class RawCompression(Module): ...@@ -58,10 +159,19 @@ class RawCompression(Module):
long='deflate', long='deflate',
kwargs={'enabled' : True, 'decompressor_class' : 'deflate'}, kwargs={'enabled' : True, 'decompressor_class' : 'deflate'},
description='Scan for raw deflate compression streams'), description='Scan for raw deflate compression streams'),
Option(short='Z',
long='lzma',
kwargs={'enabled' : True, 'decompressor_class' : 'lzma'},
description='Scan for raw LZMA compression streams'),
Option(short='S',
long='stop',
kwargs={'stop_on_first_hit' : True},
description='Stop after the first result'),
] ]
KWARGS = [ KWARGS = [
Kwarg(name='enabled', default=False), Kwarg(name='enabled', default=False),
Kwarg(name='stop_on_first_hit', default=False),
Kwarg(name='decompressor_class', default=None), Kwarg(name='decompressor_class', default=None),
] ]
...@@ -71,11 +181,12 @@ class RawCompression(Module): ...@@ -71,11 +181,12 @@ class RawCompression(Module):
def run(self): def run(self):
for fp in iter(self.next_file, None): for fp in iter(self.next_file, None):
file_done = False
fp.set_block_size(peek=self.decompressor.BLOCK_SIZE) fp.set_block_size(peek=self.decompressor.BLOCK_SIZE)
self.header() self.header()
while True: while not file_done:
(data, dlen) = fp.read_block() (data, dlen) = fp.read_block()
if not data: if not data:
break break
...@@ -84,6 +195,10 @@ class RawCompression(Module): ...@@ -84,6 +195,10 @@ class RawCompression(Module):
description = self.decompressor.decompress(data[i:i+self.decompressor.BLOCK_SIZE]) description = self.decompressor.decompress(data[i:i+self.decompressor.BLOCK_SIZE])
if description: if description:
self.result(description=description, file=fp, offset=fp.tell()-dlen+i) self.result(description=description, file=fp, offset=fp.tell()-dlen+i)
if self.stop_on_first_hit:
file_done = True
break
self.status.completed += 1
self.status.completed = fp.tell() - fp.offset self.status.completed = fp.tell() - fp.offset
......
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