def bp_func(bp_num, params): ''' Function to use as a callback on breakpoints ''' from ipython_shell import start_shell import api from utils import pp_print # The first argument of insn begin and mem write/read callbacks should # always be cpu_index cpu_index = params["cpu_index"] cpu = api.r_cpu(cpu_index) pp_print("[!] Breakpoint %s hit at address %x\n" % (bp_num, cpu.PC)) start_shell()
def mem_write(params): ''' Callback for memory writes. ''' global cm global page_status global current_layer global UNPACKER_LOG_PATH global UNPACKER_DUMP_PATH import api from cpus import X64CPU, X86CPU # Get callback parameters cpu_index = params["cpu_index"] vaddr = params["vaddr"] size = params["size"] haddr = params["haddr"] data = params["data"] # Get running process, as well as CPU object pgd = api.get_running_process(cpu_index) cpu = api.r_cpu(cpu_index) if not api.is_kernel_running(cpu_index): mask = 0xFFFFF000 if TARGET_LONG_SIZE == 4 else 0xFFFFFFFFFFFFF000 page = vaddr & mask # Set page write status (update with current layer) page_status_w[page] = current_layer # Log the page write pc = cpu.RIP if isinstance(cpu, X64CPU) else cpu.EIP if TARGET_LONG_SIZE == 4: append_log("[W] - PGD [%08x] - PAGE [%08x] - FROM [%08x]" % (pgd, page, pc)) else: append_log("[W] - PGD [%016x] - PAGE [%016x] - FROM [%016x]" % (pgd, page, pc))
def disassemble(addr, cpu_index): ''' Disassemble instruction. Receives 2 parameters: :param addr: Address from instruction to disassemble :type addr: int :param cpu_index: CPU index :type cpu_index: int ''' global logger global num_ins pgd = api.get_running_process(cpu_index) if api.get_os_bits() == 32: md = Cs(CS_ARCH_X86, CS_MODE_32) content = api.r_va(pgd, addr, 0x4) else: md = Cs(CS_ARCH_X86, CS_MODE_64) content = api.r_va(pgd, addr, 0x6) md.detail = True for insn in md.disasm(content, addr): if insn.mnemonic == "call": if len(insn.operands) > 0: mycpu = api.r_cpu(0) simbolo = None if api.get_os_bits() == 32: simbolo = api.va_to_sym(pgd, mycpu.EIP) else: simbolo = api.va_to_sym(pgd, mycpu.RIP) if simbolo != None and api.get_os_bits() != 32: ## Microsoft x64 calling convention ## logger.info( "[API]0x%x:\t%s\t%s\t[RIP]:0x%x\t%s\t[PGD]: %x", insn.address, insn.mnemonic, insn.op_str, mycpu.RIP, simbolo, pgd) num_ins = num_ins + 1 ## RCX - Arg 1 try: rcx_content = api.r_va(pgd, mycpu.RCX, 0x100) logger.info("[RCX]: 0x%x [Data]: %s", mycpu.RCX, smart_format(rcx_content, 0x100, True)) except: logger.info("[RCX]: 0x%x", mycpu.RCX) ## RDX - Arg 2 try: rdx_content = api.r_va(pgd, mycpu.RDX, 0x100) logger.info("[RDX]: 0x%x [Data]: %s", mycpu.RDX, smart_format(rdx_content, 0x100, True)) except: logger.info("[RDX]: 0x%x", mycpu.RDX) ## R8 - Arg 3 try: r8_content = api.r_va(pgd, mycpu.R8, 0x100) logger.info("[R8]: 0x%x [Data]: %s", mycpu.R8, smart_format(r8_content, 0x100, True)) except: logger.info("[R8]: 0x%x", mycpu.R8) ## R9 - Arg 4 try: r9_content = api.r_va(pgd, mycpu.R9, 0x100) logger.info("[R9]: 0x%x [Data]: %s", mycpu.R9, smart_format(r9_content, 0x100, True)) except: logger.info("[R9]: 0x%x", mycpu.R9) ## RAX - return value try: rax_content = api.r_va(pgd, mycpu.RAX, 0x100) logger.info("[RAX]: 0x%x [Data]: %s", mycpu.RAX, smart_format(rax_content, 0x100, True)) except: logger.info("[RAX]: 0x%x", mycpu.RAX) logger.info("--") elif simbolo != None: ## x86 call conventions # cdecl -> arguments are pushed on the stack in the reverse order. EAX return # syscall -> arguments are pushed on the stack right to left. # optlink -> arguments are pushed on the stack right to left. # ... logger.info( "[API]0x%x:\t%s\t%s\t[EIP]:0x%x\t%s\t[PGD]: %x", insn.address, insn.mnemonic, insn.op_str, mycpu.EIP, simbolo, pgd) num_ins = num_ins + 1 bytestoread = 0x200 try: eax_content = api.r_va(pgd, mycpu.EAX, bytestoread) logger.info( "[EAX]: 0x%x [Data]: %s", mycpu.EAX, smart_format(eax_content, bytestoread, True)) except: logger.info("[EAX]: 0x%x", mycpu.EAX) try: ecx_content = api.r_va(pgd, mycpu.ECX, bytestoread) logger.info( "[ECX]: 0x%x [Data]: %s", mycpu.ECX, smart_format(ecx_content, bytestoread, True)) except: logger.info("[ECX]: 0x%x", mycpu.ECX) try: edx_content = api.r_va(pgd, mycpu.EDX, bytestoread) logger.info( "[EDX]: 0x%x [Data]: %s", mycpu.EDX, smart_format(edx_content, bytestoread, True)) except: logger.info("[EDX]: 0x%x", mycpu.EDX) try: ebp_arg2_content = api.r_va(pgd, mycpu.EBP + 8, bytestoread) logger.info( "[EBP+8]: 0x%x [Data]: %s", mycpu.EBP + 8, smart_format(ebp_arg2_content, bytestoread, True)) except: logger.info("[EBP+8]: 0x%x", mycpu.EBP + 8) try: ebp_arg3_content = api.r_va(pgd, mycpu.EBP + 12, bytestoread) logger.info( "[EBP+12]: 0x%x [Data]: %s", mycpu.EBP + 12, smart_format(ebp_arg3_content, bytestoread, True)) except: logger.info("[EBP+12]: 0x%x", mycpu.EBP + 12) try: ebp_arg4_content = api.r_va(pgd, mycpu.EBP + 16, bytestoread) logger.info( "[EBP+16]: 0x%x [Data]: %s", mycpu.EBP + 16, smart_format(ebp_arg4_content, bytestoread, True)) except: logger.info("[EBP+16]: 0x%x", mycpu.EBP + 16) try: ebp_arg5_content = api.r_va(pgd, mycpu.EBP + 20, bytestoread) logger.info( "[EBP+20]: 0x%x [Data]: %s", mycpu.EBP + 20, smart_format(ebp_arg5_content, bytestoread, True)) except: logger.info("[EBP+20]: 0x%x", mycpu.EBP + 20) try: ebp_arg6_content = api.r_va(pgd, mycpu.EBP + 24, bytestoread) logger.info( "[EBP+24]: 0x%x [Data]: %s", mycpu.EBP + 24, smart_format(ebp_arg6_content, bytestoread, True)) except: logger.info("[EBP+24]: 0x%x", mycpu.EBP + 24) logger.info("--")
def mem_write(params): ''' Callback for memory writes. ''' global cm global page_status_x global page_status_w global current_layer global UNPACKER_LOG_PATH global UNPACKER_DUMP_PATH global section_maps global written_files global ntdll_space import api from cpus import X64CPU, X86CPU # Get callback parameters cpu_index = params["cpu_index"] vaddr = params["vaddr"] size = params["size"] haddr = params["haddr"] data = params["data"] # Get running process, as well as CPU object pgd = api.get_running_process(cpu_index) cpu = api.r_cpu(cpu_index) pc = cpu.RIP if isinstance(cpu, X64CPU) else cpu.EIP if pgd not in page_status_w: page_status_w[pgd] = {} if not api.is_kernel_running(cpu_index): mask = 0xFFFFF000 if TARGET_LONG_SIZE == 4 else 0xFFFFFFFFFFFFF000 page = vaddr & mask overlapping_section_maps = [] # Check if the memory was mapped to file, so we record a file write for such file for base, size, file_offset, file_name in section_maps: if page >= (base & mask) and page < (( (base + size) & mask) + 0x1000): overlapping_section_maps.append( (base, size, file_offset, file_name)) # If it comes from ntdll and affects to a mapped file, just ignore it, # it is likely due to relocations or loader/stuff and is prone to FPs. if (pc >= ntdll_space[0] and pc < (ntdll_space[0] + ntdll_space[1]) ) and len(overlapping_section_maps) > 0: return # Set page write status (update with current layer) page_status_w[pgd][page] = current_layer # Log the page write if TARGET_LONG_SIZE == 4: append_log("[W] - PGD [%08x] - PAGE [%08x] - FROM [%08x]" % (pgd, page, pc)) else: append_log("[W] - PGD [%016x] - PAGE [%016x] - FROM [%016x]" % (pgd, page, pc)) # Only reflect on mapped file if it doesn't come from ntdll, to avoid FPs due to relocation # fixing. # Finally, check if the memory was mapped to file, so we record a file write for such file for base, size, file_offset, file_name in overlapping_section_maps: if file_name not in written_files: written_files[file_name] = [ ((page - (base & mask)) + file_offset, 0x1000) ] else: written_files[file_name].append( ((page - (base & mask)) + file_offset, 0x1000)) append_log( "^^^---> PAGE is section mapped, FILE [%s] - Offset %x - Size %x" % (file_name, (page - (base & mask)) + file_offset, size))
def gdb_read_thread_register(thread_id, thread_list, gdb_register_index): ''' Given a GDB register index, return an str with its value. Obtain the value either from the running CPU or the saved KTRAP_FRAME. NOTE: Not all registers are supported, if so, 0's are returned. ''' from utils import ConfigurationManager as conf_m from api import r_cpu from cpus import RT_SEGMENT from cpus import RT_REGULAR if conf_m.platform == "i386-softmmu": from cpus import gdb_map_i386_softmmu as gdb_map elif conf_m.platform == "x86_64-softmmu": from cpus import gdb_map_x86_64_softmmu as gdb_map else: raise NotImplementedError( "[gdb_read_thread_register] Architecture not supported yet") # If it is not mapped to a CPU register or KTRAP_FRAME value, # we just return 0s. if gdb_register_index not in gdb_map: return "\0" * (conf_m.bitness / 8) else: str_size = gdb_map[gdb_register_index][2] cpu_index = None thread = None some_thread_running = False # First, check if we can read the register from the CPU object for element in thread_list: if element['id'] == thread_id: thread = element cpu_index = element['running'] if cpu_index: some_thread_running = True if thread is None: return None if cpu_index is None and not some_thread_running: cpu_index = 0 if cpu_index is not None: cpu = r_cpu(cpu_index) val = 0 try: if gdb_map[gdb_register_index][3] == RT_SEGMENT: val = getattr(cpu, gdb_map[gdb_register_index][0])['base'] else: val = getattr(cpu, gdb_map[gdb_register_index][0]) except: val = 0 if val == -1: val = 0 return val_to_str(val, str_size) # If the thread is not running, read it from the KTRAP_FRAME else: if os_family == OS_FAMILY_WIN: from windows_vmi import win_read_thread_register_from_ktrap_frame val = 0 try: val = win_read_thread_register_from_ktrap_frame( thread, gdb_map[gdb_register_index][1]) except Exception as e: pp_debug( "Exception after win_read_thread_register_from_ktrap_frame: " + str(e)) if val == -1: val = 0 return val_to_str(val, str_size) elif os_family == OS_FAMILY_LINUX: raise NotImplementedError( "gdb_read_thread_register not implemented yet on Linux guests")