def __init__(self, *args): super(IDADebugger, self).__init__(*args) self.hooked = False self.trace = Trace() self._module_name = 'IDADbg' self.arch = get_arch_dynamic() # init the cpu context with 0 if self.arch == 32: self.ctx = { c: '0' for c in [ 'eax', 'ebx', 'edx', 'ecx', 'ebp', 'esp', 'eip', 'edi', 'esi', 'cf', 'zf', 'sf', 'of', 'pf', 'af', 'tf', 'df' ] } elif self.arch == 64: self.ctx = { c: '0' for c in [ 'rax', 'rbx', 'rdx', 'rcx', 'rbp', 'rsp', 'rip', 'edi', 'rsi', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'cf', 'zf', 'sf', 'of', 'pf', 'af', 'tf', 'df' ] } self.IAT = [] self.func_args = defaultdict(lambda: set())
def trace_init(self): """ Init the trace. """ if self.arch is None: self.arch == get_arch_dynamic() # reset trace self.trace = Trace(reg_size=self.arch)
def gen_instruction_trace(self, start=BeginEA(), end=BADADDR): self._trace = Trace() if not self.check: self.dbg = self.load_dbg() self.dbg.hook_dbg() remove_all_colors() trace = self.dbg.gen_trace(start, end) self.dbg.unhook_dbg() return trace
def ItemDoubleClickSlot(self, index): """ TreeView DoubleClicked Slot. @param index: QModelIndex object of the clicked tree index item. @return: """ # fetch the clicked string instr = index.data(0) # if instr is an instruction, remove trace lines with said instruction self.trace = Trace( tr=[line for line in self.trace if line.disasm_str() != instr]) self.PopulateModel(self.trace)
def load(): """ 从文件加载trace Load a trace from file. Supported are IDAs txt trace files and VMAttacks json files. Further OllyDBG and ImmunityDBG traces are supported but have slightly limited analysis capabilities. :param path: system path to trace file :return: trace object """ path = '' try: fd = QtGui.QFileDialog() fd.setFileMode(QtGui.QFileDialog.AnyFile) fd.setFilters(["Text files (*.txt)", "JSON files (*.json)"]) fd.setWindowTitle('Load Trace ...') if fd.exec_(): path = fd.selectedFiles()[0] else: path = None except: msg('A Problem occured with the file selector dialog, first *.txt file in the current working directory was choosen!' ) for f in os.listdir(os.getcwd()): if f.endswith('txt'): path = f if path == '': path = asktext(40, '', 'Please provide the full path to the trace file: ') if path is not None: get_log().log('[TRC] Loaded the trace at %s\n' % path) if path.endswith('.txt'): with open(path, 'r') as f: lines = f.readlines() elif path.endswith('.json'): with open(path) as f: lines = json.load(f) else: return None trace = Trace() functions = { SegName(addr): { GetFunctionName(ea): ea for ea in Functions(SegStart(addr), SegEnd(addr)) } for addr in Segments() } try: context = defaultdict(lambda: False) # framework json trace if isinstance(lines, dict) or path.endswith('.json'): get_log().log('[TRC] The trace seems to be a VMAttack trace\n') for index in range(len(lines.keys())): line = lines[str(index)] t = Traceline(thread_id=line[0], addr=line[1], disasm=line[2], ctx=line[3], comment=line[4]) t.grade = line[5] trace.append(t) # ida trace via Win32Dbg elif lines[0].startswith('Thread '): for i in lines[3:]: if i.startswith('Thread'): break values = i.split('\t') # thread id thread_id = int(values[0], 16) # addr addr = BADADDR func_name = values[1].strip(' ').split(':') if len(func_name) == 2: try: # .segment:addr addr = int(func_name[1], 16) except: try: # .segment:func_name+offset offset = int(func_name[1].split('+')[1], 16) name = func_name[1].split('+')[0] addr = functions[func_name[0]][name] + offset except: try: # .segment:func_name-offset offset = int( i.split('-')[1].split(' ')[0], 16) name = func_name[1].split('-')[0] addr = functions[ func_name[0]][name] - offset except: if not func_name[1].startswith( 'loc_'): # .segment:func_name addr = functions[func_name[0]][ func_name[1]] else: # .segment:jmp_location addr = int(func_name[1][4:], 16) elif len(func_name) == 3: addr = int(func_name[2][4:], 16) # disasm disasm = values[2].strip(' ').lower() disasm = disasm.split(' ') disasm = [x.lstrip() for x in disasm] disasm = filter(None, disasm) if len(disasm) > 1 and disasm[1].__contains__(', '): temp = disasm.pop(1) for elem in temp.split(', '): disasm.append( elem.lstrip().lstrip('0').rstrip('h')) # remove [ebp+0] for dis in disasm: if dis.__contains__('[ebp+0]'): dis.replace('[ebp+0]', '[ebp]') # context ida_ctx = values[3].strip(' ').split(' ') for value in ida_ctx: try: a, b = value.split('=') if len(b) > 1: b = ''.join( c.rstrip('\r\n') for c in b.lstrip('0')) if b == '': b = '0' context[a.lower()] = b except: pass trace.append( Traceline(thread_id=thread_id, addr=addr, disasm=disasm, ctx=deepcopy(context))) # immunity trace elif lines[0].startswith('Address '): for i in lines[1:]: if i.__contains__('Run trace closed') or i.__contains__( 'Process terminated'): break values = i.split('\t') try: # thread_id thread_id = sum( ord(c) for c in values[1]) # immunity uses names, e.g. main # addr try: addr = int(values[0], 16) except: addr = BADADDR # disasm disasm = values[2].lower().rstrip('\r\n') disasm = disasm.split(' ', 1) if len(disasm) > 1 and disasm[1].__contains__(','): temp = disasm.pop(1) for elem in temp.split(','): disasm.append(elem.lstrip('0')) disasm = [ x.split('dword ptr ')[1] if x.__contains__('dword ptr ') else x for x in disasm ] if len(disasm) == 2 and len( re.findall(r'.*\[.*[\+\-\*].*[\+\-\*].*\].*', disasm[1])) > 0: disasm[1] = ida_offset(disasm[1]) # context if len(values) > 3: olly_ctx = values[3].lstrip(' ').rstrip( '\r\n').split(',') for value in olly_ctx: try: a, b = value.split('=') if len(b) > 1: b = ''.join(c for c in b.lstrip('0') if c not in '\n\r\t') if b == '': b = '0' context[a.lower()] = b except: pass trace.append( Traceline(thread_id=thread_id, addr=addr, disasm=disasm, ctx=deepcopy(context))) except: if i.__contains__('terminated') or i.__contains__( 'entry point'): pass # olly trace elif lines[1].startswith('main '): for i in lines[1:]: if i.__contains__('Logging stopped'): break values = i.split('\t') # thread_id thread_id = sum( ord(c) for c in values[0]) # olly uses names, e.g. main # addr try: addr = int(values[1], 16) except: addr = BADADDR # disasm disasm = values[2].lower().rstrip('\r\n') disasm = disasm.split(' ', 1) if len(disasm) > 1 and disasm[1].__contains__(','): temp = disasm.pop(1) for elem in temp.split(','): disasm.append(elem.lstrip('0')) disasm = [ x.split('dword ptr ')[1] if x.__contains__('dword ptr ') else x for x in disasm ] if len(disasm) == 2 and len( re.findall(r'.*\[.*[\+\-\*].*[\+\-\*].*\].*', disasm[1])) > 0: disasm[1] = ida_offset(disasm[1]) # context if len(values) > 3: olly_ctx = values[3].lstrip(' ').rstrip('\r\n').split( ',') for value in olly_ctx: try: a, b = value.split('=') if len(b) > 1: b = ''.join(c for c in b.lstrip('0') if c not in '\n\r\t') if b == '': b = '0' context[a.lower()] = b except: pass trace.append( Traceline(thread_id=thread_id, addr=addr, disasm=disasm, ctx=deepcopy(context))) if 'rax' in trace[-1].ctx.keys(): trace.ctx_reg_size = 64 elif 'eax' in trace[-1].ctx.keys( ) and 'rax' not in trace[-1].ctx.keys(): trace.ctx_reg_size = 32 msg("[*] Trace Loaded!\n") return trace except Exception, e: raise Exception('[*] Exception occured: \n%s\n' % (e.message))
def follow_virt_reg(trace, **kwargs): """ Follows the virtual registers and extracts the relevant trace lines to clarify how the final result in a virtual register came to be and what values(=recursively) it consists of. :param trace: instruction trace :param virt_reg_addr: the stack addr of the virtual register :param real_reg_name: reg string :return: trace consisting of relevant tracelines for the virtual register """ update = kwargs.get('update', None) manual = kwargs.get('manual', False) if manual: real_reg_name = AskStr('eax', 'Which register do you want followed?') if real_reg_name is None: real_reg_name = get_reg('rax', trace.ctx_reg_size) else: real_reg_name = get_reg(real_reg_name, trace.ctx_reg_size) else: real_reg_name = kwargs.get('real_reg_name', get_reg('rax', trace.ctx_reg_size)) virt_reg_addr = kwargs.get('virt_reg_addr', None) if virt_reg_addr is None: vr = find_virtual_regs(deepcopy(trace)) virt_reg_addr = vr[real_reg_name] if update is not None: update.pbar_update(30) backtrace = Trace() watch_addrs = set() reg_vals = set() trace = optimization_const_propagation(trace) trace = optimization_stack_addr_propagation(trace) if update is not None: update.pbar_update(10) # reversing the trace makes the backward tracersal easier trace.reverse() # get reg value at pop reg = get_reg(real_reg_name, trace.ctx_reg_size) for line in trace: if len(line.disasm) == 2: if line.disasm[0] == 'pop' and get_reg_class( line.disasm[1]) == get_reg_class(reg): reg_vals.add(line.ctx[reg]) break watch_addrs.add(virt_reg_addr) for line in trace: assert isinstance(line, Traceline) if line.is_jmp: continue try: # +1 because trace is reversed to get to prev element prev = trace[trace.index(line) + 1] for val in reg_vals.copy(): if val in line.ctx.values() and val not in prev.ctx.values(): backtrace.append(line) # if val suddenly appears in the ctx there should be 2 possibilities: # 1. it was moved from mem, so it was on the stack -> append stack addres to be watched out for if line.is_mov and line.is_op2_mem: watch_addrs.add(''.join(c for c in line.disasm[2] if c not in '[]')) reg_vals.remove(val) # 2. it was computed -> if regs played a role in the computation add them to values to watch out for elif not line.is_mov: if line.is_op1_reg: reg_vals.add(line.disasm[1]) if line.is_op1_mem: watch_addrs.add(''.join(c for c in line.disasm[1] if c not in '[]')) if line.is_op2_reg: # not necessarily the case for lea reg_vals.add(line.disasm[2]) if line.is_op2_mem: watch_addrs.add(''.join(c for c in line.disasm[2] if c not in '[]')) except Exception, e: pass #print 'reg_vals\n',line, e.message if watch_addrs: for addr in watch_addrs.copy(): try: if line.disasm[1].__contains__(addr): backtrace.append(line) reg_vals.add(line.disasm[2]) r = line.ctx.keys()[line.ctx.values().index( line.disasm[2])] for i in range(len(trace)): temp = trace[trace.index(line) + i] if len(temp.disasm) == 3: if temp.disasm[1][-2:] == r[-2:]: if get_reg_class(r[-2:]) is not None: watch_addrs.add(temp.disasm[2][1:-1]) break if line.is_mov: watch_addrs.remove(addr) except Exception, e: #print 'watch_addr\n',line, e.message pass