428 lines
13 KiB
Python
Executable File
428 lines
13 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
"""
|
|
(c) Immunity, Inc. 2004-2007
|
|
|
|
|
|
U{Immunity Inc.<http://www.immunityinc.com>}
|
|
|
|
|
|
"""
|
|
|
|
__VERSION__ = '1.1'
|
|
|
|
import struct
|
|
import debugger
|
|
import pickle
|
|
|
|
FS_UNHOOK = 0
|
|
FS_HOOK = 1 # hooked and running
|
|
FS_PAUSE = 2
|
|
|
|
HookTypes = {"ORDINARY_BP_HOOK" : 3900, "LOG_BP_HOOK" : 3909,\
|
|
"EVERY_EXCEPTION_HOOK" : 3901,\
|
|
"POST_ANALYSIS_HOOK" : 3902, "ACCESS_VIOLATION_HOOK": 3910,\
|
|
"LOAD_DLL_HOOK" : 3903, "UNLOAD_DLL_HOOK" : 3904,\
|
|
"CREATE_THREAD_HOOK" : 3905, "EXIT_THREAD_HOOK" : 3906,\
|
|
"CREATE_PROCESS_HOOK" : 3907, "EXIT_PROCESS_HOOK" : 3908,\
|
|
"PRE_BP_HOOK" : 3911}
|
|
|
|
HOOK_REG = {'ESI': '[ESP+4 ]', 'EDI': '[ESP]',\
|
|
'EBX': '[ESP+0x10]', 'EAX': '[ESP+0x1C]',\
|
|
'ECX': '[ESP+0x18]', 'EDX': '[ESP+0x14]',\
|
|
'EBP': '[ESP+0x8 ]', 'ESP': '[ESP+0xC ]'}
|
|
|
|
|
|
class FastLogHook:
|
|
def __init__(self, imm):
|
|
self.address = None
|
|
self.tbl = []
|
|
self.list = []
|
|
self.entry = []
|
|
self.hooked = False
|
|
self.mem = None
|
|
self.imm = imm
|
|
self.restore = []
|
|
self.status = FS_UNHOOK
|
|
self.AllocSize = 0
|
|
self.memAddress = 0
|
|
|
|
def isHooked(self):
|
|
return self.status == FS_HOOK
|
|
|
|
def isPause(self):
|
|
return self.status == FS_PAUSE
|
|
|
|
def Pause(self):
|
|
if not self.isHooked():
|
|
return False
|
|
|
|
# Removing Hook on every function
|
|
for ndx in range(0, len(self.tbl) ):
|
|
self.imm.writeMemory( self.tbl[ndx][0], self.restore[ndx][0] )
|
|
|
|
self.status = FS_PAUSE
|
|
return True
|
|
|
|
def Continue(self):
|
|
if not self.isPause():
|
|
return False
|
|
|
|
for ndx in range(0, len(self.tbl) ):
|
|
self.imm.writeMemory( self.tbl[ndx][0], self.restore[ndx][1] )
|
|
self.status = FS_HOOK
|
|
return True
|
|
|
|
def unHook(self):
|
|
if not self.isHooked():
|
|
return False
|
|
|
|
# Removing Hook on every function
|
|
for ndx in range(0, len(self.tbl) ):
|
|
self.imm.writeMemory( self.tbl[ndx][0], self.restore[ndx][0] ) # Cleaning up Hook Memory
|
|
self.imm.rVirtualFree( self.mem )
|
|
self.status = FS_UNHOOK
|
|
return True
|
|
|
|
def setRestore(self, restore):
|
|
self.restore = restore
|
|
|
|
def Hook(self):
|
|
self.addFastLogHook()
|
|
self.status = FS_HOOK
|
|
return True
|
|
|
|
def setMem(self, mem):
|
|
self.mem = mem
|
|
|
|
def logFunction(self, address):
|
|
if self.address:
|
|
self.tbl.append( (self.address, self.entry) )
|
|
self.entry = []
|
|
self.address = address
|
|
|
|
def logRegister(self, REG):
|
|
self.entry.append( (REG,) )
|
|
|
|
def logDirectMemory(self, address):
|
|
self.entry.append( (address,) )
|
|
|
|
def logBaseDisplacement(self, REG, offset = 0 ):
|
|
self.entry.append( ( REG, offset) )
|
|
|
|
def getAllUniqueFunctions(self):
|
|
ndx = 0
|
|
addr = self.mem
|
|
self._fn = {}
|
|
self.ret = []
|
|
|
|
while ndx != -1 :
|
|
mem = self.imm.readMemory( addr, 0x1000)
|
|
ndx = self._parseUniqueFn( mem )
|
|
addr += ndx
|
|
|
|
return self._fn
|
|
|
|
def getAllLog(self):
|
|
mem = ""
|
|
self.ret = []
|
|
flag = False
|
|
addr = self.mem
|
|
end = self.imm.readLong(self.memAddress)
|
|
|
|
mem = self.imm.readMemory( addr, end-addr)
|
|
self._parseMem( mem )
|
|
|
|
return self.ret
|
|
|
|
def _parseUniqueFn(self, mem):
|
|
mem_size = len(mem)
|
|
ndx = 0
|
|
while ndx < len(mem):
|
|
index = struct.unpack("L", mem[ ndx : ndx+4 ] )[0]
|
|
if index == 0:
|
|
return -1 # Finished correctly
|
|
if index > (len(self.tbl) + 1) :
|
|
return -1
|
|
|
|
entry = self.tbl[ index -1 ][1]
|
|
ndx += 4
|
|
size_e = len(entry)
|
|
if (size_e*4 + ndx) > ( mem_size):
|
|
return ndx - 4 # REQUEST MORE MEM
|
|
ndx += size_e * 4
|
|
|
|
addr = self.tbl[ index -1 ][0]
|
|
if self._fn.has_key( addr ):
|
|
self._fn[ addr ] += 1
|
|
else:
|
|
self._fn[ addr ] = 1
|
|
return ndx
|
|
|
|
|
|
def Clear(self):
|
|
#self.imm.writeLong( self.mem, 0x0 )
|
|
self.imm.writeLong( self.memAddress, self.mem )
|
|
|
|
|
|
def _parseMem(self, mem):
|
|
mem_size = len(mem)
|
|
ndx = 0
|
|
#self.imm.log("table: %d" % len(self.tbl) )
|
|
while ndx < len(mem) :
|
|
index = struct.unpack("L", mem[ ndx : ndx+4 ] )[0]
|
|
#self.imm.log("Index: %d" % index)
|
|
if index == 0:
|
|
return -1 # Finished correctly
|
|
if index > (len(self.tbl) + 1) :
|
|
return -1
|
|
|
|
entry = self.tbl[ index -1 ][1]
|
|
ndx += 4
|
|
size_e = len(entry)
|
|
if (size_e*4 + ndx) > ( mem_size):
|
|
return ndx - 4 # REQUEST MORE MEM
|
|
ret = struct.unpack( "L" * size_e, mem[ ndx : ndx + size_e *4 ] )
|
|
ndx += size_e * 4
|
|
self.ret.append( ( self.tbl[ index - 1 ][0], ret) )
|
|
return ndx
|
|
|
|
def get(self):
|
|
self.logFunction(None)
|
|
return self.tbl
|
|
|
|
def setTable(self, tbl):
|
|
self.tbl = tbl
|
|
|
|
def addFastLogHook(self, alloc_size = 0x100000, memAddress = 0x0):
|
|
CODE_HOOK_START = 8
|
|
#flh = hook
|
|
# Get the table of functions from the hook
|
|
self.AllocSize = alloc_size
|
|
|
|
table = self.get()
|
|
self.imm.log("TABLE SIZE: %d" % len(table) )
|
|
# Allocate memory for the hook and the log
|
|
if not memAddress:
|
|
memAddress = self.imm.remoteVirtualAlloc( alloc_size )
|
|
|
|
self.memAddress = memAddress
|
|
|
|
self.imm.log( "Logging at 0x%08x" % memAddress )
|
|
|
|
# MEMORY LOOKS LIKE:
|
|
# mem [ ptr to data ]
|
|
# mem + 4 [ deadlock ]
|
|
# mem + 8 [ start of hook code ]
|
|
# mem + n [ ... ]
|
|
# mem + n [ start of data ]
|
|
|
|
ptr = memAddress + CODE_HOOK_START
|
|
|
|
fn_restore = []
|
|
|
|
# for fn_ndx in range( 0, len(table) ):
|
|
fn_ndx = 0
|
|
while fn_ndx < len(table) :
|
|
hookAddress = table[ fn_ndx ][0]
|
|
entry = table[ fn_ndx ][1]
|
|
|
|
idx = 0
|
|
#patch_code = self.imm.assemble( "PUSH 0x%08x\nRET" % ptr )
|
|
patch_code = self.imm.assemble( "JMP 0x%08x" % ptr, address = hookAddress )
|
|
|
|
while idx < len(patch_code):
|
|
op = self.imm.disasm( hookAddress + idx )
|
|
idx += op.getOpSize()
|
|
if op.isCall() or op.isJmp():
|
|
op = None
|
|
break
|
|
|
|
# Removing the BP from the table
|
|
if not op:
|
|
self.imm.log("deleting: %d" % fn_ndx)
|
|
del table[ fn_ndx ]
|
|
continue
|
|
|
|
ex_prelude = self.imm.readMemory( hookAddress, idx )
|
|
|
|
code = self.imm._createCodeforHook( memAddress, hookAddress + idx,\
|
|
fn_ndx + 1, entry, ex_prelude, alloc_size)
|
|
|
|
self.imm.writeMemory( ptr , code )
|
|
ptr += len(code)
|
|
self.imm.writeMemory( hookAddress, patch_code )
|
|
|
|
fn_restore.append( (ex_prelude, patch_code ) ) # Correspond in index with function address
|
|
fn_ndx += 1
|
|
|
|
self.setTable( table )
|
|
if ptr % 4:
|
|
ptr = 4 + ptr & ~(4-1)
|
|
self.setMem( ptr )
|
|
self.imm.writeLong( memAddress, ptr )
|
|
self.setRestore( fn_restore )
|
|
|
|
|
|
|
|
class STDCALLFastLogHook(FastLogHook):
|
|
def __init__(self, imm):
|
|
FastLogHook.__init__(self, imm)
|
|
def logFunction(self, address, args = 0 ):
|
|
if self.address:
|
|
self.tbl.append( (self.address, self.entry) )
|
|
self.entry = []
|
|
|
|
self.address = address
|
|
for ndx in range(0, args):
|
|
self.logBaseDisplacement( "ESP", ndx*4 + 4 )
|
|
|
|
#HOOK class
|
|
class Hook:
|
|
def __init__(self):
|
|
self.type=0
|
|
self.msg=""
|
|
self.string=""
|
|
self.address=0
|
|
self.enabled=True # by default hook is enabled
|
|
|
|
def enable(self):
|
|
"""Enable hook execution"""
|
|
self.enabled=True
|
|
|
|
def disable(self):
|
|
"""Disable hook execution"""
|
|
self.enabled=False
|
|
|
|
def UnHook(self):
|
|
"""Remove the hook"""
|
|
debugger.remove_hook(self.desc)
|
|
|
|
def add(self,description,address=0,force=0,timeout=0,mode=0):
|
|
"""Add hook to Immunity Debugger hook database
|
|
@param type: Type of hook
|
|
@param desc: Descriptive string
|
|
@param force: Force hook adding
|
|
@param timeout: time to live in memory
|
|
@param mode: thread mode of ttl execution
|
|
"""
|
|
|
|
self.desc = description
|
|
self.address = address
|
|
self.force=force
|
|
self.timeout=timeout
|
|
# mode = 1 then, execute ttl hook in the same thread enviroment as the python command/script
|
|
# mode = 0 use your own thread enviroment to place and execute the ttl hook
|
|
# you'll be using mode = 0 at least you really know what you are doing.
|
|
|
|
self.mode=mode
|
|
if self.type == HookTypes["ORDINARY_BP_HOOK"]:
|
|
debugger.set_breakpoint(self.address,0x200L,"")
|
|
elif self.type == HookTypes["LOG_BP_HOOK"]:
|
|
debugger.set_logging_breakpoint(self.address)
|
|
pickled_object = pickle.dumps(self)
|
|
return debugger.add_hook( pickled_object , self.desc , self.type, self.address,self.force,self.timeout,self.mode)
|
|
|
|
def _run(self,regs):
|
|
"""regs is the actual cpu context, be sure of using this values
|
|
and not the ones from imm.getRegs() at hook time"""
|
|
self.regs=regs
|
|
self.run(regs)
|
|
|
|
def _runTimeout(self,regs):
|
|
"""regs is the actual cpu context, be sure of using this values
|
|
and not the ones from imm.getRegs() at hook time"""
|
|
self.regs=regs
|
|
self.runTimeout(regs)
|
|
|
|
|
|
# function that will be runned once the hook is triggered
|
|
def run(self,regs):
|
|
debugger.Error("Your hook doesnt seem to have run() defined")
|
|
return
|
|
|
|
def runTimeout(self,regs):
|
|
debugger.Error("Your hook doesnt seem to have runTimeout() defined")
|
|
return
|
|
|
|
|
|
class BpHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["ORDINARY_BP_HOOK"]
|
|
self.desc = "BreakpointHook"
|
|
|
|
class LogBpHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["LOG_BP_HOOK"]
|
|
self.desc = "LoggingPointHook"
|
|
|
|
class PreBpHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["PRE_BP_HOOK"]
|
|
self.desc = "PreBreakpointHook"
|
|
|
|
class AllExceptHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["EVERY_EXCEPTION_HOOK"]
|
|
self.desc = "EveryExceptionHook"
|
|
|
|
class PostAnalysisHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["POST_ANALYSIS_HOOK"]
|
|
self.desc = "PostAnalysisHook"
|
|
|
|
class AccessViolationHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["ACCESS_VIOLATION_HOOK"]
|
|
self.desc = "AcessViolationHook"
|
|
|
|
class RunUntilAV(Hook):
|
|
def __init__(self,imm):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["ACCESS_VIOLATION_HOOK"]
|
|
self.desc = "AcessViolationHook"
|
|
imm.run()
|
|
|
|
|
|
class LoadDLLHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["LOAD_DLL_HOOK"]
|
|
self.desc = "LoadDLLHook"
|
|
|
|
class UnloadDLLHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["UNLOAD_DLL_HOOK"]
|
|
self.desc = "UnloadDLLHook"
|
|
|
|
class CreateThreadHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["CREATE_THREAD_HOOK"]
|
|
self.desc = "CreateThreadHook"
|
|
|
|
class ExitThreadHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["EXIT_THREAD_HOOK"]
|
|
self.desc = "ExitThreadHook"
|
|
|
|
class CreateProcessHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["CREATE_PROCESS_HOOK"]
|
|
self.desc = "CreateProcessHook"
|
|
|
|
class ExitProcessHook(Hook):
|
|
def __init__(self):
|
|
Hook.__init__(self)
|
|
self.type = HookTypes["EXIT_PROCESS_HOOK"]
|
|
self.desc = "ExitProcessHook"
|