mirror of
https://github.com/vivisect/vivisect
synced 2026-06-08 18:04:23 +00:00
165 lines
4.9 KiB
Python
165 lines
4.9 KiB
Python
"""
|
|
A function analysis module that will find code blocks in
|
|
functions by flow and xrefs. This is basically a mandatory
|
|
module which should be snapped in *very* early by parsers.
|
|
|
|
"""
|
|
import logging
|
|
import collections
|
|
|
|
import envi
|
|
import envi.const as e_const
|
|
|
|
from vivisect.const import REF_CODE, LOC_POINTER, LOC_OP
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def analyzeFunction(vw, funcva):
|
|
blocks = {}
|
|
done = {}
|
|
mnem = collections.defaultdict(int)
|
|
todo = [funcva, ]
|
|
brefs = []
|
|
size = 0
|
|
opcount = 0
|
|
while len(todo):
|
|
|
|
start = todo.pop()
|
|
|
|
# If we've hit code we've already done, keep going.
|
|
if done.get(start):
|
|
continue
|
|
|
|
done[start] = True
|
|
blocks[start] = 0
|
|
brefs.append((start, True))
|
|
|
|
va = start
|
|
op = None
|
|
arch = envi.ARCH_DEFAULT
|
|
|
|
# Walk forward through instructions until a branch edge
|
|
while True:
|
|
loc = vw.getLocation(va)
|
|
|
|
# If it's not a location, terminate
|
|
if loc is None:
|
|
blocks[start] = va - start
|
|
brefs.append((va, False))
|
|
break
|
|
|
|
lva, lsize, ltype, linfo = loc
|
|
|
|
if ltype == LOC_POINTER:
|
|
# pointer analysis mis-identified a pointer,
|
|
# so clear and re-analyze instructions.
|
|
|
|
vw.delLocation(lva)
|
|
|
|
# assume we're adding a valid instruction, which is most likely.
|
|
if op is not None:
|
|
arch = op.iflags & envi.ARCH_MASK
|
|
|
|
vw.makeCode(va, arch=arch, fva=funcva)
|
|
|
|
loc = vw.getLocation(va)
|
|
if loc is None:
|
|
blocks[start] = va - start
|
|
brefs.append((va, False))
|
|
break
|
|
|
|
lva, lsize, ltype, linfo = loc
|
|
|
|
# If it's not an op, terminate
|
|
if ltype != LOC_OP:
|
|
blocks[start] = va - start
|
|
brefs.append((va, False))
|
|
break
|
|
|
|
try:
|
|
op = vw.parseOpcode(va)
|
|
mnem[op.mnem] += 1
|
|
except Exception as e:
|
|
logger.warning('Codeblock bad opcode at 0x%x, breaking on error %s', va, e)
|
|
break
|
|
size += lsize
|
|
opcount += 1
|
|
nextva = va + lsize
|
|
|
|
# For each of our code xrefs, create a new target.
|
|
branch = False
|
|
xrefs = vw.getXrefsFrom(va, REF_CODE)
|
|
for fromva, tova, rtype, rflags in xrefs:
|
|
# We don't handle procedural branches here...
|
|
if rflags & envi.BR_PROC:
|
|
continue
|
|
|
|
# For now, we'll skip jmp [import] thunks...
|
|
if rflags & envi.BR_DEREF:
|
|
continue
|
|
|
|
mmap = vw.getMemoryMap(tova)
|
|
if mmap:
|
|
mva, msize, mperm, mname = mmap
|
|
if mperm & e_const.MM_UNINIT:
|
|
continue
|
|
|
|
branch = True
|
|
todo.append(tova)
|
|
|
|
# If it doesn't fall through, terminate (at nextva)
|
|
if linfo & envi.IF_NOFALL:
|
|
blocks[start] = nextva - start
|
|
brefs.append((nextva, False))
|
|
break
|
|
|
|
# If we hit a branch, we are the end of a block...
|
|
if branch:
|
|
blocks[start] = nextva - start
|
|
todo.append(nextva)
|
|
break
|
|
|
|
if vw.getXrefsTo(nextva, REF_CODE):
|
|
blocks[start] = nextva - start
|
|
todo.append(nextva)
|
|
break
|
|
|
|
va = nextva
|
|
|
|
oldblocks = {va: size for (va, size, fva) in vw.getFunctionBlocks(funcva)}
|
|
# we now have an ordered list of block references!
|
|
brefs.sort()
|
|
brefs.reverse()
|
|
bcnt = 0
|
|
while len(brefs):
|
|
bva, isbegin = brefs.pop()
|
|
if not isbegin:
|
|
continue
|
|
|
|
if len(brefs) == 0:
|
|
break
|
|
|
|
# So we don't add a codeblock if we're re-analyzing a function
|
|
# (like during dynamic branch analysis)
|
|
try:
|
|
bsize = blocks[bva]
|
|
if bsize == 0:
|
|
continue
|
|
|
|
tmpcb = vw.getCodeBlock(bva)
|
|
# sometimes codeblocks can be deleted if owned by multiple functions
|
|
if bva not in oldblocks or tmpcb is None:
|
|
vw.addCodeBlock(bva, bsize, funcva)
|
|
elif bsize != oldblocks[bva]:
|
|
vw.delCodeBlock(bva)
|
|
vw.addCodeBlock(bva, bsize, funcva)
|
|
bcnt += 1
|
|
except Exception as e:
|
|
logger.warning('Codeblock analysis for 0x%.8x hit exception: %s', funcva, e)
|
|
break
|
|
|
|
vw.setFunctionMeta(funcva, 'Size', size)
|
|
vw.setFunctionMeta(funcva, 'BlockCount', bcnt)
|
|
vw.setFunctionMeta(funcva, 'InstructionCount', opcount)
|
|
vw.setFunctionMeta(funcva, 'MnemDist', dict(mnem))
|