Skip to content

Commit

Permalink
20151106a
Browse files Browse the repository at this point in the history
  • Loading branch information
DidierStevens committed Nov 6, 2015
1 parent 359bfd3 commit 7f9c878
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 6 deletions.
120 changes: 117 additions & 3 deletions oledump.py
Expand Up @@ -2,8 +2,8 @@

__description__ = 'Analyze OLE files (Compound Binary Files)'
__author__ = 'Didier Stevens'
__version__ = '0.0.18'
__date__ = '2015/09/22'
__version__ = '0.0.19'
__date__ = '2015/10/30'

"""
Expand Down Expand Up @@ -54,6 +54,7 @@
2015/09/16: Rename old OleFileIO_PL to new olefile so that local copy of the module can be used
2015/09/17: added help for pip install olefile
2015/09/22: fixed os.path.isfile(filename) bug
2015/10/30: 0.0.19 added option -E and environment variable OLEDUMP_EXTRA; added MD5 to option -i
Todo:
"""
Expand All @@ -70,6 +71,7 @@
import hashlib
import textwrap
import re
import string

try:
import yara
Expand Down Expand Up @@ -238,6 +240,7 @@ def PrintManual():
String 2: C:\Demo\ole\CALC-R~1.EXE
String 3: C:\Demo\ole\CALC-R~1.EXE
Size embedded file: 114688
MD5 embedded file: bef425b95e45c54d649a19a7c55556a0
To extract the embedded file, use option -e and redirect the output to a file like this:
C:\Demo>oledump.py -s 6 -e Book1-insert-object-calc-rol3.exe.xls > extracted.bin
Expand Down Expand Up @@ -654,7 +657,7 @@ 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]))
return 'String 1: %s\nString 2: %s\nString 3: %s\nSize embedded file: %d\nMD5 embedded file: %s\n' % (result[0], result[1], result[2], len(result[3]), hashlib.md5(result[3]).hexdigest())

def IfWIN32SetBinary(io):
if sys.platform == 'win32':
Expand Down Expand Up @@ -1045,6 +1048,110 @@ def CutData(stream, cutArgument):

return stream[positionBegin:positionEnd]


def ExtraInfoMD5(data):
return hashlib.md5(data).hexdigest()

def ExtraInfoSHA1(data):
return hashlib.sha1(data).hexdigest()

def ExtraInfoSHA256(data):
return hashlib.sha256(data).hexdigest()

def CalculateByteStatistics(dPrevalence):
sumValues = sum(dPrevalence.values())
countNullByte = dPrevalence[0]
countControlBytes = 0
countWhitespaceBytes = 0
for iter in range(1, 0x21):
if chr(iter) in string.whitespace:
countWhitespaceBytes += dPrevalence[iter]
else:
countControlBytes += dPrevalence[iter]
countControlBytes += dPrevalence[0x7F]
countPrintableBytes = 0
for iter in range(0x21, 0x7F):
countPrintableBytes += dPrevalence[iter]
countHighBytes = 0
for iter in range(0x80, 0x100):
countHighBytes += dPrevalence[iter]
entropy = 0.0
for iter in range(0x100):
if dPrevalence[iter] > 0:
prevalence = float(dPrevalence[iter]) / float(sumValues)
entropy += - prevalence * math.log(prevalence, 2)
return sumValues, entropy, countNullByte, countControlBytes, countWhitespaceBytes, countPrintableBytes, countHighBytes

def ExtraInfoENTROPY(data):
dPrevalence = {iter: 0 for iter in range(0x100)}
for char in data:
dPrevalence[ord(char)] += 1
sumValues, entropy, countNullByte, countControlBytes, countWhitespaceBytes, countPrintableBytes, countHighBytes = CalculateByteStatistics(dPrevalence)
return '%f' % entropy

def ExtraInfoHEADHEX(data):
return binascii.hexlify(data[:16])

def ExtraInfoHEADASCII(data):
return ''.join([IFF(ord(b) >= 32, b, '.') for b in data[:16]])

def ExtraInfoTAILHEX(data):
return binascii.hexlify(data[-16:])

def ExtraInfoTAILASCII(data):
return ''.join([IFF(ord(b) >= 32, b, '.') for b in data[-16:]])

def ExtraInfoHISTOGRAM(data):
dPrevalence = {iter: 0 for iter in range(0x100)}
for char in data:
dPrevalence[ord(char)] += 1
result = []
count = 0
minimum = None
maximum = None
for iter in range(0x100):
if dPrevalence[iter] > 0:
result.append('0x%02x:%d' % (iter, dPrevalence[iter]))
count += 1
if minimum == None:
minimum = iter
else:
minimum = min(minimum, iter)
if maximum == None:
maximum = iter
else:
maximum = max(maximum, iter)
result.insert(0, '%d' % count)
result.insert(1, IFF(minimum == None, '', '0x%02x' % minimum))
result.insert(2, IFF(maximum == None, '', '0x%02x' % maximum))
return ','.join(result)

def ExtraInfoBYTESTATS(data):
dPrevalence = {iter: 0 for iter in range(0x100)}
for char in data:
dPrevalence[ord(char)] += 1
sumValues, entropy, countNullByte, countControlBytes, countWhitespaceBytes, countPrintableBytes, countHighBytes = CalculateByteStatistics(dPrevalence)
return '%d,%d,%d,%d,%d' % (countNullByte, countControlBytes, countWhitespaceBytes, countPrintableBytes, countHighBytes)

def GenerateExtraInfo(extra, stream):
if extra == '':
return ''
dExtras = {'%MD5%': ExtraInfoMD5,
'%SHA1%': ExtraInfoSHA1,
'%SHA256%': ExtraInfoSHA256,
'%ENTROPY%': ExtraInfoENTROPY,
'%HEADHEX%': ExtraInfoHEADHEX,
'%HEADASCII%': ExtraInfoHEADASCII,
'%TAILHEX%': ExtraInfoTAILHEX,
'%TAILASCII%': ExtraInfoTAILASCII,
'%HISTOGRAM%': ExtraInfoHISTOGRAM,
'%BYTESTATS%': ExtraInfoBYTESTATS,
}
for variable in dExtras:
if variable in extra:
extra = extra.replace(variable, dExtras[variable](stream))
return ' ' + extra.replace(r'\t', '\t').replace(r'\n', '\n')

def OLESub(ole, prefix, rules, options):
global plugins
global decoders
Expand Down Expand Up @@ -1096,6 +1203,7 @@ def OLESub(ole, prefix, rules, options):
line = '%3s: %s %s %s' % (('%s%d' % (prefix, counter)), indicator, lenghString, PrintableName(fname))
if options.calc:
line += ' %s' % hashlib.md5(stream).hexdigest()
line += GenerateExtraInfo(options.extra, stream)
print(line)
for cPlugin in plugins:
try:
Expand Down Expand Up @@ -1323,6 +1431,9 @@ def OLEDump(filename, options):

return returnCode

def OptionsEnvironmentVariables(options):
options.extra = os.getenv('OLEDUMP_EXTRA', options.extra)

def Main():
oParser = optparse.OptionParser(usage='usage: %prog [options] [file]\n' + __description__, version='%prog ' + __version__)
oParser.add_option('-m', '--man', action='store_true', default=False, help='Print manual')
Expand All @@ -1347,6 +1458,7 @@ def Main():
oParser.add_option('--decompress', action='store_true', default=False, help='Search for compressed data in the stream and decompress it')
oParser.add_option('-V', '--verbose', action='store_true', default=False, help='verbose output with decoder errors')
oParser.add_option('-C', '--cut', type=str, default='', help='cut data')
oParser.add_option('-E', '--extra', type=str, default='', help='add extra info (environment variable: OLEDUMP_EXTRA)')
(options, args) = oParser.parse_args()

if options.man:
Expand All @@ -1358,6 +1470,8 @@ def Main():
print('Error: the expression of the cut option (-C) is invalid: %s' % options.cut)
return 0

OptionsEnvironmentVariables(options)

if len(args) > 1:
oParser.print_help()
print('')
Expand Down
43 changes: 40 additions & 3 deletions plugin_dridex.py
Expand Up @@ -2,8 +2,8 @@

__description__ = 'Dridex plugin for oledump.py'
__author__ = 'Didier Stevens'
__version__ = '0.0.8'
__date__ = '2015/05/14'
__version__ = '0.0.9'
__date__ = '2015/11/06'

"""
Expand All @@ -20,12 +20,14 @@
2015/04/08: 0.0.6 added KALLKKKASKAJJAS, based on sample 491A146F5DE3592C7D959E2869F259EF
2015/04/09: 0.0.7 used KALLKKKASKAJJAS, based on sample 14C2795BCC35C3180649494EC2BC7877
2015/04/08: 0.0.8 added GQQSfwKSTdAvZbHNhpfK, based on sample 39B38CE4E2E8D843F88C3DF9124527FC
2015/11/06: 0.0.9 added chr support; added IpkfHKQ2Sd, based on sample 0E73D64FBDF6C87935C0CFF9E65FA3BE
Todo:
"""

import re
import binascii
import array

def RoV(InputStringToBeDecrypted):
strTempText = InputStringToBeDecrypted
Expand Down Expand Up @@ -147,6 +149,35 @@ def GQQSfwKSTdAvZbHNhpfK(strData, strKey):

return result

def IpkfHKQ2Sd(secret, key):
aTable = array.array('i', [0] * (285 + 1))
aSecret = array.array('i', [0] * len(secret))
keyLength = len(key) - 1
for iIter in range(0, 255 + 1):
aTable[iIter] = iIter
for iIter in range(256, 285 + 1):
aTable[iIter] = iIter ^ 256
for iIter in range(1, 6 + 1):
aTable[iIter + 249] = ord(key[keyLength - iIter])
aTable[iIter - 1] = ord(key[iIter - 1]) ^ (255 - ord(key[keyLength - iIter]))

bCondition = False
indexKey = 0
indexTable = 0
for iIter in range(0, len(secret) - 1 + 1):
if indexKey > keyLength:
indexKey = 0
if indexTable > 285 and bCondition == False:
indexTable = 0
bCondition = not bCondition
if indexTable > 285 and bCondition == True:
indexTable = 5
bCondition = not bCondition
aSecret[iIter] = ord(secret[iIter]) ^ (aTable[indexTable] ^ ord(key[indexKey]))
indexKey = indexKey + 1
indexTable = indexTable + 1
return ''.join(map(chr, aSecret))

class cDridexDecoder(cPluginParent):
macroOnly = True
name = 'Dridex decoder'
Expand All @@ -162,6 +193,12 @@ def Analyze(self):

oREString = re.compile(r'"([^"\n]+)"')
foundStrings = oREString.findall(self.stream)
oREChr = re.compile(r'((chr[w\$]?\(\d+\)(\s*[&+]\s*)?)+)', re.IGNORECASE)
oREDigits = re.compile(r'\d+')
for foundTuple in oREChr.findall(self.stream.replace('_\r\n', '')):
chrString = ''.join(map(lambda x: chr(int(x)), oREDigits.findall(foundTuple[0])))
if chrString != '':
foundStrings.append(chrString)

for DecodingFunction in [RoV, lambda s:OlFdL0IOXbF(s, 61), NewQkeTzIIHM, lambda s:Xor(s, 0xFF), lambda s:Step(s, 2)]:
result = []
Expand All @@ -177,7 +214,7 @@ def Analyze(self):
foundStringsSmall = [foundString for foundString in foundStrings if len(foundString) <= 10]
foundStringsLarge = [foundString for foundString in foundStrings if len(foundString) > 10]
for foundStringSmall in foundStringsSmall:
for DecodingFunction in [lqjWjFO, GQQSfwKSTdAvZbHNhpfK]:
for DecodingFunction in [lqjWjFO, GQQSfwKSTdAvZbHNhpfK, IpkfHKQ2Sd]:
result = []
for foundStringLarge in foundStringsLarge:
try:
Expand Down

0 comments on commit 7f9c878

Please sign in to comment.