Skip to content

Instantly share code, notes, and snippets.

@alexander-hanel
Last active May 13, 2020 01:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexander-hanel/36a98cff1f2f6e72f6f8b235b784ab6f to your computer and use it in GitHub Desktop.
Save alexander-hanel/36a98cff1f2f6e72f6f8b235b784ab6f to your computer and use it in GitHub Desktop.
Find usage of XOR, XOR blocks, size of the XOR loop and dynamic calls.
import idautils
import operator
JMPS = [eval("idaapi."+name) for name in dir(idaapi) if "NN_j" in name]
def get_riat_func():
gpa = idc.get_name_ea_simple("GetProcAddress")
func_gpa = {}
for tt in idautils.XrefsTo(gpa, 0):
if tt.type != 3: # Data_Read:
continue
ea = tt.frm
temp = idc.get_func_attr(ea, FUNCATTR_START)
if temp not in func_gpa:
func_gpa[temp] = 1
else:
func_gpa[temp] += 1
sorted_gpa = sorted(func_gpa.items(), key=operator.itemgetter(1),reverse=True)
func_xor = {}
for func in idautils.Functions():
flags = idc.get_func_attr(func, FUNCATTR_FLAGS) # skip library & thunk functions
if flags & FUNC_LIB or flags & FUNC_THUNK:
continue
dism_addr = list(idautils.FuncItems(func))
for ea in dism_addr:
if idc.print_insn_mnem(ea) == "xor" and not idc.print_operand(ea, 1).endswith("sp") :
if idc.print_operand(ea, 0) != idc.print_operand(ea, 1):
temp = idc.get_func_attr(ea, FUNCATTR_START)
if temp not in func_xor:
func_xor[temp] = 1
else:
func_xor[temp] += 1
sorted_xor = sorted(func_xor.items(), key=operator.itemgetter(1), reverse=True)
if sorted_gpa:
if sorted_gpa[0][0] == sorted_xor[0][0]:
print("0x%x Renamed to reiat_decrypt_strings" % sorted_gpa[0][0])
idc.set_name(sorted_gpa[0][0], "reiat_decrypt_strings", SN_CHECK)
def get_basic_block(ea):
"""get basic blocks of address"""
f = idaapi.get_func(ea)
fc = idaapi.FlowChart(f)
for block in fc:
if block.start_ea <= ea:
if block.end_ea > ea:
return block.start_ea, block.end_ea
return None, None
def get_xors():
"""returns address of all XOR"""
xor_ea = []
for func in idautils.Functions():
flags = idc.get_func_attr(func, FUNCATTR_FLAGS) # skip library & thunk functions
if flags & FUNC_LIB or flags & FUNC_THUNK:
continue
dism_addr = list(idautils.FuncItems(func))
for ea in dism_addr:
if idc.print_insn_mnem(ea) == "xor" and not idc.print_operand(ea, 1).endswith("sp") :
if idc.print_operand(ea, 0) != idc.print_operand(ea, 1):
xor_ea.append(ea)
return xor_ea
def get_xor_blocks(xor_func):
"""get start and end of basic block of XOR instruction"""
if xor_func == None:
return
xor_ea = {}
dism_addr = list(idautils.FuncItems(xor_func))
for ea in dism_addr:
if idc.print_insn_mnem(ea) == "xor" and not idc.print_operand(ea, 1).endswith("sp") :
if idc.print_operand(ea, 0) != idc.print_operand(ea, 1):
start, end = get_basic_block(ea)
if start:
xor_ea[ea] = {"start": start, "end" : end}
return xor_ea
def get_xor_loop(start, end):
"""assumes before the basic block end is the jump to start of loop"""
cur_addr = end
for r in range(0,3):
loop_start = None
cur_addr = idc.prev_head(cur_addr)
ins = ida_ua.insn_t()
idaapi.decode_insn(ins, cur_addr)
if ins.itype in JMPS:
if ins.Op1.type == idaapi.o_near:
loop_start = idc.get_operand_value(cur_addr, 0)
return loop_start, end
return None, None
def analyze_xor_loop(xor, start, end):
cur_addr = start
lines = [cur_addr]
if not start or not end:
return None
for line in range(start, end):
cur_addr = idc.next_head(cur_addr)
lines.append(cur_addr)
if cur_addr == end:
break
flag_size = 0
loop_size = None
for ea in lines:
ins = ida_ua.insn_t()
idaapi.decode_insn(ins, ea)
if ins.itype == idaapi.NN_div or ins.itype == idaapi.NN_idiv:
key_size = ins.Op1.value
if ins.itype == idaapi.NN_test or ins.itype == idaapi.NN_cmp:
if ins.Op2.type == idaapi.o_imm:
# check if following the compare is a conditonal branch
temp_addr = ea
for c in range(3):
temp_addr = idc.next_head(temp_addr)
if temp_addr in lines:
inst = idautils.DecodeInstruction(temp_addr)
if inst.itype in JMPS:
if inst.itype != idaapi.NN_jmp:
loop_size = ins.Op2.value
break
if ins.Op2.type == idaapi.o_void:
continue
return loop_size
def get_dynamic_call():
for func in idautils.Functions():
flags = idc.get_func_attr(func, FUNCATTR_FLAGS) # skip library & thunk functions
if flags & FUNC_LIB or flags & FUNC_THUNK:
continue
dism_addr = list(idautils.FuncItems(func))
for ea in dism_addr:
if idc.print_insn_mnem(ea) == "call" or idc.print_insn_mnem(ea) == "jmp":
op = get_operand_type(ea, 0)
if op in [1,3]: # 1 = o_reg 3 = o_phrase
print('Dynamic Call at "0x%x %s"' % (ea, idc.generate_disasm_line(ea, 0)))
def run():
xor_eas = get_xors()
for ea in xor_eas:
xor_blocks = get_xor_blocks(ea)
for bb in xor_blocks.keys():
tt = xor_blocks[bb]
start, end = get_xor_loop(tt["start"],tt["end"] )
loop_size = analyze_xor_loop(bb, start, end)
if loop_size:
print("XOR operation at 0x%x \n\tBasic Block Start 0x%x \n\tBasic Block End %x \n\tLoop Size 0x%x" % (bb, start, end, loop_size))
get_riat_func()
get_dynamic_call()
run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment