diff --git a/arch.html b/arch.html index 52eb0d0..08943fe 100755 --- a/arch.html +++ b/arch.html @@ -3,14 +3,14 @@
- +Overloaded function to get aarch64 program counter. Note the PC is not stored in a general purpose register.
def get_pc(self, cpu):
- '''
- Overloaded function to get aarch64 program counter.
- Note the PC is not stored in a general purpose register.
- '''
- return cpu.env_ptr.pc
-
def get_return_address(self, cpu)
Looks up where ret will go
def get_return_address(self, cpu):
- '''
- Looks up where ret will go
- '''
- return self.get_reg(cpu, "LR")
-
def get_return_value(self, cpu)
@@ -1163,32 +1142,12 @@ Methods
Deprecated: use get_retval
-
-
-Expand source code
-
-def get_return_value(self, cpu):
- '''
- .. Deprecated:: use get_retval
- '''
- return self.get_retval(cpu)
-
def set_pc(self, cpu, val)
Overloaded function set AArch64 program counter
-
-
-Expand source code
-
-def set_pc(self, cpu, val):
- '''
- Overloaded function set AArch64 program counter
- '''
- cpu.env_ptr.pc = val
-
Inherited members
@@ -1283,16 +1242,6 @@ Methods
Looks up where ret will go
def get_return_address(self, cpu):
- '''
- Looks up where ret will go
- '''
- return self.get_reg(cpu, "LR") & 0xFFFF_FFFE
-
def get_return_value(self, cpu)
@@ -1301,16 +1250,6 @@ Methods
Deprecated: use get_retval
-
-
-Expand source code
-
-def get_return_value(self, cpu):
- '''
- .. Deprecated:: use get_retval
- '''
- return self.get_retval(cpu)
-
Inherited members
@@ -1580,84 +1519,24 @@ Methods
Deprecated: use get_return_address
-
-
-Expand source code
-
-def get_call_return(self, cpu):
- '''
- .. Deprecated:: use get_return_address
- '''
- return self.get_return_address(cpu)
-
def get_pc(self, cpu)
Overloaded function to return the MIPS current program counter
-
-
-Expand source code
-
-def get_pc(self, cpu):
- '''
- Overloaded function to return the MIPS current program counter
- '''
- return cpu.env_ptr.active_tc.PC
-
def get_reg(self, cpu, reg)
Overloaded function for a few mips specific registers
-
-
-Expand source code
-
-def get_reg(self, cpu, reg):
- '''
- Overloaded function for a few mips specific registers
- '''
-
- if isinstance(reg, str):
- env = cpu.env_ptr
- reg = reg.upper()
- if reg == 'HI':
- return env.CP0_EntryHi
- elif reg == 'LO':
- return env.CP0_EntryLo0
- elif reg.startswith('F') and reg[1:].isnumeric():
- num = int(reg[1:])
- _, endianness, _ = self._determine_bits()
- return int.from_bytes(bytes(env.fpus[0].fpr[num]), byteorder=endianness)
- elif reg == 'FCCR':
- return env.fpus[0].fcr0
- elif reg == 'DSPCONTROL':
- return env.active_tc.DSPControl
- elif reg == 'CP0_STATUS':
- return env.CP0_Status
-
-
- return super().get_reg(cpu, reg)
-
def get_return_address(self, cpu)
looks up where ret will go
-
-
-Expand source code
-
-def get_return_address(self,cpu):
- '''
- looks up where ret will go
- '''
- return self.get_reg(cpu, "RA")
-
def get_return_value(self, cpu, convention='default')
@@ -1666,16 +1545,6 @@ Methods
Deprecated: use get_retval
-
-
-Expand source code
-
-def get_return_value(self, cpu, convention='default'):
- '''
- .. Deprecated:: use get_retval
- '''
- return self.get_retval(cpu)
-
def get_retval(self, cpu, convention='default')
@@ -1685,41 +1554,12 @@ Methods
If A3 is 1 and convention is syscall, negate the return value.
This matches behavior of other architecures (where -ERRNO is returned
on error)
-
-
-Expand source code
-
-def get_retval(self, cpu, convention='default'):
- '''
- Overloaded to incorporate error data from A3 register for syscalls.
-
- If A3 is 1 and convention is syscall, *negate* the return value.
- This matches behavior of other architecures (where -ERRNO is returned
- on error)
- '''
-
- flip = 1
- if convention == 'syscall' and self.get_reg(cpu, "A3") == 1:
- flip = -1
-
- return flip * super().get_retval(cpu)
-
def set_pc(self, cpu, val)
Overloaded function set the MIPS program counter
-
-
-Expand source code
-
-def set_pc(self, cpu, val):
- '''
- Overloaded function set the MIPS program counter
- '''
- cpu.env_ptr.active_tc.PC = val
-
def set_retval(self, cpu, val, convention='default', failure=False)
@@ -1730,31 +1570,6 @@ Methods
to the syscall return value.
When convention == 'syscall', failure = False means A3 will bet set to 0.
Otherwise, it will be set to 1
-
-
-Expand source code
-
-def set_retval(self, cpu, val, convention='default', failure=False):
- '''
- Overloaded function so when convention is syscall, user can control
- the A3 register (which indicates syscall success/failure) in addition
- to the syscall return value.
-
- When convention == 'syscall', failure = False means A3 will bet set to 0.
- Otherwise, it will be set to 1
-
- '''
- if convention == 'syscall':
- # Set A3 register to indicate syscall success/failure
- self.set_reg(cpu, 'a3', failure)
-
- # If caller is trying to indicate error by setting a negative retval
- # for a syscall, just make it positive with A3=1
- if failure and self.panda.from_unsigned_guest(val) < 0:
- val = -1 * self.panda.from_unsigned_guest(val)
-
- return super().set_retval(cpu, val, convention)
-
Inherited members
@@ -2099,71 +1914,18 @@ Methods
Print (telescoping) each register and its values
-
-
-Expand source code
-
-def dump_regs(self, cpu):
- '''
- Print (telescoping) each register and its values
- '''
- print(f"PC: {self.get_pc(cpu):x}")
-
- for (regname, reg) in self.registers.items():
- val = self.get_reg(cpu, reg)
- print("{}: 0x{:x}".format(regname, val), end="\t")
- telescope(self.panda, cpu, val)
-
def dump_stack(self, cpu, words=8)
Print (telescoping) most recent words
words on the stack (from stack pointer to stack pointer + words
*word_size)
-
-
-Expand source code
-
-def dump_stack(self, cpu, words=8):
- '''
- Print (telescoping) most recent `words` words on the stack (from stack pointer to stack pointer + `words`*word_size)
- '''
-
- base_reg_s = "SP"
- base_reg_val = self.get_reg(cpu, self.reg_sp)
- if base_reg_val == 0:
- print("[WARNING: no stack pointer]")
- return
- word_size = int(self.panda.bits/8)
-
- _, endianness, _ = self._determine_bits()
-
- for word_idx in range(words):
- try:
- val_b = self.panda.virtual_memory_read(cpu, base_reg_val+word_idx*word_size, word_size)
- val = int.from_bytes(val_b, byteorder=endianness)
- print("[{}+0x{:0>2x}] == 0x{:0<8x}]: 0x{:0<8x}".format(base_reg_s, word_idx*word_size, base_reg_val+word_idx*word_size, val), end="\t")
- telescope(self.panda, cpu, val)
- except ValueError:
- print("[{}+0x{:0>2x}] == [memory read error]".format(base_reg_s, word_idx*word_size))
-
def dump_state(self, cpu)
Print registers and stack
-
-
-Expand source code
-
-def dump_state(self, cpu):
- """
- Print registers and stack
- """
- self.dump_regs(cpu)
- self.dump_stack(cpu)
-
def get_arg(self, cpu, idx, convention='default')
@@ -2177,89 +1939,24 @@ Methods
When doing a stack-based read, this function may raise a ValueError if the memory
read fails (i.e., paged out, invalid address).
Note for syscalls we define arg[0] as syscall number and then 1-index the actual args
-
-
-Expand source code
-
-def get_arg(self, cpu, idx, convention='default'):
- '''
- Return arg [idx] for given calling convention. This only works right as the guest
- is calling or has called a function before register values are clobbered.
-
- If arg[idx] should be stack-based, name it stack_0, stack_1... this allows mixed
- conventions where some args are in registers and others are on the stack (i.e.,
- mips32 syscalls).
-
- When doing a stack-based read, this function may raise a ValueError if the memory
- read fails (i.e., paged out, invalid address).
-
- Note for syscalls we define arg[0] as syscall number and then 1-index the actual args
- '''
-
- argloc = self._get_arg_loc(idx, convention)
-
- if self._is_stack_loc(argloc):
- return self._read_stack(cpu, argloc)
- else:
- return self.get_reg(cpu, argloc)
-
def get_args(self, cpu, num, convention='default')
-
-
-Expand source code
-
-def get_args(self, cpu, num, convention='default'):
- return [self.get_arg(cpu,i, convention) for i in range(num)]
-
def get_pc(self, cpu)
Returns the current program counter. Must be overloaded if self.reg_pc is None
-
-
-Expand source code
-
-def get_pc(self, cpu):
- '''
- Returns the current program counter. Must be overloaded if self.reg_pc is None
- '''
- if self.reg_pc:
- return self.get_reg(cpu, self.reg_pc)
- else:
- raise RuntimeError(f"get_pc unsupported for {self.panda.arch_name}")
-
def get_reg(self, cpu, reg)
Return value in a reg
which is either a register name or index (e.g., "R0" or 0)
-
-
-Expand source code
-
-def get_reg(self, cpu, reg):
- '''
- Return value in a `reg` which is either a register name or index (e.g., "R0" or 0)
- '''
- if isinstance(reg, str):
- reg = reg.upper()
- if reg == 'PC':
- return self.get_pc(cpu)
- if reg not in self.registers.keys():
- raise ValueError(f"Invalid register name {reg}")
- else:
- reg = self.registers[reg]
-
- return self._get_reg_val(cpu, reg)
-
def get_retval(self, cpu, convention='default')
@@ -2269,25 +1966,6 @@ Methods
right after a function call has returned, otherwise the register will contain
a different value.
Return value from syscalls is signed
-
-
-Expand source code
-
-def get_retval(self, cpu, convention='default'):
- '''
- Set return val to [val] for given calling convention. This only works
- right after a function call has returned, otherwise the register will contain
- a different value.
-
- Return value from syscalls is signed
- '''
- reg = self._get_ret_val_reg(cpu, convention)
- rv = self.get_reg(cpu, reg)
-
- if convention == 'syscall':
- rv = self.panda.from_unsigned_guest(rv)
- return rv
-
def set_arg(self, cpu, idx, val, convention='default')
@@ -2295,67 +1973,18 @@ Methods
Set arg [idx] to [val] for given calling convention.
Note for syscalls we define arg[0] as syscall number and then 1-index the actual args
-
-
-Expand source code
-
-def set_arg(self, cpu, idx, val, convention='default'):
- '''
- Set arg [idx] to [val] for given calling convention.
-
- Note for syscalls we define arg[0] as syscall number and then 1-index the actual args
- '''
- argloc = self._get_arg_loc(idx, convention)
-
- if self._is_stack_loc(argloc):
- return self._write_stack(cpu, argloc, val)
- else:
- return self.set_reg(cpu, argloc, val)
-
def set_pc(self, cpu, val)
Set the program counter. Must be overloaded if self.reg_pc is None
-
-
-Expand source code
-
-def set_pc(self, cpu, val):
- '''
- Set the program counter. Must be overloaded if self.reg_pc is None
- '''
- if self.reg_pc:
- return self.set_reg(cpu, self.reg_pc, val)
- else:
- raise RuntimeError(f"set_pc unsupported for {self.panda.arch_name}")
-
def set_reg(self, cpu, reg, val)
Set register reg
to a value where reg
is either a register name or index (e.g., "R0" or 0)
-
-
-Expand source code
-
-def set_reg(self, cpu, reg, val):
- '''
- Set register `reg` to a value where `reg` is either a register name or index (e.g., "R0" or 0)
- '''
- if isinstance(reg, str):
- reg = reg.upper()
- if reg not in self.registers.keys():
- raise ValueError(f"Invalid register name {reg}")
- else:
- reg = self.registers[reg]
- elif not isinstance(reg, int):
- raise ValueError(f"Can't set register {reg}")
-
- return self._set_reg_val(cpu, reg, val)
-
def set_retval(self, cpu, val, convention='default', failure=False)
@@ -2368,25 +1997,6 @@ Methods
register for mips), set that according to the failure flag.
Note the failure argument only used by subclasses that overload this function. It's provided
in the signature here so it can be set by a caller without regard for the guest architecture.
-
-
-Expand source code
-
-def set_retval(self, cpu, val, convention='default', failure=False):
- '''
- Set return val to [val] for given calling convention. This only works
- right after a function call has returned, otherwise the register will contain
- a different value.
-
- If the given architecture returns failure/success in a second register (i.e., the A3
- register for mips), set that according to the failure flag.
-
- Note the failure argument only used by subclasses that overload this function. It's provided
- in the signature here so it can be set by a caller without regard for the guest architecture.
- '''
- reg = self._get_ret_val_reg(cpu, convention)
- return self.set_reg(cpu, reg, val)
-
@@ -2474,33 +2084,12 @@ Methods
Overloaded function to return the x86 current program counter
-
-
-Expand source code
-
-def get_pc(self, cpu):
- '''
- Overloaded function to return the x86 current program counter
- '''
- return cpu.env_ptr.eip
-
def get_return_address(self, cpu)
looks up where ret will go
-
-
-Expand source code
-
-def get_return_address(self,cpu):
- '''
- looks up where ret will go
- '''
- esp = self.get_reg(cpu,"ESP")
- return self.panda.virtual_memory_read(cpu,esp,4,fmt='int')
-
def get_return_value(self, cpu)
@@ -2509,32 +2098,12 @@ Methods
Deprecated: use get_retval
-
-
-Expand source code
-
-def get_return_value(self, cpu):
- '''
- .. Deprecated:: use get_retval
- '''
- return self.get_retval(cpu)
-
def set_pc(self, cpu, val)
Overloaded function to set the x86 program counter
-
-
-Expand source code
-
-def set_pc(self, cpu, val):
- '''
- Overloaded function to set the x86 program counter
- '''
- cpu.env_ptr.eip = val
-
Inherited members
@@ -2777,16 +2346,6 @@ Methods
Overloaded function to return the x86_64 current program counter
-
-
-Expand source code
-
-def get_pc(self, cpu):
- '''
- Overloaded function to return the x86_64 current program counter
- '''
- return cpu.env_ptr.eip
-
def get_reg(self, cpu, reg)
@@ -2795,89 +2354,12 @@ Methods
X86_64 has a bunch of different ways to access registers. We support
the regular names, the 32 and 16 bit varations (e.g., EAX, AX, AL),
segment registers, and D/W/B style accesses to R8-R15
-
-
-Expand source code
-
-def get_reg(self, cpu, reg):
- '''
- X86_64 has a bunch of different ways to access registers. We support
- the regular names, the 32 and 16 bit varations (e.g., EAX, AX, AL),
- segment registers, and D/W/B style accesses to R8-R15
- '''
- if isinstance(reg, int):
- # If reg is an int, it should be an offset into our register array
- return self._get_reg_val(cpu, reg)
-
- reg = reg.upper()
- env = cpu.env_ptr
- if reg in self.seg_names:
- return self._get_segment_register(env, reg)
- elif reg in ['RIP', 'PC', 'EIP']:
- pc = self.get_pc(cpu) # changes reg to 'IP' and re-calls this
- if reg == 'EIP':
- pc &= 0xFFFFFFFF
- return pc
- elif reg.startswith('XMM'):
- raw_arr = env.xmm_regs[int(reg[3:].rstrip('HLQX'))]
- _, endianness, _ = self._determine_bits()
-
- if reg.endswith('lq'):
- value_bytes = raw_arr[0:8] # Lower 64 bits
- elif reg.endswith('hq'):
- value_bytes = raw_arr[8:16] # Higher 64 bits
- elif reg.endswith('hx'):
- value_bytes = raw_arr[4:8] # Higher 32 bits of the lower 64 bits
- else:
- value_bytes = raw_arr[0:16] # Full 128 bits
- return int.from_bytes(bytes(value_bytes), byteorder=endianness)
-
- elif reg.startswith('MM'):
- raise ValueError("MM registers unsupported")
- elif reg.startswith('YMM'):
- raise ValueError("YMM registers unsupported")
- elif reg.startswith('CR'):
- return env.cr[int(reg[2:])]
- elif reg.startswith('R') and any([reg.endswith(x) for x in 'DWB']) and reg.strip('RDWB').isnumeric():
- # R8-R15 can be accessed with D (double word), W (word) and B (byte)
- # to select the lowest 32-bits, the lowest 16 bits, or the lowest 8 bits.
- reg_idx = int(reg.strip('RDWB')) - 8
- reg_suffix = reg[-1]
- mask = {'D': 0xFFFFFFFF,
- 'W': 0xFFFF,
- 'B': 0xFF}[reg_suffix]
- return env.regs[reg_idx] & mask
- elif reg in self.reg_names_general:
- return self._get_general_purpose_register(env, reg, 0xFFFFFFFF)
- elif reg in self.reg_names_short:
- return env.regs[self.reg_names_short.index(reg)] & 0xFFFF
- elif reg in self.reg_names_byte:
- reg_idx = self.reg_names_byte.index(reg)
- if reg_idx > 3:
- reg_idx -= 4
- return (env.regs[reg_idx] >> 8) & 0xFF
- else:
- return env.regs[reg_idx] & 0xFF
- else:
- return super().get_reg(cpu, reg)
-
def get_return_address(self, cpu)
looks up where ret will go
-
-
-Expand source code
-
-def get_return_address(self, cpu):
- '''
- looks up where ret will go
- '''
- esp = self.get_reg(cpu, "RSP")
- return self.panda.virtual_memory_read(cpu, esp, 8, fmt='int')
-
def get_return_value(self, cpu)
@@ -2886,16 +2368,6 @@ Methods
Deprecated: use get_retval
-
-
-Expand source code
-
-def get_return_value(self, cpu):
- '''
- .. Deprecated:: use get_retval
- '''
- return self.get_retval(cpu)
-
def get_retval(self, cpu, convention='default')
@@ -2905,41 +2377,12 @@ Methods
In that ABI, if eflags carry bit is set, an error has occured. To standardize
pandare.arch returns across architectures/ABIs, we indicate a failure by returnning
-ERRNO.
-
-
-Expand source code
-
-def get_retval(self, cpu, convention='default'):
- '''
- Overloaded to support FreeBSD syscall ABI
- In that ABI, if eflags carry bit is set, an error has occured. To standardize
- pandare.arch returns across architectures/ABIs, we indicate a failure by returnning
- -ERRNO.
- '''
-
- error_flip = False
- if convention == 'syscall' and self.panda.get_os_family() == 'OS_FREEBSD' and \
- self.panda.libpanda.cpu_cc_compute_all(cpu.env_ptr, 1) & 1 == 1:
- error_flip = True
-
- return super().get_retval(cpu, convention) * (-1 if error_flip else 1)
-
def set_pc(self, cpu, val)
Overloaded function to set the x86_64 program counter
-
-
-Expand source code
-
-def set_pc(self, cpu, val):
- '''
- Overloaded function to set the x86_64 program counter
- '''
- cpu.env_ptr.eip = val
-
Inherited members
@@ -2971,8 +2414,8 @@ Inherited members
-
-
+
+
-
+
+
+
@@ -1244,193 +1244,54 @@ Methods
Get a reference to a given base type from the volatility symbol table
-
-
-Expand source code
-
-def base_type_from_name(self, name: str) -> VolatilityBaseType:
- '''
- Get a reference to a given base type from the volatility symbol table
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- base_type = self.panda.plugins[COSI].base_type_from_name(name)
-
- if base_type == self.panda.ffi.NULL:
- return None
-
- return VolatilityBaseType(self.panda, base_type)
-
def current_files(self)
Get information about the files open in the current process
-
-
-Expand source code
-
-def current_files(self):
- '''
- Get information about the files open in the current process
- '''
-
- files = self.panda.plugins[COSI].get_current_files(self.panda.get_cpu())
-
- if files == self.panda.ffi.NULL:
- return []
-
- return CosiFiles(self.panda, files)
-
def current_process(self)
Get info about the current process
-
-
-Expand source code
-
-def current_process(self):
- '''
- Get info about the current process
- '''
-
- proc = self.panda.plugins[COSI].get_current_cosiproc(self.panda.get_cpu())
-
- if proc == self.panda.ffi.NULL:
- return None
-
- return CosiProcess(self.panda, proc)
-
def current_thread(self)
Get info about the current thread
-
-
-Expand source code
-
-def current_thread(self):
- '''
- Get info about the current thread
- '''
-
- thread = self.panda.plugins[COSI].get_current_cosithread(self.panda.get_cpu())
-
- if thread == self.panda.ffi.NULL:
- return None
-
- return CosiThread(self.panda, thread)
-
def find_per_cpu_address(self, symbol: str) ‑> int
Get the address for a symbol given that it is a per-cpu variable
-
-
-Expand source code
-
-def find_per_cpu_address(self, symbol: str) -> int:
- '''
- Get the address for a symbol given that it is a per-cpu variable
- '''
- panda = self.panda
-
- symbol_offset = self.symbol_value_from_name(symbol)
- ptr_to_ptr = self.per_cpu_offset() + symbol_offset
-
- ptr_size = panda.bits // 8
-
- ptr = panda.virtual_memory_read(panda.get_cpu(), ptr_to_ptr, ptr_size, fmt='int') & ((1 << panda.bits) - 1)
-
- return ptr
-
def get(self, global_type, symbol, per_cpu=False)
-
-
-Expand source code
-
-def get(self, global_type, symbol, per_cpu=False):
- if per_cpu:
- addr = self.find_per_cpu_address(symbol)
- else:
- addr = self.symbol_addr_from_name(symbol)
-
- return self.panda.cosi.type_from_name(global_type).at(addr)
-
def kaslr_offset(self)
Get the KASLR offset for the given system
-
-
-Expand source code
-
-def kaslr_offset(self):
- '''
- Get the KASLR offset for the given system
- '''
-
- cpu = self.panda.get_cpu()
- offset = self.panda.plugins[COSI].kaslr_offset(cpu)
-
- return offset
-
def per_cpu_offset(self) ‑> int
Gets the offset for per cpu variable pointers
-
-
-Expand source code
-
-def per_cpu_offset(self) -> int:
- '''
- Gets the offset for per cpu variable pointers
- '''
-
- cpu = self.panda.get_cpu()
- return self.panda.plugins[COSI].current_cpu_offset(cpu)
-
def process_list(self)
Get a list of the current processes
-
-
-Expand source code
-
-def process_list(self):
- '''
- Get a list of the current processes
- '''
-
- proc_list = self.panda.plugins[COSI].cosi_get_proc_list(self.panda.get_cpu())
-
- if proc_list == self.panda.ffi.NULL:
- return []
- else:
- return CosiProcList(self.panda, proc_list)
-
def symbol_addr_from_name(self, name: str) ‑> int
@@ -1438,42 +1299,12 @@ Methods
Given a symbol name
, return the address in memory where it is located,
accounting for KASLR as needed.
-
-
-Expand source code
-
-def symbol_addr_from_name(self, name: str) -> int:
- '''
- Given a symbol `name`, return the address in memory where it is located,
- accounting for KASLR as needed.
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- addr = self.panda.plugins[COSI].symbol_addr_from_name(name)
- return addr
-
def symbol_from_name(self, name: str) ‑> VolatilitySymbol
Get a reference to a given symbol given the name of the symbol
-
-
-Expand source code
-
-def symbol_from_name(self, name: str) -> VolatilitySymbol:
- '''
- Get a reference to a given symbol given the name of the symbol
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- symbol = self.panda.plugins[COSI].symbol_from_name(name)
-
- return VolatilitySymbol(self.panda, symbol)
-
def symbol_value_from_name(self, name: str) ‑> int
@@ -1481,45 +1312,12 @@ Methods
Given a symbol name
, return the corresponding value in the volatility symbol
table, not accounting for KASLR.
-
-
-Expand source code
-
-def symbol_value_from_name(self, name: str) -> int:
- '''
- Given a symbol `name`, return the corresponding value in the volatility symbol
- table, not accounting for KASLR.
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- addr = self.panda.plugins[COSI].symbol_value_from_name(name)
- return addr
-
def type_from_name(self, name: str) ‑> VolatilityStruct
Get a reference to a given struct from the volatility symbol table
-
-
-Expand source code
-
-def type_from_name(self, name: str) -> VolatilityStruct:
- '''
- Get a reference to a given struct from the volatility symbol table
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- struct = self.panda.plugins[COSI].type_from_name(name)
-
- if struct == self.panda.ffi.NULL:
- return None
-
- return VolatilityStruct(self.panda, struct)
-
@@ -1566,20 +1364,6 @@ Methods
Get the name/path from which this file was accessed
-
-
-Expand source code
-
-def get_name(self) -> str:
- '''
- Get the name/path from which this file was accessed
- '''
-
- cstr_name = self.panda.plugins[COSI].cosi_file_name(self.inner)
- name = self.panda.ffi.string(cstr_name)
- self.panda.plugins[COSI].free_cosi_str(cstr_name)
- return name.decode('utf8')
-
@@ -1642,24 +1426,6 @@ Methods
Gets a CosiFile from this set of files based on the file descriptor.
Returns None if the fd could not be found.
-
-
-Expand source code
-
-def get_from_fd(self, fd: int):
- '''
- Gets a CosiFile from this set of files based on the file descriptor.
-
- Returns None if the fd could not be found.
- '''
-
- file_ptr = self.panda.plugins['COSI'].cosi_files_file_from_fd(self.inner, fd)
-
- if file_ptr == self.panda.ffi.NULL:
- return None
- else:
- return CosiFile(self.panda, file_ptr)
-
@@ -1947,19 +1713,6 @@ Methods
Returns a pointer to the data following the current pointer of type type_name
-
-
-Expand source code
-
-def after(self, type_name: str):
- '''
- Returns a pointer to the data following the current pointer of type `type_name`
- '''
-
- ptr = self._ptr + self._type.size()
-
- return CosiGuestPointer(self._panda, type_name, ptr)
-
def as_linux_list(self, sibling: str, list_entry_type=None) ‑> list
@@ -1972,157 +1725,42 @@ Methods
task_struct
(of which sibling
should be passed a value of "sibling"
).
So if one does current_task.children.as_linux_list("sibling")
it will return
a list of CosiGuestPointer
s pointing to task_struct
s.
-
-
-Expand source code
-
-def as_linux_list(self, sibling: str, list_entry_type=None) -> list:
- '''
- Takes a list_head* and reads it into a list. If no `list_entry_type` is provided,
- it is assumed to be equivelant to the parent struct the `list_head*` came from.
-
- For example, if current_task.children is a `list_head*`, the parent would be
- `current_task` (of type `task_struct`), so the list would default to being a list of
- `task_struct` (of which `sibling` should be passed a value of `"sibling"`).
-
- So if one does `current_task.children.as_linux_list("sibling")` it will return
- a list of `CosiGuestPointer`s pointing to `task_struct`s.
- '''
-
- if type(list_entry_type) is str:
- list_type_name = list_entry_type
- list_entry_type = self._panda.cosi.type_from_name(list_entry_type)
- elif list_entry_type is None:
- parent = self._parent
- if parent is None:
- raise ValueError("The list_head has no parent and `list_entry_type` was not provided")
- list_entry_type = parent._type
- list_type_name = parent._type_name
-
- field_offset = list_entry_type.offset_of_field(sibling)
-
- ptrs = []
- head = self.next
- current = head
- while head._ptr != current.next._ptr and current._ptr != self._ptr:
- addr = current._ptr - field_offset
- ptrs.append(CosiGuestPointer(self._panda, list_type_name, addr))
- current = current.next
-
- return ptrs
-
def before(self, type_name: str)
Returns a pointer to the data following the current pointer of type type_name
-
-
-Expand source code
-
-def before(self, type_name: str):
- '''
- Returns a pointer to the data following the current pointer of type `type_name`
- '''
-
- size = self._panda.cosi.type_from_name(type_name).size()
- print(size)
- ptr = self._ptr - size
-
- return CosiGuestPointer(self._panda, type_name, ptr)
-
def cast(self, cast_to: str)
Cast to a pointer of another type
-
-
-Expand source code
-
-def cast(self, cast_to: str):
- '''
- Cast to a pointer of another type
- '''
-
- return CosiGuestPointer(self._panda, cast_to, self._ptr, parent=self._parent)
-
def container_of(self, type_name: str, field_name: str)
Get a pointer to the struct containing this type
-
-
-Expand source code
-
-def container_of(self, type_name: str, field_name: str):
- '''
- Get a pointer to the struct containing this type
- '''
-
- container_type = self._panda.cosi.type_from_name(type_name)
- offset_in_container = container_type.offset_of_field(field_name)
-
- return container_type.at(self._ptr - offset_in_container)
-
def deref(self)
-
-
-Expand source code
-
-def deref(self):
- return self[0]
-
def get_raw_ptr(self) ‑> int
Get the address in memory this points to
-
-
-Expand source code
-
-def get_raw_ptr(self) -> int:
- '''
- Get the address in memory this points to
- '''
-
- return self._ptr
-
def null_terminated(self) ‑> str
Read a CosiGuestPointer for a char*
as a null-terminated string
-
-
-Expand source code
-
-def null_terminated(self) -> str:
- '''
- Read a CosiGuestPointer for a `char*` as a null-terminated string
- '''
-
- panda = self._panda
- inner_type = self._type_name
- cpu = panda.get_cpu()
-
- if inner_type == 'char' or inner_type == 'unsigned char':
- return panda.read_str(cpu, self._ptr)
- else:
- raise ValueError("Cannot call read_null_terminated on {inner_type}*")
-
@@ -2249,32 +1887,12 @@ Methods
-
-
-Expand source code
-
-def get_file(self):
- cstr_name = self.panda.plugins[COSI].cosi_module_file(self.inner)
- name = self.panda.ffi.string(cstr_name)
- self.panda.plugins[COSI].free_cosi_str(cstr_name)
- return name.decode('utf8')
-
def get_name(self)
-
-
-Expand source code
-
-def get_name(self):
- cstr_name = self.panda.plugins[COSI].cosi_module_name(self.inner)
- name = self.panda.ffi.string(cstr_name)
- self.panda.plugins[COSI].free_cosi_str(cstr_name)
- return name.decode('utf8')
-
@@ -2398,84 +2016,24 @@ Methods
Returns a list of this process' children
-
-
-Expand source code
-
-def children(self):
- '''
- Returns a list of this process' children
- '''
-
- children = self.panda.plugins[COSI].cosi_proc_children(
- self.panda.get_cpu(),
- self.inner
- )
-
- if children == self.panda.ffi.NULL:
- return []
-
- return CosiProcList(self.panda, children)
-
def get_name(self)
-
-
-Expand source code
-
-def get_name(self):
- cstr_name = self.panda.plugins[COSI].cosi_proc_name(self.inner)
- name = self.panda.ffi.string(cstr_name)
- self.panda.plugins[COSI].free_cosi_str(cstr_name)
- return name.decode('utf8')
-
def mappings(self)
Returns a list of the mappings of the process
-
-
-Expand source code
-
-def mappings(self):
- '''
- Returns a list of the mappings of the process
- '''
-
- mappings = self.panda.plugins[COSI].cosi_proc_get_mappings(
- self.panda.get_cpu(),
- self.inner
- )
-
- if mappings == self.panda.ffi.NULL:
- return []
-
- return CosiMappings(self.panda, mappings)
-
def open_files(self)
Returns information about all the files open in this process
-
-
-Expand source code
-
-def open_files(self):
- '''
- Returns information about all the files open in this process
- '''
-
- files = self.panda.plugins[COSI].cosi_proc_files(self.inner)
- return CosiFiles(self.panda, files)
-
@@ -2553,55 +2111,18 @@ Methods
Get whether an integer base type is signed or not
-
-
-Expand source code
-
-def is_signed(self) -> bool:
- '''
- Get whether an integer base type is signed or not
- '''
-
- return self.panda.plugins[COSI].is_base_type_signed(self.inner)
-
def name(self) ‑> str
Get the name for the given base type
-
-
-Expand source code
-
-def name(self) -> str:
- '''
- Get the name for the given base type
- '''
-
- name_ptr = self.panda.plugins[COSI].name_of_base_type(self.inner)
- name = self.panda.ffi.string(name_ptr).decode('utf8')
- self.panda.plugins[COSI].free_cosi_str(name_ptr)
-
- return name
-
def size(self) ‑> int
Get the size of the given base type in bytes
-
-
-Expand source code
-
-def size(self) -> int:
- '''
- Get the size of the given base type in bytes
- '''
-
- return self.panda.plugins[COSI].size_of_base_type(self.inner)
-
@@ -2739,18 +2260,6 @@ Methods
Get a CosiGuestPointer of this type
-
-
-Expand source code
-
-def at(self, ptr):
- '''
- Get a CosiGuestPointer of this type
- '''
-
- type_name = self.name()
- return CosiGuestPointer(self.panda, type_name, ptr)
-
def fields(self)
@@ -2758,31 +2267,6 @@ Methods
Iterate over the fields of the structure, yielding tuples in the form of
(offset, type, field_name)
-
-
-Expand source code
-
-def fields(self):
- '''
- Iterate over the fields of the structure, yielding tuples in the form of
- (offset, type, field_name)
- '''
- i = 0
-
- while True:
- field = self.get_field_by_index(i)
-
- if not field:
- break
-
- name = field
- offset = self.offset_of_field(field)
- type_name = self.type_of_field(field)
-
- yield (offset, type_name, name)
-
- i += 1
-
def get_field_by_index(self, index: int) ‑> str
@@ -2790,102 +2274,30 @@ Methods
Return the name of the field at a given index, returning None
past the end
of the fields.
-
-
-Expand source code
-
-def get_field_by_index(self, index: int) -> str:
- '''
- Return the name of the field at a given index, returning `None` past the end
- of the fields.
- '''
-
- field_name = self.panda.plugins[COSI].get_field_by_index(self.inner, index)
-
- if field_name == self.panda.ffi.NULL:
- return None
- else:
- return self.panda.ffi.string(field_name).decode('utf8')
-
def name(self) ‑> str
Get the name of the given struct
-
-
-Expand source code
-
-def name(self) -> str:
- '''
- Get the name of the given struct
- '''
-
- name_ptr = self.panda.plugins[COSI].name_of_struct(self.inner)
- name = self.panda.ffi.string(name_ptr).decode('utf8')
- self.panda.plugins[COSI].free_cosi_str(name_ptr)
-
- return name
-
def offset_of_field(self, name: str) ‑> int
Get the offset of a given field from the field name
-
-
-Expand source code
-
-def offset_of_field(self, name: str) -> int:
- '''
- Get the offset of a given field from the field name
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- return self.panda.plugins[COSI].offset_of_field(self.inner, name)
-
def size(self) ‑> int
Get the total size of the given struct in bytes
-
-
-Expand source code
-
-def size(self) -> int:
- '''
- Get the total size of the given struct in bytes
- '''
-
- return self.panda.plugins[COSI].size_of_struct(self.inner)
-
def type_of_field(self, name: str) ‑> str
Get the type of a given field from the field name
-
-
-Expand source code
-
-def type_of_field(self, name: str) -> str:
- '''
- Get the type of a given field from the field name
- '''
-
- name = name.encode('utf8')
- name = self.panda.ffi.new("char[]", name)
- type_name = self.panda.plugins[COSI].type_of_field(self.inner, name)
- type_name = self.panda.ffi.string(type_name).decode('utf8')
-
- return type_name
-
@@ -2975,55 +2387,18 @@ Methods
Get the address of the symbol in memory, accounting for KASLR
-
-
-Expand source code
-
-def addr(self) -> int:
- '''
- Get the address of the symbol in memory, accounting for KASLR
- '''
-
- return self.panda.plugins[COSI].addr_of_symbol(self.inner)
-
def name(self) ‑> str
Get the name for the given symbol
-
-
-Expand source code
-
-def name(self) -> str:
- '''
- Get the name for the given symbol
- '''
-
- name_ptr = self.panda.plugins[COSI].name_of_symbol(self.inner)
- name = self.panda.ffi.string(name_ptr).decode('utf8')
- self.panda.plugins[COSI].free_cosi_str(name_ptr)
-
- return name
-
def value(self) ‑> int
Get the raw value for the symbol from the volatility symbol table
-
-
-Expand source code
-
-def value(self) -> int:
- '''
- Get the raw value for the symbol from the volatility symbol table
- '''
-
- return self.panda.plugins[COSI].value_of_symbol(self.inner)
-
@@ -3040,8 +2415,8 @@ Methods
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/extras/fileFaker.html b/extras/fileFaker.html
index d1ba94e..2d7d6cc 100755
--- a/extras/fileFaker.html
+++ b/extras/fileFaker.html
@@ -3,16 +3,16 @@
-
+
pandare.extras.fileFaker API documentation
-
-
-
+
+
+
@@ -635,44 +635,18 @@ Methods
-
-
-Expand source code
-
-def close(self):
- self.refcount -= 1
- if self.refcount == 0: # All FDs are now closed
- if self.initial_contents == self.contents:
- self.logger.debug(f"All handles to Faker({self.filename}) closed. Unmodified contents")
- else: # it was mutated!
- self.logger.info(f"All handles to Faker({self.filename}) closed. Modified contents: {repr(self.contents)}")
-
def get_mode(self)
-
-
-Expand source code
-
-def get_mode(self):
- return 0o664 # Regular file (octal)
-
def get_size(self, bytesize)
-
-
-Expand source code
-
-def get_size(self, bytesize):
- return ceil(len(self.contents)/bytesize)
-
def read(self, size, offset)
@@ -680,22 +654,6 @@ Methods
Generate data for a given read of size.
Returns data.
-
-
-Expand source code
-
-def read(self, size, offset):
- '''
- Generate data for a given read of size. Returns data.
- '''
-
- if offset >= len(self.contents): # No bytes left to read
- return b""
- # Otherwise there are bytes left to read
- read_data = self.contents[offset:offset+size]
-
- return read_data
-
def write(self, offset, write_data)
@@ -704,25 +662,6 @@ Methods
Update contents from offset. It's a bytearray so we can't just mutate
Return how much HyperFD offset should be incremented by
XXX what about writes past end of the file?
-
-
-Expand source code
-
-def write(self, offset, write_data):
- '''
- Update contents from offset. It's a bytearray so we can't just mutate
- Return how much HyperFD offset should be incremented by
- XXX what about writes past end of the file?
- '''
- new_data = self.contents[:offset]
- new_data += write_data
- new_data += self.contents[offset+len(new_data):]
-
- self.logger.info(f"FakeFD({self.filename}) writing {new_data} at offset {offset}")
-
- self.contents = new_data
- return len(write_data)
-
@@ -941,18 +880,6 @@ Methods
-
-
-Expand source code
-
-def close(self):
- # Close all open hfds
- if len(self.hooked_fds):
- self.ff_logger.debug("Cleaning up open hyper file descriptors")
- for (fd, asid) in list(self.hooked_fds.keys()):
- self.hooked_fds[(fd, asid)].close()
- del self.hooked_fds[(fd, asid)]
-
def replace_file(self, filename, faker, disk_file='/etc/passwd')
@@ -960,21 +887,6 @@ Methods
Replace all accesses to filename with accesses to the fake file instead
which optionally may be specified by disk_file.
-
-
-Expand source code
-
-def replace_file(self, filename, faker, disk_file="/etc/passwd"):
- '''
- Replace all accesses to filename with accesses to the fake file instead
- which optionally may be specified by disk_file.
- '''
- self.faked_files[filename] = faker
-
- # XXX: We rename the files to real files to the guest kernel can manage FDs for us.
- # this may need to use different real files depending on permissions requested
- self.rename_file(filename, disk_file)
-
Inherited members
@@ -1068,46 +980,18 @@ Methods
Decrement the reference counter
-
-
-Expand source code
-
-def close(self):
- '''
- Decrement the reference counter
- '''
- assert(self.file)
- self.file.close()
- #del self # ???
-
def get_mode(self)
-
-
-Expand source code
-
-def get_mode(self):
- assert(self.file)
- return self.file.get_mode()
-
def get_size(self)
-
-
-Expand source code
-
-def get_size(self):
- assert(self.file)
- return self.file.get_mode()
-
def read(self, size)
@@ -1116,62 +1000,18 @@ Methods
Read from the file descriptor. Determine current offset
and then pass request through to FakeFile
Returns (data read, count)
-
-
-Expand source code
-
-def read(self, size):
- '''
- Read from the file descriptor. Determine current offset
- and then pass request through to FakeFile
- Returns (data read, count)
- '''
- assert(self.file)
- data = self.file.read(size, self.offset)
- self.offset+=len(data)
- return (data, len(data))
-
def seek(self, offset, whence)
-
-
-Expand source code
-
-def seek(self, offset, whence):
- # From include/uapi/linux/fs.h
- SEEK_SET = 0
- SEEK_CUR = 1
- SEEK_END = 2
-
- if whence == SEEK_SET:
- self.offset = offset
- elif whence == SEEK_CUR:
- self.offset = self.offset + offset
- elif whence == SEEK_END:
- self.offset = self.offset + offset
- else:
- raise ValueError(f"Unsupported whence {whence} in seek")
-
def write(self, data)
-
-
-Expand source code
-
-def write(self, data):
- assert(self.file)
- bytes_written = self.file.write(self.offset, data)
- self.offset +- bytes_written
- return bytes_written
-
@@ -1188,8 +1028,8 @@ Methods
-
-
+
+
-
+
+
+
@@ -701,24 +701,6 @@ Methods
Mutate a given filename into a new name at the syscall interface
-
-
-Expand source code
-
-def rename_file(self, old_name, new_name):
- '''
- Mutate a given filename into a new name at the syscall interface
- '''
- assert(old_name not in self._renamed_files), f"Already have a rename rule for {old_name}"
-
- if not isinstance(new_name, bytes):
- new_name = new_name.encode("utf8")
-
- if not new_name.endswith(b"\x00"):
- new_name += b"\x00"
-
- self._renamed_files[old_name] = new_name
-
@@ -735,8 +717,8 @@ Methods
-
-
+
+
-
+
+
+
@@ -299,44 +299,18 @@ Methods
-
-
-Expand source code
-
-def close(self):
- self.refcount -= 1
- if self.refcount == 0: # All FDs are now closed
- if self.initial_contents == self.contents:
- self.logger.debug(f"All handles to Faker({self.filename}) closed. Unmodified contents")
- else: # it was mutated!
- self.logger.info(f"All handles to Faker({self.filename}) closed. Modified contents: {repr(self.contents)}")
-
def get_mode(self)
-
-
-Expand source code
-
-def get_mode(self):
- return 0o664 # Regular file (octal)
-
def get_size(self, bytesize)
-
-
-Expand source code
-
-def get_size(self, bytesize):
- return ceil(len(self.contents)/bytesize)
-
def read(self, size, offset)
@@ -344,22 +318,6 @@ Methods
Generate data for a given read of size.
Returns data.
-
-
-Expand source code
-
-def read(self, size, offset):
- '''
- Generate data for a given read of size. Returns data.
- '''
-
- if offset >= len(self.contents): # No bytes left to read
- return b""
- # Otherwise there are bytes left to read
- read_data = self.contents[offset:offset+size]
-
- return read_data
-
def write(self, offset, write_data)
@@ -368,25 +326,6 @@ Methods
Update contents from offset. It's a bytearray so we can't just mutate
Return how much HyperFD offset should be incremented by
XXX what about writes past end of the file?
-
-
-Expand source code
-
-def write(self, offset, write_data):
- '''
- Update contents from offset. It's a bytearray so we can't just mutate
- Return how much HyperFD offset should be incremented by
- XXX what about writes past end of the file?
- '''
- new_data = self.contents[:offset]
- new_data += write_data
- new_data += self.contents[offset+len(new_data):]
-
- self.logger.info(f"FakeFD({self.filename}) writing {new_data} at offset {offset}")
-
- self.contents = new_data
- return len(write_data)
-
@@ -605,18 +544,6 @@ Methods
-
-
-Expand source code
-
-def close(self):
- # Close all open hfds
- if len(self.hooked_fds):
- self.ff_logger.debug("Cleaning up open hyper file descriptors")
- for (fd, asid) in list(self.hooked_fds.keys()):
- self.hooked_fds[(fd, asid)].close()
- del self.hooked_fds[(fd, asid)]
-
def replace_file(self, filename, faker, disk_file='/etc/passwd')
@@ -624,21 +551,6 @@ Methods
Replace all accesses to filename with accesses to the fake file instead
which optionally may be specified by disk_file.
-
-
-Expand source code
-
-def replace_file(self, filename, faker, disk_file="/etc/passwd"):
- '''
- Replace all accesses to filename with accesses to the fake file instead
- which optionally may be specified by disk_file.
- '''
- self.faked_files[filename] = faker
-
- # XXX: We rename the files to real files to the guest kernel can manage FDs for us.
- # this may need to use different real files depending on permissions requested
- self.rename_file(filename, disk_file)
-
Inherited members
@@ -925,24 +837,6 @@ Methods
Mutate a given filename into a new name at the syscall interface
-
-
-Expand source code
-
-def rename_file(self, old_name, new_name):
- '''
- Mutate a given filename into a new name at the syscall interface
- '''
- assert(old_name not in self._renamed_files), f"Already have a rename rule for {old_name}"
-
- if not isinstance(new_name, bytes):
- new_name = new_name.encode("utf8")
-
- if not new_name.endswith(b"\x00"):
- new_name += b"\x00"
-
- self._renamed_files[old_name] = new_name
-
@@ -1069,36 +963,12 @@ Methods
Retrieve ioctls whose error codes where overwritten
-
-
-Expand source code
-
-def get_forced_returns(self, with_buf_only = False):
-
- '''
- Retrieve ioctls whose error codes where overwritten
- '''
-
- return self._get_returns(self._forced_returns, with_buf_only)
-
def get_unmodified_returns(self, with_buf_only=False)
Retrieve ioctl that completed normally
-
-
-Expand source code
-
-def get_unmodified_returns(self, with_buf_only = False):
-
- '''
- Retrieve ioctl that completed normally
- '''
-
- return self._get_returns(self._unmodified_returns, with_buf_only)
-
@@ -1204,41 +1074,12 @@ Methods
Decorator to only run a function if self.mode matches the provided string
-
-
-Expand source code
-
-def mode_filter(self, mode_filter):
- '''
- Decorator to only run a function if self.mode matches the provided string
- '''
- def __mode_filter(func):
- @wraps(func)
- def wrapper(*args, **kwargs):
- if self.mode == mode_filter:
- # Mode matches - run it!
- func(*args, **kwargs)
- return wrapper
- return __mode_filter
-
def set_mode(self, new)
Helper to change mode
-
-
-Expand source code
-
-def set_mode(self, new):
- '''
- Helper to change mode
- '''
- if new != self.mode:
- print(f"Switching modes from {self.mode} to {new}")
- self.mode = new
-
@@ -1338,21 +1179,6 @@ Methods
-
-
-Expand source code
-
-def uninit(self):
- render_graph(self.procinfo, self.time_data, self.total_insns, n_cols=self.n_cols, show_ranges=self.show_ranges, show_graph=self.show_graph)
-
- # Fully reset state
- self.panda.disable_ppp("task_change")
- self.procinfo = {} # PID: info
- self.time_data = [] # [(PID, #blocks)]
- self.total_insns = 0
- self.n_insns = 0
- self.last_pid = None
-
Inherited members
@@ -1500,206 +1326,18 @@ Methods
-
-
-Expand source code
-
-def console_printed_post_boot_err(self):
- return self._console_printed_err
-
-
-
-def get_files_written(self)
-
-
-
-
-
-Expand source code
-
-def get_files_written(self):
- return self._files_written
-
-
-
-def proc_printed_err(self)
-
-
-
-
-
-Expand source code
-
-def proc_printed_err(self):
- return self._proc_printed_err
-
-
-
-
-
-class Snake
-(panda, console_capture=False, proc_name=None, log_dir=None, rm_existing_logs=False)
-
-
-Set console_capture = True to capture all console output to file, including boot messages.
-Set proc_name = "name_of_proc" to, for a named process, capture stdout/stderr and any file writes from the hypervisor, mirror results to log directory.
-Can be stacked with console capture.
-
-
-Expand source code
-
-class ProcWriteCapture():
-
- '''
- Set console_capture = True to capture all console output to file, including boot messages.
- Set proc_name = "name_of_proc" to, for a named process, capture stdout/stderr and any file writes from the hypervisor, mirror results to log directory.
- Can be stacked with console capture.
- '''
-
- def __init__(self, panda, console_capture = False, proc_name = None, log_dir = None, rm_existing_logs = False):
-
- self._panda = panda
- self._files_written = set()
- self._rm = rm_existing_logs
- self._console_capture = console_capture
- self._proc_name = proc_name
- self._proc_printed_err = False
- self._console_printed_err = False
-
- if log_dir == None:
- self._console_log_dir = Path.cwd()
- if proc_name:
- self._proc_log_dir = Path.cwd() / self._proc_name
- else:
- self._console_log_dir = Path(log_dir)
- if proc_name:
- self._proc_log_dir = Path(log_dir).joinpath(self._proc_name)
-
- # Setup logging dir
- self._console_log_dir.mkdir(parents=True, exist_ok=True)
- if proc_name:
- self._proc_log_dir.mkdir(parents=True, exist_ok=True)
- if self._rm:
- if proc_name:
- shutil.rmtree(self._proc_log_dir)
- shutil.rmtree(self._console_log_dir)
-
- # Mirror writes
- @self._panda.ppp("syscalls2", "on_sys_write_enter")
- 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 = panda.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 = panda.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 = panda.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_printed_err(self):
- return self._proc_printed_err
-
- def console_printed_post_boot_err(self):
- return self._console_printed_err
-
- def get_files_written(self):
- return self._files_written
-
-Methods
-
-
-def console_printed_post_boot_err(self)
-
--
-
-
-
-Expand source code
-
-def console_printed_post_boot_err(self):
- return self._console_printed_err
-
def get_files_written(self)
-
-
-
-Expand source code
-
-def get_files_written(self):
- return self._files_written
-
def proc_printed_err(self)
-
-
-
-Expand source code
-
-def proc_printed_err(self):
- return self._proc_printed_err
-
@@ -1716,8 +1354,8 @@ Methods
-
-
+
+
-
+
+
+
@@ -440,48 +440,6 @@ Functions
One-time init for arch-specific bit-packed ioctl cmd struct.
-
-
-Expand source code
-
-def do_ioctl_init(panda):
-
- '''
- One-time init for arch-specific bit-packed ioctl cmd struct.
- '''
-
- # Default config (x86, x86-64, ARM, AArch 64) with options for PPC
- global ioctl_initialized
- if ioctl_initialized:
- return
-
- ioctl_initialized = True
- TYPE_BITS = 8
- CMD_BITS = 8
- SIZE_BITS = 14 if panda.arch_name != "ppc" else 13
- DIR_BITS = 2 if panda.arch_name != "ppc" else 3
-
- ffi.cdef("""
- struct IoctlCmdBits {
- uint8_t type_num:%d;
- uint8_t cmd_num:%d;
- uint16_t arg_size:%d;
- uint8_t direction:%d;
- };
-
- union IoctlCmdUnion {
- struct IoctlCmdBits bits;
- uint32_t asUnsigned32;
- };
-
- enum ioctl_direction {
- IO = 0,
- IOW = 1,
- IOR = 2,
- IOWR = 3
- };
- """ % (TYPE_BITS, CMD_BITS, SIZE_BITS, DIR_BITS), packed=True)
-
@@ -598,26 +556,6 @@ Methods
'
Helper retrive original return code, handles arch-specifc ABI
-
-
-Expand source code
-
-def get_ret_code(self, panda, cpu):
-
- ''''
- Helper retrive original return code, handles arch-specifc ABI
- '''
-
- if panda.arch_name == "mipsel" or panda.arch_name == "mips":
- # Note: return values are in $v0, $v1 (regs 2 and 3 respectively), but here we only use first
- self.original_ret_code = panda.from_unsigned_guest(cpu.env_ptr.active_tc.gpr[2])
- elif panda.arch_name == "aarch64":
- self.original_ret_code = panda.from_unsigned_guest(cpu.env_ptr.xregs[0])
- elif panda.arch_name == "ppc":
- raise RuntimeError("PPC currently unsupported!")
- else: # x86/x64/ARM
- self.original_ret_code = panda.from_unsigned_guest(cpu.env_ptr.regs[0])
-
@@ -744,36 +682,12 @@ Methods
Retrieve ioctls whose error codes where overwritten
def get_forced_returns(self, with_buf_only = False):
-
- '''
- Retrieve ioctls whose error codes where overwritten
- '''
-
- return self._get_returns(self._forced_returns, with_buf_only)
-
def get_unmodified_returns(self, with_buf_only=False)
Retrieve ioctl that completed normally
def get_unmodified_returns(self, with_buf_only = False):
-
- '''
- Retrieve ioctl that completed normally
- '''
-
- return self._get_returns(self._unmodified_returns, with_buf_only)
-Decorator to only run a function if self.mode matches the provided string
def mode_filter(self, mode_filter):
- '''
- Decorator to only run a function if self.mode matches the provided string
- '''
- def __mode_filter(func):
- @wraps(func)
- def wrapper(*args, **kwargs):
- if self.mode == mode_filter:
- # Mode matches - run it!
- func(*args, **kwargs)
- return wrapper
- return __mode_filter
-
def set_mode(self, new)
Helper to change mode
def set_mode(self, new):
- '''
- Helper to change mode
- '''
- if new != self.mode:
- print(f"Switching modes from {self.mode} to {new}")
- self.mode = new
-Run guest
def run_guest(self):
- '''
- Run guest
- '''
- self.panda.run()
-def render_graph(procinfo, time_data, total_insns, n_cols=120, show_ranges=True, show_graph=True):
- col_size = total_insns / n_cols
- pids = set([x for x,y in time_data]) # really a list of (pid, tid) tuples
- merged = {} # pid: [(True, 100), False, 9999)
-
- for pid in pids:
- on_off_times = []
-
- off_count = 0
-
- for (pid2, block_c) in time_data:
- if pid2 == pid:
- # On!
- on_off_times.append((True, block_c))
- else:
- # Off
- on_off_times.append((False, block_c))
-
- merged[pid] = on_off_times
-
- # Render output: Stage 1 - PID -> procname details
- # Count Pid Name/tid Asid First Last
- # 297 1355 [bash find / 1355] 3b14e000 963083 -> 7829616
-
- if show_ranges:
- print(" Ins.Count PID TID First Last Names")
-
- for (pid, tid) in sorted(procinfo, key=lambda v: procinfo[v]['count'], reverse=True):
- details = procinfo[(pid, tid)]
- names = ", ".join([x.decode() for x in details['names']])
-
- end = f"{details['last']:<8}" if details['last'] is not None else "N/A"
- print(f"{details['count']: >10} {pid:<5} {tid:<5}{details['first']:<8} -> {end} {names}")
-
-
- # Render output: Stage 2: ascii art
- if show_graph:
- ascii_art = {} # (pid, tid): art
- for (pid, tid), times in merged.items():
- row = ""
- pending = None
- queue = merged[(pid, tid)]
- # Consume data from pending+merged in chunks of col_size
- # e.g. col_size=10 (True, 8), (False, 1), (True, 10)
- # simplifies to {True:9, False:1} and adds (True:9) to pending
-
- for cur_col in range(n_cols):
- counted = 0
- on_count = 0
- off_count = 0
- #import ipdb
- while (counted < col_size and len(queue)): #or pending is not None:
- if pending is not None:
- (on_bool, cnt) = pending
- pending = None
- else:
- old_len = len(queue)
- (on_bool, cnt) = queue.pop(0)
- assert(len(queue) < old_len), "pop don't happen"
-
- if cnt > col_size-counted: #Hard case: count part, move remainder to pending
- remainder = cnt - (col_size-counted)
- cnt = col_size-counted # Maximum allowed now
- pending = (on_bool, remainder)
-
- assert(cnt <= col_size-counted) # Now it's (always) the easy case for what's left
- if on_bool:
- on_count += cnt
- else:
- off_count += cnt
- counted += cnt
-
- # /while
- # Use on_count and off_count to determine how to label this cell
- density_map = " ▂▃▄▅▆▇"
- on_count / col_size
-
- idx = round((on_count/col_size)*(len(density_map)-1))
- if idx == 0 and on_count > 0:
- c = '.' # If any code executed, mark it
- else:
- c = density_map[idx]
- row += c
-
- ascii_art[(pid, tid)] = row
-
- # Render art
- print("PID TID | "+ "-"*(n_cols//2-4) + "HISTORY" + "-"*(n_cols//2-4) + "| NAMES")
- for (pid, tid) in sorted(ascii_art, key=lambda x: x[0]):
- row = ascii_art[(pid, tid)]
- details = procinfo[(pid, tid)]
- names = ", ".join([x.decode() for x in details['names']])
- print(f"{pid: <4} {tid: <4} |{row}| {names}")
-def uninit(self):
- render_graph(self.procinfo, self.time_data, self.total_insns, n_cols=self.n_cols, show_ranges=self.show_ranges, show_graph=self.show_graph)
-
- # Fully reset state
- self.panda.disable_ppp("task_change")
- self.procinfo = {} # PID: info
- self.time_data = [] # [(PID, #blocks)]
- self.total_insns = 0
- self.n_insns = 0
- self.last_pid = None
-def console_printed_post_boot_err(self):
- return self._console_printed_err
-
def get_files_written(self)
def get_files_written(self):
- return self._files_written
-
def proc_printed_err(self)
def proc_printed_err(self):
- return self._proc_printed_err
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_block_translate(Your_Function)
@@ -607,12 +601,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_cpu_exec_enter(Your_Function)
@@ -631,12 +619,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_insn_exec(Your_Function)
@@ -658,12 +640,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_insn_translate(Your_Function)
@@ -685,12 +661,6 @@ Returns
true if PANDA should insert instrumentation into the generated code, false otherwise
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_loadvm(Your_Function)
@@ -709,12 +679,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_after_machine_init(Your_Function)
@@ -734,12 +698,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_asid_changed(Your_Function)
@@ -763,12 +721,6 @@ Returns
true if the asid should be prevented from being changed false otherwise
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_block_exec(Your_Function)
@@ -789,12 +741,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_block_exec_invalidate_opt(Your_Function)
@@ -815,12 +761,6 @@ Returns
true if we should invalidate the current translation block and retranslate, false otherwise.
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_block_translate(Your_Function)
@@ -841,12 +781,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_cpu_exec_exit(Your_Function)
@@ -867,12 +801,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_handle_exception(Your_Function)
@@ -894,12 +822,6 @@ Returns
a new exception_index.
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_handle_interrupt(Your_Function)
@@ -920,12 +842,6 @@ Returns
new interrupt_rquest
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_loadvm(Your_Function)
@@ -944,12 +860,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_before_tcg_codegen(Your_Function)
@@ -970,12 +880,6 @@ Returns
None
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_cpu_restore_state(Your_Function)
@@ -996,12 +900,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_during_machine_init(Your_Function)
@@ -1020,12 +918,6 @@ Returns
None
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_end_block_exec(Your_Function)
@@ -1046,12 +938,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_guest_hypercall(Your_Function)
@@ -1071,12 +957,6 @@ Returns
true if the callback has processed the hypercall, false if the hypercall has been ignored.
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_hd_read(Your_Function)
@@ -1092,12 +972,6 @@ Returns
void
the type your callback must return
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_hd_write(Your_Function)
@@ -1113,12 +987,6 @@ Returns
void
the type your callback must return
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_insn_exec(Your_Function)
@@ -1140,12 +1008,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_insn_translate(Your_Function)
@@ -1167,12 +1029,6 @@ Returns
true if PANDA should insert instrumentation into the generated code, false otherwise
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_main_loop_wait(Your_Function)
@@ -1191,12 +1047,6 @@ Returns
None
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_mmio_after_read(Your_Function)
@@ -1223,12 +1073,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_mmio_before_write(Your_Function)
@@ -1255,12 +1099,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_monitor(Your_Function)
@@ -1282,12 +1120,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_phys_mem_after_read(Your_Function)
@@ -1314,12 +1146,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_phys_mem_after_write(Your_Function)
@@ -1344,12 +1170,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_phys_mem_before_read(Your_Function)
@@ -1374,12 +1194,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_phys_mem_before_write(Your_Function)
@@ -1404,12 +1218,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_pre_shutdown(Your_Function)
@@ -1428,12 +1236,6 @@ Returns
None
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_qmp(Your_Function)
@@ -1455,12 +1257,6 @@ Returns
bool
the type your callback must return
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_after_dma(Your_Function)
@@ -1485,12 +1281,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_before_dma(Your_Function)
@@ -1517,12 +1307,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_handle_packet(Your_Function)
@@ -1550,12 +1334,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_hd_transfer(Your_Function)
@@ -1584,12 +1362,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_net_transfer(Your_Function)
@@ -1618,12 +1390,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_serial_read(Your_Function)
@@ -1648,12 +1414,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_serial_receive(Your_Function)
@@ -1676,12 +1436,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_serial_send(Your_Function)
@@ -1704,12 +1458,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_replay_serial_write(Your_Function)
@@ -1732,12 +1480,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_start_block_exec(Your_Function)
@@ -1758,12 +1500,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_top_loop(Your_Function)
@@ -1782,12 +1518,6 @@ Returns
unused
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_unassigned_io_read(Your_Function)
@@ -1814,12 +1544,6 @@ Returns
True if value read was changed by a PANDA plugin and should be returned False if error-logic (invalid write) should be run
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_unassigned_io_write(Your_Function)
@@ -1846,12 +1570,6 @@ Returns
True if the write should be allowed without error False if normal behavior should be used (error-logic)
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_virt_mem_after_read(Your_Function)
@@ -1878,12 +1596,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_virt_mem_after_write(Your_Function)
@@ -1910,12 +1622,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_virt_mem_before_read(Your_Function)
@@ -1940,12 +1646,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
def @panda.cb_virt_mem_before_write(Your_Function)
@@ -1972,12 +1672,6 @@ Returns
none
-
-
-Expand source code
-
-setattr(Callbacks, fakename, lambda Your_Function: None)
-
@@ -5670,44 +5364,12 @@ Returns
ram offset (int)
Raises
ValueError if memory access fails or fmt is unsupported
-
-
-Expand source code
-
-def address_to_ram_offset(self, hwaddr, is_write):
- '''
- Convert physical address to ram offset
-
- Args:
- hwaddr (int): physical address
- is_write (bool): boolean representing if this is a write
-
- Returns:
- ram offset (int)
-
- Raises:
- ValueError if memory access fails or fmt is unsupported
- '''
-
- out = self.ffi.new("ram_addr_t*", self.ffi.cast("ram_addr_t", 0))
- value = self.libpanda.PandaPhysicalAddressToRamOffset_external(out, hwaddr, is_write)
- if value != 0:
- raise ValueError(f"address_to_ram_offset returned {value}")
- return out[0]
-
def arm_load_kernel(self, cpu, bootinfo)
-
-
-Expand source code
-
-def arm_load_kernel(self, cpu, bootinfo):
- return self.libpanda.arm_load_kernel(cpu, bootinfo)
-
def break_exec(self)
@@ -5715,17 +5377,6 @@ Raises
If called from a start block exec callback, will cause the emulation to bail before executing
the rest of the current block.
-
-
-Expand source code
-
-def break_exec(self):
- '''
- If called from a start block exec callback, will cause the emulation to bail *before* executing
- the rest of the current block.
- '''
- return self.libpanda.panda_do_break_exec()
-
def callstack_callers(self, lim, cpu)
@@ -5733,26 +5384,6 @@ Raises
Helper function for callstack_instr plugin
Handle conversion and return get_callers from callstack_instr.
-
-
-Expand source code
-
-def callstack_callers(self, lim, cpu): # XXX move into new directory, 'callstack' ?
- '''
- Helper function for callstack_instr plugin
- Handle conversion and return get_callers from callstack_instr.
- '''
- if not "callstack_instr" in self.plugins:
- progress("enabling callstack_instr plugin")
- self.load_plugin("callstack_instr")
-
- callers = self.ffi.new("uint%d_t[%d]" % (self.bits, lim))
- n = self.plugins['callstack_instr'].get_callers(callers, lim, cpu)
- c = []
- for pc in callers:
- c.append(pc)
- return c
-
def cleanup(self)
@@ -5761,51 +5392,18 @@ Raises
Unload all plugins and close pandalog.
Returns
None
-
-
-Expand source code
-
-def cleanup(self):
- '''
- Unload all plugins and close pandalog.
-
- Returns:
- None
- '''
- self.libpanda.panda_cleanup()
-
def clear_breakpoint(self, cpu, pc)
Remove a breakpoint
-
-
-Expand source code
-
-def clear_breakpoint(self, cpu, pc):
- '''
- Remove a breakpoint
- '''
- BP_GDB = 0x10
- self.libpanda.cpu_breakpoint_remove(cpu, pc, BP_GDB)
-
def cont(self)
Continue execution (run after vm_stop)
-
-
-Expand source code
-
-def cont(self):
- ''' Continue execution (run after vm_stop) '''
- self.libpanda.panda_cont()
- self.running.set()
-
def copy_to_guest(self, copy_directory, iso_name=None, absolute_paths=False, setup_script='setup.sh', timeout=None, cdrom=None)
@@ -5830,96 +5428,6 @@ Args
Returns
None
-
-
-Expand source code
-
-@blocking
-def copy_to_guest(self, copy_directory, iso_name=None, absolute_paths=False, setup_script="setup.sh", timeout=None, cdrom=None):
- '''
-
- Copy a directory from the host into the guest by
- 1) Creating an .iso image of the directory on the host
- 2) Run a bash command to mount it at the exact same path + .ro and then copy the files to the provided path
- 3) If the directory contains setup.sh, run it
-
- Args:
- copy_directory: Local directory to copy into guest
- iso_name: Name of iso file that will be generated. Defaults to [copy_directory].iso
- absolute_paths: is copy_directory an absolute or relative path
- seutp_script: name of a script which, if present inside copy_directory, will be automatically run after the copy
- timeout: maximum time each copy command will be allowed to run for, will use the `run_serial_cmd` default value unless another is provided
-
- Returns:
- None
- '''
-
- if not iso_name:
- iso_name = copy_directory + '.iso'
- make_iso(copy_directory, iso_name)
-
- if not absolute_paths:
- copy_directory = path.split(copy_directory)[-1] # Copy directory relative, not absolutely
-
-
- # Drive the guest to mount the drive
- # setup_sh:
- # Make sure cdrom didn't automount
- # Make sure guest path mirrors host path
- # if there is a setup.sh script in the directory,
- # then run that setup.sh script first (good for scripts that need to
- # prep guest environment before script runs)
- mount_dir = shlex_quote(copy_directory)
-
- mkdir_result = self.run_serial_cmd(f"mkdir -p {mount_dir} {mount_dir}.ro && echo \"mkdir_ok\"; echo \"exit code $?\"", timeout=timeout)
-
- if 'mkdir_ok' not in mkdir_result:
- raise RuntimeError(f"Failed to create mount directories inside guest: {mkdir_result}")
-
- # Tell panda to we insert the CD drive
- # TODO: the cd-drive name should be a config option, see the values in qcow.py
-
- cd_drive_name = cdrom
- if cdrom is None:
- if self.cdrom is not None:
- cd_drive_name = self.cdrom
- else:
- cd_drive_name = "ide1-cd0"
-
- errs = self.run_monitor_cmd("change {} \"{}\"".format(cd_drive_name, iso_name))
- if len(errs):
- warn(f"Warning encountered when connecting media to guest: {errs}")
-
- try:
- mount_status = "bad"
- for _ in range(10):
- if 'mount_ok' in mount_status:
- break
- mount_status = self.run_serial_cmd(f"mount /dev/cdrom {mount_dir}.ro && echo 'mount_ok' || (umount /dev/cdrom; echo 'bad')", timeout=timeout)
- sleep(1)
- else:
- # Didn't ever break
- raise RuntimeError(f"Failed to mount media inside guest: {mount_status}")
-
- # Note the . after our src/. directory - that's special syntax for cp -a
- copy_result = self.run_serial_cmd(f"cp -a {mount_dir}.ro/. {mount_dir} && echo 'copyok'", timeout=timeout)
-
- # NB: exact match here causing issues so making things more flexible
- if not ('copyok' in copy_result):
- raise RuntimeError(f"Copy to rw directory failed: {copy_result}")
-
- finally:
- # Ensure we disconnect the CD drive after the mount + copy, even if it fails
- self.run_serial_cmd("umount /dev/cdrom") # This can fail and that's okay, we'll forece eject
- sleep(1)
- errs = self.run_monitor_cmd(f"eject -f {cd_drive_name}")
- if len(errs):
- warn(f"Warning encountered when disconnecting media from guest: {errs}")
-
- if isfile(pjoin(copy_directory, setup_script)):
- setup_result = self.run_serial_cmd(f"{mount_dir}/{setup_script}", timeout=timeout)
- progress(f"[Setup command]: {setup_result}")
-
def cpu_class_by_name(self, name, cpu_model)
@@ -5936,76 +5444,30 @@ Args
Returns
ObjectClass struct
-
-
-Expand source code
-
-def cpu_class_by_name(self, name, cpu_model):
- '''
- Gets cpu class from name.
- Calls cpu_class_by_name QEMU function.
-
- Args:
- name: typename from python string
- cpu_model: string specified cpu model
-
- Returns:
- ObjectClass struct
- '''
- return self.libpanda.cpu_class_by_name(name, cpu_model)
-
def create_external_gic(self, vbi, irqs, gic_vers, secure)
-
-
-Expand source code
-
-def create_external_gic(self, vbi, irqs, gic_vers, secure):
- return self.libpanda.create_external_gic(vbi, irqs, gic_vers, secure)
-
def create_internal_gic(self, vbi, irqs, gic_vers)
-
-
-Expand source code
-
-def create_internal_gic(self, vbi, irqs, gic_vers):
- return self.libpanda.create_internal_gic(vbi, irqs, gic_vers)
-
def create_one_flash(self, name, flashbase, flashsize, filename, mr)
-
-
-Expand source code
-
-def create_one_flash(self, name, flashbase, flashsize, filename, mr):
- return self.libpanda.create_one_flash(name, flashbase, flashsize, filename, mr)
-
def create_virtio_devices(self, vbi, pic)
-
-
-Expand source code
-
-def create_virtio_devices(self, vbi, pic):
- return self.libpanda.create_virtio_devices(vbi, pic)
-
def current_asid(self, cpu)
@@ -6022,22 +5484,6 @@ Returns
integer
value of current ASID
-
-
-Expand source code
-
-def current_asid(self, cpu):
- '''
- Get current Application Specific ID
-
- Args:
- cpu (CPUState): CPUState structure
-
- Returns:
- integer: value of current ASID
- '''
- return self.libpanda.panda_current_asid(cpu)
-
def current_pc(self, cpu)
@@ -6054,24 +5500,6 @@ Return
Deprecated: Use panda.arch.get_pc(cpu) instead
-
-
-Expand source code
-
-def current_pc(self, cpu):
- '''
- Get current program counter
-
- Args:
- cpu (CPUState): CPUState structure
-
- Return:
- integer value of current program counter
-
- .. Deprecated:: Use panda.arch.get_pc(cpu) instead
- '''
- return self.libpanda.panda_current_pc(cpu)
-
def current_sp(self, cpu)
@@ -6085,66 +5513,18 @@ Args
Return
int: Value of stack pointer
-
-
-Expand source code
-
-def current_sp(self, cpu):
- '''
- Get current stack pointer
-
- Args:
- cpu (CPUState): CPUState structure
-
- Return:
- int: Value of stack pointer
- '''
- return self.libpanda.panda_current_sp_external(cpu)
-
def delete_callback(self, name)
Completely delete a registered panda callback by name
-
-
-Expand source code
-
-def delete_callback(self, name):
- '''
- Completely delete a registered panda callback by name
- '''
- if name not in self.registered_callbacks.keys():
- raise ValueError("No callback has been registered with name '{}'".format(name))
-
- handle = self.registered_callbacks[name]['handle']
- self.libpanda.panda_unregister_callbacks(handle)
- if not hasattr(self,"old_cb_list"):
- self.old_cb_list = []
- self.old_cb_list.append(self.registered_callbacks[name])
- del self.registered_callbacks[name]['handle']
- del self.registered_callbacks[name]
-
def delete_callbacks(self)
-
-
-Expand source code
-
-def delete_callbacks(self):
- #for name in self.registered_callbacks.keys():
- while len(self.registered_callbacks.keys()) > 0:
- self.delete_callback(list(self.registered_callbacks.keys())[0])
-
- # Disable PPP callbacks
- for name in list(self.ppp_registered_cbs) if hasattr(self, 'ppp_registered_cbs') else []:
- self.disable_ppp(name)
-
def delvm(self, snapshot_name)
@@ -6158,42 +5538,12 @@ Args
Returns
None
-
-
-Expand source code
-
-def delvm(self, snapshot_name):
- '''
- Delete snapshot with specified name
- Args:
- snapshot_name (str): name of the snapshot
-
- Returns:
- None
- '''
-
- if debug:
- progress ("Deleting snapshot " + snapshot_name)
-
- # Stop guest, queue up delete, then continue
- self.vm_stop()
- charptr = self.ffi.new("char[]", bytes(snapshot_name, "utf-8"))
- self.queue_main_loop_wait_fn(self.libpanda.panda_delvm, [charptr])
-
def delvm_sync(self, snapshot_name)
-
-
-Expand source code
-
-@blocking
-def delvm_sync(self, snapshot_name):
- self.run_monitor_cmd("delvm {}".format(snapshot_name))
-
def disable_callback(self, name, forever=False)
@@ -6202,32 +5552,6 @@ Returns
Disable a panda plugin using its handle and cb.number as a unique ID
If forever is specified, we'll never reenable the call- useful when
you want to really turn off something with a procname filter.
-
-
-Expand source code
-
-def disable_callback(self, name, forever=False):
- '''
- Disable a panda plugin using its handle and cb.number as a unique ID
- If forever is specified, we'll never reenable the call- useful when
- you want to really turn off something with a procname filter.
- '''
- # During shutdown callback may be deleted before a request to enable comes through
- if self.ending:
- return
-
- if name not in self.registered_callbacks.keys():
- raise RuntimeError("No callback has been registered with name '{}'".format(name))
- self.registered_callbacks[name]['enabled'] = False
- handle = self.registered_callbacks[name]['handle']
- cb = self.registered_callbacks[name]['callback']
- pcb = self.registered_callbacks[name]['pcb']
- #progress("Disabling callback '{}' on '{}' handle={}".format(name, cb.name, handle))
- self.libpanda.panda_disable_callback_helper(handle, cb.number, pcb)
-
- if forever:
- del self.registered_callbacks[name]
-
def disable_hook2(self, hook_name)
@@ -6237,75 +5561,24 @@ Returns
Deprecated: Use the hooks plugin instead.
-
-
-Expand source code
-
-def disable_hook2(self,hook_name):
- '''
- Set a hook2-plugin hook's status to inactive.
-
- .. Deprecated:: Use the hooks plugin instead.
- '''
- if hook_name in self.hook_list2:
- self.plugins['hooks2'].disable_hooks2(self.hook_list2[hook_name])
- else:
- print("ERROR: Your hook name was not in the hook list")
-
def disable_hypercall(self, fn)
-
-
-Expand source code
-
-def disable_hypercall(self, fn):
- if fn in self.hypercalls:
- magic = self.hypercalls[fn][1]
- if type(magic) is int:
- self.plugins['hypercaller'].unregister_hypercall(magic)
- elif type(magic) is list:
- for m in magic:
- self.plugins['hypercaller'].unregister_hypercall(m)
- else:
- breakpoint()
- print("ERROR: Your hypercall was not in the hook list")
-
def disable_llvm(self)
Disables the use of the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
-
-
-Expand source code
-
-def disable_llvm(self):
- '''
- Disables the use of the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
- '''
- self.libpanda.panda_disable_llvm()
-
def disable_llvm_helpers(self)
Disables the use of Helpers for the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
-
-
-Expand source code
-
-def disable_llvm_helpers(self):
- '''
- Disables the use of Helpers for the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
- '''
- self.libpanda.panda_disable_llvm_helpers()
-
def disable_memcb(self)
@@ -6313,18 +5586,6 @@ Returns
Disable memory callbacks. Must be enabled for memory callbacks to work.
pypanda enables this automatically with some callbacks.
-
-
-Expand source code
-
-def disable_memcb(self):
- '''
- Disable memory callbacks. Must be enabled for memory callbacks to work.
- pypanda enables this automatically with some callbacks.
- '''
- self._memcb = False
- self.libpanda.panda_disable_memcb()
-
def disable_plugin(self, handle)
@@ -6338,22 +5599,6 @@ Args
Return
None
-
-
-Expand source code
-
-def disable_plugin(self, handle):
- '''
- Disable plugin.
-
- Args:
- handle (int): pointer to handle returned by plugin
-
- Return:
- None
- '''
- self.libpanda.panda_disable_plugin(handle)
-
def disable_ppp(self, name)
@@ -6374,39 +5619,6 @@ Return
...
panda.disable_ppp("custom")
-
-
-Expand source code
-
-def disable_ppp(self, name):
- '''
- Disable a ppp-style callback by name.
- Unlike regular panda callbacks which can be enabled/disabled/deleted, PPP callbacks are only enabled/deleted (which we call disabled)
-
- Example usage to register my_run with syscalls2 as a 'on_sys_open_return' and then disable:
- ```
- @ppp("syscalls2", "on_sys_open_return")
- def my_fun(cpu, pc, filename, flags, mode):
- ...
-
- panda.disable_ppp("my_fun")
- ```
-
- -- OR --
-
- ```
- @ppp("syscalls2", "on_sys_open_return", name="custom")
- def my_fun(cpu, pc, filename, flags, mode):
- ...
- ```
-
- panda.disable_ppp("custom")
- '''
-
- (f, plugin_name, attr) = self.ppp_registered_cbs[name]
- getattr(self.plugins[plugin_name], f'ppp_remove_cb_{attr}')(f) # All PPP cbs start with this string.
- del self.ppp_registered_cbs[name] # It's now safe to be garbage collected
-
def disable_precise_pc(self)
@@ -6414,37 +5626,12 @@ Return
By default, QEMU does not update the program counter after every instruction.
This function disables precise tracking of the program counter.
-
-
-Expand source code
-
-def disable_precise_pc(self):
- '''
- By default, QEMU does not update the program counter after every instruction.
- This function disables precise tracking of the program counter.
- '''
- self.libpanda.panda_disable_precise_pc()
-
def disable_tb_chaining(self)
This function disables translation block chaining in QEMU
-
-
-Expand source code
-
-def disable_tb_chaining(self):
- '''
- This function disables translation block chaining in QEMU
- '''
- if not self.disabled_tb_chaining:
- if debug:
- progress("Disabling TB chaining")
- self.disabled_tb_chaining = True
- self.libpanda.panda_disable_tb_chaining()
-
def disas2(self, code, size)
@@ -6452,17 +5639,6 @@ Return
Call panda_disas to diasassemble an amount of code at a pointer.
FIXME: seem to not match up to PANDA definition
-
-
-Expand source code
-
-def disas2(self, code, size):
- '''
- Call panda_disas to diasassemble an amount of code at a pointer.
- FIXME: seem to not match up to PANDA definition
- '''
- self.libpanda.panda_disas(code, size)
-
def do_panda_finish(self)
@@ -6472,21 +5648,6 @@ Return
guest should have exited by now, but queue this after
(blocking) shutdown commands in our internal async queue
so it must also be labeled as blocking.
-
-
-Expand source code
-
- @blocking
- def do_panda_finish(self):
- '''
- Call panda_finish. Note this isn't really blocking - the
- guest should have exited by now, but queue this after
- (blocking) shutdown commands in our internal async queue
- so it must also be labeled as blocking.
- '''
-# assert (not self.running.is_set()), "Can't finish while still running"
- self.panda_finish()
-
def drive_get(self, blocktype, bus, unit)
@@ -6504,70 +5665,18 @@ Args
Returns
DriveInfo struct
-
-
-Expand source code
-
-def drive_get(self, blocktype, bus, unit):
- '''
- Gets DriveInfo struct from user specified information.
-
- Args:
- blocktype: BlockInterfaceType structure
- bus: integer bus
- unit: integer unit
-
- Returns:
- DriveInfo struct
- '''
- return self.libpanda.drive_get(blocktype,bus,unit)
-
def enable_all_callbacks(self)
Enable all python callbacks that have been disabled
-
-
-Expand source code
-
-def enable_all_callbacks(self):
- '''
- Enable all python callbacks that have been disabled
- '''
- for name in self.registered_callbacks.keys():
- self.enable_callback(name)
-
def enable_callback(self, name)
Enable a panda plugin using its handle and cb.number as a unique ID
-
-
-Expand source code
-
-def enable_callback(self, name):
- '''
- Enable a panda plugin using its handle and cb.number as a unique ID
- '''
-
- # During shutdown callback may be deleted before a request to enable comes through
- if self.ending:
- return
-
- if name not in self.registered_callbacks.keys():
- raise RuntimeError("No callback has been registered with name '{}'".format(name))
-
- self.registered_callbacks[name]['enabled'] = True
- handle = self.registered_callbacks[name]['handle']
- cb = self.registered_callbacks[name]['callback']
- pcb = self.registered_callbacks[name]['pcb']
- #progress("Enabling callback '{}' on '{}' handle = {}".format(name, cb.name, handle))
- self.libpanda.panda_enable_callback_helper(handle, cb.number, pcb)
-
def enable_hook2(self, hook_name)
@@ -6577,21 +5686,6 @@ Returns
Deprecated: Use the hooks plugin instead.
-
-
-Expand source code
-
-def enable_hook2(self,hook_name):
- '''
- Set a hook2-plugin hook's status to active.
-
- .. Deprecated:: Use the hooks plugin instead.
- '''
- if hook_name in self.hook_list2:
- self.plugins['hooks2'].enable_hooks2(self.hook_list2[hook_name])
- else:
- print("ERROR: Your hook name was not in the hook list")
-
def enable_internal_callbacks(self)
@@ -6600,52 +5694,18 @@ Returns
Enable all our internal callbacks that start with __ such as __main_loop_wait
and __asid_changed. Important in case user has done a panda.end_analysis()
and then (re)called run
-
-
-Expand source code
-
-def enable_internal_callbacks(self):
- '''
- Enable all our internal callbacks that start with __ such as __main_loop_wait
- and __asid_changed. Important in case user has done a panda.end_analysis()
- and then (re)called run
- '''
- for name in self.registered_callbacks.keys():
- if name.startswith("__") and not self.registered_callbacks[name]['enabled']:
- self.enable_callback(name)
-
def enable_llvm(self)
Enables the use of the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
-
-
-Expand source code
-
-def enable_llvm(self):
- '''
- Enables the use of the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
- '''
- self.libpanda.panda_enable_llvm()
-
def enable_llvm_helpers(self)
Enables the use of Helpers for the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
-
-
-Expand source code
-
-def enable_llvm_helpers(self):
- '''
- Enables the use of Helpers for the LLVM JIT in replacement of the TCG (QEMU intermediate language and compiler) backend.
- '''
- self.libpanda.panda_enable_llvm_helpers()
-
def enable_memcb(self)
@@ -6653,18 +5713,6 @@ Returns
Enable memory callbacks. Must be called for memory callbacks to work.
pypanda enables this automatically with some callbacks.
-
-
-Expand source code
-
-def enable_memcb(self):
- '''
- Enable memory callbacks. Must be called for memory callbacks to work.
- pypanda enables this automatically with some callbacks.
- '''
- self._memcb = True
- self.libpanda.panda_enable_memcb()
-
def enable_plugin(self, handle)
@@ -6678,22 +5726,6 @@ Args
Return
None
-
-
-Expand source code
-
-def enable_plugin(self, handle):
- '''
- Enable plugin.
-
- Args:
- handle (int): pointer to handle returned by plugin
-
- Return:
- None
- '''
- self.libpanda.panda_enable_plugin(handle)
-
def enable_precise_pc(self)
@@ -6701,36 +5733,12 @@ Return
By default, QEMU does not update the program counter after every instruction.
This function enables precise tracking of the program counter. After enabling precise PC tracking, the program counter will be available in env->panda_guest_pc and can be assumed to accurately reflect the guest state.
-
-
-Expand source code
-
-def enable_precise_pc(self):
- '''
- By default, QEMU does not update the program counter after every instruction.
- This function enables precise tracking of the program counter. After enabling precise PC tracking, the program counter will be available in env->panda_guest_pc and can be assumed to accurately reflect the guest state.
- '''
- self.libpanda.panda_enable_precise_pc()
-
def enable_tb_chaining(self)
This function enables translation block chaining in QEMU
-
-
-Expand source code
-
-def enable_tb_chaining(self):
- '''
- This function enables translation block chaining in QEMU
- '''
- if debug:
- progress("Enabling TB chaining")
- self.disabled_tb_chaining = False
- self.libpanda.panda_enable_tb_chaining()
-
def end_analysis(self)
@@ -6741,28 +5749,6 @@ Return
If called from async thread or a callback, it will also unblock panda.run()
Note here we use the async class's internal thread to process these
without needing to wait for tasks in the main async thread
-
-
-Expand source code
-
-def end_analysis(self):
- '''
- Stop running machine.
-
- Call from any thread to unload all plugins and stop all queued functions.
- If called from async thread or a callback, it will also unblock panda.run()
-
- Note here we use the async class's internal thread to process these
- without needing to wait for tasks in the main async thread
- '''
- self.athread.ending = True
- self.ending = True
- self.unload_plugins()
- if self.running.is_set() or self.initializing.is_set():
-
- # If we were running, stop the execution and check if we crashed
- self.queue_async(self.stop_run, internal=True)
-
def end_record(self)
@@ -6774,21 +5760,6 @@ Raises
Exception
raises exception if there was an error stopping recording.
-
-
-Expand source code
-
-def end_record(self):
- """Stop active recording.
-
- Raises:
- Exception: raises exception if there was an error stopping recording.
- """
- result = self.libpanda.panda_record_end()
- res_string_enum = self.ffi.string(self.ffi.cast("RRCTRL_ret",result))
- if res_string_enum != "RRCTRL_OK":
- raise Exception(f"record method failed with RTCTL_ret {res_string_enum} ({result})")
-
def end_replay(self)
@@ -6801,74 +5772,24 @@ Raises
Raises:
Exception: raises exception if no replay is active or termination failed.
-
-
-Expand source code
-
-def end_replay(self):
- '''
- Terminates a currently running replay
-
- Returns:
- None
-
- Raises:
- Exception: raises exception if no replay is active or termination failed.
- '''
-
- if self._in_replay is False:
- raise Exception("Tried to terminate replay while not in replay mode!")
-
- result = self.libpanda.panda_replay_end()
-
- res_string_enum = self.ffi.string(self.ffi.cast("RRCTRL_ret",result))
- if res_string_enum != "RRCTRL_OK":
- raise Exception(f"ending record method failed with RTCTL_ret {res_string_enum} ({result})")
-
def error_report(self, s)
-
-
-Expand source code
-
-def error_report(self, s):
- return self.libpanda.error_report(s)
-
def exit_cpu_loop(self)
Stop cpu execution at nearest juncture.
-
-
-Expand source code
-
-def exit_cpu_loop(self):
- '''
- Stop cpu execution at nearest juncture.
- '''
- self.libpanda.panda_exit_loop = True
-
def finish_serial_cmd(self)
-
-
-Expand source code
-
-def finish_serial_cmd(self):
- result = self.serial_console.send_eol()
- result = self.serial_console.expect()
- return result
-
def flush_tb(self)
@@ -6876,17 +5797,6 @@ Raises
This function requests that the translation block cache be flushed as soon as possible. If running with translation block chaining turned off (e.g. when in LLVM mode or replay mode), this will happen when the current translation block is done executing.
Flushing the translation block cache is additionally necessary if the plugin makes changes to the way code is translated. For example, by using panda_enable_precise_pc.
-
-
-Expand source code
-
-def flush_tb(self):
- '''
- This function requests that the translation block cache be flushed as soon as possible. If running with translation block chaining turned off (e.g. when in LLVM mode or replay mode), this will happen when the current translation block is done executing.
- Flushing the translation block cache is additionally necessary if the plugin makes changes to the way code is translated. For example, by using panda_enable_precise_pc.
- '''
- return self.libpanda.panda_do_flush_tb()
-
def from_unsigned_guest(self, x)
@@ -6904,26 +5814,6 @@ Returns
int
Python integer representing x as a signed value
-
-
-Expand source code
-
-def from_unsigned_guest(self, x):
- '''
- Convert an unsigned int32/unsigned int64 from the guest
- (depending on guest bit-size) to a (signed) python int
-
- Args:
- x (int): Python integer representing an unsigned value in the guest's pointer-size
-
- Returns:
- int: Python integer representing x as a signed value
- '''
- if x >= 2**(self.bits-1): # If highest bit is set, it's negative
- return (x - 2**self.bits)
- else: # Else it's positive
- return x
-
def g_malloc0(self, size)
@@ -6937,22 +5827,6 @@ Args
Returns
buffer of the requested size from g_malloc
-
-
-Expand source code
-
-def g_malloc0(self, size):
- '''
- Helper function to call glib malloc
-
- Args:
- size (int): size to call with malloc
-
- Returns:
- buffer of the requested size from g_malloc
- '''
- return self.libpanda.g_malloc0(size)
-
def garray_len(self, garray)
@@ -6969,22 +5843,6 @@ Returns
int
length of the array
-
-
-Expand source code
-
-def garray_len(self, garray):
- '''
- Convenience function to get array length of glibc array.
-
- Args:
- g (garray): Pointer to a glibc array
-
- Returns:
- int: length of the array
- '''
- return self.libpanda.garray_len(garray)
-
def get_best_matching_symbol(self, cpu, pc=None, asid=None)
@@ -7000,41 +5858,12 @@ Args
asid
: int
ASID, defaults to current
-
-
-Expand source code
-
-def get_best_matching_symbol(self, cpu, pc=None, asid=None):
- '''
- Use the dynamic symbols plugin to get the best matching symbol for a given program counter.
-
- Args:
- cpu (CPUState): CPUState structure
- pc (int): program counter, defaults to current
- asid (int): ASID, defaults to current
- '''
- if asid is None:
- asid = self.current_asid(cpu)
- if pc is None:
- pc = self.current_pc(cpu)
- return self.plugins['dynamic_symbols'].get_best_matching_symbol(cpu, pc, asid)
-
def get_build_dir(self)
-
-
-Expand source code
-
-def get_build_dir(self):
- if self.build_dir is None:
- self.build_dir = find_build_dir(self.arch_name)
- environ["PANDA_DIR"] = self.build_dir
- return self.build_dir
-
def get_cpu(self)
@@ -7047,20 +5876,6 @@ Returns
CPUState
cpu
-
-
-Expand source code
-
-def get_cpu(self):
- '''
- This function returns first_cpu CPUState object from QEMU.
- XXX: You rarely want this
-
- Returns:
- CPUState: cpu
- '''
- return self.libpanda.get_cpu()
-
def get_current_process(self, cpu)
@@ -7074,23 +5889,6 @@ Returns
None
on failure
-
-
-Expand source code
-
-def get_current_process(self, cpu):
- '''
- Get the current process as an OsiProc struct.
-
- Returns:
- string: process name
- None: on failure
- '''
- proc = self.plugins['osi'].get_current_process(cpu)
- if proc == self.ffi.NULL:
- return None
- return proc
-
def get_file_name(self, cpu, fd)
@@ -7104,29 +5902,6 @@ Returns
None
on failure
-
-
-Expand source code
-
-def get_file_name(self, cpu, fd):
- '''
- Get the name of a file from a file descriptor.
-
- Returns:
- string: file name
- None: on failure
- '''
- proc = self.plugins['osi'].get_current_process(cpu)
- if proc == self.ffi.NULL:
- return None
- try:
- fname_ptr = self.plugins['osi_linux'].osi_linux_fd_to_filename(cpu, proc, fd)
- except OverflowError:
- return None
- if fname_ptr == self.ffi.NULL:
- return None
- return self.ffi.string(fname_ptr)
-
def get_id(self, cpu)
@@ -7143,22 +5918,6 @@ Returns
integer
value of current hw_proc_id
-
-
-Expand source code
-
-def get_id(self, cpu):
- '''
- Get current hw_proc_id ID
-
- Args:
- cpu (CPUState): CPUState structure
-
- Returns:
- integer: value of current hw_proc_id
- '''
- return self.plugins["hw_proc_id"].get_id(cpu)
-
def get_mapping_by_addr(self, cpu, addr)
@@ -7181,49 +5940,6 @@ Returns
None
on failure
-
-
-Expand source code
-
-def get_mapping_by_addr(self, cpu, addr):
- '''
- Return the OSI mapping that matches the address specified.
-
- Requires: OSI
-
- Args:
- cpu: CPUState struct
- addr: int
-
- Returns:
- OsiModule: dataclass representation of OsiModule structure with strings converted to python strings
- Note that the strings will be None if their pointer was null
- None: on failure
- '''
- @dataclass
- class OsiModule:
- '''dataclass representation of OsiModule structu'''
- base: int
- file: str
- modd: int
- name: str
- size: int
- mappings = self.get_mappings(cpu)
- for m in mappings:
- if m == self.ffi.NULL:
- continue
- if addr >= m.base and addr < m.base+m.size:
- if m.name != self.ffi.NULL:
- name = self.ffi.string(m.name).decode("utf-8")
- else:
- name = None
- if m.file != self.ffi.NULL:
- file = self.ffi.string(m.file).decode("utf-8")
- else:
- file = None
- return OsiModule(m.base, file, m.modd, name, m.size)
- return None
-
def get_mappings(self, cpu)
@@ -7241,27 +5957,6 @@ Returns
GArrayIterator
iterator of OsiModule structures
-
-
-Expand source code
-
-def get_mappings(self, cpu):
- '''
- Get all active memory mappings in the system.
-
- Requires: OSI
-
- Args:
- cpu: CPUState struct
-
- Returns:
- pandare.utils.GArrayIterator: iterator of OsiModule structures
- '''
- current = self.plugins['osi'].get_current_process(cpu)
- maps = self.plugins['osi'].get_mappings(cpu, current)
- map_len = self.garray_len(maps)
- return GArrayIterator(self.plugins['osi'].get_one_module, maps, map_len, self.plugins['osi'].cleanup_garray)
-
def get_os_family(self)
@@ -7273,67 +5968,18 @@ Returns
string
one of OS_UNKNOWN, OS_WINDOWS, OS_LINUX, OS_FREEBSD
-
-
-Expand source code
-
-def get_os_family(self):
- '''
- Get the current OS family name. Valid values are the entries in `OSFamilyEnum`
-
- Returns:
- string: one of OS_UNKNOWN, OS_WINDOWS, OS_LINUX, OS_FREEBSD
- '''
-
- family_num = self.libpanda.panda_os_familyno
- family_name = self.ffi.string(self.ffi.cast("PandaOsFamily", family_num))
- return family_name
-
def get_plugin_path(self)
-
-
-Expand source code
-
-def get_plugin_path(self):
- if self.plugin_path is None:
- build_dir = self.get_build_dir()
- rel_dir = pjoin(*[build_dir, self.arch_name+"-softmmu", "panda", "plugins"])
-
- if build_dir == "/usr/local/bin/":
- # Installed - use /usr/local/lib/panda/plugins
- self.plugin_path = f"/usr/local/lib/panda/{self.arch_name}"
- elif isdir(rel_dir):
- self.plugin_path = rel_dir
- else:
- raise ValueError(f"Could not find plugin path. Build dir={build_dir}")
- return self.plugin_path
-
def get_process_name(self, cpu)
Get the name of the current process. May return None if OSI cannot identify the current process
-
-
-Expand source code
-
-def get_process_name(self, cpu):
- '''
- Get the name of the current process. May return None if OSI cannot identify the current process
- '''
- proc = self.plugins['osi'].get_current_process(cpu)
- if proc == self.ffi.NULL or proc.name == self.ffi.NULL:
- return None
-
- procname = self.ffi.string(proc.name).decode('utf8', 'ignore')
- return self.ffi.string(proc.name).decode('utf8', 'ignore')
-
def get_processes(self, cpu)
@@ -7351,26 +5997,6 @@ Returns
GArrayIterator
iterator of OsiProc structures
-
-
-Expand source code
-
-def get_processes(self, cpu):
- '''
- Get all running processes in the system. Includes kernel modules on Linux.
-
- Requires: OSI
-
- Args:
- cpu: CPUState struct
-
- Returns:
- pandare.utils.GArrayIterator: iterator of OsiProc structures
- '''
- processes = self.plugins['osi'].get_processes(cpu)
- processes_len = self.garray_len(processes)
- return GArrayIterator(self.plugins['osi'].get_one_proc, processes, processes_len, self.plugins['osi'].cleanup_garray)
-
def get_processes_dict(self, cpu)
@@ -7390,85 +6016,18 @@ Returns
Dict
processes as described above
-
-
-Expand source code
-
-def get_processes_dict(self, cpu):
- '''
- Get all running processes for the system at this moment in time as a dictionary.
-
- The dictionary maps proceses by their PID. Each mapping returns a dictionary containing the process name, its pid,
- and its parent pid (ppid).
-
- Requires: OSI
-
- Args:
- cpu: CPUState struct
-
- Returns:
- Dict: processes as described above
- '''
-
- procs = {} #pid: {name: X, pid: Y, parent_pid: Z})
-
- for proc in self.get_processes(cpu):
- assert(proc != self.ffi.NULL)
- assert(proc.pid not in procs)
- procs[proc.pid] = {'name': self.ffi.string(proc.name).decode('utf8', 'ignore'),
- 'pid': proc.pid,
- 'parent_pid': proc.ppid,
- 'create_time': proc.create_time}
- assert(not (proc.pid != 0 and proc.pid == proc.ppid)) # No cycles allowed other than at 0
- return procs
-
def get_system_memory(self)
-
-
-Expand source code
-
-def get_system_memory(self):
- return self.libpanda.get_system_memory()
-
def get_volatility_symbols(self, debug=False)
-
-
-Expand source code
-
-def get_volatility_symbols(self, debug=False):
- try:
- from .volatility_cli_classes import CommandLineMoreEfficient
- from volatility.framework import contexts
- from volatility.framework.layers.linear import LinearlyMappedLayer
- from volatility.framework.automagic import linux
- except ImportError:
- print("Warning: Failed to import volatility")
- return None
- if "linux" in self.os_type:
- if not hasattr(self, "_vmlinux"):
- self.make_panda_file_handler(debug=debug)
- constructed_original = CommandLineMoreEfficient().run()
- linux.LinuxUtilities.aslr_mask_symbol_table(
- constructed_original.context, constructed_original.config['vmlinux'], constructed_original.config['primary'])
- self._vmlinux = contexts.Module(
- constructed_original.context, constructed_original.config['vmlinux'], constructed_original.config['primary'], 0)
- else:
- LinearlyMappedLayer.read.cache_clear() # smearing technique
- return self._vmlinux
- else:
- print("Unsupported.")
- return None
-
def hook(self, addr, enabled=True, kernel=None, asid=None, cb_type='start_block_exec')
@@ -7476,78 +6035,6 @@ Returns
Decorate a function to setup a hook: when a guest goes to execute a basic block beginning with addr,
the function will be called with args (CPUState, TranslationBlock)
-
-
-Expand source code
-
-def hook(self, addr, enabled=True, kernel=None, asid=None, cb_type="start_block_exec"):
- '''
- Decorate a function to setup a hook: when a guest goes to execute a basic block beginning with addr,
- the function will be called with args (CPUState, TranslationBlock)
- '''
-
- def decorator(fun):
- if cb_type == "before_tcg_codegen" or cb_type == "after_block_translate" or cb_type == "before_block_exec" or cb_type == "start_block_exec" or cb_type == "end_block_exec":
- hook_cb_type = self.ffi.callback("void(CPUState*, TranslationBlock* , struct hook *)")
- elif cb_type == "after_block_exec":
- hook_cb_type = self.ffi.callback("void(CPUState*, TranslationBlock* , uint8_t, struct hook *)")
- elif cb_type == "before_block_translate":
- hook_cb_type = self.ffi.callback("void(CPUState* env, target_ptr_t pc, struct hook*)")
- elif cb_type == "before_block_exec_invalidate_opt":
- hook_cb_type = self.ffi.callback("bool(CPUState* env, TranslationBlock*, struct hook*)")
- else:
- print("function type not supported")
- return
- type_num = getattr(self.libpanda, "PANDA_CB_"+cb_type.upper())
-
- if debug:
- print("Registering breakpoint at 0x{:x} -> {} == {}".format(addr, fun, 'cdata_cb'))
-
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- #print(pandatype, type(r)) # XXX Can we use pandatype to determine requried return and assert if incorrect
- #assert(isinstance(r, int)), "Invalid return type?"
- #print(fun, r) # Stuck with TypeError in _run_and_catch? Enable this to find where the bug is.
- return r
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- return 0
-
- # Inform the plugin that it has a new breakpoint at addr
- hook_cb_passed = hook_cb_type(_run_and_catch)
- new_hook = self.ffi.new("struct hook*")
- new_hook.type = type_num
- new_hook.addr = addr
- if kernel or asid is None:
- new_hook.asid = 0
- else:
- new_hook.asid = asid
-
- setattr(new_hook.cb,cb_type, hook_cb_passed)
- if kernel:
- new_hook.km = self.libpanda.MODE_KERNEL_ONLY
- elif kernel == False:
- new_hook.km = self.libpanda.MODE_USER_ONLY
- else:
- new_hook.km = self.libpanda.MODE_ANY
- new_hook.enabled = enabled
-
- self.plugins['hooks'].add_hook(new_hook)
- self.hook_list.append((new_hook, hook_cb_passed))
-
- def wrapper(*args, **kw):
- return _run_and_catch(args,kw)
- return wrapper
- return decorator
-
def hook2(self, name, kernel=True, procname=None, libname=None, trace_start=0, trace_stop=0, range_begin=0, range_end=0)
@@ -7557,73 +6044,6 @@ Returns
Deprecated: Use the hooks plugin instead.
-
-
-Expand source code
-
-def hook2(self,name, kernel=True, procname=None, libname=None, trace_start=0, trace_stop=0, range_begin=0, range_end=0):
- '''
- Decorator to create a hook with the hooks2 plugin.
-
- .. Deprecated:: Use the hooks plugin instead.
- '''
-
- if procname == None:
- procname = self.ffi.NULL
- if libname == None:
- libname = self.ffi.NULL
-
-
- if procname != self.ffi.NULL:
- procname = self.ffi.new("char[]",bytes(procname,"utf-8"))
- if libname != self.ffi.NULL:
- libname = self.ffi.new("char[]",bytes(libname,"utf-8"))
- '''
- Decorate a function to setup a hook: when a guest goes to execute a basic block beginning with addr,
- the function will be called with args (CPUState, TranslationBlock)
- '''
- def decorator(fun):
- # Ultimately, our hook resolves as a before_block_exec_invalidate_opt callback so we must match its args
- hook_cb_type = self.ffi.callback("bool (CPUState*, TranslationBlock*, void*)")
- # Inform the plugin that it has a new breakpoint at addr
-
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- #print(pandatype, type(r)) # XXX Can we use pandatype to determine requried return and assert if incorrect
- #assert(isinstance(r, int)), "Invalid return type?"
- #print(fun, r) # Stuck with TypeError in _run_and_catch? Enable this to find where the bug is.
- return r
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- return True
-
-
- hook_cb_passed = hook_cb_type(_run_and_catch)
- if not hasattr(self, "hook_gc_list"):
- self.hook_gc_list = [hook_cb_passed]
- else:
- self.hook_gc_list.append(hook_cb_passed)
-
- # I don't know what this is/does
- cb_data =self.ffi.NULL
- hook_number = self.plugins['hooks2'].add_hooks2(hook_cb_passed, cb_data, kernel, \
- procname, libname, trace_start, trace_stop, range_begin,range_end)
-
- self.hook_list2[name] = hook_number
-
- def wrapper(*args, **kw):
- return _run_and_catch(*args, **kw)
- return wrapper
- return decorator
-
def hook2_single_insn(self, name, pc, kernel=False, procname=None, libname=None)
@@ -7633,22 +6053,6 @@ Returns
Deprecated: Use the hooks plugin instead.
-
-
-Expand source code
-
-def hook2_single_insn(self, name, pc, kernel=False, procname=None, libname=None):
- '''
- Helper function to hook a single instruction with the hooks2 plugin.
-
- .. Deprecated:: Use the hooks plugin instead.
- '''
- if procname == None:
- procname = self.ffi.NULL
- if libname == None:
- libname = self.ffi.NULL
- return self.hook2(name, kernel=kernel, procname=procname,libname=libname,range_begin=pc, range_end=pc)
-
def hook_mem(self, start_address, end_address, on_before, on_after, on_read, on_write, on_virtual, on_physical, enabled)
@@ -7659,50 +6063,18 @@ Returns
TODO
Fully document mem-hook decorators
-
-
-Expand source code
-
-def hook_mem(self, start_address, end_address, on_before, on_after, on_read, on_write, on_virtual, on_physical, enabled):
- '''
- Decorator to hook a memory range with the mem_hooks plugin
-
- .. todo:: Fully document mem-hook decorators
- '''
- return self._hook_mem(start_address,end_address,on_before,on_after,on_read, on_write, on_virtual, on_physical, enabled)
-
def hook_phys_mem_read(self, start_address, end_address, on_before=True, on_after=False, enabled=True)
Decorator to hook physical memory reads with the mem_hooks plugin
-
-
-Expand source code
-
-def hook_phys_mem_read(self, start_address, end_address, on_before=True, on_after=False, enabled=True):
- '''
- Decorator to hook physical memory reads with the mem_hooks plugin
- '''
- return self._hook_mem(start_address,end_address,on_before,on_after, True, False, False, True, True)
-
def hook_phys_mem_write(self, start_address, end_address, on_before=True, on_after=False)
Decorator to hook physical memory writes with the mem_hooks plugin
-
-
-Expand source code
-
-def hook_phys_mem_write(self, start_address, end_address, on_before=True, on_after=False):
- '''
- Decorator to hook physical memory writes with the mem_hooks plugin
- '''
- return self._hook_mem(start_address,end_address,on_before,on_after, False, True, False, True, True)
-
def hook_symbol(self, libraryname, symbol, kernel=False, name=None, cb_type='start_block_exec')
@@ -7729,92 +6101,6 @@ Returns
Decorated function is called when (before/after is determined by cb_type) guest goes to call
the specified symbol in the specified library.
-
-
-Expand source code
-
-def hook_symbol(self, libraryname, symbol, kernel=False, name=None, cb_type="start_block_exec"):
- '''
- Decorate a function to setup a hook: when a guest goes to execute a basic block beginning with addr,
- the function will be called with args (CPUState, TranslationBlock, struct hook)
-
- Args:
- libraryname (string): Name of library containing symbol to be hooked. May be None to match any.
- symbol (string, int): Name of symbol or offset into library to hook
- kernel (bool): if hook should be applied exclusively in kernel mode
- name (string): name of hook, defaults to function name
- cb_type (string): callback-type, defaults to start_block_exec
-
- Returns:
- None: Decorated function is called when (before/after is determined by cb_type) guest goes to call
- the specified symbol in the specified library.
- '''
-
- def decorator(fun):
- if cb_type == "before_tcg_codegen" or cb_type == "after_block_translate" or cb_type == "before_block_exec" or cb_type == "start_block_exec" or cb_type == "end_block_exec":
- hook_cb_type = self.ffi.callback("void(CPUState*, TranslationBlock* , struct hook *)")
- elif cb_type == "after_block_exec":
- hook_cb_type = self.ffi.callback("void(CPUState*, TranslationBlock* , uint8_t, struct hook *)")
- elif cb_type == "before_block_translate":
- hook_cb_type = self.ffi.callback("void(CPUState* env, target_ptr_t pc, struct hook*)")
- elif cb_type == "before_block_exec_invalidate_opt":
- hook_cb_type = self.ffi.callback("bool(CPUState* env, TranslationBlock*, struct hook*)")
- else:
- print("function type not supported")
- return
-
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- return r
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- if cb_type == "before_block_exec_invalidate_opt":
- return False
- return None
-
-
- # Inform the plugin that it has a new breakpoint at addr
- hook_cb_passed = hook_cb_type(_run_and_catch)
- new_hook = self.ffi.new("struct symbol_hook*")
- type_num = getattr(self.libpanda, "PANDA_CB_"+cb_type.upper())
- new_hook.type = type_num
- if libraryname is not None:
- libname_ffi = self.ffi.new("char[]",bytes(libraryname,"utf-8"))
- else:
- libname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- self.ffi.memmove(new_hook.section,libname_ffi,len(libname_ffi))
-
- new_hook.hook_offset = False
- if symbol is not None:
- if isinstance(symbol, int):
- new_hook.offset = symbol
- new_hook.hook_offset = True
- symbolname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- else:
- symbolname_ffi = self.ffi.new("char[]",bytes(symbol,"utf-8"))
- new_hook.hook_offset = False
- else:
- symbolname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- self.ffi.memmove(new_hook.name,symbolname_ffi,len(symbolname_ffi))
- setattr(new_hook.cb,cb_type, hook_cb_passed)
- hook_ptr = self.plugins['hooks'].add_symbol_hook(new_hook)
- if name is not None:
- self.named_hooks[name] = hook_ptr
- self.hook_list.append((fun, new_hook,hook_cb_passed, hook_ptr))
-
- def wrapper(*args, **kw):
- _run_and_catch(args,kw)
- return wrapper
- return decorator
-
def hook_symbol_resolution(self, libraryname, symbol, name=None)
@@ -7836,151 +6122,24 @@ Returns
None
Decorated function is called when guest resolves the specified symbol in the specified library.
-
-
-Expand source code
-
-def hook_symbol_resolution(self, libraryname, symbol, name=None):
- '''
- Decorate a function to setup a hook: when a guest process resolves a symbol
- the function will be called with args (CPUState, struct hook_symbol_resolve, struct symbol, OsiModule)
-
- Args:
- libraryname (string): Name of library containing symbol to be hooked. May be None to match any.
- symbol (string, int): Name of symbol or offset into library to hook
- name (string): name of hook, defaults to function name
-
- Returns:
- None: Decorated function is called when guest resolves the specified symbol in the specified library.
- '''
- #Mostly based on hook_symbol below
- def decorator(fun):
- sh = self.ffi.new("struct hook_symbol_resolve*")
- sh.hook_offset = False
- if symbol is not None:
- if isinstance(symbol, int):
- sh.offset = symbol
- sh.hook_offset = True
- symbolname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- else:
- symbolname_ffi = self.ffi.new("char[]",bytes(symbol,"utf-8"))
- else:
- symbolname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- self.ffi.memmove(sh.name,symbolname_ffi,len(symbolname_ffi))
-
- if libraryname is not None:
- libname_ffi = self.ffi.new("char[]",bytes(libraryname,"utf-8"))
- else:
- libname_ffi = self.ffi.new("char[]",bytes("\x00\x00\x00\x00","utf-8"))
- self.ffi.memmove(sh.section,libname_ffi,len(libname_ffi))
-
- #sh.id #not used here
- sh.enabled = True
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- return r
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- return None
-
- sr_hook_cb_type = self.ffi.callback("void (struct hook_symbol_resolve *sh, struct symbol s, target_ulong asid)")
- sr_hook_cb_ptr = sr_hook_cb_type(_run_and_catch)
- sh.cb = sr_hook_cb_ptr
- hook_ptr = self.plugins['dynamic_symbols'].hook_symbol_resolution(sh)
- self.sr_hooks.append((sh, sr_hook_cb_ptr, hook_ptr))
-
- def wrapper(*args, **kw):
- _run_and_catch(args,kw)
- return wrapper
- return decorator
-
def hook_virt_mem_read(self, start_address, end_address, on_before=True, on_after=False)
Decorator to hook virtual memory reads with the mem_hooks plugin
-
-
-Expand source code
-
-def hook_virt_mem_read(self, start_address, end_address, on_before=True, on_after=False):
- '''
- Decorator to hook virtual memory reads with the mem_hooks plugin
- '''
- return self._hook_mem(start_address,end_address,on_before,on_after, True, False, True, False, True)
-
def hook_virt_mem_write(self, start_address, end_address, on_before=True, on_after=False)
Decorator to hook virtual memory writes with the mem_hooks plugin
-
-
-Expand source code
-
-def hook_virt_mem_write(self, start_address, end_address, on_before=True, on_after=False):
- '''
- Decorator to hook virtual memory writes with the mem_hooks plugin
- '''
- return self._hook_mem(start_address,end_address,on_before,on_after, False, True, True, False, True)
-
def hypercall(self, magic)
-
-
-Expand source code
-
-def hypercall(self, magic):
- def decorator(fun):
- hypercall_cb_type = self.ffi.callback("hypercall_t")
-
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- return r
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- return None
-
- hook_cb_passed = hypercall_cb_type(_run_and_catch)
- if type(magic) is int:
- self.plugins['hypercaller'].register_hypercall(magic, hook_cb_passed)
- elif type(magic) is list:
- for m in magic:
- if type(m) is int:
- self.plugins['hypercaller'].register_hypercall(m, hook_cb_passed)
- else:
- raise TypeError("Magic list must consist of integers")
- else:
- raise TypeError("Magics must be either an int or list of ints")
-
- def wrapper(*args, **kw):
- _run_and_catch(args,kw)
- self.hypercalls[wrapper] = [hook_cb_passed,magic]
- return wrapper
- return decorator
-
def in_kernel(self, cpustate)
@@ -7988,17 +6147,6 @@ Returns
Returns true if the processor is in the privilege level corresponding to kernel mode for any of the PANDA supported architectures.
Legacy alias for in_kernel_mode().
-
-
-Expand source code
-
-def in_kernel(self, cpustate):
- '''
- Returns true if the processor is in the privilege level corresponding to kernel mode for any of the PANDA supported architectures.
- Legacy alias for in_kernel_mode().
- '''
- return self.libpanda.panda_in_kernel_external(cpustate)
-
def in_kernel_code_linux(self, cpustate)
@@ -8015,22 +6163,6 @@ Returns
Bool
If the processor is running in Linux kernel space code.
-
-
-Expand source code
-
-def in_kernel_code_linux(self, cpustate):
- '''
- Check if the processor is running in linux kernelspace.
-
- Args:
- cpu (CPUState): CPUState structure
-
- Returns:
- Bool: If the processor is running in Linux kernel space code.
- '''
- return self.libpanda.panda_in_kernel_code_linux_external(cpustate)
-
def in_kernel_mode(self, cpustate)
@@ -8048,23 +6180,6 @@ Returns
If the processor is in the privilege level corresponding to kernel mode
for the given architecture
-
-
-Expand source code
-
-def in_kernel_mode(self, cpustate):
- '''
- Check if the processor is running in priviliged mode.
-
- Args:
- cpu (CPUState): CPUState structure
-
- Returns:
- Bool: If the processor is in the privilege level corresponding to kernel mode
- for the given architecture
- '''
- return self.libpanda.panda_in_kernel_mode_external(cpustate)
-
def interact(self, confirm_quit=True)
@@ -8076,53 +6191,12 @@ Returns
that directly renders output to the user. Then we don't have to handle
buffering and other problems. But we will need to re-enable the serial_console
interface after this returns
-
-
-Expand source code
-
-@blocking
-def interact(self, confirm_quit=True):
- '''
- Expose console interactively until user types pandaquit
- Must be run in blocking thread.
-
- TODO: This should probably repace self.serial_console with something
- that directly renders output to the user. Then we don't have to handle
- buffering and other problems. But we will need to re-enable the serial_console
- interface after this returns
- '''
- print("PANDA: entering interactive mode. Type pandaquit to exit")
- prompt = self.expect_prompt.decode("utf8") if self.expect_prompt and isinstance(self.expect_prompt, bytes) else "$ "
- if not prompt.endswith(" "): prompt += " "
- while True:
- cmd = input(prompt) # TODO: Strip all control characters - Ctrl-L breaks things
- if cmd.strip() == 'pandaquit':
- if confirm_quit:
- q = input("PANDA: Quitting interactive mode. Are you sure? (y/n) ")
- if len(q) and q.lower()[0] == 'y':
- break
- else:
- continue
- else: # No confirm - just break
- break
- r = self.run_serial_cmd(cmd) # XXX: may timeout
- print(r)
-
def is_callback_enabled(self, name)
-
-
-Expand source code
-
-def is_callback_enabled(self, name):
- if name not in self.registered_callbacks.keys():
- raise RuntimeError("No callback has been registered with name '{}'".format(name))
- return self.registered_callbacks[name]['enabled']
-
def load_plugin(self, name, args={})
@@ -8138,151 +6212,18 @@ Args
Returns
None.
-
-
-Expand source code
-
-def load_plugin(self, name, args={}):
- '''
- Load a C plugin, optionally with arguments
-
- Args:
- name (str): Name of plugin
- args (dict): Arguments matching key to value. e.g. {"key": "value"} sets option `key` to `value`.
-
- Returns:
- None.
- '''
- if debug:
- progress ("Loading plugin %s" % name),
-
- argstrs_ffi = []
- if isinstance(args, dict):
- for k,v in args.items():
- this_arg_s = "{}={}".format(k,v)
- this_arg = self.ffi.new("char[]", bytes(this_arg_s, "utf-8"))
- argstrs_ffi.append(this_arg)
-
- n = len(args.keys())
- elif isinstance(args, list):
- for arg in args:
- this_arg = self.ffi.new("char[]", bytes(arg, "utf-8"))
- argstrs_ffi.append(this_arg)
- n = len(args)
-
- else:
- raise ValueError("Arguments to load plugin must be a list or dict of key/value pairs")
-
- # First set qemu_path so plugins can load (may be unnecessary after the first time)
- assert(self.panda), "Unknown location of PANDA"
- panda_name_ffi = self.ffi.new("char[]", bytes(self.panda,"utf-8"))
- self.libpanda.panda_set_qemu_path(panda_name_ffi)
-
- if len(argstrs_ffi):
- plugin_args = argstrs_ffi
- else:
- plugin_args = self.ffi.NULL
-
- charptr = self.ffi.new("char[]", bytes(name,"utf-8"))
- self.libpanda.panda_require_from_library(charptr, plugin_args, len(argstrs_ffi))
- self._load_plugin_library(name)
-
def lookup_gic(self, n)
-
-
-Expand source code
-
-def lookup_gic(self,n):
- return self.libpanda.lookup_gic(n)
-
def make_panda_file_handler(self, debug=False)
Constructs a file and file handler that volatility can't ignore to back by PANDA physical memory
-
-
-Expand source code
-
-def make_panda_file_handler(self, debug=False):
- '''
- Constructs a file and file handler that volatility can't ignore to back by PANDA physical memory
- '''
- from urllib.request import BaseHandler
- if 'PandaFileHandler' in globals(): # already initialized
- return
- panda = self
-
- class PandaFile(object):
- def __init__(self, length, panda):
- self.pos = 0
- self.length = length
- self.closed = False
- self.mode = "rb"
- self.name = "/tmp/panda.panda"
- self.panda = panda
- self.classname = type(self).__name__
-
- def readable(self):
- return self.closed
-
- def read(self, size=1):
- if self.panda.bits == 32 and self.panda.arch_name == "i386":
- data = self.panda.physical_memory_read(
- self.pos & 0xfffffff, size)
- else:
- data = self.panda.physical_memory_read(self.pos, size)
- if debug:
- print(self.classname+": Reading " +
- str(size)+" bytes from "+hex(self.pos))
- self.pos += size
- return data
-
- def peek(self, size=1):
- return self.panda.physical_memory_read(self.pos, size)
-
- def seek(self, pos, whence=0):
- if whence == 0:
- self.pos = pos
- elif whence == 1:
- self.pos += pos
- else:
- self.pos = self.length - pos
- if self.pos > self.length:
- print(self.classname+": We've gone off the deep end")
- if debug:
- print(self.classname+" Seeking to address "+hex(self.pos))
-
- def tell(self):
- return self.pos
-
- def close(self):
- self.closed = True
-
- class PandaFileHandler(BaseHandler):
- def default_open(self, req):
- if 'panda.panda' in req.full_url:
- length = panda.libpanda.ram_size
- if length > 0xc0000000:
- length += 0x40000000 # 3GB hole
- if debug:
- print(type(self).__name__ +
- ": initializing PandaFile with length="+hex(length))
- return PandaFile(length=length, panda=panda)
- else:
- return None
-
- def file_close(self):
- return True
-
- globals()["PandaFileHandler"] = PandaFileHandler
-
def map_memory(self, name, size, address)
@@ -8300,28 +6241,6 @@ Args
Returns
None
-
-
-Expand source code
-
-def map_memory(self, name, size, address):
-
- '''
- Make a new memory region.
-
- Args:
- name (str): This is an internal reference name for this region. Must be unique.
- size (int): number of bytes the region should be.
- address (int): start address of region
-
- Returns:
- None
- '''
-
- name_c = self.ffi.new("char[]", bytes(name, "utf-8"))
- size = ceil(size/1024)*1024 # Must be page-aligned
- return self.libpanda.map_memory(name_c, size, address)
-
def memory_region_add_subregion(self, mr, offset, sr)
@@ -8347,31 +6266,6 @@ Args
Returns
None
-
-
-Expand source code
-
-def memory_region_add_subregion(self, mr, offset, sr):
- '''
- Calls memory_region_add_subregion from QEMU.
- memory_region_add_subregion: Add a subregion to a container.
-
- Adds a subregion at @offset. The subregion may not overlap with other
- subregions (except for those explicitly marked as overlapping). A region
- may only be added once as a subregion (unless removed with
- memory_region_del_subregion()); use memory_region_init_alias() if you
- want a region to be a subregion in multiple locations.
-
- Args:
- mr: the region to contain the new subregion; must be a container initialized with memory_region_init().
- offset: the offset relative to @mr where @subregion is added.
- subregion: the subregion to be added.
-
- Returns:
- None
- '''
- return self.libpanda.memory_region_add_subregion(mr,offset,sr)
-
def memory_region_allocate_system_memory(self, mr, obj, name, ram_size)
@@ -8392,26 +6286,6 @@ Args
Returns
None
-
-
-Expand source code
-
-def memory_region_allocate_system_memory(self, mr, obj, name, ram_size):
- '''
- Allocates Memory region by user specificiation.
- Calls memory_region_allocation_system_memory QEMU function.
-
- Args:
- mr: MemoryRegion struct
- obj: Object struct
- name (str): Region name
- ram_size (int): RAM size
-
- Returns:
- None
- '''
- return self.libpanda.memory_region_allocate_system_memory(mr, obj, name, ram_size)
-
def memory_region_init_ram_from_file(self, mr, owner, name, size, share, path)
@@ -8433,57 +6307,18 @@ Args
share
%true if memory must be mmaped with the MAP_SHARED flag
path
-the path in which to allocate the RAM.
-errp
-pointer to Error*, to store an error if it happens.
-
-Returns
-None
-
-
-Expand source code
-
-def memory_region_init_ram_from_file(self, mr, owner, name, size, share, path):
- '''
- Calls memory_region_init_ram_from_file from QEMU.
- memory_region_init_ram_from_file: Initialize RAM memory region with a mmap-ed backend.
-
- Args:
- mr: the #MemoryRegion to be initialized.
- owner: the object that tracks the region's reference count
- name: the name of the region.
- size: size of the region.
- share: %true if memory must be mmaped with the MAP_SHARED flag
- path: the path in which to allocate the RAM.
- errp: pointer to Error*, to store an error if it happens.
-
- Returns:
- None
- '''
- return self.libpanda.memory_region_init_ram_from_file(mr, owner, name, size, share, path, self.libpanda.error_fatal)
-
+the path in which to allocate the RAM.
+errp
+pointer to Error*, to store an error if it happens.
+
+Returns
+None
def memsavep(self, file_out)
Calls QEMU memsavep on your specified python file.
-
-
-Expand source code
-
-def memsavep(self, file_out):
- '''
- Calls QEMU memsavep on your specified python file.
- '''
- # this part was largely copied from https://cffi.readthedocs.io/en/latest/ref.html#support-for-file
-
- file_out.flush() # make sure the file is flushed
- newfd = dup(file_out.fileno()) # make a copy of the file descriptor
- fileptr = self.C.fdopen(newfd, b"w")
- self.libpanda.panda_memsavep(fileptr)
- self.C.fclose(fileptr)
-
def object_class_by_name(self, name)
@@ -8495,23 +6330,6 @@ Returns
name (str): string defined by user
Returns
struct as specified by name
-
-
-Expand source code
-
-def object_class_by_name(self, name):
- '''
- Returns class as ObjectClass from name specified.
- Calls object_class_by_name QEMU function.
-
- Args
- name (str): string defined by user
-
- Returns:
- struct as specified by name
- '''
- return self.libpanda.object_class_by_name(name)
-
def object_class_get_name(self, objclass)
@@ -8523,23 +6341,6 @@ Returns
objclass: class to obtain the QOM typename for.
Returns
String QOM typename for klass.
-
-
-Expand source code
-
-def object_class_get_name(self, objclass):
- '''
- Gets String QOM typename from object class.
- Calls object_class_get_name QEMU function.
-
- Args::
- objclass: class to obtain the QOM typename for.
-
- Returns:
- String QOM typename for klass.
- '''
- return self.libpanda.object_class_get_name(objclass)
-
def object_new(self, name)
@@ -8557,26 +6358,6 @@ Args
Returns
The newly allocated and instantiated object.
-
-
-Expand source code
-
-def object_new(self, name):
- '''
- Creates a new QEMU object from typename.
- This function will initialize a new object using heap allocated memory.
- The returned object has a reference count of 1, and will be freed when
- the last reference is dropped.
- Calls object_new QEMU function.
-
- Args:
- name (str): The name of the type of the object to instantiate.
-
- Returns:
- The newly allocated and instantiated object.
- '''
- return self.libpanda.object_new(name)
-
def object_property_find(self, obj, name)
@@ -8595,25 +6376,6 @@ Args
Returns
struct ObjectProperty pointer
-
-
-Expand source code
-
-def object_property_find(self, obj, name):
- '''
- Look up a property for an object and return its #ObjectProperty if found.
- Calls object_property_find QEMU function.
-
- Args:
- obj: the object
- name: the name of the property
- errp: returns an error if this function fails
-
- Returns:
- struct ObjectProperty pointer
- '''
- return self.libpanda.object_property_find(obj,name, self.ffi.NULL)
-
def object_property_get_bool(self, obj, name)
@@ -8630,24 +6392,6 @@ Args
Returns
the value of the property, converted to a boolean, or NULL if an error occurs (including when the property value is not a bool).
-
-
-Expand source code
-
-def object_property_get_bool(self, obj, name):
- '''
- Pull boolean from object.
- Calls object_property_get_bool QEMU function.
-
- Args:
- obj: the object
- name: the name of the property
-
- Returns:
- the value of the property, converted to a boolean, or NULL if an error occurs (including when the property value is not a bool).
- '''
- return self.libpanda.object_property_get_bool(obj,name,self.libpanda.error_abort)
-
def object_property_get_int(self, obj, name)
@@ -8662,24 +6406,6 @@ Returns
Returns:
the value of the property, converted to an integer, or negative if an error occurs (including when the property value is not an integer).
-
-
-Expand source code
-
-def object_property_get_int(self, obj, name):
- '''
- Gets integer in QEMU object. Reads an integer value from this property.
- Calls object_property_get_int QEMU function.
-
- Paramaters:
- obj: the object
- name: the name of the property
-
- Returns:
- the value of the property, converted to an integer, or negative if an error occurs (including when the property value is not an integer).
- '''
- return self.libpanda.object_property_get_int(obj, name, self.libpanda.error_abort)
-
def object_property_get_link(self, obj, name)
@@ -8698,25 +6424,6 @@ Args
Returns
the value of the property, resolved from a path to an Object, or NULL if an error occurs (including when the property value is not a string or not a valid object path).
-
-
-Expand source code
-
-def object_property_get_link(self, obj, name):
- '''
- Reads an object's canonical path to a property.
- Calls object_property_get_link QEMU function.
-
- Args:
- obj: the object
- name: the name of the property
- errp: returns an error if this function fails
-
- Returns:
- the value of the property, resolved from a path to an Object, or NULL if an error occurs (including when the property value is not a string or not a valid object path).
- '''
- return self.libpanda.object_property_get_link(obj,name,self.libpanda.error_abort)
-
def object_property_set_bool(self, obj, value, name)
@@ -8730,25 +6437,6 @@ Returns
errp: returns an error if this function fails
Returns
None
-
-
-Expand source code
-
-def object_property_set_bool(self, obj, value, name):
- '''
- Writes a bool value to a property.
- Calls object_property_set_bool QEMU function.
-
- Args::
- value: the value to be written to the property
- name: the name of the property
- errp: returns an error if this function fails
-
- Returns:
- None
- '''
- return self.libpanda.object_property_set_bool(obj,value,name,self.libpanda.error_abort)
-
def object_property_set_int(self, obj, value, name)
@@ -8765,24 +6453,6 @@ Args
Returns
None
-
-
-Expand source code
-
-def object_property_set_int(self,obj, value, name):
- '''
- Set integer in QEMU object. Writes an integer value to a property.
- Calls object_property_set_int QEMU function.
-
- Args:
- value: the value to be written to the property
- name: the name of the property
-
- Returns:
- None
- '''
- return self.libpanda.object_property_set_int(obj, value, name, self.libpanda.error_abort)
-
def object_property_set_link(self, obj, val, name)
@@ -8801,41 +6471,12 @@ Args
Returns
None
-
-
-Expand source code
-
-def object_property_set_link(self, obj, val, name):
- '''
- Writes an object's canonical path to a property.
- Calls object_property_set_link QEMU function.
-
- Args:
- value: the value to be written to the property
- name: the name of the property
- errp: returns an error if this function fails
-
- Returns:
- None
- '''
- return self.libpanda.object_property_set_link(obj,val,name,self.libpanda.error_abort)
-
def panda_finish(self)
Final stage call to underlying panda_finish with initialization.
-
-
-Expand source code
-
-def panda_finish(self):
- '''
- Final stage call to underlying panda_finish with initialization.
- '''
- return self.libpanda.panda_finish()
-
def physical_memory_read(self, addr, length, fmt='bytearray')
@@ -8859,28 +6500,6 @@ Returns
Raises
ValueError if memory access fails or fmt is unsupported
-
-
-Expand source code
-
-def physical_memory_read(self, addr, length, fmt='bytearray'):
- '''
- Read guest physical memory. In the specified format. Note that the `ptrlist` format
- returns a list of integers, each of the specified architecture's pointer size.
-
- Args:
- addr (int): Address
- length (int): length of array you would like returned
- fmt (str): format for returned array. Options: 'bytearray', 'int', 'str', 'ptrlist'
-
- Returns:
- Union[bytearray, int, str, list[int]]: memory data
-
- Raises:
- ValueError if memory access fails or fmt is unsupported
- '''
- return self._memory_read(None, addr, length, physical=True, fmt=fmt)
-
def physical_memory_write(self, addr, buf)
@@ -8898,26 +6517,6 @@ Returns
None
Raises
ValueError if the call to panda.physical_memory_write fails (e.g., if you pass a pointer to an invalid memory region)
-
-
-Expand source code
-
-def physical_memory_write(self, addr, buf):
- '''
- Write guest physical memory.
-
- Args:
- addr (int): Address
- buf (bytestring): byte string to write into memory
-
- Returns:
- None
-
- Raises:
- ValueError if the call to panda.physical_memory_write fails (e.g., if you pass a pointer to an invalid memory region)
- '''
- self._memory_write(None, addr, buf, physical=True)
-
def ppp(self, plugin_name, attr, name=None, autoload=True)
@@ -8929,128 +6528,18 @@ Raises
@ppp("syscalls2", "on_sys_open_return")
def my_fun(cpu, pc, filename, flags, mode):
…
-
-
-Expand source code
-
-def ppp(self, plugin_name, attr, name=None, autoload=True):
- '''
- Decorator for plugin-to-plugin interface. Note this isn't in decorators.py
- becuase it uses the panda object.
-
- Example usage to register my_run with syscalls2 as a 'on_sys_open_return'
- @ppp("syscalls2", "on_sys_open_return")
- def my_fun(cpu, pc, filename, flags, mode):
- ...
- '''
-
- if plugin_name not in self.plugins and autoload: # Could automatically load it?
- print(f"PPP automatically loaded plugin {plugin_name}")
-
- if not hasattr(self, "ppp_registered_cbs"):
- self.ppp_registered_cbs = {}
- # We use this to traak fn_names->fn_pointers so we can later disable by name
-
- # XXX: if we don't save the cffi generated callbacks somewhere in Python,
- # they may get garbage collected even though the c-code could still has a
- # reference to them which will lead to a crash. If we stop using this to track
- # function names, we need to keep it or something similar to ensure the reference
- # count remains >0 in python
-
- def decorator(fun):
- local_name = name # We need a new varaible otherwise we have scoping issues, maybe
- if local_name is None:
- local_name = fun.__name__
-
- def _run_and_catch(*args, **kwargs): # Run function but if it raises an exception, stop panda and raise it
- if not hasattr(self, "exit_exception"):
- try:
- r = fun(*args, **kwargs)
- #print(pandatype, type(r)) # XXX Can we use pandatype to determine requried return and assert if incorrect
- #assert(isinstance(r, int)), "Invalid return type?"
- if return_type is not None:
- try:
- return self.ffi.cast(return_type, r)
- except TypeError:
- # consider throwing an exception
- return self.ffi.cast(return_type, 0)
- except Exception as e:
- # exceptions wont work in our thread. Therefore we print it here and then throw it after the
- # machine exits.
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
- # this works in all current callback cases. CFFI auto-converts to void, bool, int, and int32_t
- if return_type is not None:
- return self.ffi.cast(return_type, 0)
-
- cast_rc = self.ffi.callback(attr+"_t")(_run_and_catch) # Wrap the python fn in a c-callback.
- return_type = self.ffi.typeof(cast_rc).result
-
- if return_type.cname == "void":
- return_type = None
-
- if local_name == "<lambda>":
- local_name = f"<lambda_{self.lambda_cnt}>"
- self.lambda_cnt += 1
-
- if local_name in self.ppp_registered_cbs:
- print(f"Warning: replacing existing PPP callback '{local_name}' since it was re-registered")
- self.disable_ppp(local_name)
-
- assert (local_name not in self.ppp_registered_cbs), f"Two callbacks with conflicting name: {local_name}"
-
- # Ensure function isn't garbage collected, and keep the name->(fn, plugin_name, attr) map for disabling
- self.ppp_registered_cbs[local_name] = (cast_rc, plugin_name, attr)
-
- getattr(self.plugins[plugin_name], f'ppp_add_cb_{attr}')(cast_rc) # All PPP cbs start with this string.
- return cast_rc
- return decorator
-
def pyperiph_read_cb(self, cpu, pc, physaddr, size, val_ptr)
-
-
-Expand source code
-
-def pyperiph_read_cb(self, cpu, pc, physaddr, size, val_ptr):
- pp = self._addr_to_pyperipheral(physaddr)
- if pp is None:
- return False
-
- val = pp.read_memory(physaddr, size)
- buf = self.ffi.buffer(val_ptr, size)
-
- fmt = "{}{}".format(self._end2fmt[self.endianness], self._num2fmt[size])
-
- pack_into(fmt, buf, 0, val)
-
- return True
-
def pyperiph_write_cb(self, cpu, pc, physaddr, size, val)
-
-
-Expand source code
-
-def pyperiph_write_cb(self, cpu, pc, physaddr, size, val):
- pp = self._addr_to_pyperipheral(physaddr)
- if pp is None:
- return False
-
- pp.write_memory(physaddr, size, val)
- return True
-
def queue_async(self, f, internal=False)
@@ -9065,38 +6554,6 @@ Args
be decorated with @pandare.blocking
. You generally want to use panda.queue_blocking
over this function.
Returns
None
-
-
-Expand source code
-
-def queue_async(self, f, internal=False):
- '''
- Explicitly queue work in the asynchronous work queue.
-
- Args:
- f: A python function with no arguments to be called at a later time. The function should
- be decorated with `@pandare.blocking`. You generally want to use `panda.queue_blocking` over this function.
-
- Returns:
- None
- '''
-
- # this takes the blocking function and handles errors
- @blocking
- def wrapper():
- try:
- f()
- except Exception as e:
- if self.catch_exceptions:
- self.exit_exception = e
- self.end_analysis()
- else:
- raise e
-
- # Keep the original function name instead of replacing it with 'wrapper'
- wrapper.__name__ = f.__name__
- self.athread.queue(wrapper, internal=internal)
-
def queue_blocking(self, func, queue=True)
@@ -9129,50 +6586,6 @@ Args
Returns
None
-
-
-Expand source code
-
-def queue_blocking(self, func, queue=True):
- """
- Decorator to mark a function as `blocking`, and (by default) queue it to run asynchronously.
- This should be used to mark functions that will drive guest execution. Functions will be run
- in the order they are defined. For more precise control, use `panda.queue_async`.
-
-
- ```
- @panda.queue_blocking
- def do_something():
- panda.revert_sync('root')
- print(panda.run_serial_cmd('whoami'))
- panda.end_analysis()
- ```
-
- is equivalent to
-
- ```
- @blocking
- def run_whoami():
- panda.revert_sync('root')
- print(panda.run_serial_cmd('whoami'))
- panda.end_analysis()
-
- panda.queue_async(run_whoami)
- ```
-
- Args:
- func (function): Function to queue
- queue (bool): Should function automatically be queued
-
- Returns:
- None
-
- """
- f = blocking(func)
- if queue:
- self.queue_async(f)
- return f
-
def queue_main_loop_wait_fn(self, fn, args=[])
@@ -9180,17 +6593,6 @@ Returns
Queue a function to run at the next main loop
fn is a function we want to run, args are arguments to apss to it
-
-
-Expand source code
-
-def queue_main_loop_wait_fn(self, fn, args=[]):
- '''
- Queue a function to run at the next main loop
- fn is a function we want to run, args are arguments to apss to it
- '''
- self.main_loop_wait_fnargs.append((fn, args))
-
def read_str(self, cpu, ptr, max_length=None)
@@ -9213,36 +6615,6 @@ Returns
string
Data read from memory
-
-
-Expand source code
-
-def read_str(self, cpu, ptr, max_length=None):
- '''
- Helper to read a null-terminated string from guest memory given a pointer and CPU state
- May return an exception if the call to panda.virtual_memory_read fails (e.g., if you pass a
- pointer to an unmapped page)
-
- Args:
- cpu (CPUState): CPUState structure
- ptr (int): Pointer to start of string
- max_length (int): Optional length to stop reading at
-
- Returns:
- string: Data read from memory
-
- '''
- r = b""
- idx = 0
- while (max_length is None or idx < max_length):
- next_char = self.virtual_memory_read(cpu, ptr, 1) # If this raises an exn, don't mask it
- if next_char == b"\x00":
- break
- r += next_char
- ptr += 1
- idx += 1
- return r.decode("utf8", "ignore")
-
def record(self, recording_name, snapshot_name=None)
@@ -9257,34 +6629,10 @@ Args
Before recording starts restore to this snapshot name. Defaults to None.
Raises
-
-Exception
-- raises exception if there was an error starting recording.
-
-
-
-Expand source code
-
-def record(self, recording_name, snapshot_name=None):
- """Begins active recording with name provided.
-
- Args:
- recording_name (string): name of recording to save.
- snapshot_name (string, optional): Before recording starts restore to this snapshot name. Defaults to None.
-
- Raises:
- Exception: raises exception if there was an error starting recording.
- """
- if snapshot_name == None:
- snapshot_name_ffi = self.ffi.NULL
- else:
- snapshot_name_ffi = self.ffi.new("char[]",snapshot_name.encode())
- recording_name_ffi = self.ffi.new("char[]", recording_name.encode())
- result = self.libpanda.panda_record_begin(recording_name_ffi,snapshot_name_ffi)
- res_string_enum = self.ffi.string(self.ffi.cast("RRCTRL_ret",result))
- if res_string_enum != "RRCTRL_OK":
- raise Exception(f"record method failed with RTCTL_ret {res_string_enum} ({result})")
-
+
+Exception
+- raises exception if there was an error starting recording.
+
def record_cmd(self, guest_command, copy_directory=None, iso_name=None, setup_command=None, recording_name='recording', snap_name='root', ignore_errors=False)
@@ -9298,63 +6646,6 @@ Raises
4) Begin the recording (name controlled by recording_name)
5) Press enter in the guest to begin the command. Wait until it finishes.
6) End the recording
-
-
-Expand source code
-
-@blocking
-def record_cmd(self, guest_command, copy_directory=None, iso_name=None, setup_command=None, recording_name="recording", snap_name="root", ignore_errors=False):
- '''
- Take a recording as follows:
- 0) Revert to the specified snapshot name if one is set. By default 'root'. Set to `None` if you have already set up the guest and are ready to record with no revert
- 1) Create an ISO of files that need to be copied into the guest if copy_directory is specified. Copy them in
- 2) Run the setup_command in the guest, if provided
- 3) Type the command you wish to record but do not press enter to begin execution. This avoids the recording capturing the command being typed
- 4) Begin the recording (name controlled by recording_name)
- 5) Press enter in the guest to begin the command. Wait until it finishes.
- 6) End the recording
- '''
- # 0) Revert to the specified snapshot
- if snap_name is not None:
- self.revert_sync(snap_name) # Can't use self.revert because that would would run async and we'd keep going before the revert happens
-
- # 1) Make copy_directory into an iso and copy it into the guest - It will end up at the exact same path
- if copy_directory: # If there's a directory, build an ISO and put it in the cddrive
- # Make iso
- self.copy_to_guest(copy_directory, iso_name)
-
- # 2) Run setup_command, if provided before we start the recording (good place to CD or install, etc)
- if setup_command:
- print(f"Running setup command {setup_command}")
- r = self.run_serial_cmd(setup_command)
- print(f"Setup command results: {r}")
-
- # 3) type commmand (note we type command, start recording, finish command)
- self.type_serial_cmd(guest_command)
-
- # 4) start recording
- self.run_monitor_cmd("begin_record {}".format(recording_name))
-
- # 5) finish command
- result = self.finish_serial_cmd()
-
- if debug:
- progress("Result of `{}`:".format(guest_command))
- print("\t"+"\n\t".join(result.split("\n"))+"\n")
-
- if "No such file or directory" in result and not ignore_errors:
- print("Bad output running command: {}".format(result))
- raise RuntimeError("Command not found while taking recording")
-
- if "cannot execute binary file" in result and not ignore_errors:
- print("Bad output running command: {}".format(result))
- raise RuntimeError("Could not execute binary while taking recording")
-
- # 6) End recording
- self.run_monitor_cmd("end_record")
-
- print("Finished recording")
-
def recording_exists(self, name)
@@ -9371,67 +6662,12 @@ Returns
boolean
true if file exists, false otherwise
-
-
-Expand source code
-
-def recording_exists(self, name):
- '''
- Checks if a recording file exists on disk.
-
- Args:
- name (str): name of the recording to check for (e.g., `foo` which uses `foo-rr-snp` and `foo-rr-nondet.log`)
-
- Returns:
- boolean: true if file exists, false otherwise
- '''
- if exists(name + "-rr-snp") or rr2_contains_member(name, "snapshot"):
- return True
-
def register_callback(self, callback, function, name, enabled=True, procname=None)
-
-
-Expand source code
-
-def register_callback(self, callback, function, name, enabled=True, procname=None):
- # CB = self.callback.main_loop_wait
- # func = main_loop_wait_cb
- # name = main_loop_wait
-
- if name in self.registered_callbacks:
- print(f"Warning: replacing existing callback '{name}' since it was re-registered")
- self.delete_callback(name)
-
- cb = self.callback_dictionary[callback]
-
- # Generate a unique handle for each callback type using the number of previously registered CBs of that type added to a constant
- self.plugin_register_count += 1
- handle = self.ffi.cast('void *', self.plugin_register_count)
-
- # XXX: We should have another layer of indirection here so we can catch
- # exceptions raised during execution of the CB and abort analysis
- pcb = self.ffi.new("panda_cb *", {cb.name:function})
-
- if debug:
- progress("Registered function '{}' to run on callback {}".format(name, cb.name))
-
- self.libpanda.panda_register_callback_helper(handle, cb.number, pcb)
- self.registered_callbacks[name] = {"procname": procname, "enabled": True, "callback": cb,
- "handle": handle, "pcb": pcb, "function": function} # XXX: if function is not saved here it gets GC'd and everything breaks! Watch out!
-
- if not enabled: # Note the registered_callbacks dict starts with enabled true and then we update it to false as necessary here
- self.disable_callback(name)
-
- if "block" in cb.name and "start" not in cb.name and "end" not in cb.name:
- if not self.disabled_tb_chaining:
- print("Warning: disabling TB chaining to support {} callback".format(cb.name))
- self.disable_tb_chaining()
-
def register_cb_decorators(self)
@@ -9441,31 +6677,6 @@ Returns
XXX Don't add any other methods with names starting with 'cb_'
Callbacks can be called as @panda.cb_XYZ in which case they'll take default arguments and be named the same as the decorated function
Or they can be called as @panda.cb_XYZ(name='A', procname='B', enabled=True). Defaults: name is function name, procname=None, enabled=True unless procname set
-
-
-Expand source code
-
-def register_cb_decorators(self):
- '''
- Setup callbacks and generate self.cb_XYZ functions for cb decorators
- XXX Don't add any other methods with names starting with 'cb_'
- Callbacks can be called as @panda.cb_XYZ in which case they'll take default arguments and be named the same as the decorated function
- Or they can be called as @panda.cb_XYZ(name='A', procname='B', enabled=True). Defaults: name is function name, procname=None, enabled=True unless procname set
- '''
- for cb_name, pandatype in zip(self.callback._fields, self.callback):
- def closure(closed_cb_name, closed_pandatype): # Closure on cb_name and pandatype
- def f(*args, **kwargs):
- if len(args): # Called as @panda.cb_XYZ without ()s- no arguments to decorator but we get the function name instead
- # Call our decorator with only a name argument ON the function itself
- fun = args[0]
- return self._generated_callback(closed_pandatype, **{"name": fun.__name__})(fun)
- else:
- # Otherwise, we were called as @panda.cb_XYZ() with potential args - Just return the decorator and it's applied to the function
- return self._generated_callback(closed_pandatype, *args, **kwargs)
- return f
-
- setattr(self, 'cb_'+cb_name, closure(cb_name, pandatype))
-
def register_pyperipheral(self, object)
@@ -9473,78 +6684,18 @@ Returns
Registers a python peripheral, and the necessary attributes to the
panda-object, if not present yet.
-
-
-Expand source code
-
-def register_pyperipheral(self, object):
- """
- Registers a python peripheral, and the necessary attributes to the
- panda-object, if not present yet.
- """
-
- # if we are the first pyperipheral, register the pp-dict
- if not hasattr(self, "pyperipherals"):
- self.pyperipherals = []
- self.pyperipherals_registered_cb = False
- self._num2fmt = {1: "B", 2: "H", 4: "I", 8: "Q"}
- self._end2fmt = {"little": "<", "big": ">"}
-
- self._validate_object(object)
-
- if self.pyperipherals_registered_cb is False:
- self.register_callback(
- self.callback.unassigned_io_read,
- self.callback.unassigned_io_read(self.pyperiph_read_cb),
- "pyperipheral_read_callback",
- )
-
- self.register_callback(
- self.callback.unassigned_io_write,
- self.callback.unassigned_io_write(self.pyperiph_write_cb),
- "pyperipheral_write_callback",
- )
-
- self.pyperipherals_registered_cb = True
-
- self.pyperipherals.append(object)
-
def require(self, name)
Load a C plugin with no arguments. Deprecated. Use load_plugin
-
-
-Expand source code
-
-def require(self, name):
- '''
- Load a C plugin with no arguments. Deprecated. Use load_plugin
- '''
- self.load_plugin(name, args={})
-
def reset(self)
In the next main loop, reset to boot
-
-
-Expand source code
-
-def reset(self):
- """In the next main loop, reset to boot"""
- if debug:
- progress ("Resetting machine to start state")
-
- # Stop guest, queue up revert, then continue
- self.vm_stop()
- self.queue_main_loop_wait_fn(self.libpanda.panda_reset)
- self.queue_main_loop_wait_fn(self.libpanda.panda_cont)
-
def revert_async(self, snapshot_name)
@@ -9553,31 +6704,6 @@ Returns
Request a snapshot revert, eventually. This is fairly dangerous
because you don't know when it finishes. You should be using revert_sync
from a blocking function instead
-
-
-Expand source code
-
-def revert_async(self, snapshot_name): # In the next main loop, revert
- '''
- Request a snapshot revert, eventually. This is fairly dangerous
- because you don't know when it finishes. You should be using revert_sync
- from a blocking function instead
- '''
- if not hasattr(self, 'warned_async'):
- self.warned_async = True
- print("WARNING: panda.revert_async may be deprecated in the near future")
- if debug:
- progress ("Loading snapshot " + snapshot_name)
-
- # Stop guest, queue up revert, then continue
- timer_start = time()
- self.vm_stop()
- charptr = self.ffi.new("char[]", bytes(snapshot_name, "utf-8"))
- self.queue_main_loop_wait_fn(self.libpanda.panda_revert, [charptr])
- self.queue_main_loop_wait_fn(self.libpanda.panda_cont)
- if debug:
- self.queue_main_loop_wait_fn(self._finish_timer, [timer_start, "Loaded snapshot"])
-
def