SHARE
TWEET

modified oledump.py

a guest Dec 23rd, 2014 2,108 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/usr/bin/env python
  2. # Rodel Mendrez: added -m option to automatically dump the macro code when a macro stream is found in the document
  3. __description__ = 'Process command'
  4. __author__ = 'Didier Stevens'
  5. __version__ = '0.0.1'
  6. __date__ = '2014/08/26'
  7.  
  8. """
  9.  
  10. Source code put in public domain by Didier Stevens, no Copyright
  11. https://DidierStevens.com
  12. Use at your own risk
  13.  
  14. # http://www.wordarticles.com/Articles/Formats/StreamCompression.php
  15.  
  16. History:
  17.   2014/08/21: start
  18.   2014/08/22: added ZIP support
  19.   2014/08/23: added stdin support
  20.   2014/08/25: added options extract and info
  21.   2014/08/26: bugfix pipe
  22.  
  23. Todo:
  24. """
  25.  
  26. import optparse
  27. import OleFileIO_PL
  28. import sys
  29. import math
  30. import os
  31. import zipfile
  32. import cStringIO
  33.  
  34. dumplinelength = 16
  35. MALWARE_PASSWORD = 'infected'
  36.  
  37. #Convert 2 Bytes If Python 3
  38. def C2BIP3(string):
  39.     if sys.version_info[0] > 2:
  40.         return bytes([ord(x) for x in string])
  41.     else:
  42.         return string
  43.  
  44. # CIC: Call If Callable
  45. def CIC(expression):
  46.     if callable(expression):
  47.         return expression()
  48.     else:
  49.         return expression
  50.  
  51. # IFF: IF Function
  52. def IFF(expression, valueTrue, valueFalse):
  53.     if expression:
  54.         return CIC(valueTrue)
  55.     else:
  56.         return CIC(valueFalse)
  57.  
  58. def File2String(filename):
  59.     try:
  60.         f = open(filename, 'rb')
  61.     except:
  62.         return None
  63.     try:
  64.         return f.read()
  65.     except:
  66.         return None
  67.     finally:
  68.         f.close()
  69.  
  70. class cDumpStream():
  71.     def __init__(self):
  72.         self.text = ''
  73.  
  74.     def Addline(self, line):
  75.         if line != '':
  76.             self.text += line + '\n'
  77.  
  78.     def Content(self):
  79.         return self.text
  80.  
  81. def HexDump(data):
  82.     oDumpStream = cDumpStream()
  83.     hexDump = ''
  84.     for i, b in enumerate(data):
  85.         if i % dumplinelength == 0 and hexDump != '':
  86.             oDumpStream.Addline(hexDump)
  87.             hexDump = ''
  88.         hexDump += IFF(hexDump == '', '', ' ') + '%02X' % ord(b)
  89.     oDumpStream.Addline(hexDump)
  90.     return oDumpStream.Content()
  91.  
  92. def CombineHexAscii(hexDump, asciiDump):
  93.     if hexDump == '':
  94.         return ''
  95.     return hexDump + '  ' + (' ' * (3 * (16 - len(asciiDump)))) + asciiDump
  96.  
  97. def HexAsciiDump(data):
  98.     oDumpStream = cDumpStream()
  99.     hexDump = ''
  100.     asciiDump = ''
  101.     for i, b in enumerate(data):
  102.         if i % dumplinelength == 0:
  103.             if hexDump != '':
  104.                 oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
  105.             hexDump = '%08X:' % i
  106.             asciiDump = ''
  107.         hexDump+= ' %02X' % ord(b)
  108.         asciiDump += IFF(ord(b) >= 32 and ord(b), b, '.')
  109.     oDumpStream.Addline(CombineHexAscii(hexDump, asciiDump))
  110.     return oDumpStream.Content()
  111.  
  112. #Fix for http://bugs.python.org/issue11395
  113. def StdoutWriteChunked(data):
  114.     while data != '':
  115.         sys.stdout.write(data[0:10000])
  116.         try:
  117.             sys.stdout.flush()
  118.         except IOError:
  119.             return
  120.         data = data[10000:]
  121.  
  122. def PrintableName(fname):
  123.     return repr('/'.join(fname))
  124.  
  125. def ParseTokenSequence(data):
  126.     flags = ord(data[0])
  127.     data = data[1:]
  128.     result = []
  129.     for mask in [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80]:
  130.         if len(data) > 0:
  131.             if flags & mask:
  132.                 result.append(data[0:2])
  133.                 data = data[2:]
  134.             else:
  135.                 result.append(data[0])
  136.                 data = data[1:]
  137.     return result, data
  138.  
  139. def OffsetBits(data):
  140.     numberOfBits = int(math.ceil(math.log(len(data), 2)))
  141.     if numberOfBits < 4:
  142.         numberOfBits = 4
  143.     elif numberOfBits > 12:
  144.         numberOfBits = 12
  145.     return numberOfBits
  146.  
  147. def Bin(number):
  148.     result = bin(number)[2:]
  149.     while len(result) < 16:
  150.         result = '0' + result
  151.     return result
  152.  
  153. def DecompressChunk(compressedChunk):
  154.     header = ord(compressedChunk[0]) + ord(compressedChunk[1]) * 0x100
  155.     size = (header & 0x0FFF) + 3
  156.     flagCompressed = header & 0x8000
  157.     data = compressedChunk[2:2 + size - 2]
  158.  
  159.     if flagCompressed == 0:
  160.         return data, compressedChunk[size:]
  161.  
  162.     decompressedChunk = ''
  163.     while len(data) != 0:
  164.         tokens, data = ParseTokenSequence(data)
  165.         for token in tokens:
  166.             if len(token) == 1:
  167.                 decompressedChunk += token
  168.             else:
  169.                 numberOfOffsetBits = OffsetBits(decompressedChunk)
  170.                 copyToken = ord(token[0]) + ord(token[1]) * 0x100
  171.                 offset = 1 + (copyToken >> (16 - numberOfOffsetBits))
  172.                 length = 3 + (((copyToken << numberOfOffsetBits) & 0xFFFF) >> numberOfOffsetBits)
  173.                 copy = decompressedChunk[-offset:]
  174.                 copy = copy[0:length]
  175.                 lengthCopy = len(copy)
  176.                 while length > lengthCopy: #a#
  177.                     if length - lengthCopy >= lengthCopy:
  178.                         copy += copy[0:lengthCopy]
  179.                         length -= lengthCopy
  180.                     else:
  181.                         copy += copy[0:length - lengthCopy]
  182.                         length -= length - lengthCopy
  183.                 decompressedChunk += copy
  184.     return decompressedChunk, compressedChunk[size:]
  185.  
  186. def Decompress(compressedData):
  187.     if compressedData[0] != chr(1):
  188.         return None
  189.     remainder = compressedData[1:]
  190.     decompressed = ''
  191.     while len(remainder) != 0:
  192.         decompressedChunk, remainder = DecompressChunk(remainder)
  193.         decompressed += decompressedChunk
  194.     return decompressed
  195.  
  196. def SearchAndDecompress(data):
  197.     position = data.find('\x00Attribut')
  198.     if position == -1:
  199.         compressedData = data
  200.     else:
  201.         compressedData = data[position - 3:]
  202.     result = Decompress(compressedData)
  203.     if result == None:
  204.         return 'Error: unable to decompress'
  205.     else:
  206.         return result
  207.  
  208. def IsZIPFile(filename):
  209.     return filename.lower().endswith('.zip') and File2String(filename)[0:2] == 'PK'
  210.  
  211. def ReadWORD(data):
  212.     if len(data) < 2:
  213.         return None, None
  214.     return ord(data[0]) + ord(data[1]) *0x100, data[2:]
  215.  
  216. def ReadDWORD(data):
  217.     if len(data) < 4:
  218.         return None, None
  219.     return ord(data[0]) + ord(data[1]) *0x100 + ord(data[2]) *0x10000 + ord(data[3]) *0x1000000, data[4:]
  220.  
  221. def ReadNullTerminatedString(data):
  222.     position = data.find('\x00')
  223.     if position == -1:
  224.         return None, None
  225.     return data[:position], data[position + 1:]
  226.  
  227. def ExtractOle10Native(data):
  228.     size, data = ReadDWORD(data)
  229.     if size == None:
  230.         return []
  231.     dummy, data = ReadWORD(data)
  232.     if dummy == None:
  233.         return []
  234.     filename, data = ReadNullTerminatedString(data)
  235.     if filename == None:
  236.         return []
  237.     pathname, data = ReadNullTerminatedString(data)
  238.     if pathname == None:
  239.         return []
  240.     dummy, data = ReadDWORD(data)
  241.     if dummy == None:
  242.         return []
  243.     dummy, data = ReadDWORD(data)
  244.     if dummy == None:
  245.         return []
  246.     temppathname, data = ReadNullTerminatedString(data)
  247.     if temppathname == None:
  248.         return []
  249.     sizeEmbedded, data = ReadDWORD(data)
  250.     if sizeEmbedded == None:
  251.         return []
  252.     if len(data) < sizeEmbedded:
  253.         return []
  254.  
  255.     return [filename, pathname, temppathname, data[:sizeEmbedded]]
  256.  
  257. def Extract(data):
  258.     result = ExtractOle10Native(data)
  259.     if result == []:
  260.         return 'Error: extraction failed'
  261.     return result[3]
  262.  
  263. def Info(data):
  264.     result = ExtractOle10Native(data)
  265.     if result == []:
  266.         return 'Error: extraction failed'
  267.     return 'String 1: %s\nString 2: %s\nString 3: %s\nSize embedded file: %d\n' % (result[0], result[1], result[2], len(result[3]))
  268.  
  269. def IfWIN32SetBinary(io):
  270.     if sys.platform == 'win32':
  271.         import msvcrt
  272.         msvcrt.setmode(io.fileno(), os.O_BINARY)
  273.  
  274. def OLEDump(filename, options):
  275.     if options.raw:
  276.         print SearchAndDecompress(File2String(filename))
  277.         return
  278.  
  279.     if filename == '':
  280.         IfWIN32SetBinary(sys.stdin)
  281.         ole = OleFileIO_PL.OleFileIO(cStringIO.StringIO(sys.stdin.read()))
  282.     elif IsZIPFile(filename):
  283.         oZipfile = zipfile.ZipFile(filename, 'r')
  284.         oZipContent = oZipfile.open(oZipfile.infolist()[0], 'r', C2BIP3(MALWARE_PASSWORD))
  285.         ole = OleFileIO_PL.OleFileIO(cStringIO.StringIO(oZipContent.read()))
  286.         oZipContent.close()
  287.     else:
  288.         if OleFileIO_PL.isOleFile(filename) is not True:
  289.             print >>sys.stderr, 'Error - %s is not a valid OLE file.' % infile
  290.             sys.exit(1)
  291.         ole = OleFileIO_PL.OleFileIO(filename)
  292.  
  293.    
  294.     if options.macrodump:
  295.         for fname in ole.listdir():
  296.             stream = ole.openstream(fname).read()
  297.             if '\x00Attribut' in stream:
  298.                 print PrintableName(fname)
  299.                 StdoutWriteChunked(SearchAndDecompress(stream))
  300.             #print('%2d: %s %6d %s' % (counter, IFF('\x00Attribut' in stream, 'M', ' '), len(stream), PrintableName(fname)))
  301.         return
  302.                
  303.     if options.select == '':
  304.         counter = 1
  305.         for fname in ole.listdir():
  306.             stream = ole.openstream(fname).read()
  307.             print('%2d: %s %6d %s' % (counter, IFF('\x00Attribut' in stream, 'M', ' '), len(stream), PrintableName(fname)))
  308.             counter += 1
  309.     else:
  310.         if options.dump:
  311.             DumpFunction = lambda x:x
  312.             IfWIN32SetBinary(sys.stdout)
  313.         elif options.hexdump:
  314.             DumpFunction = HexDump
  315.         elif options.vbadecompress:
  316.             DumpFunction = SearchAndDecompress            
  317.         elif options.extract:
  318.             DumpFunction = Extract
  319.             IfWIN32SetBinary(sys.stdout)
  320.         elif options.info:
  321.             DumpFunction = Info
  322.         else:
  323.             DumpFunction = HexAsciiDump
  324.         counter = 1
  325.         for fname in ole.listdir():
  326.             if counter == int(options.select):
  327.                 StdoutWriteChunked(DumpFunction(ole.openstream(fname).read()))
  328.                 break
  329.             counter += 1
  330.     ole.close()
  331.  
  332. def Main():
  333.     oParser = optparse.OptionParser(usage='usage: %prog [options] [file]\n' + __description__, version='%prog ' + __version__)
  334.     oParser.add_option('-s', '--select', default='', help='select item nr for dumping')
  335.     oParser.add_option('-d', '--dump', action='store_true', default=False, help='perform dump')
  336.     oParser.add_option('-x', '--hexdump', action='store_true', default=False, help='perform hex dump')
  337.     oParser.add_option('-a', '--asciidump', action='store_true', default=False, help='perform ascii dump')
  338.     oParser.add_option('-v', '--vbadecompress', action='store_true', default=False, help='VBA decompression')
  339.     oParser.add_option('-r', '--raw', action='store_true', default=False, help='raw file, attempt VBA decompression')
  340.     oParser.add_option('-e', '--extract', action='store_true', default=False, help='extract OLE embedded file')
  341.     oParser.add_option('-i', '--info', action='store_true', default=False, help='print extra info for selected item')
  342.     oParser.add_option('-m', '--macrodump', action='store_true', default=False, help='attempt to dump macro script')
  343.     (options, args) = oParser.parse_args()
  344.  
  345.     if len(args) > 1:
  346.         oParser.print_help()
  347.         print('')
  348.         print('  Source code put in the public domain by Didier Stevens, no Copyright')
  349.         print('  Use at your own risk')
  350.         print('  https://DidierStevens.com')
  351.         return
  352.     elif len(args) == 0:
  353.         OLEDump('', options)
  354.     else:
  355.         OLEDump(args[0], options)
  356.  
  357. if __name__ == '__main__':
  358.     Main()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top