def on_sys_read_return(cpu, pc, fd, buf, count): proc = panda.plugins['osi'].get_current_process(cpu) procname = ffi.string(proc.name) if proc != ffi.NULL else "error" fname_ptr = panda.plugins['osi_linux'].osi_linux_fd_to_filename(cpu, proc, fd) fname = ffi.string(fname_ptr) if fname_ptr != ffi.NULL else "error" rc = panda.plugins['syscalls2'].get_syscall_retval(cpu) print(f"[PANDA] {procname} read {rc} bytes from {fname}")
def __init__(self, panda, cpu, fd, cmd, guest_ptr, use_osi_linux = False): ''' Do unpacking, optionally using OSI for process and file name info. ''' do_ioctl_init(panda.arch_name) self.cmd = ffi.new("union IoctlCmdUnion*") self.cmd.asUnsigned32 = cmd self.original_ret_code = None self.osi = use_osi_linux # Optional syscall argument: pointer to buffer if (self.cmd.bits.arg_size > 0): try: self.has_buf = True self.guest_ptr = guest_ptr self.guest_buf = panda.virtual_memory_read(cpu, self.guest_ptr, self.cmd.bits.arg_size) except ValueError: raise RuntimeError("Failed to read guest buffer: ioctl({})".format(str(self.cmd))) else: self.has_buf = False self.guest_ptr = None self.guest_buf = None # Optional OSI usage: process and file name if self.osi: proc = panda.plugins['osi'].get_current_process(cpu) proc_name_ptr = proc.name file_name_ptr = panda.plugins['osi_linux'].osi_linux_fd_to_filename(cpu, proc, panda.ffi.cast("int", fd)) self.proc_name = ffi.string(proc_name_ptr).decode() if proc_name_ptr != ffi.NULL else "unknown" self.file_name = ffi.string(file_name_ptr).decode() if file_name_ptr != ffi.NULL else "unknown" else: self.proc_name = None self.file_name = None
def on_sys_read_return(cpu, pc, fd, buf, count): proc = panda.plugins['osi'].get_current_process(cpu) procname = ffi.string(proc.name) if proc != ffi.NULL else "error" fname_ptr = panda.plugins['osi_linux'].osi_linux_fd_to_filename( cpu, proc, fd) fname = ffi.string(fname_ptr) if fname_ptr != ffi.NULL else "error" print(f"[PANDA] {procname} read from {fname}") if b"cat" in procname: populate_ghidra(cpu, pc) import ipdb ipdb.set_trace()
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 new_asid(cpu, oldasid, newasid): global reverted print("ASID", reverted, panda.arch) if reverted and panda.arch in osi_supported: # If osi unsupported, bail proc = panda.plugins['osi'].get_current_process(cpu) name = ffi.string(proc.name) if name not in seen: seen.add(name) return 0
def get_calltree(cpu): # Print the calltree to the current process proc = panda.plugins['osi'].get_current_process(cpu) if proc == ffi.NULL: print("Error determining current process") return procs = panda.get_processes_dict(cpu) chain = [{'name': ffi.string(proc.name).decode('utf8', 'ignore'), 'pid': proc.pid, 'parent_pid': proc.ppid}] while chain[-1]['pid'] > 1 and chain[-1]['parent_pid'] in procs.keys(): chain.append(procs[chain[-1]['parent_pid']]) return " -> ".join(f"{item['name']} ({item['pid']})" for item in chain[::-1])
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=0x{bits.cmd_num:x},type=0x{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 ntmap(cpu, pc, SectionHandle, ProcessHandle, BaseAddress, ZeroBits, CommitSize, SectionOffset, ViewSize, InheritDisposition, AllocationType, Win32Protect): proc = panda.get_process_name(cpu) #print("PC 0x{:x}".format(pc)) print(proc) if proc == process_name: mappings = panda.get_mappings(cpu) for mapping in mappings: print( "Name: " + ffi.string(mapping.name).decode(), "Base: 0x{:x} Size: 0x{:x}".format(mapping.base, mapping.size)) if ffi.string(mapping.name).decode().lower() == "kernel32.dll": global base base = mapping.base global si si = mapping.size print("Enabling memory callback") panda.enable_callback("cb1") print("Enabling hypercall callback") panda.enable_callback("hyper") print("Disabling MapViewOfSections hook") panda.disable_ppp("ntmap")
def bbe(cpu, tb, exit_code): global bb_ctr if (not panda.in_kernel(cpu)) and (exit_code <= 1): bb_ctr += 1 if (bb_ctr % 1000) == 0: proc = panda.plugins['osi'].get_current_process(cpu) if proc == ffi.NULL: return proc_name = ffi.string(proc.name).decode("utf-8") pc = panda.current_pc(cpu) in_dll = panda.plugins['osi'].in_shared_object(cpu, proc) print(f"{proc_name}@{pc:016x}, in SO? {in_dll}")
def populate_ghidra(cpu, pc): tid = currentProgram.startTransaction("BRIDGE: Change Memory Sections") memory = currentProgram.getMemory() delete_all_memory_segments(memory, monitor) names = set() for mapping in panda.get_mappings(cpu): if mapping.file != ffi.NULL: name = ffi.string(mapping.file).decode() else: name = "[unknown]" while name in names: from random import randint name += ":" + hex(randint(0, 100000000)) names.add(name) memory.createInitializedBlock(name, toAddr(mapping.base), mapping.size, 0, monitor, False) memory_read = read_memory(cpu, mapping.base, mapping.size) if memory_read: memory.setBytes(toAddr(mapping.base), read_memory(cpu, mapping.base, mapping.size)) analyzeAll(currentProgram) #import ghidra.app.decompiler as decomp decomp = b.remote_import("ghidra.app.decompiler") # ## get the decompiler interface iface = decomp.DecompInterface() # ## decompile the function iface.openProgram(currentProgram) fn = getFunctionContaining(toAddr(pc)) d = iface.decompileFunction(fn, 5, monitor) ## get the C code as string if not d.decompileCompleted(): print(d.getErrorMessage()) else: code = d.getDecompiledFunction() ccode = code.getC() print(ccode) setCurrentLocation(toAddr(pc)) currentProgram.endTransaction(tid, True)
def proc_write_capture_on_sys_write_enter(cpu, pc, fd, buf, cnt): try_read = False # Capture console output if self._console_capture: # Fun trick: lazy eval of OSI # Based on the idea that a non-POSIX FD will only be used after boot is finished an OSI is functional # Note: doesn't capture boot logs (would require hooking kernel's printk, not write syscall) if (fd == 1) or (fd == 2) or (fd == 3): try_read = True else: curr_proc = panda.plugins['osi'].get_current_process(cpu) file_name_ptr = panda.plugins[ 'osi_linux'].osi_linux_fd_to_filename( cpu, curr_proc, fd) file_path = ffi.string(file_name_ptr).decode() if ("tty" in file_path): try_read = True if try_read: try: data = panda.virtual_memory_read(cpu, buf, cnt) except ValueError: raise RuntimeError( f"Failed to read buffer: addr 0x{buf:016x}") if fd == 2: self._console_printed_err = True log_file = self._console_log_dir.joinpath("console.out") with open(log_file, "ab") as f: f.write(data) self._files_written.add(str(log_file)) # Use OSI to capture logs for a named process if self._proc_name: curr_proc = panda.plugins['osi'].get_current_process(cpu) curr_proc_name = ffi.string(curr_proc.name).decode() if self._proc_name == curr_proc_name: if not try_read: # If we didn't already read this data in once for console capture try: data = panda.virtual_memory_read(cpu, buf, cnt) except ValueError: raise RuntimeError( f"Failed to read buffer: proc \'{curr_proc_name}\', addr 0x{buf:016x}" ) file_name_ptr = panda.plugins[ 'osi_linux'].osi_linux_fd_to_filename( cpu, curr_proc, fd) file_path = ffi.string(file_name_ptr).decode() # For informational purposes only, collection not reliant on this exact mapping if fd == 1: # POSIX stdout file_path += ".stdout" elif fd == 2: # POSIX stderr file_path += ".stderr" self._proc_printed_err = True log_file = self._proc_log_dir.joinpath( file_path.replace("//", "_").replace("/", "_")) with open(log_file, "ab") as f: f.write(data) self._files_written.add(str(log_file))
def proc_write_capture_on_sys_write_enter(cpu, pc, fd, buf, cnt): curr_proc = panda.plugins['osi'].get_current_process(cpu) file_name_ptr = panda.plugins['osi_linux'].osi_linux_fd_to_filename(cpu, curr_proc, fd) if ffi.string(file_name_ptr).decode() == "/dev/ttyS0": print(panda.virtual_memory_read(cpu, buf, cnt).decode(), end="")
def rec(cpu, procname, asid, pid): print(ffi.string(procname).decode()) if ffi.string(procname).decode() == process_name: print(ffi.string(procname).decode(), " Started!") if not rep: panda.run_monitor_cmd("begin_record rec")
def bbe(cpu, tb): proc = panda.plugins['osi'].get_current_process(cpu) name = ffi.string(proc.name) if name not in printed: printed.add(name) print(name)