def find_unusual_xors(functions):
# TODO find xors in tight loops
candidate_functions = []
for fva in functions:
cva = fva
while cva != idaapi.BADADDR and cva < idc.FindFuncEnd(fva):
if idc.GetMnem(cva) == "xor":
if idc.GetOpnd(cva, 0) != idc.GetOpnd(cva, 1):
g_logger.debug("suspicious XOR instruction at 0x%08X in function 0x%08X: %s", cva, fva,
idc.GetDisasm(cva))
ph = idc.PrevHead(cva)
nh = idc.NextHead(cva)
ip = idc.GetDisasm(ph)
ia = idc.GetDisasm(nh)
if ip and ia:
g_logger.debug("Instructions: %s; %s; %s", ip, idc.GetDisasm(cva), ia)
if ph or nh:
if is_security_cookie(cva, ph, nh):
g_logger.debug("XOR related to security cookie: %s", idc.GetDisasm(cva))
else:
g_logger.debug("unusual XOR: %s", idc.GetDisasm(cva))
candidate_functions.append(fva)
break
cva = idc.NextHead(cva)
return candidate_functions
python类NextHead()的实例源码
def find_suspicous_movs(functions):
candidate_functions = []
regs = ["esp", "ebp", "rsp", "rbp"]
for fva in functions:
for (loopStart, loopEnd) in find_tight_loops(fva):
cva = loopStart
while cva <= loopEnd:
if idc.GetMnem(cva) == "mov":
if is_list_item_in_s(regs, idc.GetOpnd(cva, 0)):
cva = idc.NextHead(cva)
continue
# identify register dereferenced writes to memory, e.g. mov [eax], cl
if idc.GetOpType(cva, 0) == OP_TYPE.BASE_INDEX.value:
if idc.GetOpType(cva, 1) not in [OP_TYPE.IMMEDIATE.value, OP_TYPE.IMMEDIATE_FAR.value,
OP_TYPE.IMMEDIATE_NEAR.value]:
g_logger.debug("suspicious MOV instruction at 0x%08X in function 0x%08X: %s", cva, fva,
idc.GetDisasm(cva))
candidate_functions.append(fva)
break
cva = idc.NextHead(cva)
return candidate_functions
def enum_function_addrs(fva):
'''
yield the effective addresses of each instruction in the given function.
these addresses are not guaranteed to be in any order.
Args:
fva (int): the starting address of a function
Returns:
sequence[int]: the addresses of each instruction
'''
f = idaapi.get_func(fva)
if not f:
raise ValueError('not a function')
for block in idaapi.FlowChart(f):
ea = block.startEA
while ea <= block.endEA:
yield ea
ea = idc.NextHead(ea)
def revise_syscall(rename=False):
if not rename:
print('Change the function name with `CGCHeler.revise_syscall(True)`.')
# visit all instructions
start_ea, end_ea = utils.get_seg_range('.text')
eax = -1
ip = start_ea
while ip < end_ea and ip != idaapi.BADADDR:
if 'int' in idc.GetMnem(ip) and '80h' == idc.GetOpnd(ip, 0):
if eax != -1:
# fix comment and function name
print('{}: {}'.format(hex(ip), syscall_table[eax]))
idc.MakeComm(ip, 'CGC syscall: {}'.format(syscall_table[eax]))
if rename:
print('Change {} to {}'.format(idc.GetFunctionName(ip), syscall_table[eax]))
idc.MakeName(idc.GetFunctionAttr(ip, idc.FUNCATTR_START), syscall_table[eax])
elif 'mov' in idc.GetMnem(ip) and 'eax' == idc.GetOpnd(ip, 0) and 5 == idc.GetOpType(ip, 1):
value = idc.GetOpnd(ip, 1)
if re.search('^[0-9]+$', value) != None:
eax = int(value)
if eax > 7 or eax < 1:
eax = -1
ip = idc.NextHead(ip)
def create_call_map(self, ftype):
assert_ida_available()
import idc
import idautils
seg_mapping = {idc.SegName(x): (idc.SegStart(x), idc.SegEnd(x)) for x in idautils.Segments()}
imports = seg_mapping[".idata"] if ftype == PE else seg_mapping['.plt']
start, stop = seg_mapping[".text"]
current = start
while current <= stop:
inst = current
if idc.GetMnem(inst) in ["call", "jmp"]:
value = idc.GetOperandValue(inst, 0)
name = idc.GetOpnd(inst, 0)
if imports[0] <= value <= imports[1]:
entry = self.config.call_map.add()
entry.address = inst
entry.name = name
current = idc.NextHead(current, stop)
def find_dispatch_by_struct_index():
"""Attempts to locate the dispatch function based off it being loaded in a structure
at offset 70h, based off of https://github.com/kbandla/ImmunityDebugger/blob/master/1.73/Libs/driverlib.py """
out = set()
for function_ea in idautils.Functions():
flags = GetFunctionFlags(function_ea)
# skip library functions
if flags & FUNC_LIB:
continue
func = idaapi.get_func(function_ea)
addr = func.startEA
while addr < func.endEA:
if GetMnem(addr) == 'mov':
if '+70h' in GetOpnd(addr, 0) and idc.GetOpType(addr, 1) == 5:
out.add(GetOpnd(addr, 1))
addr = idc.NextHead(addr)
return out
def highlight_non_zero_xor(self, ea):
highlight_eas = []
if self.get_mnem(ea) == "xor":
if idc.GetOpnd(ea, 0) != idc.GetOpnd(ea, 1):
ph = idc.PrevHead(ea)
nh = idc.NextHead(ea)
ip = idc.GetDisasm(ph)
ia = idc.GetDisasm(nh)
if ph or nh:
if not self.is_security_cookie(ea, ph, nh):
highlight_eas.append(ea)
MySetColor(ea, self.color)
return highlight_eas
def is_security_cookie(self, va, ph, nh):
# for security cookie check the xor should use ESP or EBP
if idc.GetOpnd(va, 1) not in ["esp", "ebp", "rsp", "rbp"]:
return False
if "security" in idc.GetOpnd(ph, 1):
return True
elif "security" in idc.GetDisasm(nh):
return True
elif "security" in idc.GetDisasm(idc.NextHead(nh)):
return True
return False
def highlight_instructions(highlighters):
ea = idc.NextHead(0)
while ea != idaapi.BADADDR:
for h in highlighters:
h.highlight(ea)
ea = idc.NextHead(ea)
def is_security_cookie(va, ph, nh):
# for security cookie check the xor should use ESP or EBP
if idc.GetOpnd(va, 1) not in ["esp", "ebp", "rsp", "rbp"]:
return False
if "security" in idc.GetOpnd(ph, 1):
return True
elif "security" in idc.GetDisasm(nh):
return True
elif "security" in idc.GetDisasm(idc.NextHead(nh)):
return True
return False
def nextMnemonic(ea, mnem, maxaddr=0xc0*0x1000000):
res = idc.GetMnem(ea)
if res == "": return idc.BADADDR
if res == mnem: return ea
return nextMnemonic( idc.NextHead(ea, maxaddr), mnem, maxaddr )
def save_function_addr(addr, func):
max_addr = addr + 30
while True:
addr = idc.NextHead(addr)
if addr < max_addr:
if GetMnem(addr) == "mov" and "eax" in GetOpnd(addr, 1):
MakeName(GetOperandValue(addr,0), "p_"+dec)
break
else:
break
def iter_lines():
"""
Iterate through all line addresses in the IDB
Yields addresses of all lines.
"""
for ea in idautils.Segments():
seg_start = idc.SegStart(ea)
seg_end = idc.SegEnd(ea)
cur_addr = seg_start
while (cur_addr < seg_end) and (cur_addr != idaapi.BADADDR):
yield cur_addr
cur_addr = idc.NextHead(cur_addr)
def compute_nb_instr(self):
return 0 # FIXME: by iterating all segments
count = 0
start, stop = self.seg_mapping[".text"] # TODO: Iterate all executable segs
current = start
while current <= stop:
if idc.isCode(idc.GetFlags(current)):
count += 1
current = idc.NextHead(current, stop)
return count
def generate_dead_alive_dump(self):
f = Path("dead_or_alive_dump.txt")
handle = f.open("w")
for cfg in self.functions_cfg.values():
for bb in cfg.values():
for i in bb.instrs:
status = bb.instrs_status[i] if bb.is_alive() else Status.DEAD
size = idc.NextHead(i)-i
handle.write(u"%x,%d,%s\n" % (i, size, status))
handle.close()
def decode_here_clicked(self):
inst = idc.here()
if not idc.isCode(idc.GetFlags(inst)):
print "Not code instruction"
else:
raw = idc.GetManyBytes(inst, idc.NextHead(inst)-inst)
s = to_hex(raw)
self.decode_ir(s)
def _fill_instrs(self):
cur_addr = self.startEA
while cur_addr != idc.BADADDR:
self.instrs.append(cur_addr)
cur_addr = idc.NextHead(cur_addr, self.endEA)
def get_code_and_blocks(ea):
"""Extracts the control flow graph for the function at the given address.
Returns a dictionary with the instructions (ea->insn.Instruction) and a list
of the basic blocs (bbl.BasicBlock)."""
code = {}
blocks = {}
ida_blocks = set(idaapi.FlowChart(idaapi.get_func(ea)))
for bb in ida_blocks:
# XXX: it seems that it's not a bug but inter-function jumps!
if bb.startEA == bb.endEA: # skip that .. it's IDA's bug
#print "skipping block %x : %x in func %x"%(bb.startEA, bb.endEA, ea)
continue
blocks[bb.startEA] = bbl.BasicBlock(bb.startEA, bb.endEA, {})
for head in idautils.Heads(bb.startEA, bb.endEA):
ibytes = idc.GetManyBytes(head, idc.ItemEnd(head) - head)
spd = idc.GetSpd(head)
code[head] = insn.Instruction(head, ibytes, spd)
blocks[bb.startEA].instrs.append(code[head])
next_head = idc.NextHead(head, bb.endEA)
if idaapi.isFlow(idc.GetFlags(next_head)):
code[head].succ.add(next_head)
for suc_bb in (s for s in bb.succs() if s.startEA != s.endEA):
#assume head is the last instruction of the block
code[head].succ.add(suc_bb.startEA)
for bb in (b for b in ida_blocks if b.startEA != b.endEA):
for suc_bb in (s for s in bb.succs() if s.startEA != s.endEA):
# a jump with zero offset (like, jz 0) gives two succs to the same bb
if blocks[suc_bb.startEA] not in blocks[bb.startEA].successors:
blocks[bb.startEA].successors.append(blocks[suc_bb.startEA])
blocks[bb.startEA].successors.sort(key=lambda x: x.begin, reverse=True)
#FIXME: find a better way ..
for block in blocks.itervalues():
if block.instrs[0].addr == ea:
#print "found the entry!:", block.instrs
block.instrs[0].f_entry = True
block.type |= bbl.BasicBlock.ENTRY
break
else:
print "BUG: could not find function entry in instrs!!"
#print "blocks:", blocks
return code, blocks.values()
#XXX: good test function in 0x070016E7 (BIB.dll)