def virt_mem_after_read(cpustate, pc, addr, size, buf): curbuf = ffi.cast("char*", buf) current = panda.get_current_process(cpustate) if current != ffi.NULL: if size >= 5: buf_addr = hex(int(ffi.cast("uint64_t", buf))) buf_str = ffi.string(ffi.cast("char*",buf)).decode(errors='ignore') print("Read buf: %s, size: %x, at pc: %x %s" %(buf_addr[2:], size, addr, buf_str))
def on_sys_read_return(cpu, pc, fd, buf, count): asid = panda.current_asid(cpu) if is_hooked(fd, asid): # We need to make up a file. Grab our contents/fn f = file_descriptors[(fd, asid)] assert (f.is_fake), "Can't fake a non-faked FD" faker = files_faked[f.filename] logger.debug( f"Hooking return of read for FD={fd} corresponding to file {f.filename}" ) """ if faker.fake_contents: # Static file contents # index into it based on f.offset return up to count contents = faker.fake_contents+"\x00" if f.offset >= len(contents): # No bytes left to read logger.debug(f"\t Returning EOF") cpu.env_ptr.regs[R_EAX] = 0 return else: # Bytes to read file_contents = contents[f.offset:f.offset+count].encode("utf8") logger.debug(f"\t Set buffer at 0x{buf:x} to: {file_contents}") write_result = panda.virtual_memory_write(cpu, buf, file_contents) # Write buffer - May fail if page isn't mapped if write_result < 0: # Page not mapped. Make guest retry - Don't update our fake file's offset because this read will fail logger.info(f"\t Failed to write data into guest memory") cpu.env_ptr.regs[R_EAX] = ffi.cast("unsigned char", -11) # Return EAGAIN to make the guest retry (with page mapped, hopefully) return cpu.env_ptr.regs[R_EAX] = len(file_contents) # Bytes written f.offset += len(file_contents) return else: # Function """ # First try a junk write to see if memory write will fail (avoids calling class twice) try: panda.virtual_memory_write(cpu, buf, b'PANDA_TEST_DATA') except Exception: # Page not mapped. Make guest retry cpu.env_ptr.regs[R_EAX] = ffi.cast("unsigned char", -11) # Return EAGAIN return # Call fn and update guest memory (data, ret_val) = faker.get_data(f, count) if data and len(data): try: panda.virtual_memory_write(cpu, buf, data) except Exception: # Page not mapped. Make guest retry. XXX: calls get_data twice logger.info( f"\t Failed to write data into guest memory. XXX Duplicate call to get_data" ) cpu.env_ptr.regs[R_EAX] = ffi.cast("unsigned char", -11) # Return EAGAIN return cpu.env_ptr.regs[R_EAX] = ret_val
def syscall_enter(cpu, pc, call, ctx): for arg_idx in range(call.nargs): # Debug prints #type_str = ffi.string(ffi.cast("syscall_argtype_t", call.argt[arg_idx])) #print(f"\tArg{arg_idx}: size {call.argsz[arg_idx]}, type {type_str}") # Log all pointers passed to syscalls - strings or poitners to buffers if call.argt[arg_idx] in [ argtypes['SYSCALL_ARG_PTR'], argtypes['SYSCALL_ARG_STR'] ]: arg_ptr = int( ffi.cast('uint64_t*', ctx.args)[arg_idx] ) # Cast to uint64_t's _BEFORE_ we access (weird) TODO asid = panda.current_asid(cpu) proc = panda.plugins['osi'].get_current_process(cpu) syscall_name = ffi.string(call.name).decode( 'utf8') if call.name != ffi.NULL else "unknown" if asid not in asid_to_procname: proc_name = ffi.string(proc.name).decode('utf8') if ( proc.name != ffi.NULL) else "unknown" asid_to_procname[asid] = proc_name proc_name = asid_to_procname[asid] if proc_name in procnames_of_interest: print( f"Process: {proc_name} ({ctx.asid}) syscall {syscall_name} with buffer at 0x{arg_ptr:x}" ) if arg_ptr not in identified_buffers.keys(): identified_buffers[arg_ptr] = [] identified_buffers[arg_ptr].append( (asid, proc_name, panda.rr_get_guest_instr_count(), syscall_name))
def on_sys_open_return(cpu, pc, fname_ptr, flags, mode): asid = panda.current_asid(cpu) fd = cpu.env_ptr.regs[R_EAX] global pending_hyperfile if not pending_hyperfile: # Return EAGAIN to make the guest retry (with page mapped, hopefully) cpu.env_ptr.regs[R_EAX] = ffi.cast("unsigned char", -11) return file_descriptors[(fd, asid)] = pending_hyperfile if pending_hyperfile.is_fake: logger.debug( f"Hook stored info for fake FD {fd} = {pending_hyperfile.filename}" ) pending_hyperfile = None
def __str__(self): if self.osi: self_str = "\'{}\' using \'{}\' - ".format(self.proc_name, self.file_name) else: self_str = "" bits = self.cmd.bits direction = ffi.string(ffi.cast("enum ioctl_direction", bits.direction)) ioctl_desc = f"dir={direction},arg_size={bits.arg_size:x},cmd={bits.cmd_num:x},type={bits.type_num:x}" if (self.guest_ptr == None): self_str += f"ioctl({ioctl_desc}) -> {self.original_ret_code}" else: self_str += f"ioctl({ioctl_desc},ptr={self.guest_ptr:08x},buf={self.guest_buf}) -> {self.original_ret_code}" return self_str
def before_write(cpu, pc, start_addr, size, buf): for addr in range(start_addr, start_addr + size): if addr not in identified_buffers: continue buf_base = buf + (addr - start_addr) data = ffi.string(ffi.cast('char*', buf_base)) # Find the last instruction (highest icount) that wrote to the buffer, # but before the syscall's icount write_icount = panda.rr_get_guest_instr_count() asid = panda.current_asid(cpu) for (old_asid, proc_name, icount_use, _) in identified_buffers[addr]: if old_asid != asid: continue if icount_use < write_icount: continue in_kernel = panda.in_kernel(cpu) # Identify what module we're currently in so we can get a relative offset for module in panda.get_mappings(cpu): mod_base = None mod_name = ffi.string(module.name).decode( "utf8") if module.name != ffi.NULL else '(null)' if mod_name in procnames_of_interest: if mod_name not in base_addresses or module.base < base_addresses[ mod_name]: base_addresses[mod_name] = module.base # Debug: print memory map at each write we care about #print(f"0x{module.base:012x} - 0x{module.base+module.size:012x}: {mod_name}") if addr >= module.base and addr < module.base + module.size: # Then it's in this module mod_name = ffi.string(module.name).decode( "utf8") if module.name != ffi.NULL else '(null)' mod_base = module.base break else: print( f"Warning: No loaded module owns address 0x{addr:x}. Skipping" ) continue # Identify where PC is at time of write ''' for module in panda.get_mappings(cpu): if pc >= module.base and pc < module.base+module.size: # Then it's in this module name = ffi.string(module.name).decode("utf8") if module.name != ffi.NULL else '(null)' print(f"PC 0x{pc:x} is in {name} offset: 0x{pc-module.base:x}") ''' if (asid, addr, icount_use) not in last_write_before.keys(): last_write_before[(asid, addr, icount_use)] = (write_icount, pc, mod_name, mod_base, in_kernel) else: last_write_icount = last_write_before[(asid, addr, icount_use)][0] if write_icount > last_write_icount: # Replace with new write last_write_before[(asid, addr, icount_use)] = (write_icount, pc, mod_name, mod_base, in_kernel)
def handle_packet(cpustate,buf,size,direction,old_buf_addr): buf_uint8 = ffi.cast("uint8_t*", buf) packets.append(Ether([buf_uint8[i] for i in range(size)])) return 0