mirror of
https://github.com/vivisect/vivisect
synced 2026-06-08 18:04:23 +00:00
9534f16495
* wip * remove * fixes * more * Fix --------- Co-authored-by: atlas0fd00m <atlas@r4780y.com>
969 lines
31 KiB
Python
969 lines
31 KiB
Python
"""
|
|
The vivisect CLI.
|
|
"""
|
|
import re
|
|
import sys
|
|
import shlex
|
|
import pprint
|
|
import socket
|
|
import logging
|
|
import traceback
|
|
import collections
|
|
from getopt import getopt
|
|
|
|
import vtrace
|
|
import vivisect
|
|
import vivisect.vamp as viv_vamp
|
|
import vivisect.vector as viv_vector
|
|
import vivisect.reports as viv_reports
|
|
import vivisect.tools.graphutil as viv_graph
|
|
|
|
import vivisect.tools.fscope as v_t_fscope
|
|
import vivisect.tools.graphutil as v_t_graph
|
|
|
|
import visgraph.pathcore as vg_path
|
|
|
|
import vtrace.lockstep as vt_lockstep
|
|
|
|
import vdb
|
|
|
|
import envi.exc as e_exc
|
|
import envi.cli as e_cli
|
|
import envi.common as e_common
|
|
import envi.memory as e_memory
|
|
import envi.expression as e_expr
|
|
import envi.memcanvas as e_canvas
|
|
import envi.memcanvas.renderers as e_render
|
|
|
|
from vivisect.const import *
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class VivCli(e_cli.EnviCli, vivisect.VivWorkspace):
|
|
'''
|
|
A class that builds upon the VivWorkspace to provide command line capabilities so that
|
|
things like the Vivisect UI can provide a cleaner interface that just a direct python
|
|
shell. It inherits the same parameters as the VivWorkspace (autosave and confdir currently)
|
|
|
|
To add a new command, simply add a new function called `do_<cmdname>`.
|
|
'''
|
|
def __init__(self, **kwargs):
|
|
e_cli.EnviCli.__init__(self, self, symobj=self)
|
|
vivisect.VivWorkspace.__init__(self, **kwargs)
|
|
self.canvas.addRenderer("bytes", e_render.ByteRend())
|
|
self.canvas.addRenderer("u_int_16", e_render.ShortRend())
|
|
self.canvas.addRenderer("u_int_32", e_render.LongRend())
|
|
self.canvas.addRenderer("u_int_64", e_render.QuadRend())
|
|
import vivisect.renderers as viv_rend
|
|
self.canvas.addRenderer("viv", viv_rend.WorkspaceRenderer(self))
|
|
self.prompt = "viv> "
|
|
self.addScriptPathEnvVar('VIV_SCRIPT_PATH')
|
|
|
|
def getExpressionLocals(self):
|
|
locs = e_cli.EnviCli.getExpressionLocals(self)
|
|
locs['vw'] = self
|
|
locs['vprint'] = self.vprint
|
|
locs['vivisect'] = vivisect
|
|
return locs
|
|
|
|
def do_report(self, line):
|
|
"""
|
|
Fire a report module by python path name.
|
|
|
|
Usage: report <python.path.to.report.module>
|
|
"""
|
|
if not line:
|
|
self.vprint("Report Modules")
|
|
for descr, modname in viv_reports.listReportModules():
|
|
self.vprint("Path: %32s (Name: %s)" % (modname, descr))
|
|
return
|
|
|
|
cols, results = viv_reports.runReportModule(self, line)
|
|
for va, row in results.items():
|
|
self.canvas.addVaText("%s:\n" % self.getName(va), va)
|
|
|
|
for indx in range(len(cols)):
|
|
valu = row[indx]
|
|
name, typename = cols[indx]
|
|
self.canvas.addVaText(" " + name, va)
|
|
self.canvas.addText(": %s\n" % valu)
|
|
self.canvas.addText("\n")
|
|
|
|
def do_pathcount(self, line):
|
|
'''
|
|
Mostly for testing the graph stuff... this will likely be removed.
|
|
|
|
(does not count paths with loops currently...)
|
|
|
|
Usage: pathcount <func_expr>
|
|
'''
|
|
if not line:
|
|
return self.do_help('pathcount')
|
|
try:
|
|
fva = self.parseExpression(line)
|
|
if not self.isFunction(fva):
|
|
self.vprint('Not a function!')
|
|
return
|
|
|
|
except Exception as e:
|
|
self.vprint(str(e))
|
|
return
|
|
|
|
g = v_t_graph.buildFunctionGraph(self, fva)
|
|
pathcnt = 0
|
|
for path in v_t_graph.getCodePaths(g):
|
|
self.vprint('Path through 0x%.8x: %s' % (fva, [hex(p[0]) for p in path]))
|
|
pathcnt += 1
|
|
self.vprint('Total Paths: %d' % pathcnt)
|
|
|
|
def do_symboliks(self, line):
|
|
'''
|
|
Use the new symboliks subsystem. (NOTE: i386 only for a bit...)
|
|
|
|
Usage: symboliks [ options ]
|
|
|
|
-A Run the emu and show the state of the machine for all found paths
|
|
to the given address
|
|
|
|
'''
|
|
if not line:
|
|
return self.do_help("symboliks")
|
|
|
|
watchaddr = None
|
|
|
|
argv = e_cli.splitargs(line)
|
|
try:
|
|
opts, argv = getopt(argv, 'A:')
|
|
except Exception:
|
|
return self.do_help('symboliks')
|
|
|
|
for opt, optarg in opts:
|
|
if opt == '-A':
|
|
# TODO: USE THIS
|
|
watchaddr = self.parseExpression(optarg)
|
|
|
|
va = self.parseExpression(argv[0])
|
|
fva = self.getFunction(va)
|
|
|
|
import vivisect.symboliks as viv_symboliks
|
|
import vivisect.symboliks.common as sym_common
|
|
import vivisect.symboliks.effects as viv_sym_effects
|
|
import vivisect.symboliks.analysis as vsym_analysis
|
|
|
|
symctx = vsym_analysis.getSymbolikAnalysisContext(self)
|
|
|
|
for emu, effects in symctx.getSymbolikPaths(fva):
|
|
|
|
self.vprint('PATH %s' % ('='*60))
|
|
|
|
for eff in effects:
|
|
|
|
eff.reduce(emu)
|
|
if eff.efftype in (EFFTYPE_CONSTRAIN, EFFTYPE_CALLFUNC):
|
|
self.vprint(str(eff))
|
|
|
|
for addrsym, valsym in emu._sym_mem.values():
|
|
addrsym = addrsym.reduce(emu=emu)
|
|
valsym = valsym.reduce(emu=emu)
|
|
if emu.isLocalMemory(addrsym):
|
|
continue
|
|
self.vprint('[ %s ] = %s' % (addrsym, valsym))
|
|
self.vprint('RETURN %r' % emu.getFunctionReturn().reduce())
|
|
|
|
def do_names(self, line):
|
|
'''
|
|
Show any names which contain the given argument.
|
|
|
|
Usage: names <name_regex>
|
|
|
|
FIXME unify do_sym from vdb into symbol context!
|
|
'''
|
|
if not line:
|
|
return self.do_help('names')
|
|
|
|
regex = re.compile(line, re.I)
|
|
for va, name in self.getNames():
|
|
if regex.search(name):
|
|
ftup = self.getFileAndOffset(va)
|
|
if ftup:
|
|
fname, fbase, off = ftup
|
|
self.vprint('0x%.8x: %s (%r + 0x%x)' % (va, name, fname, off))
|
|
else:
|
|
self.vprint("WARNING: 0x%x: %r doesn't exist in a file!" % (va, name))
|
|
|
|
def do_save(self, line):
|
|
"""
|
|
Save the current workspace.
|
|
|
|
Usage: save
|
|
"""
|
|
self.vprint("Saving workspace...")
|
|
self.saveWorkspace()
|
|
self.vprint("...save complete!")
|
|
|
|
def do_xrefs(self, line):
|
|
"""
|
|
Show xrefs for a particular location.
|
|
|
|
Usage: xrefs [options] <va_expr>
|
|
-T Show xrefs *to* the given address
|
|
-F Show xrefs *from* the given address (default)
|
|
"""
|
|
parser = e_cli.VOptionParser()
|
|
parser.add_option('-T', action='store_true', dest='xrto')
|
|
parser.add_option('-F', action='store_true', dest='xrfrom')
|
|
argv = shlex.split(line)
|
|
try:
|
|
options, argv = parser.parse_args(argv)
|
|
except Exception as e:
|
|
self.vprint(repr(e))
|
|
return self.do_help('xrefs')
|
|
|
|
if len(argv) < 1:
|
|
self.vprint('Supply a va_expr')
|
|
return self.do_help('xrefs')
|
|
|
|
va = self.parseExpression(argv[0])
|
|
|
|
fptr = []
|
|
if options.xrto:
|
|
fptr.append(self.getXrefsTo)
|
|
if options.xrfrom:
|
|
fptr.append(self.getXrefsFrom)
|
|
|
|
for func in fptr:
|
|
for xrfr, xrto, rtype, rflags in func(va):
|
|
tname = ref_type_names.get(rtype, 'Unknown')
|
|
self.vprint('\tFrom: 0x%.8x, To: 0x%.8x, Type: %s, Flags: 0x%.8x' % (xrfr, xrto, tname, rflags))
|
|
|
|
def do_searchopcodes(self, line):
|
|
'''
|
|
search opcodes/function for a pattern
|
|
|
|
searchopcodes [-f <funcva>] [options] <pattern>
|
|
-f [fva] - focus on one function
|
|
-c - search comments
|
|
-o - search operands
|
|
-t - search text
|
|
-M <color> - mark opcodes (default = orange)
|
|
-R - pattern is REGEX (otherwise just text)
|
|
|
|
'''
|
|
parser = e_cli.VOptionParser()
|
|
parser.add_option('-f', action='store', dest='funcva', type='int')
|
|
parser.add_option('-c', action='store_true', dest='searchComments')
|
|
parser.add_option('-o', action='store_true', dest='searchOperands')
|
|
parser.add_option('-t', action='store_true', dest='searchText')
|
|
parser.add_option('-M', action='store', dest='markColor', default='orange')
|
|
parser.add_option('-R', action='store_true', dest='is_regex')
|
|
|
|
argv = shlex.split(line)
|
|
try:
|
|
options, args = parser.parse_args(argv)
|
|
except Exception as e:
|
|
self.vprint(repr(e))
|
|
return self.do_help('searchopcodes')
|
|
|
|
pattern = ' '.join(args)
|
|
if len(pattern) == 0:
|
|
self.vprint('you must specify a pattern')
|
|
return self.do_help('searchopcodes')
|
|
|
|
# generate our interesting va list
|
|
valist = []
|
|
if options.funcva:
|
|
# setup valist from function data
|
|
try:
|
|
fva = options.funcva
|
|
graph = viv_graph.buildFunctionGraph(self, fva)
|
|
except Exception as e:
|
|
self.vprint(repr(e))
|
|
return
|
|
|
|
for nva, node in graph.getNodes():
|
|
va = nva
|
|
endva = nva + node.get('cbsize')
|
|
while va < endva:
|
|
lva, lsz, ltype, ltinfo = self.getLocation(va)
|
|
valist.append(va)
|
|
va += lsz
|
|
|
|
else:
|
|
# the whole workspace is our oyster
|
|
valist = [va for va, lvsz, ltype, ltinfo in self.getLocations(LOC_OP)]
|
|
|
|
res = []
|
|
canv = e_canvas.StringMemoryCanvas(self)
|
|
|
|
defaultSearchAll = True
|
|
for va in valist:
|
|
try:
|
|
addthis = False
|
|
op = self.parseOpcode(va)
|
|
|
|
# search comment
|
|
if options.searchComments:
|
|
defaultSearchAll = False
|
|
cmt = self.getComment(va)
|
|
if cmt is not None:
|
|
|
|
if options.is_regex:
|
|
if len(re.findall(pattern, cmt)):
|
|
addthis = True
|
|
|
|
else:
|
|
if pattern in cmt:
|
|
addthis = True
|
|
|
|
# search operands
|
|
if options.searchOperands:
|
|
defaultSearchAll = False
|
|
for opidx, oper in enumerate(op.opers):
|
|
# we're writing to a temp canvas, so clear it before each test
|
|
canv.clearCanvas()
|
|
oper = op.opers[opidx]
|
|
oper.render(canv, op, opidx)
|
|
operepr = canv.strval
|
|
|
|
if options.is_regex:
|
|
if len(re.findall(pattern, operepr)):
|
|
addthis = True
|
|
|
|
else:
|
|
if pattern in operepr:
|
|
addthis = True
|
|
|
|
# if we're doing non-regex, let's test against real numbers
|
|
# (instead of converting to hex and back)
|
|
numpattrn = pattern
|
|
try:
|
|
numpattrn = int(numpattrn, 0)
|
|
except:
|
|
pass
|
|
|
|
if numpattrn in vars(oper).values():
|
|
addthis = True
|
|
|
|
# search full text
|
|
if options.searchText or defaultSearchAll:
|
|
# search through the rendering of the opcode, as well as the comment
|
|
canv.clearCanvas()
|
|
op.render(canv)
|
|
oprepr = canv.strval
|
|
cmt = self.getComment(va)
|
|
if cmt is not None:
|
|
oprepr += " ; " + cmt
|
|
|
|
if options.is_regex:
|
|
if len(re.findall(pattern, oprepr)):
|
|
addthis = True
|
|
|
|
else:
|
|
if pattern in oprepr:
|
|
addthis = True
|
|
# only want one listing of each va, no matter how many times it matches
|
|
if addthis:
|
|
res.append(va)
|
|
except:
|
|
self.vprint(''.join(traceback.format_exception(*sys.exc_info())))
|
|
|
|
if len(res) == 0:
|
|
self.vprint('pattern not found: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern)))
|
|
return
|
|
|
|
# set the color for each finding
|
|
color = options.markColor
|
|
colormap = {va: color for va in res}
|
|
if self._viv_gui is not None:
|
|
from vqt.main import vqtevent
|
|
vqtevent('viv:colormap', colormap)
|
|
|
|
self.vprint('matches for: %s (%s)' % (pattern.encode('utf-8').hex(), repr(pattern)))
|
|
for va in res:
|
|
mbase, msize, mperm, mfile = self.memobj.getMemoryMap(va)
|
|
pname = e_memory.reprPerms(mperm)
|
|
sname = self.reprPointer(va)
|
|
|
|
try:
|
|
op = self.parseOpcode(va)
|
|
self.canvas.renderMemory(va, len(op))
|
|
cmt = self.getComment(va)
|
|
if cmt is not None:
|
|
self.canvas.addText('\t\t; %s (Perms: %s, Smartname: %s)' % (cmt, pname, sname))
|
|
|
|
self.canvas.addText('\n')
|
|
except e_exc.SegmentationViolation as e:
|
|
logger.debug("segv at 0x%x", va)
|
|
|
|
self.vprint('done (%d results).' % len(res))
|
|
|
|
def do_imports(self, line):
|
|
"""
|
|
Show the imports in the workspace (or potentially just one file)
|
|
|
|
Usage: imports [fname]
|
|
"""
|
|
self.canvas.addText("Imports:\n")
|
|
for va, size, ltype, tinfo in self.getImports():
|
|
# FIXME warn them...
|
|
# (but should we though?)
|
|
if not tinfo.startswith(line):
|
|
continue
|
|
vastr = self.arch.pointerString(va)
|
|
self.canvas.addVaText(vastr, va)
|
|
self.canvas.addText(" ")
|
|
self.canvas.addNameText(tinfo, tinfo)
|
|
self.canvas.addText("\n")
|
|
|
|
def do_fscope(self, line):
|
|
'''
|
|
The fscope command can be used to enumerate things from the
|
|
scope of one function and down it's calling graph.
|
|
|
|
Usage: fscope [options] <func_addr_expr>
|
|
|
|
-I - Show import calls from this function scope
|
|
-S - Show strings from this function scope
|
|
|
|
Example: fscope -I kernel32.CreateFileW
|
|
(Show imports called by CreateFileW and down...)
|
|
|
|
'''
|
|
showimp = False
|
|
showstr = False
|
|
|
|
argv = e_cli.splitargs(line)
|
|
try:
|
|
opts, args = getopt(argv, 'IS')
|
|
except Exception:
|
|
return self.do_help('fscope')
|
|
|
|
if not len(args) or not len(opts):
|
|
return self.do_help('fscope')
|
|
|
|
for opt, optarg in opts:
|
|
if opt == '-I':
|
|
showimp = True
|
|
elif opt == '-S':
|
|
showstr = True
|
|
|
|
for expr in args:
|
|
|
|
va = self.parseExpression(expr)
|
|
|
|
if showimp:
|
|
for callva, impname in v_t_fscope.getImportCalls(self, va):
|
|
pstr = self.arch.pointerString(callva)
|
|
self.canvas.addVaText(pstr, callva)
|
|
# FIXME best name symbol etc?
|
|
self.canvas.addText(' %s\n' % impname)
|
|
|
|
if showstr:
|
|
for refva, strva, strbytes in v_t_fscope.getStringRefs(self, va):
|
|
pstr = self.arch.pointerString(refva)
|
|
self.canvas.addVaText(pstr, refva)
|
|
self.canvas.addText(' ')
|
|
self.canvas.addVaText(strbytes, strva)
|
|
self.canvas.addText('\n')
|
|
|
|
def do_exports(self, line):
|
|
"""
|
|
List the exports in the workspace (or in a specific file).
|
|
|
|
Usage: exports [fname]
|
|
"""
|
|
edict = collections.defaultdict(list)
|
|
for va, etype, name, filename in self.getExports():
|
|
edict[filename].append((name, va))
|
|
|
|
if line:
|
|
x = edict.get(line)
|
|
if x is None:
|
|
self.vprint("Unknown fname: %s" % line)
|
|
return
|
|
edict = {line: x}
|
|
|
|
fnames = list(edict.keys())
|
|
fnames.sort()
|
|
for fname in fnames:
|
|
self.canvas.addNameText(fname, fname)
|
|
self.canvas.addText(":\n")
|
|
exports = edict.get(fname)
|
|
exports.sort()
|
|
for ename, eva in exports:
|
|
pstr = self.arch.pointerString(eva)
|
|
self.canvas.addText(" ")
|
|
self.canvas.addVaText(pstr, eva)
|
|
self.canvas.addText(" ")
|
|
self.canvas.addNameText(ename, ename)
|
|
self.canvas.addText("\n")
|
|
|
|
def do_filemeta(self, line):
|
|
|
|
'''
|
|
Show/List file metadata.
|
|
|
|
Usage: filemeta [ fname [ keyname ] ]
|
|
|
|
Example: filemeta kernel32
|
|
Example: filemeta kernel32 md5
|
|
'''
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) == 0:
|
|
|
|
self.vprint('Loaded Files:')
|
|
for fname in self.getFiles():
|
|
self.vprint(' %s' % fname)
|
|
|
|
elif len(argv) == 1:
|
|
d = self.getFileMetaDict(argv[0])
|
|
self.vprint(pprint.pformat(d))
|
|
|
|
elif len(argv) == 2:
|
|
val = self.getFileMeta(argv[0], argv[1])
|
|
self.vprint('%s (%s):' % (argv[1], argv[0]))
|
|
self.vprint(pprint.pformat(val))
|
|
|
|
else:
|
|
self.do_help('filemeta')
|
|
|
|
def do_funcmeta(self, line):
|
|
"""
|
|
Show/Set function metadata.
|
|
Usage: funcmeta <func_expr> [key <value_expr>]
|
|
|
|
"""
|
|
# FIXME make a search thing here!
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) == 0:
|
|
return self.do_help("funcmeta")
|
|
|
|
if len(argv) == 1:
|
|
va = self.parseExpression(argv[0])
|
|
meta = self.getFunctionMetaDict(va)
|
|
self.vprint(pprint.pformat(meta))
|
|
|
|
elif len(argv) == 3:
|
|
va = self.parseExpression(argv[0])
|
|
name = argv[1]
|
|
locs = self.getExpressionLocals()
|
|
val = e_expr.evaluate(argv[2], locs)
|
|
self.setFunctionMeta(va, name, val)
|
|
|
|
def do_loc(self, line):
|
|
"""
|
|
Display the repr of a single location by va.
|
|
|
|
Usage: loc <va_expr>
|
|
"""
|
|
if not line:
|
|
return self.do_help("loc")
|
|
|
|
addr = self.parseExpression(line)
|
|
loc = self.getLocation(addr)
|
|
if loc is None:
|
|
s = self.arch.pointerString(addr)
|
|
self.vprint("Unknown location: %s" % s)
|
|
r = self.reprLocation(loc)
|
|
self.vprint(r)
|
|
|
|
def do_make(self, line):
|
|
"""
|
|
Create new instances of locations in the vivisect workspace.
|
|
|
|
Usage: make [options] <va_expr>
|
|
-c Make code
|
|
-f Make function
|
|
-s Make a string
|
|
-u Make a unicode string
|
|
-n <size> Make a number
|
|
-p <size> Make a pad
|
|
-S <structname> Make a structure
|
|
"""
|
|
argv = e_cli.splitargs(line)
|
|
try:
|
|
opts, args = getopt(argv, "csufn:p:S:")
|
|
except Exception as e:
|
|
logger.warning(str(e))
|
|
return self.do_help("make")
|
|
|
|
if len(args) != 1 or len(opts) != 1:
|
|
return self.do_help("make")
|
|
|
|
addr = self.parseExpression(args[0])
|
|
opt, optarg = opts[0]
|
|
|
|
if opt == "-f":
|
|
logger.debug('new function (manual-cli): 0x%x', addr)
|
|
self.makeFunction(addr)
|
|
|
|
elif opt == "-c":
|
|
self.makeCode(addr)
|
|
|
|
elif opt == "-s":
|
|
self.makeString(addr)
|
|
|
|
elif opt == "-u":
|
|
self.makeUnicode(addr)
|
|
|
|
elif opt == "-n":
|
|
size = self.parseExpression(optarg)
|
|
self.makeNumber(addr, size)
|
|
|
|
elif opt == "-p":
|
|
size = self.parseExpression(optarg)
|
|
self.makePad(addr, size)
|
|
|
|
elif opt == "-S":
|
|
self.makeStructure(addr, optarg)
|
|
|
|
else:
|
|
return self.do_help("make")
|
|
|
|
def do_emulate(self, line):
|
|
"""
|
|
Create an emulator for the given function, and drop into a vdb
|
|
interface to step through the code.
|
|
|
|
(vdb CLI will appear in controlling terminal...)
|
|
|
|
Usage: emulate <va_expr>
|
|
"""
|
|
if not line:
|
|
return self.do_help("emulate")
|
|
|
|
addr = self.parseExpression(line)
|
|
emu = self.getEmulator(va=addr)
|
|
|
|
trace = vt_lockstep.TraceEmulator(emu)
|
|
|
|
db = vdb.Vdb(trace=trace)
|
|
db.cmdloop()
|
|
|
|
def _do_argtrack(self, line):
|
|
#FIXME: this is currently broken and needs to be revamped to use
|
|
# apiCall and VivTaints
|
|
"""
|
|
Track input arguments to the given function by name or address.
|
|
|
|
(this is currently broken. sorry!)
|
|
|
|
Usage: argtrack <func_addr_expr> <arg_idx>
|
|
"""
|
|
if not line:
|
|
return self.do_help("argtrack")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) != 2:
|
|
return self.do_help("argtrack")
|
|
|
|
try:
|
|
fva = self.parseExpression(argv[0])
|
|
except Exception:
|
|
self.vprint("Invalid Address Expression: %s" % argv[0])
|
|
return
|
|
|
|
try:
|
|
idx = self.parseExpression(argv[1])
|
|
except Exception:
|
|
self.vprint("Invalid Index Expression: %s" % argv[1])
|
|
return
|
|
|
|
if self.getFunction(fva) != fva:
|
|
self.vprint("Invalid Function Address: (0x%.8x) %s" % (fva, line))
|
|
|
|
for pleaf in viv_vector.trackArgOrigin(self, fva, idx):
|
|
|
|
self.vprint('='*80)
|
|
|
|
path = vg_path.getPathToNode(pleaf)
|
|
path.reverse()
|
|
|
|
for pnode in path:
|
|
fva = vg_path.getNodeProp(pnode, 'fva')
|
|
argv = vg_path.getNodeProp(pnode, 'argv')
|
|
callva = vg_path.getNodeProp(pnode, 'cva')
|
|
argidx = vg_path.getNodeProp(pnode, 'argidx')
|
|
if callva is not None:
|
|
aval, amagic = argv[argidx]
|
|
arepr = '0x%.8x' % aval
|
|
if amagic is not None:
|
|
arepr = repr(amagic)
|
|
frepr = 'UNKNOWN'
|
|
if fva is not None:
|
|
frepr = '0x%.8x' % fva
|
|
self.vprint('func: %s calls at: 0x%.8x with his own: %s' % (frepr, callva, arepr))
|
|
self.vprint("="*80)
|
|
|
|
def do_chat(self, line):
|
|
"""
|
|
Echo a message to any other users of a shared workspace.
|
|
|
|
Usage: chat oh hai! Checkout 0x7c778030
|
|
"""
|
|
if len(line) == 0:
|
|
return self.do_help('chat')
|
|
|
|
self.chat(line)
|
|
|
|
def do_codepath(self, line):
|
|
"""
|
|
Enumerate and show any known code paths from the specified
|
|
from address expression to the to address expression.
|
|
Usage: codepath <from_expr> <to_expr>
|
|
"""
|
|
if not line:
|
|
return self.do_help("codepath")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) != 2:
|
|
return self.do_help("codepath")
|
|
|
|
try:
|
|
frva = self.parseExpression(argv[0])
|
|
except Exception:
|
|
self.vprint("Invalid From Va: %s" % argv[0])
|
|
return
|
|
|
|
try:
|
|
tova = self.parseExpression(argv[1])
|
|
except Exception:
|
|
self.vprint("Invalid To Va: %s" % argv[1])
|
|
return
|
|
|
|
self.vprint("Tracking Paths From 0x%.8x to 0x%.8x" % (frva, tova))
|
|
|
|
paths = viv_vector.getCodePaths(self, frva, tova)
|
|
self.vprint("Function VA\tBlock VA\tSize\tFunction Name")
|
|
count = 0
|
|
for blist in paths:
|
|
count += 1
|
|
self.vprint("="*30)
|
|
for bva, bsize, fva in blist:
|
|
fname = self.getName(fva)
|
|
self.vprint("0x%.8x\t0x%.8x\t%4d\t%s" % (fva, bva, bsize, fname))
|
|
if count == 0:
|
|
self.vprint("None!")
|
|
return
|
|
|
|
def do_vampsig(self, line):
|
|
"""
|
|
Generate a vamp signature string for the given function's first block.
|
|
"""
|
|
if not line:
|
|
return self.do_help("vampsig")
|
|
|
|
va = self.parseExpression(line)
|
|
|
|
fva = self.getFunction(va)
|
|
if fva is None:
|
|
self.vprint("Invalid Function Address: 0x%.8x (%s)" % (va, line))
|
|
|
|
sig, mask = viv_vamp.genSigAndMask(self, fva)
|
|
self.vprint("SIGNATURE: %s" % e_common.hexify(sig))
|
|
self.vprint("MASK: %s" % e_common.hexify(mask))
|
|
|
|
def do_vdb(self, line):
|
|
'''
|
|
Execute vdb GUI from within vivisect (allowing special hooks between them...)
|
|
(Optionally, specify a host to use for remote vdb debugging)
|
|
|
|
Usage: vdb [<remote_host>]
|
|
'''
|
|
if line:
|
|
try:
|
|
socket.gethostbyname(line)
|
|
except Exception:
|
|
self.vprint('Invalid Remote Host: %s' % line)
|
|
|
|
vtrace.remote = line
|
|
|
|
import vivisect.vdbext as viv_vdbext
|
|
viv_vdbext.runVdb(self._viv_gui)
|
|
|
|
def do_switch(self, line):
|
|
'''
|
|
Wire up a switch case.
|
|
|
|
Usage: switch <jmp_va> <count> [offset]
|
|
where:
|
|
jmp_va - the va of the "jmp reg" instruction
|
|
count - number of switch case indices are covered by this switch case jmp
|
|
offset - first switch case index.
|
|
|
|
eg: switch case handles i == 32 thru 47, count = 16, offset = 32
|
|
'''
|
|
if not line:
|
|
return self.do_help("switch")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) < 2:
|
|
return self.do_help("switch")
|
|
|
|
offset = 0
|
|
try:
|
|
if argv[0].startswith("0x"):
|
|
jmpva = int(argv[0], 16)
|
|
else:
|
|
jmpva = int(argv[0])
|
|
|
|
if argv[1].startswith("0x"):
|
|
count = int(argv[1], 16)
|
|
else:
|
|
count = int(argv[1])
|
|
|
|
if len(argv) == 3:
|
|
if argv[2].startswith("0x"):
|
|
offset = int(argv[2], 16)
|
|
else:
|
|
offset = int(argv[2])
|
|
|
|
import vivisect.analysis.generic.symswitchcase as vagss
|
|
import vivisect.analysis.generic.codeblocks as vagc
|
|
sc = vagss.SwitchCase(self, jmpva)
|
|
sc.makeSwitch(count, offset)
|
|
funcva = self.getFunction(jmpva)
|
|
vagc.analyzeFunction(self, funcva)
|
|
except ValueError as e:
|
|
self.vprint("exception: %r" % e)
|
|
return self.do_help("switch")
|
|
|
|
def do_m_switch(self, line):
|
|
'''
|
|
Wire up a switch-case based on an array of pointers
|
|
|
|
Usage: m_switch <jmp_va> <array_va> <count> [baseva] [offset] [size]
|
|
where:
|
|
jmp_va - the va of the "jmp reg" instruction
|
|
array_va - the location of the first pointer in the array
|
|
count - number of switch case indices are covered by this switch case jmp
|
|
baseva - add the offset to this address (eg. _GOT_)
|
|
offset - first switch case index.
|
|
[size] - size (default: pointer size)
|
|
|
|
eg: switch case handles i == 32 thru 47, count = 16, offset = 32
|
|
'''
|
|
if not line:
|
|
return self.do_help("m_switch")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) < 3:
|
|
return self.do_help("m_switch")
|
|
|
|
size = 0
|
|
baseva = 0
|
|
offset = 0
|
|
try:
|
|
jmpva = self.parseExpression(argv[0])
|
|
array = self.parseExpression(argv[1])
|
|
count = self.parseExpression(argv[2])
|
|
|
|
if len(argv) > 3:
|
|
baseva = self.parseExpression(argv[3])
|
|
|
|
if len(argv) > 4:
|
|
offset = self.parseExpression(argv[4])
|
|
|
|
if len(argv) > 5:
|
|
size = self.parseExpression(argv[5])
|
|
|
|
# link up the switchcase using data provided
|
|
import vivisect.analysis.generic.symswitchcase as vagss
|
|
import vivisect.analysis.generic.codeblocks as vagc
|
|
vagss.link_up(self, jmpva, array, count, offset, baseva, size)
|
|
|
|
# reanalyze codeblocks for the function (since it should have changed)
|
|
funcva = self.getFunction(jmpva)
|
|
if funcva is not None:
|
|
vagc.analyzeFunction(self, funcva)
|
|
|
|
except ValueError as e:
|
|
self.vprint("exception: %r" % e)
|
|
return self.do_help("m_switch")
|
|
|
|
def do_plt(self, line):
|
|
'''
|
|
Parse an entire PLT Section
|
|
|
|
Usage: plt <pltva> <pltsize>
|
|
'''
|
|
if not line:
|
|
return self.do_help("plt")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
if len(argv) != 2:
|
|
return self.do_help("plt")
|
|
|
|
sva = self.parseExpression(argv[0])
|
|
ssize = self.parseExpression(argv[1])
|
|
|
|
import vivisect.analysis.elf.elfplt as vaee
|
|
vaee.analyzePLT(self, sva, ssize)
|
|
|
|
def do_plt_function(self, line):
|
|
'''
|
|
Make a PLT function at a virtual address
|
|
|
|
Usage: plt_function <va>
|
|
'''
|
|
if not line:
|
|
return self.do_help("plt_function")
|
|
|
|
fva = self.parseExpression(line)
|
|
|
|
import vivisect.analysis.elf.elfplt as vaee
|
|
vaee.analyzeFunction(self, fva)
|
|
|
|
def do_leaders(self, line):
|
|
'''
|
|
Manage Leader Sessions. This is useful if sessions are left hanging
|
|
if a leader disconnects.
|
|
|
|
Usage: leaders (list,kill,killall)
|
|
|
|
list - show leader sessions
|
|
kill <uuidhex> - kill a particular session
|
|
killall - kill all leader sessions for a given binary
|
|
mod <uuid> <user> <sessionname> - rename a leader session/user
|
|
|
|
Example:
|
|
leaders kill 197a7797ba1511ecbe41091ba860c051
|
|
'''
|
|
if not line:
|
|
return self.do_help("leaders")
|
|
|
|
argv = e_cli.splitargs(line)
|
|
try:
|
|
if argv[0] == 'list':
|
|
self.vprint("Current Leader Sessions:")
|
|
for uuid, (user, fname) in self.getLeaderSessions().items():
|
|
curva = self.getLeaderLoc(uuid)
|
|
self.vprint(" * %s (user: %s uuid: %s)" % (fname, user, uuid))
|
|
|
|
elif argv[0] == 'mod':
|
|
uuid = argv[1]
|
|
user = argv[2]
|
|
if len(argv) > 4:
|
|
sessionname = ' '.join(argv[3:])
|
|
else:
|
|
sessionname = argv[3]
|
|
|
|
self.modifyLeaderSession(uuid, user, sessionname)
|
|
|
|
elif argv[0] == 'kill':
|
|
if len(argv) < 2:
|
|
self.vprint('kill requires a valid <uuid> argument')
|
|
|
|
uuid = argv[1]
|
|
self.killLeaderSession(uuid)
|
|
|
|
elif argv[0] == 'killall':
|
|
for uuid, (user, fname) in self.getLeaderSessions().items():
|
|
self.vprint("*killing %r (%r, %r)" % (uuid, user, fname))
|
|
self.killLeaderSession(uuid)
|
|
|
|
except Exception as e:
|
|
self.vprint("Error: %r" % e)
|