Last active
May 13, 2020 01:47
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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