Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- __description__ = 'Process command'
- __author__ = 'Didier Stevens'
- __version__ = '0.0.1'
- __date__ = '2014/08/26'
- """
- Source code put in public domain by Didier Stevens, no Copyright
- https://DidierStevens.com
- Use at your own risk
- # http://www.wordarticles.com/Articles/Formats/StreamCompression.php
- History:
- 2014/08/21: start
- 2014/08/22: added ZIP support
- 2014/08/23: added stdin support
- 2014/08/25: added options extract and info
- 2014/08/26: bugfix pipe
- Todo:
- """
- import optparse
- import OleFileIO_PL
- import sys
- import math
- import os
- import zipfile
- import cStringIO
- dumplinelength = 16
- MALWARE_PASSWORD = 'infected'
- #Convert 2 Bytes If Python 3
- def C2BIP3(string):
- if sys.version_info[0] > 2:
- return bytes([ord(x) for x in string])
- else:
- return string
- # CIC: Call If Callable
- def CIC(expression):
- if callable(expression):
- return expression()
- else:
- return expression
- # IFF: IF Function
- def IFF(expression, valueTrue, valueFalse):
- if expression:
- return CIC(valueTrue)
- else:
- return CIC(valueFalse)
- def File2String(filename):
- try:
- f = open(filename, 'rb')
- except:
- return None
- try:
- return f.read()
- except:
- return None
- finally:
- f.close()
- class cDumpStream():
- def __init__(self):
- self.text = ''
- def Addline(self, line):
- if line != '':
- self.text += line + '\n'
- def Content(self):
- return self.text
- def HexDump(data):
- oDumpStream = cDumpStream()
- hexDump = ''
- for i, b in enumerate(data):
- if i % dumplinelength == 0 and hexDump != '':
- oDumpStream.Addline(hexDump)
- hexDump = ''
- hexDump += IFF(hexDump == '', '', ' ') + '%02X' % ord(b)
- oDumpStream.Addline(hexDump)
- return oDumpStream.Content()
- def CombineHexAscii(hexDump, asciiDump):
- if hexDump == '':
- return ''
- return hexDump + ' ' + (' ' * (3 * (16 - len(asciiDump)))) + asciiDump
- def HexAsciiDump(data):
- oDumpStream = cDumpStream()
- hexDump = ''
- asciiDump = ''
- for i, b in enumerate(data):
- if i % dumplinelength == 0:
- if hexDump != '':
- oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
- hexDump = '%08X:' % i
- asciiDump = ''
- hexDump+= ' %02X' % ord(b)
- asciiDump += IFF(ord(b) >= 32 and ord(b), b, '.')
- oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
- return oDumpStream.Content()
- #Fix for http://bugs.python.org/issue11395
- def StdoutWriteChunked(data):
- while data != '':
- sys.stdout.write(data[0:10000])
- try:
- sys.stdout.flush()
- except IOError:
- return
- data = data[10000:]
- def PrintableName(fname):
- return repr('/'.join(fname))
- def ParseTokenSequence(data):
- flags = ord(data[0])
- data = data[1:]
- result = []
- for mask in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]:
- if len(data) > 0:
- if flags & mask:
- result.append(data[0:2])
- data = data[2:]
- else:
- result.append(data[0])
- data = data[1:]
- return result, data
- def OffsetBits(data):
- numberOfBits = int(math.ceil(math.log(len(data), 2)))
- if numberOfBits < 4:
- numberOfBits = 4
- elif numberOfBits > 12:
- numberOfBits = 12
- return numberOfBits
- def Bin(number):
- result = bin(number)[2:]
- while len(result) < 16:
- result = '0' + result
- return result
- def DecompressChunk(compressedChunk):
- header = ord(compressedChunk[0]) + ord(compressedChunk[1]) * 0x100
- size = (header & 0x0FFF) + 3
- flagCompressed = header & 0x8000
- data = compressedChunk[2:2 + size - 2]
- if flagCompressed == 0:
- return data, compressedChunk[size:]
- decompressedChunk = ''
- while len(data) != 0:
- tokens, data = ParseTokenSequence(data)
- for token in tokens:
- if len(token) == 1:
- decompressedChunk += token
- else:
- numberOfOffsetBits = OffsetBits(decompressedChunk)
- copyToken = ord(token[0]) + ord(token[1]) * 0x100
- offset = 1 + (copyToken >> (16 - numberOfOffsetBits))
- length = 3 + (((copyToken << numberOfOffsetBits) & 0xFFFF) >> numberOfOffsetBits)
- copy = decompressedChunk[-offset:]
- copy = copy[0:length]
- lengthCopy = len(copy)
- while length > lengthCopy: #a#
- if length - lengthCopy >= lengthCopy:
- copy += copy[0:lengthCopy]
- length -= lengthCopy
- else:
- copy += copy[0:length - lengthCopy]
- length -= length - lengthCopy
- decompressedChunk += copy
- return decompressedChunk, compressedChunk[size:]
- def Decompress(compressedData):
- if compressedData[0] != chr(1):
- return None
- remainder = compressedData[1:]
- decompressed = ''
- while len(remainder) != 0:
- decompressedChunk, remainder = DecompressChunk(remainder)
- decompressed += decompressedChunk
- return decompressed
- def SearchAndDecompress(data):
- position = data.find('\x00Attribut')
- if position == -1:
- compressedData = data
- else:
- compressedData = data[position - 3:]
- result = Decompress(compressedData)
- if result == None:
- return 'Error: unable to decompress'
- else:
- return result
- def IsZIPFile(filename):
- return filename.lower().endswith('.zip') and File2String(filename)[0:2] == 'PK'
- def ReadWORD(data):
- if len(data) < 2:
- return None, None
- return ord(data[0]) + ord(data[1]) *0x100, data[2:]
- def ReadDWORD(data):
- if len(data) < 4:
- return None, None
- return ord(data[0]) + ord(data[1]) *0x100 + ord(data[2]) *0x10000 + ord(data[3]) *0x1000000, data[4:]
- def ReadNullTerminatedString(data):
- position = data.find('\x00')
- if position == -1:
- return None, None
- return data[:position], data[position + 1:]
- def ExtractOle10Native(data):
- size, data = ReadDWORD(data)
- if size == None:
- return []
- dummy, data = ReadWORD(data)
- if dummy == None:
- return []
- filename, data = ReadNullTerminatedString(data)
- if filename == None:
- return []
- pathname, data = ReadNullTerminatedString(data)
- if pathname == None:
- return []
- dummy, data = ReadDWORD(data)
- if dummy == None:
- return []
- dummy, data = ReadDWORD(data)
- if dummy == None:
- return []
- temppathname, data = ReadNullTerminatedString(data)
- if temppathname == None:
- return []
- sizeEmbedded, data = ReadDWORD(data)
- if sizeEmbedded == None:
- return []
- if len(data) < sizeEmbedded:
- return []
- return [filename, pathname, temppathname, data[:sizeEmbedded]]
- def Extract(data):
- result = ExtractOle10Native(data)
- if result == []:
- return 'Error: extraction failed'
- return result[3]
- def Info(data):
- result = ExtractOle10Native(data)
- if result == []:
- return 'Error: extraction failed'
- return 'String 1: %s\nString 2: %s\nString 3: %s\nSize embedded file: %d\n' % (result[0], result[1], result[2], len(result[3]))
- def IfWIN32SetBinary(io):
- if sys.platform == 'win32':
- import msvcrt
- msvcrt.setmode(io.fileno(), os.O_BINARY)
- def OLEDump(filename, options):
- if options.raw:
- print SearchAndDecompress(File2String(filename))
- return
- if filename == '':
- IfWIN32SetBinary(sys.stdin)
- ole = OleFileIO_PL.OleFileIO(cStringIO.StringIO(sys.stdin.read()))
- elif IsZIPFile(filename):
- oZipfile = zipfile.ZipFile(filename, 'r')
- oZipContent = oZipfile.open(oZipfile.infolist()[0], 'r', C2BIP3(MALWARE_PASSWORD))
- ole = OleFileIO_PL.OleFileIO(cStringIO.StringIO(oZipContent.read()))
- oZipContent.close()
- else:
- if OleFileIO_PL.isOleFile(filename) is not True:
- print >>sys.stderr, 'Error - %s is not a valid OLE file.' % infile
- sys.exit(1)
- ole = OleFileIO_PL.OleFileIO(filename)
- if options.select == '':
- counter = 1
- for fname in ole.listdir():
- stream = ole.openstream(fname).read()
- print('%2d: %s %6d %s' % (counter, IFF('\x00Attribut' in stream, 'M', ' '), len(stream), PrintableName(fname)))
- counter += 1
- else:
- if options.dump:
- DumpFunction = lambda x:x
- IfWIN32SetBinary(sys.stdout)
- elif options.hexdump:
- DumpFunction = HexDump
- elif options.vbadecompress:
- DumpFunction = SearchAndDecompress
- elif options.extract:
- DumpFunction = Extract
- IfWIN32SetBinary(sys.stdout)
- elif options.info:
- DumpFunction = Info
- else:
- DumpFunction = HexAsciiDump
- counter = 1
- for fname in ole.listdir():
- if counter == int(options.select):
- StdoutWriteChunked(DumpFunction(ole.openstream(fname).read()))
- break
- counter += 1
- ole.close()
- def Main():
- oParser = optparse.OptionParser(usage='usage: %prog [options] [file]\n' + __description__, version='%prog ' + __version__)
- oParser.add_option('-s', '--select', default='', help='select item nr for dumping')
- oParser.add_option('-d', '--dump', action='store_true', default=False, help='perform dump')
- oParser.add_option('-x', '--hexdump', action='store_true', default=False, help='perform hex dump')
- oParser.add_option('-a', '--asciidump', action='store_true', default=False, help='perform ascii dump')
- oParser.add_option('-v', '--vbadecompress', action='store_true', default=False, help='VBA decompression')
- oParser.add_option('-r', '--raw', action='store_true', default=False, help='raw file, attempt VBA decompression')
- oParser.add_option('-e', '--extract', action='store_true', default=False, help='extract OLE embedded file')
- oParser.add_option('-i', '--info', action='store_true', default=False, help='print extra info for selected item')
- (options, args) = oParser.parse_args()
- if len(args) > 1:
- oParser.print_help()
- print('')
- print(' Source code put in the public domain by Didier Stevens, no Copyright')
- print(' Use at your own risk')
- print(' https://DidierStevens.com')
- return
- elif len(args) == 0:
- OLEDump('', options)
- else:
- OLEDump(args[0], options)
- if __name__ == '__main__':
- Main()
Add Comment
Please, Sign In to add comment