227 lines
8.0 KiB
Python
Executable File
227 lines
8.0 KiB
Python
Executable File
"""
|
|
recognize.py - Function Recongnizing using heuristic patterns.
|
|
|
|
(c) Immunity, Inc. 2004-2007
|
|
|
|
|
|
U{Immunity Inc.<http://www.immunityinc.com>}
|
|
|
|
"""
|
|
|
|
|
|
__VERSION__ = '1.0'
|
|
import immlib
|
|
import immutils
|
|
import getopt
|
|
import string
|
|
import os
|
|
import csv
|
|
from librecognition import *
|
|
|
|
DESC="Function Recognizing using heuristic patterns."
|
|
|
|
def usage(imm):
|
|
imm.Log("!recognize -{a|m} -n name [ -x address ] [ -i filename ] [-v version/extra]")
|
|
imm.Log("!recognize -d [ -i filename ] -n name")
|
|
imm.Log("!recognize -l [-i filename] [-n name]")
|
|
imm.Log("!recognize -f -n name [-i filename] [-v version/extra] [-o module] [-h heuristic_threasold]")
|
|
imm.Log("!recognize -r -x address [-i filename] [-h heuristic_threasold]")
|
|
imm.Log(" ex (find a pattern, accept 80%% of match): !recognize -f -n iTunes.AntiDebuggers -h 80 -o iTunes.exe")
|
|
imm.Log(" ex (resolv an address, accept 93%% of match): !recognize -r -x 004EDE00 -h 93")
|
|
imm.Log(" ex (add a pattern): !recognize -a -x 004EDE00 -n iTunes.AntiDebuggers -i itunes.dat -v 7.4.1")
|
|
imm.Log(" ex (add a pattern guessing the address from labels or symbols): !recognize -a -n _SPExternalAlloc@4")
|
|
imm.Log(" ex (modify a pattern): !recognize -m -x 004EDE00 -n iTunes.AntiDebuggers -i itunes.dat -v protections_disabled")
|
|
imm.Log(" ex (delete a pattern): !recognize -d -i itunes.dat -n iTunes.AntiDebuggers")
|
|
imm.Log(" ex (list patterns): !recognize -l -i itunes.dat -n antidebug", focus=1)
|
|
return ""
|
|
|
|
def main(args):
|
|
imm = immlib.Debugger()
|
|
|
|
imm.Log("################# Immunity's Function Recognizing ################")
|
|
imm.markBegin()
|
|
|
|
if not args:
|
|
usage(imm)
|
|
return "not enough args"
|
|
|
|
try:
|
|
opts, notused = getopt.getopt(args, "amdlfrx:n:i:h:v:o:")
|
|
except getopt.GetoptError:
|
|
usage(imm)
|
|
return "Wrong Arguments (Check usage on the Log Window)"
|
|
|
|
defaultfilename = os.path.join("Data", "default.dat")
|
|
name = address = id = action = module = filename = None
|
|
version = ""
|
|
heuristic = 90
|
|
for o,a in opts:
|
|
if o == '-x':
|
|
address = imm.getAddress(a)
|
|
if address < 0:
|
|
imm.Log("invalid address or expresion")
|
|
usage(imm)
|
|
return "address error!"
|
|
if o == '-o':
|
|
module = a
|
|
if o == '-n':
|
|
name = string.strip(a, " '\"\\{}%;,")
|
|
if o == "-i":
|
|
filename = os.path.basename(string.strip(a, " '\"{}%;,"))+".dat"
|
|
if not filename:
|
|
usage(imm)
|
|
return "invalid filename"
|
|
filename = os.path.join("Data",filename)
|
|
if o == '-v':
|
|
version = string.strip(a, " '\"\\{}%;,")
|
|
if o == "-h":
|
|
try:
|
|
heuristic = int(a)
|
|
except:
|
|
imm.Log("invalid heuristic threasold")
|
|
usage(imm)
|
|
return "heuristic theashold error!"
|
|
if o in ["-a","-m","-d","-l","-f","-r"]:
|
|
action = o[1]
|
|
|
|
if not action:
|
|
usage(imm)
|
|
return "no action set"
|
|
|
|
#add/modify an element
|
|
if action == "a" or action == "m":
|
|
if not filename: filename = defaultfilename
|
|
if not name:
|
|
usage(imm)
|
|
return "insufficient arguments to add/modify an entry"
|
|
|
|
if not address:
|
|
tmp = imm.getAddressOfExpression(name)
|
|
if tmp > 0:
|
|
address = tmp
|
|
else:
|
|
return "name hasn't a known address"
|
|
|
|
modif = False
|
|
recon = FunctionRecognition(imm, filename)
|
|
for d in recon.dictionaries:
|
|
if name == d[0]:
|
|
if action == "a":
|
|
usage(imm)
|
|
return "the name '%s' is already in the selected dictionary" % name
|
|
if action == "m":
|
|
modif = True
|
|
break
|
|
if action == "m" and not modif:
|
|
usage(imm)
|
|
return "the name '%s' wasn't found in the selected dictionary" % name
|
|
|
|
tmp = recon.makeFunctionHash(address, compressed=True)
|
|
file = extractFile(imm, address)
|
|
definition = [ name, tmp[0], tmp[1][0], tmp[1][1], tmp[2], version, file, string.join(tmp[3],"|") ]
|
|
remakeDictionary(imm, recon, filename, definition, action)
|
|
imm.Log("Element '%s' added/modified" % name, focus=1)
|
|
|
|
#delete an element
|
|
if action == "d":
|
|
if not name:
|
|
usage(imm)
|
|
return "incomplete information to delete an element"
|
|
if not filename: filename = defaultfilename
|
|
delete = False
|
|
recon = FunctionRecognition(imm, filename)
|
|
for d in recon.dictionaries:
|
|
if name == d[0]:
|
|
delete = True
|
|
break
|
|
if not delete:
|
|
usage(imm)
|
|
return "the function '%s' wasn't found in the selected dictionary" % name
|
|
remakeDictionary(imm, recon, filename, name, action)
|
|
imm.Log("Element '%s' deleted" % name, focus=1)
|
|
|
|
#list elements
|
|
if action == "l":
|
|
recon = FunctionRecognition(imm, filename)
|
|
list = []
|
|
for values in recon.dictionaries:
|
|
if not name or name.lower() in values[0].lower():
|
|
list.append([values[0],values[5],values[6],values[4], os.path.basename(values[-1])[:-4]])
|
|
if not list:
|
|
return "the name '%s' wasn't found in the dictionaries" % name
|
|
else:
|
|
imm.Log("-" * 156)
|
|
imm.Log("|%-30s|%-40s|%-20s|%-40s|%-20s|" % ("real name","version/extra","binary file","SHA1","repository"))
|
|
imm.Log("-" * 156)
|
|
for v in list:
|
|
imm.Log("|%-30s|%-40s|%-20s|%-40s|%-20s|" % (v[0][0:30],v[1][0:40],v[2][0:20],v[3][0:40], v[4][0:20]), focus=1)
|
|
imm.Log("-" * 156)
|
|
|
|
#search for an element
|
|
if action == "f":
|
|
if not name:
|
|
usage(imm)
|
|
return "incomplete information to search"
|
|
|
|
#we need to maintain separated csv indexes
|
|
dict = FunctionRecognition(imm, filename)
|
|
recon = FunctionRecognition(imm, filename)
|
|
addy = None
|
|
for values in dict.dictionaries:
|
|
if name.lower() in values[0].lower():
|
|
tmp = recon.searchFunctionByName(values[0], heuristic, module, version)
|
|
if tmp:
|
|
for addy,heu in tmp:
|
|
imm.Log("Function '%s' address: %08X (%d%%)" % (values[0], addy,heu), addy, focus=1)
|
|
if addy:
|
|
imm.gotoDisasmWindow(addy)
|
|
else:
|
|
imm.Log("We can't find a function that fullfit all the requirements", focus=1)
|
|
|
|
#resolv an address to a function name
|
|
if action == "r":
|
|
if not address:
|
|
usage(imm)
|
|
return "we need an address to resolv"
|
|
|
|
recon = FunctionRecognition(imm, filename)
|
|
name = recon.resolvFunctionByAddress(address)
|
|
if name:
|
|
imm.Log("function at %08X FOUND: %s" % (address, name), address, focus=1)
|
|
imm.gotoDisasmWindow(address)
|
|
else:
|
|
imm.Log("function not found", focus=1)
|
|
|
|
return "Done in %d secs! see the log for details" % imm.markEnd()
|
|
|
|
def remakeDictionary(imm, recon, filename, data, action):
|
|
tmpfd = os.tmpfile()
|
|
writer = csv.writer(tmpfd)
|
|
if action == "a" or action == "m":
|
|
writer.writerow(data)
|
|
|
|
for row in recon.dictionaries:
|
|
row.pop() #drop the filename added by the CSV iterator (always the last element)
|
|
if action == "a":
|
|
writer.writerow(row)
|
|
if action == "m" and data[0] != row[0]:
|
|
writer.writerow(row)
|
|
if action == "d" and data != row[0]:
|
|
writer.writerow(row)
|
|
tmpfd.flush()
|
|
del recon
|
|
del writer
|
|
|
|
fd = open(filename, "wb")
|
|
tmpfd.seek(0)
|
|
for line in tmpfd:
|
|
fd.write(line)
|
|
tmpfd.close()
|
|
fd.close()
|
|
|
|
def extractFile(imm, address):
|
|
for mod in imm.getAllModules().values():
|
|
if mod.getBaseAddress() <= address and address <= mod.getBaseAddress()+mod.getSize():
|
|
return os.path.basename(mod.getPath())
|
|
return ""
|