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类GetMnem()的实例源码
def GetInstructionType(instr_addr):
instr_mnem = idc.GetMnem(instr_addr)
if instr_mnem.startswith('call'):
return CALL_INSTRUCTION
elif instr_mnem.startswith('j'):
# It seems that there is no other type of instructions
# starting with j in x86/x86_64
return BRANCH_INSTRUCTION
for assign_instr_mnem in assign_instructions_general:
if instr_mnem.startswith(assign_instr_mnem):
return ASSIGNMENT_INSTRUCTION
for assign_instr_mnem in assign_instructions_fp:
if instr_mnem.startswith(assign_instr_mnem):
return ASSIGNMENT_INSTRUCTION
for compare_instruction in compare_instructions:
if instr_mnem.startswith(compare_instruction):
return COMPARE_INSTRUCTION
for stack_push_instruction in stack_push_instructions:
if instr_mnem.startswith(stack_push_instruction):
return STACK_PUSH_INSTRUCTION
for stack_pop_instruction in stack_pop_instructions:
if instr_mnem.startswith(stack_pop_instruction):
return STACK_POP_INSTRUCTION
return OTHER_INSTRUCTION
def find_shifts(functions):
candidate_functions = {}
# TODO better to compare number of shifts to overall instruction count?
# TODO find shifts in tight loops
shift_mnems = set(["shl", "shr", "sar", "sal", "rol", "ror"])
shift_mnems_len = len(shift_mnems)
for fva in functions:
found_shifts = set([])
cva = fva
while cva != idaapi.BADADDR and cva < idc.FindFuncEnd(fva):
i = idc.GetMnem(cva)
if i in shift_mnems:
found_shifts.add(i)
g_logger.debug("shift instruction: %s va: 0x%x function: 0x%x", idc.GetDisasm(cva), cva, fva)
cva = idc.NextHead(cva)
candidate_functions[fva] = 1 - ((shift_mnems_len - len(found_shifts)) / float(shift_mnems_len))
return candidate_functions
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 get_import_thunk(import_addr):
'''
find import thunk for the given import pointer.
this is a function that simply jumps to the external implementation of the routine.
Args:
import_addr (int): address of import table pointer.
Returns:
int: address of function thunk.
Raises:
ValueError: when the thunk does not exist.
'''
for xref in idautils.XrefsTo(import_addr):
if xref.type != 3: # XrefTypeName(3) == 'Data_Read'
continue
if idc.GetMnem(xref.frm) != 'jmp':
continue
return xref.frm
raise ValueError('thunk does not exist')
def color_head(ea):
flags = idc.GetFlags(ea)
if not idc.isCode(flags):
return
mnem = idc.GetMnem(ea)
if mnem == 'call':
logger.debug('call: 0x%x', ea)
idc.SetColor(ea, idc.CIC_ITEM, CALL_COLOR)
elif mnem == 'xor':
if idc.GetOpnd(ea, 0) != idc.GetOpnd(ea, 1):
logger.debug('non-zero xor: 0x%x', ea)
idc.SetColor(ea, idc.CIC_ITEM, ENCRYPT_COLOR)
elif mnem in ('sdit', 'sgdt', 'sldt', 'smsw', 'str', 'in', 'cpuid'):
logger.debug('anti-vm: 0x%x', ea)
idc.SetColor(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR)
elif mnem == 'in':
if idc.GetOpnd(ea, 0) in ("3", "2D"):
logger.debug('anti-debug: 0x%x', ea)
idc.SetColor(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR)
elif mnem in ('rdtsc', 'icebp'):
logger.debug('anti-debug: 0x%x', ea)
idc.SetColor(ea, idc.CIC_ITEM, ANTIANALYSIS_COLOR)
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 detect_start_and_stop(self): # FIXME:Duplicate code with core (or something similar)
start, stop = 0, 0
if self.core.ftype == "PE":
start, stop = self.core.fun_mapping["start"]
else:
if "main" in self.core.fun_mapping:
start, stop = self.core.fun_mapping["main"]
elif "start" in self.core.fun_mapping:
if "__libc_start_main" in self.core.fun_mapping:
instrs = list(idautils.FuncItems(self.core.fun_mapping["start"][0]))
instrs.reverse()
for inst in instrs:
arg1 = idc.GetOperandValue(inst, 0)
if idc.GetMnem(inst) == "push":
start, stop = arg1, self.core.fun_mapping["start"][1]
break
else:
start, stop = self.core.fun_mapping["start"]
else:
start, stop = idc.BeginEA(), 0
self.start, self.stop = start, stop
def find_all_ioctls():
"""
From the currently selected address attempts to traverse all blocks inside the current function to find all immediate values which
are used for a comparison/sub immediately before a jz. Returns a list of address, second operand pairs.
"""
ioctls = []
# Find the currently selected function and get a list of all of it's basic blocks
addr = idc.ScreenEA()
f = idaapi.get_func(addr)
fc = idaapi.FlowChart(f, flags=idaapi.FC_PREDS)
for block in fc:
# grab the last two instructions in the block
last_inst = idc.PrevHead(block.endEA)
penultimate_inst = idc.PrevHead(last_inst)
# If the penultimate instruction is cmp or sub against an immediate value immediatly preceding a 'jz'
# then it's a decent guess that it's an IOCTL code (if this is a disptach function)
if idc.GetMnem(penultimate_inst) in ['cmp', 'sub'] and idc.GetOpType(penultimate_inst, 1) == 5:
if idc.GetMnem(last_inst) == 'jz':
ioctl_tracker.add_ioctl(penultimate_inst)
for inst in ioctl_tracker.ioctl_locs:
value = get_operand_value(inst)
ioctls.append((inst, value))
return ioctls
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 str_fun_xrefs():
str_fun_xref = {}
for s in IDAStrings:
for ref in idautils.DataRefsTo(s.ea):
f = idaapi.get_func(ref)
if not f:
continue
if idc.GetMnem(ref) == "":
continue
f_ea = f.startEA
try:
#because we are only carrying the string value itself, duplications should be removed.
#This is important because of OFFS/ADR instruction double references being very typical,
#and multiple allocations/frees in same function causing extra references too.
str_fun_xref[f_ea].add(str(s))
except:
str_fun_xref[f_ea] = set([str(s)])
return str_fun_xref
def ret_addr(ea):
#we can't assume Thumb only, so we also keep ARM cases, just adjust addr in Thumb cases
if (ea % 2) != 0:
ea -= 1
f_ea = idaapi.get_func(ea)
if not f_ea:
return False
#Preceding or Previous?
# Not necessarily all preceding will be a call to a ret instruction,
# but "the" prev should be always the one.
i = idautils.DecodePreviousInstruction(ea)
if i and "BL" in idc.GetMnem(i.ea):
return True
return False
def get_mnem(self, ea):
return idc.GetMnem(ea)
def is_jump(va):
'''
return True if the instruction at the given address appears to be a jump.
'''
return idc.GetMnem(va).startswith('j')
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 prevMnemonic(ea, mnem, minaddr=0):
res = idc.GetMnem(ea)
#print "%x -> %s"% (ea, res)
if res == "": return idc.BADADDR
if res == mnem: return ea
return prevMnemonic( idc.PrevHead(ea, minaddr), mnem, minaddr )
def getDispatchCode(ea):
# get dispatch code out of an instruction
first, second = (idc.GetOpnd(ea, 0), idc.GetOperandValue(ea, 1))
if first == 'eax':
return second
raise ValueError("Search resulted in address %08x, but instruction '%s' does fulfill requested constraints"% (ea, idc.GetMnem(ea)))
def FindLastAssignment(ea, register):
start,end = database.guessrange(ea)
while ea > start:
ea = database.prev(ea)
m = idc.GetMnem(ea)
r = idc.GetOpnd(ea, 0)
if m == 'mov' and r == register:
return ea
continue
raise ValueError('FindLastAssignment(0x%x, %s) Found no matches'% (ea, register))
def process_routine(self, rtn_addr, pred_addr=None, rtn_i=1, total_rtn=1):
if rtn_addr not in self.functions_cfg:
self.functions_cfg[rtn_addr] = MyFlowGraph(rtn_addr)
cfg = self.functions_cfg[rtn_addr]
path_to = self.config_to_path_function(cfg)
if pred_addr is None:
candidates = {x for x in idautils.FuncItems(rtn_addr) if idc.GetMnem(x) in cond_jump}
else:
candidates = {pred_addr}
nb_candidates = len(candidates)
self.functions_candidates[rtn_addr] = set()
self.functions_spurious_instrs[rtn_addr] = set()
self.progressbar_loading.reset()
self.progressbar_loading.setMaximum(len(candidates))
name = idc.GetFunctionName(rtn_addr)
self.result_widget.webview.append("\n=> Function:%s\n" % name)
self.log("[result]", "Start processing function: 0x%x" % rtn_addr)
for i, addr in zip(xrange(len(candidates)), candidates):
path = path_to(addr)
res = self.process_addr(rtn_addr, addr, path)
if self.STOP:
return
elif res is None:
continue
dead_br = "/" if res.dead_branch is None else "%x" % res.dead_branch
self.result_widget.webview.append("%x:\t%s\t\tK:%d\tDead:%s" % (addr, to_status_name(res.status), res.k, dead_br))
self.result_widget.webview.verticalScrollBar().setValue(self.result_widget.webview.verticalScrollBar().maximum())
self.loading_stat.setText("Fun: %d/%d Addr: %d/%d" % (rtn_i, total_rtn, i+1, nb_candidates))
self.progressbar_loading.setValue(self.progressbar_loading.value()+1)
self.functions_candidates[rtn_addr].add(addr)
def set_start_stop(self, ftype):
assert_ida_available()
import idc
import idaapi
import idautils
fun_mapping = {idc.GetFunctionName(x): (idaapi.get_func(x).startEA, idaapi.get_func(x).endEA-1)
for x in idautils.Functions()}
start = idc.BeginEA()
stop = 0
if ftype == PE:
start, stop = fun_mapping["start"]
else:
if not idc.isCode(idc.GetFlags(start)):
if idc.MakeCode(start) == 0:
print "Fail to decode instr !"
idaapi.autoWait()
if idc.GetFunctionName(start) == "":
if idc.MakeFunction(start) == 0:
print "Fail to create function !"
idaapi.autoWait()
fun_mapping = {idc.GetFunctionName(x): (idaapi.get_func(x).startEA, idaapi.get_func(x).endEA-1)
for x in idautils.Functions()}
if "main" in fun_mapping:
start, stop = fun_mapping["main"]
elif "start" in fun_mapping:
if "__libc_start_main" in fun_mapping:
instrs = list(idautils.FuncItems(fun_mapping["start"][0]))
instrs.reverse()
for inst in instrs:
arg1 = idc.GetOperandValue(inst, 0)
if idc.GetMnem(inst) == "push":
start, stop = arg1, fun_mapping["start"][1]
break
else:
start, stop = fun_mapping["start"]
self.config.start, self.config.stop = start, stop
def get_args(addr):
""" Retreives the passed arguments to the decryption function. We are only interested in the key
and offset to the encrypted string.
addr: (int) Address at which the decryption function was called.
Returns:
key: (int) The key used to decrypt the string.
enc_str: (list) Byte array of encrypted string.
ins_addr: (int) Address at which the encrypted byte array is referenced.
"""
found = False
foundstr = False
foundkey = False
while not found:
addr = idc.PrevHead(addr)
if idc.GetMnem(addr) == "mov" and "r8d" in idc.GetOpnd(addr,0):
#print "[+] Found key: 0x%08x at 0x%016x" % (idc.GetOperandValue(addr,1)& 0xffffffff, addr)
key = idc.GetOperandValue(addr,1) & 0xffffffff
foundkey = True
if idc.GetMnem(addr) == "lea" and "rdx" in idc.GetOpnd(addr,0):
#print "[+] Found str: 0x%016x at 0x%016x" % (idc.GetOperandValue(addr,1), addr)
enc_str_addr = idc.GetOperandValue(addr,1)
enc_str = get_encoded_string(enc_str_addr)
ins_addr = addr
foundstr = True
if foundkey and foundstr:
found = True
return key, enc_str, ins_addr
def ret_addr(ea):
#we can't assume Thumb only, so we also keep ARM cases, just adjust addr in Thumb cases
if (ea % 2) != 0:
ea -= 1
'''
#calculating code segment ranges every time is wasteful
code_segs = []
for s in idautils.Segments():
if idaapi.segtype(s) == idaapi.SEG_CODE:
s_end = idc.GetSegmentAttr(s, SEGATTR_END)
code_segs.append({"start" : s, "end" : s_end})
if not reduce(lambda x, y: x or y, map(lambda x: (x["start"] <= ea) and (x["end"] > ea), code_segs)):
return False
'''
#this is-in-function check is enough (segment check redundant) if we trust function ID'ing anyway.
f_ea = idaapi.get_func(ea)
if not f_ea:
return False
#Preceding or Previous?
# Not necessarily all preceding will be a call to a ret instruction,
# but "the" prev should be always the one.
i = idautils.DecodePreviousInstruction(ea)
if i and "BL" in idc.GetMnem(i.ea):
return True
return False
def main():
base_addr = 0
ea = 0
idc.MakeFunction(ea)
# heuristic
while(true):
mnemonic = idc.GetMnem(ea)
if "LDR" in mnemonic:
base_str = idc.GetOpnd(ea, 1)
base_addr = int(base_str.split("=")[1], 16)
break
ea += 4
print("[+] rebasing to address 0x%x" % (base_addr))
idc.rebase_program(base_addr, idc.MSF_FIXONCE)
idaapi.autoWait()
segment_start = base_addr
segment_end = idc.GetSegmentAttr(segment_start, idc.SEGATTR_END)
ea = segment_start
print("[+] searching and defining functions")
while ea != idc.BADADDR:
ea = idc.FindBinary(ea, idc.SEARCH_DOWN, "BF A9", 16)
if ea != idc.BADADDR:
ea = ea - 2
if (ea % 4) == 0 and idc.GetFlags(ea) < 0x200:
# print("[+] defining a function at 0x%x" % (ea))
idc.MakeFunction(ea)
ea = ea + 4
idc.AnalyzeArea(segment_start, segment_end)
idaapi.autoWait()
def def_functions(s_start):
num_added_functions = 0
s_addr = s_start
s_end = idc.GetSegmentAttr(s_start, SEGATTR_END) #idc.SegEnd(segm)
print "0x%08x 0x%08x" % (s_start, s_end)
while (s_addr < s_end):
print "Testing address 0x%08x" % s_addr
#optimization assumes that function chunks are consecutive (no "function-in-function" monkey business)
if (idaapi.get_func(s_addr)):
next_func = idc.NextFunction(s_addr)
ea = s_addr
for c in idautils.Chunks(s_addr):
#only use chunks in lookahead that do not jump over the next function and that are not smaller than where we are atm.
if (c[1] > ea) and (c[1] <= next_func):
ea = c[1]
if ea == s_addr:
s_addr += 2
else:
s_addr = ea
#s_addr += 4
continue
else:
#This is not a good optimization, there WILL be data refs to function start addresses sometimes.
'''
if sum(1 for _ in (CodeRefsTo(s_addr, 1))) != 0:
s_addr += 4
continue
'''
#also add STMFD
if ((idc.GetMnem(s_addr) == "STM") and ("SP!" in idc.GetOpnd(s_addr, 0)) and ("LR" in idc.GetOpnd(s_addr, 1))) or (((idc.GetMnem(s_addr) == "PUSH") or (idc.GetMnem(s_addr) == "PUSH.W") or (idc.GetMnem(s_addr) == "STR.W") ) and ("LR" in idc.GetOpnd(s_addr, 0))):
print "Found function at 0x%08x" % s_addr
idc.MakeFunction(s_addr)
f = idaapi.get_func(s_addr)
if (type(f) == type(None)):
print "Failed to create function! Undefined instructions?"
s_addr += 2
else:
num_added_functions += 1
ea = -1
for c in idautils.Chunks(s_addr):
if c[1] > ea:
ea = c[1]
if ea != -1:
s_addr = ea
#failed?
else:
s_addr += 2
else:
s_addr += 2
print "finished segment"
return num_added_functions