diff --git a/arch.html b/arch.html index 52eb0d0..08943fe 100755 --- a/arch.html +++ b/arch.html @@ -3,14 +3,14 @@ - + pandare.arch API documentation - - - + + + @@ -1128,33 +1128,12 @@

Methods

Overloaded function to get aarch64 program counter. Note the PC is not stored in a general purpose register.

-
- -Expand source code - -
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

-
- -Expand source code - -
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

-
- -Expand source code - -
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 CosiGuestPointers pointing to task_structs.

-
- -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

- - + + - - - - -
-

-
    -
    -
    -

    Search results provided by Lunr.js

    -
    - - - - - \ 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

    -
    - -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)
    -
    @@ -790,8 +704,8 @@

    Methods

    - - + + - + + + @@ -386,41 +386,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
    -
    @@ -503,16 +474,6 @@

    Methods

    Run guest

    -
    - -Expand source code - -
    def run_guest(self):
    -    '''
    -    Run guest
    -    '''
    -    self.panda.run()
    -

    Inherited members

    @@ -538,8 +499,8 @@

    Inherited members

    - - + + - + + + @@ -400,104 +400,6 @@

    Functions

    -
    - -Expand source code - -
    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}")
    -
    @@ -600,21 +502,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

    @@ -644,8 +531,8 @@

    Inherited members

    - - + + - + + + @@ -429,39 +429,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
    -
    @@ -478,8 +457,8 @@

    Methods

    - - + + - + + + @@ -580,12 +580,6 @@

    Returns

    none
    -
    - -Expand source code - -
    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_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 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 revert_sync(self, snapshot_name) @@ -9593,34 +6719,6 @@

    Returns

    String
    error message. Empty on success.
    -
    - -Expand source code - -
    @blocking
    -def revert_sync(self, snapshot_name):
    -    '''
    -    Args:
    -        snapshot_name: name of snapshot in the current qcow to load
    -
    -    Returns:
    -        String: error message. Empty on success.
    -    '''
    -    result = self.run_monitor_cmd("loadvm {}".format(snapshot_name))
    -    # On success we should get no result
    -
    -    if result.startswith("Length mismatch"):
    -        raise RuntimeError("QEMU machine's RAM size doesn't match snapshot RAM size!")
    -
    -    if "does not have the requested snapshot" in result:
    -        raise ValueError(f"Snapshot '{snapshot_name}' not present in {self.qcow}")
    -
    -    result = result.strip()
    -    if len(result):
    -        warn(f"snapshot load returned error {result}")
    -
    -    return result
    -
    def rr_get_guest_instr_count(self) @@ -9632,19 +6730,6 @@

    Returns

    int
    Current instruction count
    -
    - -Expand source code - -
    def rr_get_guest_instr_count(self):
    -    '''
    -    Returns record/replay guest instruction count.
    -
    -    Returns:
    -        int: Current instruction count
    -    '''
    -    return self.libpanda.rr_get_guest_instr_count_external()
    -
    def run(self) @@ -9660,78 +6745,12 @@

    Returns

    None
    When emulation has finished due to guest termination, replay conclusion or a call to Panda.end_analysis()
    -
    - -Expand source code - -
    def run(self):
    -    '''
    -    This function starts our running PANDA instance from Python. At termination this function returns and the script continues to run after it.
    -
    -    This function starts execution of the guest. It blocks until guest finishes.
    -    It also initializes panda object, clears main_loop_wait fns, and sets up internal callbacks.
    -
    -    Args:
    -        None
    -
    -    Returns:
    -        None: When emulation has finished due to guest termination, replay conclusion or a call to `Panda.end_analysis`
    -    '''
    -
    -    if len(self.main_loop_wait_fnargs):
    -        if debug:
    -            print("Clearing prior main_loop_wait fns:", self.main_loop_wait_fnargs)
    -        self.main_loop_wait_fnargs = [] # [(fn, args), ...]
    -
    -    self.ending = False
    -
    -    if debug:
    -        progress ("Running")
    -
    -    self.initializing.set()
    -    if not self._initialized_panda:
    -        self._initialize_panda()
    -    self.initializing.clear()
    -
    -    if not self.started.is_set():
    -        self.started.set()
    -
    -    self.athread.ending = False
    -
    -    # Ensure our internal CBs are always enabled
    -    self.enable_internal_callbacks()
    -    self._setup_internal_signal_handler()
    -    self.running.set()
    -    self.libpanda.panda_run() # Give control to panda
    -    self.running.clear() # Back from panda's execution (due to shutdown or monitor quit)
    -    self.unload_plugins() # Unload pyplugins and C plugins
    -    self.delete_callbacks() # Unload any registered callbacks
    -    self.plugins = plugin_list(self)
    -    # Write PANDALOG, if any
    -    #self.libpanda.panda_cleanup_record()
    -    if self._in_replay:
    -        self.reset()
    -    if hasattr(self, "exit_exception"):
    -        saved_exception = self.exit_exception
    -        del self.exit_exception
    -        raise saved_exception
    -
    def run_monitor_cmd(self, cmd)
    -
    - -Expand source code - -
    @blocking
    -def run_monitor_cmd(self, cmd):
    -    self.monitor_console.sendline(cmd.encode("utf8"))
    -    result = self.monitor_console.expect()
    -    return result
    -
    def run_replay(self, replaypfx) @@ -9745,34 +6764,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def run_replay(self, replaypfx):
    -    '''
    -    Load a replay and run it. Starts PANDA execution and returns after end of VM execution.
    -
    -    Args:
    -        replaypfx (str): Replay name/path (e.g., "foo" or "./dir/foo")
    -
    -    Returns:
    -        None
    -    '''
    -    if (not isfile(replaypfx+"-rr-snp") or not isfile(replaypfx+"-rr-nondet.log")) and not rr2_recording(replaypfx):
    -        raise ValueError("Replay files not present to run replay of {}".format(replaypfx))
    -
    -    self.ending = False
    -
    -    if debug:
    -        progress ("Replaying %s" % replaypfx)
    -
    -    charptr = self.ffi.new("char[]",bytes(replaypfx,"utf-8"))
    -    self.libpanda.panda_replay_begin(charptr)
    -    self._in_replay = True
    -    self.run()
    -    self._in_replay = False
    -
    def run_serial_cmd(self, cmd, no_timeout=False, timeout=None) @@ -9794,39 +6785,6 @@

    Returns

    String
    all the output (stdout + stderr) printed after typing your command and pressing enter until the next prompt was printed.
    -
    - -Expand source code - -
    @blocking
    -def run_serial_cmd(self, cmd, no_timeout=False, timeout=None):
    -    '''
    -    Run a command inside the guest through a terminal exposed over a serial port. Can only be used if your guest is configured in this way
    -
    -    Guest output will be analyzed until we see the expect_prompt regex printed (i.e., the PS1 prompt)
    -
    -    Args:
    -        cmd: command to run.
    -        timeout: maximum time to wait for the command to finish
    -        no_timeout: if set, don't ever timeout
    -
    -    Returns:
    -        String: all the output (stdout + stderr) printed after typing your command and pressing enter until the next prompt was printed.
    -    '''
    -
    -    if timeout is None:
    -        timeout = 30
    -
    -    if self.serial_console is None:
    -        raise RuntimeError("Cannot run serial commands without providing PANDA an expect_prompt")
    -    self.running.wait() # Can only run serial when guest is running
    -    self.serial_console.sendline(cmd.encode("utf8"))
    -    if no_timeout:
    -        result = self.serial_console.expect(timeout=9999) # "Don't ever timeout" above is a bit of an exaggeration
    -    else:
    -        result = self.serial_console.expect(timeout=timeout)
    -    return result
    -
    def run_serial_cmd_async(self, cmd, delay=1) @@ -9834,82 +6792,18 @@

    Returns

    Type a command and press enter in the guest. Return immediately. No results available Only use this if you know what you're doing!

    -
    - -Expand source code - -
    @blocking
    -def run_serial_cmd_async(self, cmd, delay=1):
    -    '''
    -    Type a command and press enter in the guest. Return immediately. No results available
    -    Only use this if you know what you're doing!
    -    '''
    -    self.running.wait() # Can only run serial when guest is running
    -    self.serial_console.sendline(cmd.encode("utf8"))
    -    if delay:
    -        sleep(delay) # Ensure it has a chance to run
    -
    def run_volatility(self, plugin, debug=False)
    -
    - -Expand source code - -
    def run_volatility(self, plugin, debug=False):
    -    try:
    -        from .volatility_cli_classes import CommandLineRunFullCommand, StringTextRenderer
    -    except ImportError:
    -        print("Warning: Failed to import volatility")
    -        return None
    -    self.make_panda_file_handler(debug=debug)
    -    cmd = CommandLineRunFullCommand().run("-q -f panda.panda " + plugin)
    -    output = StringTextRenderer().render(cmd.run())
    -    return output
    -
    def serial_read_until(self, byte_sequence)
    -
    - -Expand source code - -
    @blocking
    -def serial_read_until(self, byte_sequence):
    -    if len(self.serial_unconsumed_data) > 0:
    -        found_idx = self.serial_unconsumed_data.find(byte_sequence)
    -        if found_idx >= 0:
    -            match = self.serial_unconsumed_data[ : found_idx]
    -            self.serial_unconsumed_data = self.serial_unconsumed_data[found_idx + 1 : ]
    -            return match
    -    while self.serial_socket != None:
    -        try:
    -            readable, _, _ = select.select([self.serial_socket], [], [], 0.5)
    -            if len(readable) == 0:
    -                continue
    -            data = self.serial_socket.recv(65535)
    -        except Exception as e:
    -            if '[Errno 11]' in str(e) or '[Errno 35]' in str(e):
    -                # EAGAIN
    -                continue
    -            raise Exception("Data Read Error: {}".format(e.message))
    -        if not data:
    -            raise Exception('Connection Closed by Server')
    -
    -        self.serial_unconsumed_data += data
    -        found_idx = self.serial_unconsumed_data.find(byte_sequence)
    -        if found_idx >= 0:
    -            match = self.serial_unconsumed_data[ : found_idx]
    -            self.serial_unconsumed_data = self.serial_unconsumed_data[found_idx + 1 : ]
    -            return match
    -    return None
    -
    def set_breakpoint(self, cpu, pc) @@ -9917,18 +6811,6 @@

    Returns

    Set a GDB breakpoint such that when the guest hits PC, execution is paused and an attached GDB instance can introspect on guest memory. Requires starting panda with -s, at least for now

    -
    - -Expand source code - -
    def set_breakpoint(self, cpu, pc):
    -    '''
    -    Set a GDB breakpoint such that when the guest hits PC, execution is paused and an attached
    -    GDB instance can introspect on guest memory. Requires starting panda with -s, at least for now
    -    '''
    -    BP_GDB = 0x10
    -    self.libpanda.cpu_breakpoint_insert(cpu, pc, BP_GDB, self.ffi.NULL)
    -
    def set_os_name(self, os_name) @@ -9950,33 +6832,6 @@

    Returns

    Returns: None
    -
    - -Expand source code - -
    def set_os_name(self, os_name):
    -    """
    -    Set OS target. Equivalent to "-os" flag on the command line. Matches the form of:
    -
    -        "windows[-_]32[-_]xpsp[23]",
    -        "windows[-_]32[-_]2000",
    -        "windows[-_]32[-_]7sp[01]",
    -        "windows[-_]64[-_]7sp[01]",
    -        "linux[-_]32[-_].+",
    -        "linux[-_]64[-_].+",
    -        "freebsd[-_]32[-_].+",
    -        "freebsd[-_]64[-_].+",
    -
    -        Args:
    -            os_name (str): Name that matches the format for the os flag.
    -
    -        Returns:
    -            None
    -    """
    -    print ("os_name=[%s]" % os_name)
    -    os_name_new = self.ffi.new("char[]", bytes(os_name, "utf-8"))
    -    self.libpanda.panda_set_os_name(os_name_new)
    -
    def set_pandalog(self, name) @@ -9990,23 +6845,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def set_pandalog(self, name):
    -    '''
    -    Enable recording to a pandalog (plog) named `name`
    -
    -    Args:
    -        name (str): filename to output data to
    -
    -    Returns:
    -        None
    -    '''
    -    charptr = self.ffi.new("char[]", bytes(name, "utf-8"))
    -    self.libpanda.panda_start_pandalog(charptr)
    -
    def snap(self, snapshot_name) @@ -10020,32 +6858,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def snap(self, snapshot_name):
    -    '''
    -    Create snapshot with specified name
    -
    -    Args:
    -        snapshot_name (str): name of the snapshot
    -
    -    Returns:
    -        None
    -    '''
    -    if debug:
    -        progress ("Creating snapshot " + snapshot_name)
    -
    -    # Stop guest execution, queue up a snapshot, 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_snap, [charptr])
    -    self.queue_main_loop_wait_fn(self.libpanda.panda_cont)
    -    if debug:
    -        self.queue_main_loop_wait_fn(self._finish_timer, [timer_start, "Saved snapshot"])
    -
    def stop_run(self) @@ -10055,70 +6867,24 @@

    Returns

    In other words, once this is called, panda.run() will finish and your main thread will continue. If you also want to unload plugins, use end_analysis instead

    XXX: This doesn't work in replay mode

    -
    - -Expand source code - -
    @blocking
    -def stop_run(self):
    -    '''
    -    From a blocking thread, request vl.c loop to break. Returns control flow in main thread.
    -    In other words, once this is called, panda.run() will finish and your main thread will continue.
    -    If you also want to unload plugins, use end_analysis instead
    -
    -    XXX: This doesn't work in replay mode
    -    '''
    -    self.libpanda.panda_break_vl_loop_req = True
    -
    def string_to_condition(self, string: str)
    -
    - -Expand source code - -
    def string_to_condition(self, string: str):
    -    s = self.string_to_solver(string)
    -    asrts = s.assertions()
    -    if len(asrts) == 0:
    -        return None 
    -    return asrts[0]
    -
    def string_to_expr(self, string: str)
    -
    - -Expand source code - -
    def string_to_expr(self, string: str):
    -    eq = self.string_to_condition(string)
    -    if eq and len(eq.children()) > 0:
    -        return eq.children()[0]
    -    return None
    -
    def string_to_solver(self, string: str)
    -
    - -Expand source code - -
    def string_to_solver(self, string: str):
    -    from z3 import Solver
    -    s = Solver()
    -    s.from_string(string)
    -    return s
    -
    def sysbus_create_varargs(self, name, addr) @@ -10133,111 +6899,37 @@

    Args

    hwaddr

    Returns

    -

    DeviceState struct

    -
    - -Expand source code - -
    def sysbus_create_varargs(self, name, addr):
    -    '''
    -    Returns DeviceState struct from user specified information
    -    Calls sysbus_create_varargs QEMU function.
    -
    -    Args:
    -        name (str):
    -        addr (int): hwaddr
    -
    -    Returns:
    -        DeviceState struct
    -    '''
    -    return self.libpanda.sysbus_create_varargs(name,addr, self.ffi.NULL)
    -
    +

    DeviceState struct

    def taint_check_laddr(self, addr, off)

    returns boolean result checking if this laddr is tainted

    -
    - -Expand source code - -
    def taint_check_laddr(self, addr, off):
    -    '''
    -    returns boolean result checking if this laddr is tainted
    -    '''
    -    self._assert_taint_enabled()
    -    return self.plugins['taint2'].taint2_query_laddr(addr, off) > 0
    -
    def taint_check_ram(self, addr)

    returns boolean representing if physical address is tainted.

    -
    - -Expand source code - -
    def taint_check_ram(self, addr):
    -    '''
    -    returns boolean representing if physical address is tainted.
    -    '''
    -    self._assert_taint_enabled()
    -    return self.plugins['taint2'].taint2_query_ram(addr) > 0
    -
    def taint_check_reg(self, reg_num)

    Checks if register reg_num is tainted. Returns boolean.

    -
    - -Expand source code - -
    def taint_check_reg(self, reg_num):
    -    '''
    -    Checks if register reg_num is tainted. Returns boolean.
    -    '''
    -    self._assert_taint_enabled()
    -    for offset in range(self.register_size):
    -        if self.plugins['taint2'].taint2_query_reg(reg_num, offset) > 0:
    -            return True
    -    return False
    -
    def taint_enable(self)

    Enable taint.

    -
    - -Expand source code - -
    def taint_enable(self):
    -    '''
    -    Enable taint.
    -    '''
    -    self.plugins["taint2"].taint2_enable_taint()
    -
    def taint_enabled(self)

    Checks to see if taint2 plugin has been loaded

    -
    - -Expand source code - -
    def taint_enabled(self):
    -    '''
    -    Checks to see if taint2 plugin has been loaded
    -    '''
    -    return self._plugin_loaded("taint2") and self.plugins["taint2"].taint2_enabled()
    -
    def taint_get_laddr(self, addr, offset) @@ -10246,24 +6938,6 @@

    Returns

    returns array of results, one for each byte in this laddr None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_laddr(self, addr, offset):
    -    '''
    -    returns array of results, one for each byte in this laddr
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    if self.plugins['taint2'].taint2_query_laddr(addr, offset) > 0:
    -        query_res = self.ffi.new("QueryResult *")
    -        self.plugins['taint2'].taint2_query_laddr_full(addr, offset, query_res)
    -        tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -        return tq
    -    else:
    -        return None
    -
    def taint_get_ram(self, addr) @@ -10272,24 +6946,6 @@

    Returns

    returns array of results, one for each byte in this register None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_ram(self, addr):
    -    '''
    -    returns array of results, one for each byte in this register
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    if self.plugins['taint2'].taint2_query_ram(addr) > 0:
    -        query_res = self.ffi.new("QueryResult *")
    -        self.plugins['taint2'].taint2_query_ram_full(addr, query_res)
    -        tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -        return tq
    -    else:
    -        return None
    -
    def taint_get_reg(self, reg_num) @@ -10298,224 +6954,60 @@

    Returns

    Returns array of results, one for each byte in this register None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_reg(self, reg_num):
    -    '''
    -    Returns array of results, one for each byte in this register
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    res = []
    -    for offset in range(self.register_size):
    -        if self.plugins['taint2'].taint2_query_reg(reg_num, offset) > 0:
    -            query_res = self.ffi.new("QueryResult *")
    -            self.plugins['taint2'].taint2_query_reg_full(reg_num, offset, query_res)
    -            tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -            res.append(tq)
    -        else:
    -            res.append(None)
    -    return res
    -
    def taint_label_ram(self, addr, label)

    Labels ram at address with label.

    -
    - -Expand source code - -
    def taint_label_ram(self, addr, label):
    -    '''
    -    Labels ram at address with label.
    -    '''
    -    self._assert_taint_enabled()
    -    self.plugins["taint2"].taint2_label_ram(addr, label)
    -
    def taint_label_reg(self, reg_num, label)

    Labels taint register reg_num with label.

    -
    - -Expand source code - -
    def taint_label_reg(self, reg_num, label):
    -    '''
    -    Labels taint register reg_num with label.
    -    '''
    -    self._assert_taint_enabled()
    -    for i in range(self.register_size):
    -        self.plugins["taint2"].taint2_label_reg(reg_num, i, label)
    -
    def taint_sym_branch_meta(self)
    -
    - -Expand source code - -
    def taint_sym_branch_meta(self):
    -    branch_meta_ptr_ffi = self.ffi.new('SymbolicBranchMeta **')
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_branch_meta(n_ptr_ffi, branch_meta_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return []
    -    meta_ptr = self.ffi.unpack(branch_meta_ptr_ffi, 1)[0]
    -    metas_ffi = self.ffi.unpack(meta_ptr, n)
    -    # Meta only has a pc field now
    -    metas = [
    -        meta_ffi.pc
    -        for meta_ffi in metas_ffi
    -    ]
    -    return metas
    -
    def taint_sym_enable(self)

    Inform python that taint is enabled.

    -
    - -Expand source code - -
    def taint_sym_enable(self):
    -    """
    -    Inform python that taint is enabled.
    -    """
    -    if not self.taint_enabled():
    -        self.taint_enable()
    -        progress("taint symbolic not enabled -- enabling")
    -    self.plugins["taint2"].taint2_enable_sym()
    -
    def taint_sym_label_ram(self, addr, label)
    -
    - -Expand source code - -
    def taint_sym_label_ram(self, addr, label):
    -    self._assert_taint_sym_enabled()
    -    self.plugins['taint2'].taint2_sym_label_ram(addr,label)
    -
    def taint_sym_label_reg(self, reg_num, label)
    -
    - -Expand source code - -
    def taint_sym_label_reg(self, reg_num, label):
    -    # label all bytes in this register.
    -    # or at least four of them
    -    # XXX label must increment by panda.register_size after the call
    -    self._assert_taint_sym_enabled()
    -    self.taint_sym_enable()
    -    for i in range(self.register_size):
    -        self.plugins['taint2'].taint2_sym_label_reg(reg_num, i, label+i)
    -
    def taint_sym_path_constraints(self)
    -
    - -Expand source code - -
    def taint_sym_path_constraints(self):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_path_constraints(n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return []
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    solver = self.string_to_solver(expr_str)
    -    return solver.assertions() if solver != None else []
    -
    def taint_sym_query_ram(self, addr, size=1)
    -
    - -Expand source code - -
    def taint_sym_query_ram(self, addr, size=1):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_query_ram(addr, size, n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return None
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    return self.string_to_expr(expr_str)
    -
    def taint_sym_query_reg(self, addr)
    -
    - -Expand source code - -
    def taint_sym_query_reg(self, addr):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_query_reg(addr, n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return None
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    return self.string_to_expr(expr_str)
    -
    def to_unsigned_guest(self, x) @@ -10533,46 +7025,12 @@

    Returns

    int
    Python integer representing x as an unsigned value in the guest's pointer-size.
    -
    - -Expand source code - -
    def to_unsigned_guest(self, x):
    -    '''
    -    Convert a singed python int to an unsigned int32/unsigned int64
    -    depending on guest bit-size
    -
    -    Args:
    -        x (int): Python integer
    -
    -    Returns:
    -        int: Python integer representing x as an unsigned value in the guest's pointer-size.
    -    '''
    -    import ctypes
    -    if self.bits == 32:
    -        return ctypes.c_uint32(x).value
    -    elif self.bits == 64:
    -        return ctypes.c_uint64(x).value
    -    else:
    -        raise ValueError("Unsupported number of bits")
    -
    def type_serial_cmd(self, cmd)
    -
    - -Expand source code - -
    @blocking
    -def type_serial_cmd(self, cmd):
    -    #Can send message into socket without guest running (no self.running.wait())
    -    if isinstance(cmd, str):
    -        cmd = cmd.encode('utf8')
    -    self.serial_console.send(cmd) # send, not sendline
    -
    def unload_plugin(self, name) @@ -10586,25 +7044,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def unload_plugin(self, name):
    -    '''
    -    Unload plugin with given name.
    -
    -    Args:
    -        name (str): Name of plug
    -
    -    Returns:
    -        None
    -    '''
    -    if debug:
    -        progress ("Unloading plugin %s" % name),
    -    name_ffi = self.ffi.new("char[]", bytes(name,"utf-8"))
    -    self.libpanda.panda_unload_plugin_by_name(name_ffi)
    -
    def unload_plugins(self) @@ -10615,28 +7054,6 @@

    Returns

    XXX: If called during shutdown/exit, c plugins won't be unloaded because the next main_loop_wait will never happen. Instead, call panda.panda_finish directly (which is done at the end of panda.run())

    -
    - -Expand source code - -
    def unload_plugins(self):
    -    '''
    -    Disable all python plugins and request to unload all c plugins
    -    at the next main_loop_wait.
    -
    -    XXX: If called during shutdown/exit, c plugins won't be unloaded
    -    because the next main_loop_wait will never happen. Instead, call
    -    panda.panda_finish directly (which is done at the end of panda.run())
    -    '''
    -    if debug:
    -        progress ("Disabling all python plugins, unloading all C plugins")
    -
    -    # In next main loop wait, unload all python plugin
    -    self.queue_main_loop_wait_fn(self._unload_pyplugins)
    -
    -    # Then unload C plugins. May be unsafe to do except from the top of the main loop (taint segfaults otherwise)
    -    self.queue_main_loop_wait_fn(self.libpanda.panda_unload_plugins)
    -
    def unregister_pyperipheral(self, pyperiph) @@ -10645,35 +7062,6 @@

    Returns

    deregisters a python peripheral. The pyperiph parameter can be either an object, or an address Returns true if the pyperipheral was successfully removed, else false.

    -
    - -Expand source code - -
    def unregister_pyperipheral(self, pyperiph):
    -    """
    -    deregisters a python peripheral.
    -    The pyperiph parameter can be either an object, or an address
    -    Returns true if the pyperipheral was successfully removed, else false.
    -    """
    -
    -    if isinstance(pyperiph, int) is True:
    -        pp = self._addr_to_pyperipheral(pyperiph)
    -        if pp is None:
    -            return False
    -    else:
    -        if pyperiph not in self.pyperipherals:
    -            return False
    -        pp = pyperiph
    -
    -    self.pyperipherals.remove(pp)
    -
    -    # If we dont have any pyperipherals left, unregister callbacks
    -    if len(self.pyperipherals) == 0:
    -        self.disable_callback("pyperipheral_read_callback", forever=True)
    -        self.disable_callback("pyperipheral_write_callback", forever=True)
    -        self.pyperipherals_registered_cb = False
    -    return True
    -
    def virt_to_phys(self, cpu, addr) @@ -10689,26 +7077,6 @@

    Args

    Return

    int: physical address

    -
    - -Expand source code - -
    def virt_to_phys(self, cpu, addr):
    -    '''
    -    Convert virtual address to physical address.
    -
    -    Args:
    -        cpu (CPUState): CPUState struct
    -        addr (int): virtual address to convert
    -
    -    Return:
    -        int: physical address
    -    '''
    -    if "osi_linux" in self.plugins.keys() or self._plugin_loaded("osi_linux"):
    -        return self.plugins["osi_linux"].osi_linux_virt_to_phys(cpu, addr)
    -    else:
    -        return self.libpanda.panda_virt_to_phys_external(cpu, addr)
    -
    def virtual_memory_read(self, cpu, addr, length, fmt='bytearray') @@ -10733,29 +7101,6 @@

    Returns

    Raises

    ValueError if memory access fails or fmt is unsupported

    -
    - -Expand source code - -
    def virtual_memory_read(self, cpu, addr, length, fmt='bytearray'):
    -    '''
    -    Read guest virtual memory.
    -
    -    Args:
    -        cpu (CPUState): CPUState structure
    -        addr (int): Address
    -        length (int): length of data you would like returned
    -        fmt: format for returned array. See `physical_memory_read`.
    -
    -    Returns:
    -        Union[bytearray, int, str, list[int]]: memory data
    -
    -    Raises:
    -        ValueError if memory access fails or fmt is unsupported
    -    '''
    -
    -    return self._memory_read(cpu, addr, length, physical=False, fmt=fmt)
    -
    def virtual_memory_write(self, cpu, addr, buf) @@ -10775,57 +7120,18 @@

    Returns

    None

    Raises

    ValueError if the call to panda.virtual_memory_write fails (e.g., if you pass a pointer to an unmapped page)

    -
    - -Expand source code - -
    def virtual_memory_write(self, cpu, addr, buf):
    -    '''
    -    Write guest virtual memory.
    -
    -    Args:
    -        cpu (CPUState): CPUState structure
    -        address (int): Address
    -        buf (bytestr): byte string to write into memory
    -
    -    Returns:
    -        None
    -
    -    Raises:
    -        ValueError if the call to panda.virtual_memory_write fails (e.g., if you pass a pointer to an unmapped page)
    -    '''
    -    self._memory_write(cpu, addr, buf, physical=False)
    -
    def vm_stop(self, code=4)

    Stop execution, default code means RUN_STATE_PAUSED

    -
    - -Expand source code - -
    def vm_stop(self, code=4):
    -    ''' Stop execution, default code means RUN_STATE_PAUSED '''
    -    self.libpanda.panda_stop(code)
    -
    def was_aborted(self)

    Returns true if panda was aborted.

    -
    - -Expand source code - -
    def was_aborted(self):
    -    '''
    -    Returns true if panda was aborted.
    -    '''
    -    return self.libpanda.panda_was_aborted()
    -
    @@ -11013,36 +7319,6 @@

    Static methods

    def __init__(self, panda): print(self.ppp.Server.do_add(1))
    -
    - -Expand source code - -
    @staticmethod
    -def ppp_export(method):
    -    '''
    -    Decorator to apply to a class method in a PyPlugin to indicate that other plugins should
    -    be allowed to call this function. Example:
    -
    -        from pandare import PyPlugin
    -        Class Server(PyPlugin):
    -            def __init__(self, panda):
    -                pass
    -
    -            @PyPlugin.ppp_export
    -            def do_add(self, x):
    -                return x+1
    -
    -        Class Client(PyPlugin):
    -            def __init__(self, panda):
    -                print(self.ppp.Server.do_add(1))
    -    '''
    -    @wraps(method)
    -    def f(*args, **kwargs):
    -        return method(*args, **kwargs)
    -    f.__is_pyplugin_ppp = True
    -    f.__original_method = method
    -    return f
    -

    Instance variables

    @@ -11089,56 +7365,12 @@

    Methods

    Returns either the argument as a string or None if the argument wasn't passed (arguments passed in bool form (i.e., set but with no value) instead of key/value form will also return None).

    -
    - -Expand source code - -
    def get_arg(self, arg_name):
    -    '''
    -    Returns either the argument as a string or None if the argument
    -    wasn't passed (arguments passed in bool form (i.e., set but with no value)
    -    instead of key/value form will also return None).
    -    '''
    -    if arg_name in self.args:
    -        return self.args[arg_name]
    -
    -    return None
    -
    def get_arg_bool(self, arg_name)

    Returns True if the argument is set and has a truthy value

    -
    - -Expand source code - -
    def get_arg_bool(self, arg_name):
    -    '''
    -    Returns True if the argument is set and has a truthy value
    -    '''
    -
    -    if arg_name not in self.args:
    -        # Argument name unset - it's false
    -        return False
    -
    -    arg_val = self.args[arg_name]
    -    if isinstance(arg_val, bool):
    -        # If it's a python bol already, just return it
    -        return arg_val
    -
    -    if isinstance(arg_val, str):
    -        # string of true/y/1  is True
    -        return arg_val.lower() in ['true', 'y', '1']
    -
    -    if isinstance(arg_val, int):
    -        # Nonzero is True
    -        return arg_val != 0
    -
    -    # If it's not a string, int, or bool something is weird
    -    raise ValueError(f"Unsupported arg type: {type(arg_val)}")
    -
    def ppp_cb_boilerplate(self, cb_name) @@ -11146,35 +7378,6 @@

    Methods

    "Define" a PPP-style function in this plugin. Note that there is no type information because this is Python. Run via .ppp[cb_name].run(…)

    -
    - -Expand source code - -
    def ppp_cb_boilerplate(self, cb_name):
    -    '''
    -    "Define" a PPP-style function in this plugin. Note that there is no type
    -    information because this is Python. Run via .ppp[cb_name].run(...)
    -    '''
    -    plugin_name = self.__class__.__name__
    -
    -    if cb_name in self.ppp_cbs:
    -        raise ValueError(f"PPP function {cb_name} is being redefined in {plugin_name}")
    -
    -    # Add two callbacks into our PPP namesapce: fn_add and fn_run
    -    this_ppp_cb = _PPP_CB()
    -    self.ppp.add(self.__class__.__name__, "ppp_reg_cb_" + cb_name, this_ppp_cb.add_callback)
    -    self.ppp.add(self.__class__.__name__, "ppp_run_cb_" + cb_name, this_ppp_cb.run)
    -
    -    # Make sure we have a helper self.ppp[class].ppp_reg_cb which just calls
    -    # the ppp_reg_[cb_name] we just saved
    -    try:
    -        getattr(getattr(self.ppp, self.__class__.__name__), "ppp_reg_cb")
    -    except AttributeError:
    -        def _reg_cb(target_ppp, func):
    -            getattr(getattr(self.ppp,
    -                self.__class__.__name__), "ppp_reg_cb_" + target_ppp)(func)
    -        self.ppp.add(self.__class__.__name__, "ppp_reg_cb", _reg_cb)
    -
    def ppp_run_cb(self, target_ppp, *args) @@ -11182,17 +7385,6 @@

    Methods

    Trigger a previously defind PPP-style callback named target_ppp in this plugin with args Any other pyplugins which have registered a function to run on this callback will be called with args.

    -
    - -Expand source code - -
    def ppp_run_cb(self, target_ppp, *args):
    -    '''
    -    Trigger a previously defind PPP-style callback named `target_ppp` in this plugin with `args`
    -    Any other pyplugins which have registered a function to run on this callback will be called with `args`.
    -    '''
    -    getattr(getattr(self.ppp, self.__class__.__name__), "ppp_run_cb_" + target_ppp)(*args)
    -
    @@ -11209,8 +7401,8 @@

    Methods

    - - + + - + + + @@ -7291,44 +7291,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) @@ -7336,17 +7304,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) @@ -7354,26 +7311,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) @@ -7382,51 +7319,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) @@ -7451,96 +7355,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) @@ -7557,76 +7371,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) @@ -7643,22 +7411,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) @@ -7675,24 +7427,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) @@ -7706,66 +7440,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) @@ -7779,42 +7465,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) @@ -7823,32 +7479,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) @@ -7858,75 +7488,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) @@ -7934,18 +7513,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) @@ -7959,22 +7526,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) @@ -7995,39 +7546,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) @@ -8035,37 +7553,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) @@ -8073,17 +7566,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) @@ -8093,21 +7575,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) @@ -8125,70 +7592,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) @@ -8198,21 +7613,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) @@ -8221,52 +7621,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) @@ -8274,18 +7640,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) @@ -8299,22 +7653,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) @@ -8322,36 +7660,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) @@ -8362,28 +7676,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) @@ -8395,21 +7687,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) @@ -8422,74 +7699,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) @@ -8497,17 +7724,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) @@ -8525,26 +7741,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) @@ -8558,22 +7754,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) @@ -8590,22 +7770,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) @@ -8621,41 +7785,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) @@ -8668,20 +7803,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) @@ -8695,23 +7816,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) @@ -8725,29 +7829,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) @@ -8764,22 +7845,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) @@ -8802,49 +7867,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) @@ -8862,27 +7884,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) @@ -8894,67 +7895,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) @@ -8972,26 +7924,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) @@ -9011,85 +7943,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') @@ -9097,78 +7962,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) @@ -9178,73 +7971,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) @@ -9254,22 +7980,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) @@ -9280,50 +7990,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') @@ -9350,92 +8028,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) @@ -9457,151 +8049,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) @@ -9609,17 +8074,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) @@ -9634,24 +8088,8 @@

    Args

    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)
    -
    +
    If the processor is running in Linux kernel space code.
    +
    def in_kernel_mode(self, cpustate) @@ -9669,23 +8107,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) @@ -9697,53 +8118,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={}) @@ -9759,151 +8139,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) @@ -9921,28 +8168,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) @@ -9968,31 +8193,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) @@ -10013,26 +8213,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) @@ -10060,51 +8240,12 @@

    Args

    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)
    -
    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) @@ -10116,23 +8257,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) @@ -10144,23 +8268,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) @@ -10178,26 +8285,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) @@ -10216,25 +8303,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) @@ -10251,24 +8319,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) @@ -10283,24 +8333,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_set_bool(self, obj, value, name) @@ -10351,25 +8364,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) @@ -10386,24 +8380,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 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') @@ -10480,28 +8427,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) @@ -10519,26 +8444,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) @@ -10550,128 +8455,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_read_cb(self, cpu, pc, physaddr, size, val_ptr) +
    +
    +
    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) @@ -10686,38 +8481,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) @@ -10750,50 +8513,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=[]) @@ -10801,17 +8520,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) @@ -10834,36 +8542,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) @@ -10882,30 +8560,6 @@

    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})")
    -
    def record_cmd(self, guest_command, copy_directory=None, iso_name=None, setup_command=None, recording_name='recording', snap_name='root', ignore_errors=False) @@ -10919,63 +8573,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) @@ -10992,67 +8589,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) @@ -11062,31 +8604,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) @@ -11094,78 +8611,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) @@ -11174,31 +8631,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 revert_sync(self, snapshot_name) @@ -11214,34 +8646,6 @@

    Returns

    String
    error message. Empty on success.
    -
    - -Expand source code - -
    @blocking
    -def revert_sync(self, snapshot_name):
    -    '''
    -    Args:
    -        snapshot_name: name of snapshot in the current qcow to load
    -
    -    Returns:
    -        String: error message. Empty on success.
    -    '''
    -    result = self.run_monitor_cmd("loadvm {}".format(snapshot_name))
    -    # On success we should get no result
    -
    -    if result.startswith("Length mismatch"):
    -        raise RuntimeError("QEMU machine's RAM size doesn't match snapshot RAM size!")
    -
    -    if "does not have the requested snapshot" in result:
    -        raise ValueError(f"Snapshot '{snapshot_name}' not present in {self.qcow}")
    -
    -    result = result.strip()
    -    if len(result):
    -        warn(f"snapshot load returned error {result}")
    -
    -    return result
    -
    def rr_get_guest_instr_count(self) @@ -11253,19 +8657,6 @@

    Returns

    int
    Current instruction count
    -
    - -Expand source code - -
    def rr_get_guest_instr_count(self):
    -    '''
    -    Returns record/replay guest instruction count.
    -
    -    Returns:
    -        int: Current instruction count
    -    '''
    -    return self.libpanda.rr_get_guest_instr_count_external()
    -
    def run(self) @@ -11281,78 +8672,12 @@

    Returns

    None
    When emulation has finished due to guest termination, replay conclusion or a call to Panda.end_analysis()
    -
    - -Expand source code - -
    def run(self):
    -    '''
    -    This function starts our running PANDA instance from Python. At termination this function returns and the script continues to run after it.
    -
    -    This function starts execution of the guest. It blocks until guest finishes.
    -    It also initializes panda object, clears main_loop_wait fns, and sets up internal callbacks.
    -
    -    Args:
    -        None
    -
    -    Returns:
    -        None: When emulation has finished due to guest termination, replay conclusion or a call to `Panda.end_analysis`
    -    '''
    -
    -    if len(self.main_loop_wait_fnargs):
    -        if debug:
    -            print("Clearing prior main_loop_wait fns:", self.main_loop_wait_fnargs)
    -        self.main_loop_wait_fnargs = [] # [(fn, args), ...]
    -
    -    self.ending = False
    -
    -    if debug:
    -        progress ("Running")
    -
    -    self.initializing.set()
    -    if not self._initialized_panda:
    -        self._initialize_panda()
    -    self.initializing.clear()
    -
    -    if not self.started.is_set():
    -        self.started.set()
    -
    -    self.athread.ending = False
    -
    -    # Ensure our internal CBs are always enabled
    -    self.enable_internal_callbacks()
    -    self._setup_internal_signal_handler()
    -    self.running.set()
    -    self.libpanda.panda_run() # Give control to panda
    -    self.running.clear() # Back from panda's execution (due to shutdown or monitor quit)
    -    self.unload_plugins() # Unload pyplugins and C plugins
    -    self.delete_callbacks() # Unload any registered callbacks
    -    self.plugins = plugin_list(self)
    -    # Write PANDALOG, if any
    -    #self.libpanda.panda_cleanup_record()
    -    if self._in_replay:
    -        self.reset()
    -    if hasattr(self, "exit_exception"):
    -        saved_exception = self.exit_exception
    -        del self.exit_exception
    -        raise saved_exception
    -
    def run_monitor_cmd(self, cmd)
    -
    - -Expand source code - -
    @blocking
    -def run_monitor_cmd(self, cmd):
    -    self.monitor_console.sendline(cmd.encode("utf8"))
    -    result = self.monitor_console.expect()
    -    return result
    -
    def run_replay(self, replaypfx) @@ -11366,34 +8691,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def run_replay(self, replaypfx):
    -    '''
    -    Load a replay and run it. Starts PANDA execution and returns after end of VM execution.
    -
    -    Args:
    -        replaypfx (str): Replay name/path (e.g., "foo" or "./dir/foo")
    -
    -    Returns:
    -        None
    -    '''
    -    if (not isfile(replaypfx+"-rr-snp") or not isfile(replaypfx+"-rr-nondet.log")) and not rr2_recording(replaypfx):
    -        raise ValueError("Replay files not present to run replay of {}".format(replaypfx))
    -
    -    self.ending = False
    -
    -    if debug:
    -        progress ("Replaying %s" % replaypfx)
    -
    -    charptr = self.ffi.new("char[]",bytes(replaypfx,"utf-8"))
    -    self.libpanda.panda_replay_begin(charptr)
    -    self._in_replay = True
    -    self.run()
    -    self._in_replay = False
    -
    def run_serial_cmd(self, cmd, no_timeout=False, timeout=None) @@ -11415,39 +8712,6 @@

    Returns

    String
    all the output (stdout + stderr) printed after typing your command and pressing enter until the next prompt was printed.
    -
    - -Expand source code - -
    @blocking
    -def run_serial_cmd(self, cmd, no_timeout=False, timeout=None):
    -    '''
    -    Run a command inside the guest through a terminal exposed over a serial port. Can only be used if your guest is configured in this way
    -
    -    Guest output will be analyzed until we see the expect_prompt regex printed (i.e., the PS1 prompt)
    -
    -    Args:
    -        cmd: command to run.
    -        timeout: maximum time to wait for the command to finish
    -        no_timeout: if set, don't ever timeout
    -
    -    Returns:
    -        String: all the output (stdout + stderr) printed after typing your command and pressing enter until the next prompt was printed.
    -    '''
    -
    -    if timeout is None:
    -        timeout = 30
    -
    -    if self.serial_console is None:
    -        raise RuntimeError("Cannot run serial commands without providing PANDA an expect_prompt")
    -    self.running.wait() # Can only run serial when guest is running
    -    self.serial_console.sendline(cmd.encode("utf8"))
    -    if no_timeout:
    -        result = self.serial_console.expect(timeout=9999) # "Don't ever timeout" above is a bit of an exaggeration
    -    else:
    -        result = self.serial_console.expect(timeout=timeout)
    -    return result
    -
    def run_serial_cmd_async(self, cmd, delay=1) @@ -11455,82 +8719,18 @@

    Returns

    Type a command and press enter in the guest. Return immediately. No results available Only use this if you know what you're doing!

    -
    - -Expand source code - -
    @blocking
    -def run_serial_cmd_async(self, cmd, delay=1):
    -    '''
    -    Type a command and press enter in the guest. Return immediately. No results available
    -    Only use this if you know what you're doing!
    -    '''
    -    self.running.wait() # Can only run serial when guest is running
    -    self.serial_console.sendline(cmd.encode("utf8"))
    -    if delay:
    -        sleep(delay) # Ensure it has a chance to run
    -
    def run_volatility(self, plugin, debug=False)
    -
    - -Expand source code - -
    def run_volatility(self, plugin, debug=False):
    -    try:
    -        from .volatility_cli_classes import CommandLineRunFullCommand, StringTextRenderer
    -    except ImportError:
    -        print("Warning: Failed to import volatility")
    -        return None
    -    self.make_panda_file_handler(debug=debug)
    -    cmd = CommandLineRunFullCommand().run("-q -f panda.panda " + plugin)
    -    output = StringTextRenderer().render(cmd.run())
    -    return output
    -
    def serial_read_until(self, byte_sequence)
    -
    - -Expand source code - -
    @blocking
    -def serial_read_until(self, byte_sequence):
    -    if len(self.serial_unconsumed_data) > 0:
    -        found_idx = self.serial_unconsumed_data.find(byte_sequence)
    -        if found_idx >= 0:
    -            match = self.serial_unconsumed_data[ : found_idx]
    -            self.serial_unconsumed_data = self.serial_unconsumed_data[found_idx + 1 : ]
    -            return match
    -    while self.serial_socket != None:
    -        try:
    -            readable, _, _ = select.select([self.serial_socket], [], [], 0.5)
    -            if len(readable) == 0:
    -                continue
    -            data = self.serial_socket.recv(65535)
    -        except Exception as e:
    -            if '[Errno 11]' in str(e) or '[Errno 35]' in str(e):
    -                # EAGAIN
    -                continue
    -            raise Exception("Data Read Error: {}".format(e.message))
    -        if not data:
    -            raise Exception('Connection Closed by Server')
    -
    -        self.serial_unconsumed_data += data
    -        found_idx = self.serial_unconsumed_data.find(byte_sequence)
    -        if found_idx >= 0:
    -            match = self.serial_unconsumed_data[ : found_idx]
    -            self.serial_unconsumed_data = self.serial_unconsumed_data[found_idx + 1 : ]
    -            return match
    -    return None
    -
    def set_breakpoint(self, cpu, pc) @@ -11538,18 +8738,6 @@

    Returns

    Set a GDB breakpoint such that when the guest hits PC, execution is paused and an attached GDB instance can introspect on guest memory. Requires starting panda with -s, at least for now

    -
    - -Expand source code - -
    def set_breakpoint(self, cpu, pc):
    -    '''
    -    Set a GDB breakpoint such that when the guest hits PC, execution is paused and an attached
    -    GDB instance can introspect on guest memory. Requires starting panda with -s, at least for now
    -    '''
    -    BP_GDB = 0x10
    -    self.libpanda.cpu_breakpoint_insert(cpu, pc, BP_GDB, self.ffi.NULL)
    -
    def set_os_name(self, os_name) @@ -11566,38 +8754,11 @@

    Returns

    "freebsd[-_]64[-_].+", Args: - os_name (str): Name that matches the format for the os flag. - -Returns: - None -
    -
    - -Expand source code - -
    def set_os_name(self, os_name):
    -    """
    -    Set OS target. Equivalent to "-os" flag on the command line. Matches the form of:
    -
    -        "windows[-_]32[-_]xpsp[23]",
    -        "windows[-_]32[-_]2000",
    -        "windows[-_]32[-_]7sp[01]",
    -        "windows[-_]64[-_]7sp[01]",
    -        "linux[-_]32[-_].+",
    -        "linux[-_]64[-_].+",
    -        "freebsd[-_]32[-_].+",
    -        "freebsd[-_]64[-_].+",
    -
    -        Args:
    -            os_name (str): Name that matches the format for the os flag.
    -
    -        Returns:
    -            None
    -    """
    -    print ("os_name=[%s]" % os_name)
    -    os_name_new = self.ffi.new("char[]", bytes(os_name, "utf-8"))
    -    self.libpanda.panda_set_os_name(os_name_new)
    -
    + os_name (str): Name that matches the format for the os flag. + +Returns: + None +
    def set_pandalog(self, name) @@ -11611,23 +8772,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def set_pandalog(self, name):
    -    '''
    -    Enable recording to a pandalog (plog) named `name`
    -
    -    Args:
    -        name (str): filename to output data to
    -
    -    Returns:
    -        None
    -    '''
    -    charptr = self.ffi.new("char[]", bytes(name, "utf-8"))
    -    self.libpanda.panda_start_pandalog(charptr)
    -
    def snap(self, snapshot_name) @@ -11641,32 +8785,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def snap(self, snapshot_name):
    -    '''
    -    Create snapshot with specified name
    -
    -    Args:
    -        snapshot_name (str): name of the snapshot
    -
    -    Returns:
    -        None
    -    '''
    -    if debug:
    -        progress ("Creating snapshot " + snapshot_name)
    -
    -    # Stop guest execution, queue up a snapshot, 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_snap, [charptr])
    -    self.queue_main_loop_wait_fn(self.libpanda.panda_cont)
    -    if debug:
    -        self.queue_main_loop_wait_fn(self._finish_timer, [timer_start, "Saved snapshot"])
    -
    def stop_run(self) @@ -11676,70 +8794,24 @@

    Returns

    In other words, once this is called, panda.run() will finish and your main thread will continue. If you also want to unload plugins, use end_analysis instead

    XXX: This doesn't work in replay mode

    -
    - -Expand source code - -
    @blocking
    -def stop_run(self):
    -    '''
    -    From a blocking thread, request vl.c loop to break. Returns control flow in main thread.
    -    In other words, once this is called, panda.run() will finish and your main thread will continue.
    -    If you also want to unload plugins, use end_analysis instead
    -
    -    XXX: This doesn't work in replay mode
    -    '''
    -    self.libpanda.panda_break_vl_loop_req = True
    -
    def string_to_condition(self, string: str)
    -
    - -Expand source code - -
    def string_to_condition(self, string: str):
    -    s = self.string_to_solver(string)
    -    asrts = s.assertions()
    -    if len(asrts) == 0:
    -        return None 
    -    return asrts[0]
    -
    def string_to_expr(self, string: str)
    -
    - -Expand source code - -
    def string_to_expr(self, string: str):
    -    eq = self.string_to_condition(string)
    -    if eq and len(eq.children()) > 0:
    -        return eq.children()[0]
    -    return None
    -
    def string_to_solver(self, string: str)
    -
    - -Expand source code - -
    def string_to_solver(self, string: str):
    -    from z3 import Solver
    -    s = Solver()
    -    s.from_string(string)
    -    return s
    -
    def sysbus_create_varargs(self, name, addr) @@ -11755,110 +8827,36 @@

    Args

    Returns

    DeviceState struct

    -
    - -Expand source code - -
    def sysbus_create_varargs(self, name, addr):
    -    '''
    -    Returns DeviceState struct from user specified information
    -    Calls sysbus_create_varargs QEMU function.
    -
    -    Args:
    -        name (str):
    -        addr (int): hwaddr
    -
    -    Returns:
    -        DeviceState struct
    -    '''
    -    return self.libpanda.sysbus_create_varargs(name,addr, self.ffi.NULL)
    -
    def taint_check_laddr(self, addr, off)

    returns boolean result checking if this laddr is tainted

    -
    - -Expand source code - -
    def taint_check_laddr(self, addr, off):
    -    '''
    -    returns boolean result checking if this laddr is tainted
    -    '''
    -    self._assert_taint_enabled()
    -    return self.plugins['taint2'].taint2_query_laddr(addr, off) > 0
    -
    def taint_check_ram(self, addr)

    returns boolean representing if physical address is tainted.

    -
    - -Expand source code - -
    def taint_check_ram(self, addr):
    -    '''
    -    returns boolean representing if physical address is tainted.
    -    '''
    -    self._assert_taint_enabled()
    -    return self.plugins['taint2'].taint2_query_ram(addr) > 0
    -
    def taint_check_reg(self, reg_num)

    Checks if register reg_num is tainted. Returns boolean.

    -
    - -Expand source code - -
    def taint_check_reg(self, reg_num):
    -    '''
    -    Checks if register reg_num is tainted. Returns boolean.
    -    '''
    -    self._assert_taint_enabled()
    -    for offset in range(self.register_size):
    -        if self.plugins['taint2'].taint2_query_reg(reg_num, offset) > 0:
    -            return True
    -    return False
    -
    def taint_enable(self)

    Enable taint.

    -
    - -Expand source code - -
    def taint_enable(self):
    -    '''
    -    Enable taint.
    -    '''
    -    self.plugins["taint2"].taint2_enable_taint()
    -
    def taint_enabled(self)

    Checks to see if taint2 plugin has been loaded

    -
    - -Expand source code - -
    def taint_enabled(self):
    -    '''
    -    Checks to see if taint2 plugin has been loaded
    -    '''
    -    return self._plugin_loaded("taint2") and self.plugins["taint2"].taint2_enabled()
    -
    def taint_get_laddr(self, addr, offset) @@ -11867,24 +8865,6 @@

    Returns

    returns array of results, one for each byte in this laddr None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_laddr(self, addr, offset):
    -    '''
    -    returns array of results, one for each byte in this laddr
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    if self.plugins['taint2'].taint2_query_laddr(addr, offset) > 0:
    -        query_res = self.ffi.new("QueryResult *")
    -        self.plugins['taint2'].taint2_query_laddr_full(addr, offset, query_res)
    -        tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -        return tq
    -    else:
    -        return None
    -
    def taint_get_ram(self, addr) @@ -11893,24 +8873,6 @@

    Returns

    returns array of results, one for each byte in this register None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_ram(self, addr):
    -    '''
    -    returns array of results, one for each byte in this register
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    if self.plugins['taint2'].taint2_query_ram(addr) > 0:
    -        query_res = self.ffi.new("QueryResult *")
    -        self.plugins['taint2'].taint2_query_ram_full(addr, query_res)
    -        tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -        return tq
    -    else:
    -        return None
    -
    def taint_get_reg(self, reg_num) @@ -11919,224 +8881,60 @@

    Returns

    Returns array of results, one for each byte in this register None if no taint. QueryResult struct otherwise

    -
    - -Expand source code - -
    def taint_get_reg(self, reg_num):
    -    '''
    -    Returns array of results, one for each byte in this register
    -    None if no taint.  QueryResult struct otherwise
    -    '''
    -    self._assert_taint_enabled()
    -    res = []
    -    for offset in range(self.register_size):
    -        if self.plugins['taint2'].taint2_query_reg(reg_num, offset) > 0:
    -            query_res = self.ffi.new("QueryResult *")
    -            self.plugins['taint2'].taint2_query_reg_full(reg_num, offset, query_res)
    -            tq = TaintQuery(query_res, self.plugins['taint2'], self.ffi)
    -            res.append(tq)
    -        else:
    -            res.append(None)
    -    return res
    -
    def taint_label_ram(self, addr, label)

    Labels ram at address with label.

    -
    - -Expand source code - -
    def taint_label_ram(self, addr, label):
    -    '''
    -    Labels ram at address with label.
    -    '''
    -    self._assert_taint_enabled()
    -    self.plugins["taint2"].taint2_label_ram(addr, label)
    -
    def taint_label_reg(self, reg_num, label)

    Labels taint register reg_num with label.

    -
    - -Expand source code - -
    def taint_label_reg(self, reg_num, label):
    -    '''
    -    Labels taint register reg_num with label.
    -    '''
    -    self._assert_taint_enabled()
    -    for i in range(self.register_size):
    -        self.plugins["taint2"].taint2_label_reg(reg_num, i, label)
    -
    def taint_sym_branch_meta(self)
    -
    - -Expand source code - -
    def taint_sym_branch_meta(self):
    -    branch_meta_ptr_ffi = self.ffi.new('SymbolicBranchMeta **')
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_branch_meta(n_ptr_ffi, branch_meta_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return []
    -    meta_ptr = self.ffi.unpack(branch_meta_ptr_ffi, 1)[0]
    -    metas_ffi = self.ffi.unpack(meta_ptr, n)
    -    # Meta only has a pc field now
    -    metas = [
    -        meta_ffi.pc
    -        for meta_ffi in metas_ffi
    -    ]
    -    return metas
    -
    def taint_sym_enable(self)

    Inform python that taint is enabled.

    -
    - -Expand source code - -
    def taint_sym_enable(self):
    -    """
    -    Inform python that taint is enabled.
    -    """
    -    if not self.taint_enabled():
    -        self.taint_enable()
    -        progress("taint symbolic not enabled -- enabling")
    -    self.plugins["taint2"].taint2_enable_sym()
    -
    def taint_sym_label_ram(self, addr, label)
    -
    - -Expand source code - -
    def taint_sym_label_ram(self, addr, label):
    -    self._assert_taint_sym_enabled()
    -    self.plugins['taint2'].taint2_sym_label_ram(addr,label)
    -
    def taint_sym_label_reg(self, reg_num, label)
    -
    - -Expand source code - -
    def taint_sym_label_reg(self, reg_num, label):
    -    # label all bytes in this register.
    -    # or at least four of them
    -    # XXX label must increment by panda.register_size after the call
    -    self._assert_taint_sym_enabled()
    -    self.taint_sym_enable()
    -    for i in range(self.register_size):
    -        self.plugins['taint2'].taint2_sym_label_reg(reg_num, i, label+i)
    -
    def taint_sym_path_constraints(self)
    -
    - -Expand source code - -
    def taint_sym_path_constraints(self):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_path_constraints(n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return []
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    solver = self.string_to_solver(expr_str)
    -    return solver.assertions() if solver != None else []
    -
    def taint_sym_query_ram(self, addr, size=1)
    -
    - -Expand source code - -
    def taint_sym_query_ram(self, addr, size=1):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_query_ram(addr, size, n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return None
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    return self.string_to_expr(expr_str)
    -
    def taint_sym_query_reg(self, addr)
    -
    - -Expand source code - -
    def taint_sym_query_reg(self, addr):
    -    # Prepare ptr for returned string
    -    str_ptr_ffi = self.ffi.new('char**')
    -    # Prepare ptr for string size
    -    n_ptr_ffi = self.ffi.new('uint32_t *', 0)
    -
    -    self.plugins['taint2'].taint2_sym_query_reg(addr, n_ptr_ffi, str_ptr_ffi)
    -    # Unpack size
    -    n = self.ffi.unpack(n_ptr_ffi, 1)[0]
    -    if n == 0:
    -        return None
    -    # Unpack cstr
    -    str_ptr = self.ffi.unpack(str_ptr_ffi, 1)[0]
    -    str_bs = self.ffi.unpack(str_ptr, n)
    -    expr_str = str(str_bs, 'utf-8')
    -    return self.string_to_expr(expr_str)
    -
    def to_unsigned_guest(self, x) @@ -12154,46 +8952,12 @@

    Returns

    int
    Python integer representing x as an unsigned value in the guest's pointer-size.
    -
    - -Expand source code - -
    def to_unsigned_guest(self, x):
    -    '''
    -    Convert a singed python int to an unsigned int32/unsigned int64
    -    depending on guest bit-size
    -
    -    Args:
    -        x (int): Python integer
    -
    -    Returns:
    -        int: Python integer representing x as an unsigned value in the guest's pointer-size.
    -    '''
    -    import ctypes
    -    if self.bits == 32:
    -        return ctypes.c_uint32(x).value
    -    elif self.bits == 64:
    -        return ctypes.c_uint64(x).value
    -    else:
    -        raise ValueError("Unsupported number of bits")
    -
    def type_serial_cmd(self, cmd)
    -
    - -Expand source code - -
    @blocking
    -def type_serial_cmd(self, cmd):
    -    #Can send message into socket without guest running (no self.running.wait())
    -    if isinstance(cmd, str):
    -        cmd = cmd.encode('utf8')
    -    self.serial_console.send(cmd) # send, not sendline
    -
    def unload_plugin(self, name) @@ -12207,25 +8971,6 @@

    Args

    Returns

    None

    -
    - -Expand source code - -
    def unload_plugin(self, name):
    -    '''
    -    Unload plugin with given name.
    -
    -    Args:
    -        name (str): Name of plug
    -
    -    Returns:
    -        None
    -    '''
    -    if debug:
    -        progress ("Unloading plugin %s" % name),
    -    name_ffi = self.ffi.new("char[]", bytes(name,"utf-8"))
    -    self.libpanda.panda_unload_plugin_by_name(name_ffi)
    -
    def unload_plugins(self) @@ -12236,28 +8981,6 @@

    Returns

    XXX: If called during shutdown/exit, c plugins won't be unloaded because the next main_loop_wait will never happen. Instead, call panda.panda_finish directly (which is done at the end of panda.run())

    -
    - -Expand source code - -
    def unload_plugins(self):
    -    '''
    -    Disable all python plugins and request to unload all c plugins
    -    at the next main_loop_wait.
    -
    -    XXX: If called during shutdown/exit, c plugins won't be unloaded
    -    because the next main_loop_wait will never happen. Instead, call
    -    panda.panda_finish directly (which is done at the end of panda.run())
    -    '''
    -    if debug:
    -        progress ("Disabling all python plugins, unloading all C plugins")
    -
    -    # In next main loop wait, unload all python plugin
    -    self.queue_main_loop_wait_fn(self._unload_pyplugins)
    -
    -    # Then unload C plugins. May be unsafe to do except from the top of the main loop (taint segfaults otherwise)
    -    self.queue_main_loop_wait_fn(self.libpanda.panda_unload_plugins)
    -
    def unregister_pyperipheral(self, pyperiph) @@ -12266,35 +8989,6 @@

    Returns

    deregisters a python peripheral. The pyperiph parameter can be either an object, or an address Returns true if the pyperipheral was successfully removed, else false.

    -
    - -Expand source code - -
    def unregister_pyperipheral(self, pyperiph):
    -    """
    -    deregisters a python peripheral.
    -    The pyperiph parameter can be either an object, or an address
    -    Returns true if the pyperipheral was successfully removed, else false.
    -    """
    -
    -    if isinstance(pyperiph, int) is True:
    -        pp = self._addr_to_pyperipheral(pyperiph)
    -        if pp is None:
    -            return False
    -    else:
    -        if pyperiph not in self.pyperipherals:
    -            return False
    -        pp = pyperiph
    -
    -    self.pyperipherals.remove(pp)
    -
    -    # If we dont have any pyperipherals left, unregister callbacks
    -    if len(self.pyperipherals) == 0:
    -        self.disable_callback("pyperipheral_read_callback", forever=True)
    -        self.disable_callback("pyperipheral_write_callback", forever=True)
    -        self.pyperipherals_registered_cb = False
    -    return True
    -
    def virt_to_phys(self, cpu, addr) @@ -12310,26 +9004,6 @@

    Args

    Return

    int: physical address

    -
    - -Expand source code - -
    def virt_to_phys(self, cpu, addr):
    -    '''
    -    Convert virtual address to physical address.
    -
    -    Args:
    -        cpu (CPUState): CPUState struct
    -        addr (int): virtual address to convert
    -
    -    Return:
    -        int: physical address
    -    '''
    -    if "osi_linux" in self.plugins.keys() or self._plugin_loaded("osi_linux"):
    -        return self.plugins["osi_linux"].osi_linux_virt_to_phys(cpu, addr)
    -    else:
    -        return self.libpanda.panda_virt_to_phys_external(cpu, addr)
    -
    def virtual_memory_read(self, cpu, addr, length, fmt='bytearray') @@ -12354,29 +9028,6 @@

    Returns

    Raises

    ValueError if memory access fails or fmt is unsupported

    -
    - -Expand source code - -
    def virtual_memory_read(self, cpu, addr, length, fmt='bytearray'):
    -    '''
    -    Read guest virtual memory.
    -
    -    Args:
    -        cpu (CPUState): CPUState structure
    -        addr (int): Address
    -        length (int): length of data you would like returned
    -        fmt: format for returned array. See `physical_memory_read`.
    -
    -    Returns:
    -        Union[bytearray, int, str, list[int]]: memory data
    -
    -    Raises:
    -        ValueError if memory access fails or fmt is unsupported
    -    '''
    -
    -    return self._memory_read(cpu, addr, length, physical=False, fmt=fmt)
    -
    def virtual_memory_write(self, cpu, addr, buf) @@ -12396,57 +9047,18 @@

    Returns

    None

    Raises

    ValueError if the call to panda.virtual_memory_write fails (e.g., if you pass a pointer to an unmapped page)

    -
    - -Expand source code - -
    def virtual_memory_write(self, cpu, addr, buf):
    -    '''
    -    Write guest virtual memory.
    -
    -    Args:
    -        cpu (CPUState): CPUState structure
    -        address (int): Address
    -        buf (bytestr): byte string to write into memory
    -
    -    Returns:
    -        None
    -
    -    Raises:
    -        ValueError if the call to panda.virtual_memory_write fails (e.g., if you pass a pointer to an unmapped page)
    -    '''
    -    self._memory_write(cpu, addr, buf, physical=False)
    -
    def vm_stop(self, code=4)

    Stop execution, default code means RUN_STATE_PAUSED

    -
    - -Expand source code - -
    def vm_stop(self, code=4):
    -    ''' Stop execution, default code means RUN_STATE_PAUSED '''
    -    self.libpanda.panda_stop(code)
    -
    def was_aborted(self)

    Returns true if panda was aborted.

    -
    - -Expand source code - -
    def was_aborted(self):
    -    '''
    -    Returns true if panda was aborted.
    -    '''
    -    return self.libpanda.panda_was_aborted()
    -
    @@ -12463,8 +9075,8 @@

    Raises

    - - + + - + + + @@ -1088,31 +1088,12 @@

    Methods

    -
    - -Expand source code - -
    def abort(self):
    -    self.running = False
    -
    def connect(self, filelike)
    -
    - -Expand source code - -
    def connect(self, filelike):
    -    if type(filelike) == int:
    -        self.fd = filelike
    -    else:
    -        self.fd = filelike.fileno()
    -    self.poller = select.poll()
    -    self.poller.register(self.fd, select.POLLIN)
    -
    def consume_partial(self) @@ -1120,19 +1101,6 @@

    Methods

    Get the message so far and reset. To ensure that we're not consuming the final line at we just don't clear

    -
    - -Expand source code - -
    def consume_partial(self):
    -    '''
    -    Get the message so far and reset.
    -    To ensure that we're not consuming the final line at we just don't clear
    -    '''
    -    result = self.get_partial()
    -    self.prior_lines = []
    -    return result
    -
    def expect(self, expectation=None, timeout=30) @@ -1145,209 +1113,42 @@

    Methods

    1) Render ANSI control characters in current line (may affect prior lines) 2) Check if the line we just parsed matches the provided expectation (if so we're done) 3) Append current_line into prior_lines

    -
    - -Expand source code - -
    def expect(self, expectation=None, timeout=30):
    -    '''
    -    Assumptions: as you send a command, the guest may send back
    -        The same command + ansi control codes.
    -        The epxectation value will show up on the start of a line.
    -
    -
    -    We add characters into current_line as we recv them. At each newline we
    -    1) Render ANSI control characters in current line (may affect prior lines)
    -    2) Check if the line we just parsed matches the provided expectation (if so we're done)
    -    3) Append current_line into prior_lines
    -    '''
    -
    -    if expectation:
    -        raise ValueError("Deprecated interface - must set expectation in class init")
    -
    -    if self.fd is None:
    -        raise RuntimeError("Must connect() prior to expect()")
    -
    -    self.current_line = bytearray()
    -    start_time = monotonic()
    -    time_passed = 0
    -    while (timeout is None or time_passed < timeout) and self.running:
    -        if timeout is not None:
    -            time_passed = (monotonic() - start_time)
    -            time_left = timeout - time_passed
    -        else:
    -            time_left = float("inf")
    -        ready = self.poller.poll(min(time_left, 1))
    -
    -        # Debug - flush debug logs
    -        if self.logfile:
    -            self.logfile.flush()
    -
    -        if self.fd in [fd for (fd, _) in ready]:
    -            try:
    -                char = os.read(self.fd, 1)
    -            except OSError as e:
    -                if e.errno in [EAGAIN, EWOULDBLOCK]:
    -                    continue
    -                else: raise
    -
    -            self.current_line.extend(char)
    -
    -            # Debugging - log current line to file
    -            if self.logfile:
    -                self.logfile.write(("\n\n" + repr(self.prior_lines) + " Current line = " + repr(self.current_line)).encode())
    -
    -            # Translate the current_line buffer into plaintext, then determine if we're finished (bc we see new prompt)
    -            # note this drops the echo'd command
    -            if self.current_line.endswith(b"\n"):
    -                # End of line - need to potentially unansi and move into prior_lines
    -                if self.use_unansi:
    -                    self.unansi()
    -                else:
    -                    self.prior_lines.append(self.current_line[:-1].decode(errors='ignore'))
    -                    self.current_line = bytearray()
    -
    -                # Now we have command\nresults..........\nprompt
    -                #self.logfile.write(b"\n UNANSIs to: " + repr(self.prior_lines).encode()+b"\n")
    -
    -
    -            #lines = [x.replace("\r", "") for x in plaintext.split("\n")]
    -            # Check current line to see if it ends with prompt (indicating we finished)
    -            # current_line is a bytearray. Need it as a string
    -            current_line_s = self.current_line.decode(errors='ignore')
    -
    -            end_match = self.expectation_ends_re.match(current_line_s)
    -            if end_match is not None:
    -                # This line matches the end regex - it's either like root@host:... or it's [output]root@host:...
    -                # We'll use self.expectation_re on the current line to identify where the prompt is and grab any final output
    -                final_output = end_match.groups(1)[0]
    -                if len(final_output):
    -                    self.prior_lines.append(final_output)
    -                    current_line_s = current_line_s[len(final_output):]
    -
    -                # Note we may have a line like [output]root@.... in which case we need to identify where the prompt was
    -                self.last_prompt = current_line_s
    -
    -                # Drop command we sent - note it won't be a direct match with last_cmd because of weird escape codes
    -                # which are based on guest line position when printed - i.e., it would only be an exact
    -                # match if we knew and included the prompt when the command was run. Let's just always drop it
    -                if len(self.prior_lines) > 1:
    -                    self.prior_lines = self.prior_lines[1:]
    -                else:
    -                    self.prior_lines = []
    -
    -                plaintext = "\n".join(self.prior_lines)
    -                self.prior_lines = []
    -                return plaintext
    -
    -    if not self.running: # Aborted
    -        return None
    -
    -    if self.logfile:
    -        self.logfile.flush()
    -
    -    full_buffer = self.prior_lines + [self.current_line]
    -    raise TimeoutExpired(f"{self.name} Read message \n{full_buffer}\n")
    -
    def get_partial(self)

    Get the message

    -
    - -Expand source code - -
    def get_partial(self):
    -    '''
    -    Get the message
    -    '''
    -    if len(self.prior_lines):
    -        return "\n".join(self.prior_lines)
    -    return ""
    -
    def is_connected(self)
    -
    - -Expand source code - -
    def is_connected(self):
    -    return self.fd != None
    -
    def send(self, msg)
    -
    - -Expand source code - -
    def send(self, msg):
    -    if not self.consumed_first: # Before we send anything, consume header
    -        pre = self.expect("")
    -        self.consumed_first = True
    -
    -    # Newlines will call problems
    -    assert len(msg.decode(errors='ignore').split("\n")) <= 2, "Multiline cmds unsupported"
    -    self.last_msg = msg.decode(errors='ignore').replace("\n", "")
    -    os.write(self.fd, msg)
    -    if self.logfile:
    -        self.logfile.write(msg)
    -        self.logfile.flush()
    -
    def send_eol(self)
    -
    - -Expand source code - -
    def send_eol(self): # Just send an EOL
    -    if self.last_msg:
    -        self.last_msg+="\n"
    -    os.write(self.fd, b"\n")
    -    if self.logfile:
    -        self.logfile.write(b"\n")
    -        self.logfile.flush()
    -
    def sendline(self, msg=b'')
    -
    - -Expand source code - -
    def sendline(self, msg=b""):
    -    assert(self.fd is not None), "Must connect before sending"
    -    self.send(msg + b"\n")
    -
    def set_logging(self, name)
    -
    - -Expand source code - -
    def set_logging(self, name):
    -    self.logfile = open(name, "wb")
    -
    def unansi(self) @@ -1360,269 +1161,12 @@

    Methods

    evaluate the commands to render real text output

    See https://notes.burke.libbey.me/ansi-escape-codes/ and http://ascii-table.com/ansi-escape-sequences-vt-100.php for ansi escape code details

    -
    - -Expand source code - -
    def unansi(self):
    -    '''
    -    Take the string in self.current_line and any prior lines in self.prior_lines and render ANSI.
    -    prior lines should be plain strings while current_line may contain escapes
    -
    -    Given a string with ansi control codes, emulate behavior to generate the resulting string. 
    -
    -    First we split input into a list of ('fn', [args]) / ('text', ['foo']) ansi commands then
    -    evaluate the commands to render real text output
    -
    -    See https://notes.burke.libbey.me/ansi-escape-codes/ and
    -    http://ascii-table.com/ansi-escape-sequences-vt-100.php for ansi escape code details
    -    '''
    -
    -    # Join prior lines into a single text element in our reformatted list
    -    reformatted = []
    -    if len(self.prior_lines):
    -        reformatted = [('text', ['\n'.join(self.prior_lines)])]
    -
    -    # Then split current line into the tuple format describe above
    -    msg = self.current_line
    -
    -    if isinstance(msg, str):
    -        msg = msg.encode()
    -
    -    # Check for simple case where no ansi is in line - if so just copy into prior_lines
    -    if b'\x1b' not in msg:
    -        text = "".join([chr(x) for x in msg]).strip()
    -        self.prior_lines.append(text)
    -        self.current_line = bytearray()
    -        return
    -
    -    start_args = re.compile(br"^(\d+);")
    -    last_arg = re.compile(rb"^(\d+)")
    -
    -    last_text = ""
    -    idx = 0 # XXX: mutates during loop
    -    while idx < len(msg):
    -        if msg[idx] != 0x1b:
    -            last_text += chr(msg[idx])
    -        else:
    -            if len(last_text):
    -                reformatted.append(('text', [last_text]))
    -                last_text = ""
    -
    -            if idx+3 <= len(msg) and msg[idx+1] == ord('['):
    -                args = []
    -                shift = idx+2
    -                arg_s = msg[shift:]
    -                while start_args.match(arg_s):
    -                    arg = start_args.match(arg_s).groups()[0].decode()
    -                    args.append(arg)
    -                    shift += len(arg)+1 # for ;
    -                    arg_s = msg[shift:]
    -
    -                # Last arg is just #
    -                if last_arg.match(arg_s):
    -                    arg = last_arg.match(arg_s).groups()[0].decode()
    -                    shift += len(arg)
    -                    args.append(arg)
    -                    arg_s = msg[shift:]
    -
    -                # Next is one char for cmd
    -                cmd = chr(msg[shift])
    -                reformatted.append((cmd, args))
    -
    -                idx = shift # final char
    -        idx += 1
    -    if len(last_text):
    -        reformatted.append(('text', [last_text]))
    -
    -    # Now render it!
    -    # Note the very first line will \r to beginning, then 'C' forward to go past expect prompt
    -
    -    lines_out = [" "*len(self.last_prompt)] # Starting point - it's an approximation since we don't know real current prompt
    -    cur_line = 0
    -    line_pos = len(self.last_prompt)
    -    store_ptr = (0, 0)
    -
    -    def _dump(lines_out, cur_line, line_pos):
    -        print("-"*100)
    -        for idx, line in enumerate(lines_out):
    -            print("'", line, "'")
    -            if cur_line == idx:
    -                print((" "*(line_pos-1) if line_pos > 0 else "") + "^")
    -        print("="*100)
    -
    -    for idx, (typ, args) in enumerate(reformatted):
    -        #print(typ, args)
    -        if typ == 'text':
    -            n = args[0]
    -            for idx, char in enumerate(n):
    -                if char == '\n':
    -                    cur_line += 1 
    -                    while cur_line >= len(lines_out):
    -                        lines_out.append("")
    -                if char == '\r':
    -                    line_pos = 0
    -                    continue # Don't clobber old char
    -
    -                line = list(lines_out[cur_line])
    -                if (line_pos) >= len(line):
    -                    line.append(char)
    -                else:
    -                    #if line[line_pos] != ' ':
    -                    #    print("Replace", repr(line[line_pos]) , "with", repr(char))
    -                    line[line_pos] = char
    -                lines_out[cur_line] = "".join(line)
    -
    -                if char not in ['\n', '\r']:
    -                    line_pos += 1
    -
    -        else:
    -            args[:] = [int(x) for x in args]
    -
    -            if typ == 'A':
    -                if not len(args):
    -                    # Incomplete
    -                    continue
    -
    -                n = args[0]
    -
    -                if cur_line - n < 0: # Need to shift
    -                    cur_line = 0
    -                else:
    -                    cur_line -= n
    -                assert(cur_line >= 0)
    -
    -            elif typ == 'B':
    -                if not len(args):
    -                    # Incomplete
    -                    continue
    -                n = args[0]
    -                cur_line += n
    -                while cur_line >= len(lines_out):
    -                    lines_out.append(" "*100)
    -
    -            elif typ == 'D':
    -                n = 1 # Default move left 1
    -                if len(args):
    -                    n = args[0]
    -
    -                line_pos -= n
    -                if line_pos < 0:
    -                    line_pos = 0
    -                assert(line_pos >= 0)
    -
    -            elif typ == 'C': # Move right
    -                n = 1 # Default move 1
    -                if len(args):
    -                    n = args[0]
    -
    -                line_pos += n
    -                if line_pos > len(lines_out[cur_line])-1:
    -                    line_pos = len(lines_out)-1
    -                assert(line_pos >= 0)
    -
    -            elif typ == 'J':
    -                # Optional arg 0, 1, 2
    -                n = 0 # default
    -                if len(args):
    -                    n = args[0]
    -                if n == 0:
    -                    # clear down
    -                    lines_out = lines_out[:cur_line+1]
    -                elif n == 1:
    -                    # clear up
    -                    lines_out = lines_out[cur_line:]
    -                elif n == 2:
    -                    # clear everything
    -                    lines_out = [""]
    -                    cur_line = 0
    -                    line_pos = 0
    -                    store_ptr = (0, 0)
    -
    -            elif typ == 'K':
    -                # Optional arg 0, 1, 2
    -                n = 0 # default
    -                if len(args):
    -                    n = args[0]
    -
    -                # HURISTIC-y hack: linux loves to have a line 123456 then do K(0) 6\r\n
    -                # so if the next line is text and the text is [eol]\r\n where [eol] matches the end of this
    -                # line - align cur_pos
    -                if len(reformatted) > idx+1: # Have another message
    -                    (next_typ, next_args) = reformatted[idx+1]
    -                    if next_typ == 'text':
    -                        if '\r\n' in next_args[0]:
    -                            next_lines = next_args[0].split("\r\n")
    -                            if lines_out[cur_line].strip().endswith(next_lines[0]):
    -                                # Its the buggy case. Just align it such that we clear the text
    -                                # that's about to get echoed
    -                                line_pos = line_pos - len(next_lines[0])
    -
    -                if n == 0:
    -                    # clear right of cursor
    -                    lines_out[cur_line] = lines_out[cur_line][:line_pos]
    -                elif n == 1:
    -                    # clear left of cursor
    -                    lines_out[cur_line] = (" "*line_pos)+lines_out[cur_line][line_pos:]
    -                elif n == 2:
    -                    # clear whole line
    -                    lines_out[cur_line] = " "*len(lines_out[cur_line])
    -
    -            elif typ == 'H':
    -                n = args[0]-1
    -                m = args[1]-1
    -                cur_line = n
    -                line_pos = m
    -
    -                while cur_line >= len(lines_out):
    -                    lines_out.append("")
    -
    -                while line_pos > len(lines_out[cur_line]):
    -                    lines_out[cur_line] += " "
    -
    -            elif typ == 'T':
    -                # Scroll window down
    -                pass
    -            elif typ == 'S':
    -                # Scroll window up
    -                pass
    -
    -            elif typ == 's':
    -                store_ptr = (cur_line, line_pos)
    -            elif typ == 'u':
    -                (cur_line, line_pos) = store_ptr
    -
    -            elif typ == 'm':
    -                # alter character attributes - just ignore
    -                pass
    -
    -            else:
    -                raise ValueError(f"Unsupported ANSI command {typ}")
    -        #_dump(lines_out, cur_line, line_pos)
    -
    -    # Done processing - update variables
    -    self.prior_lines = lines_out[:-1] # Strings
    -    self.current_line = bytearray() # Bytearray
    -    if len(lines_out[-1].strip()):
    -        self.current_line.append(lines_out[-1])
    -
    def update_expectation(self, expectation)
    -
    - -Expand source code - -
    def update_expectation(self, expectation):
    -    if isinstance(expectation, bytes):
    -        expectation = expectation.decode()
    -    self.last_prompt = expectation # approximation
    -    self.expectation_re = re.compile(expectation)
    -    self.expectation_ends_re = re.compile(r'(.*)' + expectation)
    -
    @@ -1657,8 +1201,8 @@

    Ancestors

    - - + + - + + + @@ -373,8 +373,8 @@

    Classes

    - - + + - + + + @@ -499,36 +499,6 @@

    Static methods

    def __init__(self, panda): print(self.ppp.Server.do_add(1))
    -
    - -Expand source code - -
    @staticmethod
    -def ppp_export(method):
    -    '''
    -    Decorator to apply to a class method in a PyPlugin to indicate that other plugins should
    -    be allowed to call this function. Example:
    -
    -        from pandare import PyPlugin
    -        Class Server(PyPlugin):
    -            def __init__(self, panda):
    -                pass
    -
    -            @PyPlugin.ppp_export
    -            def do_add(self, x):
    -                return x+1
    -
    -        Class Client(PyPlugin):
    -            def __init__(self, panda):
    -                print(self.ppp.Server.do_add(1))
    -    '''
    -    @wraps(method)
    -    def f(*args, **kwargs):
    -        return method(*args, **kwargs)
    -    f.__is_pyplugin_ppp = True
    -    f.__original_method = method
    -    return f
    -

    Instance variables

    @@ -575,56 +545,12 @@

    Methods

    Returns either the argument as a string or None if the argument wasn't passed (arguments passed in bool form (i.e., set but with no value) instead of key/value form will also return None).

    -
    - -Expand source code - -
    def get_arg(self, arg_name):
    -    '''
    -    Returns either the argument as a string or None if the argument
    -    wasn't passed (arguments passed in bool form (i.e., set but with no value)
    -    instead of key/value form will also return None).
    -    '''
    -    if arg_name in self.args:
    -        return self.args[arg_name]
    -
    -    return None
    -
    def get_arg_bool(self, arg_name)

    Returns True if the argument is set and has a truthy value

    -
    - -Expand source code - -
    def get_arg_bool(self, arg_name):
    -    '''
    -    Returns True if the argument is set and has a truthy value
    -    '''
    -
    -    if arg_name not in self.args:
    -        # Argument name unset - it's false
    -        return False
    -
    -    arg_val = self.args[arg_name]
    -    if isinstance(arg_val, bool):
    -        # If it's a python bol already, just return it
    -        return arg_val
    -
    -    if isinstance(arg_val, str):
    -        # string of true/y/1  is True
    -        return arg_val.lower() in ['true', 'y', '1']
    -
    -    if isinstance(arg_val, int):
    -        # Nonzero is True
    -        return arg_val != 0
    -
    -    # If it's not a string, int, or bool something is weird
    -    raise ValueError(f"Unsupported arg type: {type(arg_val)}")
    -
    def ppp_cb_boilerplate(self, cb_name) @@ -632,35 +558,6 @@

    Methods

    "Define" a PPP-style function in this plugin. Note that there is no type information because this is Python. Run via .ppp[cb_name].run(…)

    -
    - -Expand source code - -
    def ppp_cb_boilerplate(self, cb_name):
    -    '''
    -    "Define" a PPP-style function in this plugin. Note that there is no type
    -    information because this is Python. Run via .ppp[cb_name].run(...)
    -    '''
    -    plugin_name = self.__class__.__name__
    -
    -    if cb_name in self.ppp_cbs:
    -        raise ValueError(f"PPP function {cb_name} is being redefined in {plugin_name}")
    -
    -    # Add two callbacks into our PPP namesapce: fn_add and fn_run
    -    this_ppp_cb = _PPP_CB()
    -    self.ppp.add(self.__class__.__name__, "ppp_reg_cb_" + cb_name, this_ppp_cb.add_callback)
    -    self.ppp.add(self.__class__.__name__, "ppp_run_cb_" + cb_name, this_ppp_cb.run)
    -
    -    # Make sure we have a helper self.ppp[class].ppp_reg_cb which just calls
    -    # the ppp_reg_[cb_name] we just saved
    -    try:
    -        getattr(getattr(self.ppp, self.__class__.__name__), "ppp_reg_cb")
    -    except AttributeError:
    -        def _reg_cb(target_ppp, func):
    -            getattr(getattr(self.ppp,
    -                self.__class__.__name__), "ppp_reg_cb_" + target_ppp)(func)
    -        self.ppp.add(self.__class__.__name__, "ppp_reg_cb", _reg_cb)
    -
    def ppp_run_cb(self, target_ppp, *args) @@ -668,17 +565,6 @@

    Methods

    Trigger a previously defind PPP-style callback named target_ppp in this plugin with args Any other pyplugins which have registered a function to run on this callback will be called with args.

    -
    - -Expand source code - -
    def ppp_run_cb(self, target_ppp, *args):
    -    '''
    -    Trigger a previously defind PPP-style callback named `target_ppp` in this plugin with `args`
    -    Any other pyplugins which have registered a function to run on this callback will be called with `args`.
    -    '''
    -    getattr(getattr(self.ppp, self.__class__.__name__), "ppp_run_cb_" + target_ppp)(*args)
    -
    @@ -695,8 +581,8 @@

    Methods

    - - + + - + + + @@ -799,78 +799,24 @@

    Methods

    Enable flask mode for this instance of the PyPlugin manager. Registered PyPlugins which support flask will be made available at the web interfaces.

    -
    - -Expand source code - -
    def enable_flask(self, host='127.0.0.1', port=8080):
    -    '''
    -    Enable flask mode for this instance of the PyPlugin manager. Registered PyPlugins
    -    which support flask will be made available at the web interfaces.
    -    '''
    -    if len(self.plugins) and not self.silence_warning:
    -        print("WARNING: You've previously registered some PyPlugins prior to enabling flask")
    -        print(f"Plugin(s) {self.plugins.keys()} will be unable to use flask")
    -
    -    from flask import Flask, Blueprint
    -    self.flask = True
    -    self.app = Flask(__name__)
    -    self.blueprint = Blueprint
    -    self.host = host
    -    self.port = port
    -
    def get_plugin(self, pluginclass)
    -
    - -Expand source code - -
    def get_plugin(self, pluginclass):
    -    # Lookup name
    -    # XXX should this end with .class.__name__?
    -    name = pluginclass if isinstance(pluginclass, str) else pluginclass.__name__
    -    if not self.is_loaded(pluginclass, name):
    -        raise ValueError(f"Plugin {name} is not loaded")
    -    return self.plugins[name]
    -
    def get_ppp_funcs(self, plugin)
    -
    - -Expand source code - -
    def get_ppp_funcs(self, plugin):
    -    # iterate over each attribute in the class `plugin`
    -    for item_name in dir(plugin):
    -        item = getattr(plugin, item_name)
    -        # check if the class attribute has a field called `__is_pyplugin_ppp`
    -        #print("CHECK:", item, hasattr(item, "__is_pyplugin_ppp"))
    -        if hasattr(item, "_PyPlugin__is_pyplugin_ppp") and getattr(item, "_PyPlugin__is_pyplugin_ppp"):
    -            self.ppp.add(plugin.__class__.__name__, item.__name__, item)
    -
    def is_loaded(self, pluginclass)
    -
    - -Expand source code - -
    def is_loaded(self, pluginclass):
    -    # XXX should this end with .class.__name__?
    -    name = pluginclass if isinstance(pluginclass, str) else pluginclass.__name__
    -    return name in self.plugins
    -
    def load(self, pluginclasses, args=None, template_dir=None) @@ -883,77 +829,6 @@

    Methods

    or a tuple of (path_to_module.py, [classnames]) where classnames is a list of clases subclasses which subclass PyPlugin.

    Each plugin class will be stored in self.plugins under the class name

    -
    - -Expand source code - -
    def load(self, pluginclasses, args=None, template_dir=None):
    -    '''
    -    Load (aka register) a PyPANDA plugin  to run. It can later be unloaded
    -    by using panda.pyplugins.unload(name).
    -
    -    pluginclasses can either be an uninstantiated python class, a list of such classes,
    -    or a tuple of (path_to_module.py, [classnames]) where classnames is a list of
    -    clases subclasses which subclass PyPlugin.
    -
    -    Each plugin class will be stored in self.plugins under the class name
    -    '''
    -
    -    if args is None:
    -        args = {}
    -
    -    pluginpath = None
    -    if isinstance(pluginclasses, tuple):
    -        # Tuple: use self.load_plugin_class to load the requested classes from
    -        # the provided file
    -        pluginpath, clsnames = pluginclasses
    -        pluginclasses = self.load_plugin_class(pluginpath, clsnames)
    -
    -    elif not isinstance(pluginclasses, list):
    -        # Single element: make it a list with one item
    -        pluginclasses = [pluginclasses]
    -
    -    # This is a little tricky - we can't just instantiate
    -    # an instance of the object- it may use self.get_arg
    -    # in its init method. To allow this behavior, we create
    -    # the object, use the __preinit__ function defined above
    -    # and then ultimately call the __init__ method
    -    # See https://stackoverflow.com/a/6384982/2796854
    -
    -    for pluginclass in pluginclasses:
    -        if not isinstance(pluginclass, type) or not issubclass(pluginclass, PyPlugin):
    -            raise ValueError(f"{pluginclass} must be an uninstantiated subclass of PyPlugin")
    -
    -        # If PyPlugin is in scope it should not be treated as a plugin
    -        if pluginclass is PyPlugin:
    -            continue
    -
    -        name = pluginclass.__name__
    -
    -        self.plugins[name] = pluginclass.__new__(pluginclass)
    -        self.plugins[name].__preinit__(self, args)
    -        self.get_ppp_funcs(self.plugins[name])
    -        self.plugins[name].__init__(self.panda)
    -
    -        # Setup webserver if necessary
    -        if self.flask and hasattr(self.plugins[name], 'webserver_init') and \
    -                callable(self.plugins[name].webserver_init):
    -            self.plugins[name].flask = self.app
    -
    -            # If no template_dir was provided, try using ./templates in the dir of the plugin
    -            # if we know it, otherwise ./templates
    -            if template_dir is None:
    -                if pluginpath is not None:
    -                    template_dir = (Path(pluginpath).parent / "templates").absolute()
    -                elif (Path(".") / "templates").exists():
    -                    template_dir = (Path(".") / "templates").absolute()
    -                else:
    -                    print("Warning: pyplugin couldn't find a template dir")
    -
    -            bp = self.blueprint(name, __name__, template_folder=template_dir)
    -            self.plugins[name].webserver_init(bp)
    -            self.app.register_blueprint(bp, url_prefix=f"/{name}")
    -
    def load_all(self, plugin_file, args=None, template_dir=None) @@ -973,41 +848,6 @@

    Args

    Returns

    String list of PyPlugin class names loaded from the plugin_file

    -
    - -Expand source code - -
    def load_all(self, plugin_file, args=None, template_dir=None):
    -    '''
    -    Given a path to a python file, load every PyPlugin defined in that file
    -    by identifying all classes that subclass PyPlugin and passing them to
    -    self.load()
    -
    -    Args:
    -        plugin_file (str): A path specifying a Python file from which PyPlugin classes should be loaded
    -        args (dict): Optional. A dictionary of arguments to pass to the PyPlugin
    -        template_dir (string): Optional. A directory for template files, passed through to `self.load`.
    -
    -    Returns:
    -        String list of PyPlugin class names loaded from the plugin_file
    -    '''
    -    spec = importlib.util.spec_from_file_location("plugin_file", plugin_file)
    -    if spec is None:
    -        # Likely an invalid path
    -        raise ValueError(f"Unable to load {plugin_file}")
    -
    -    module = importlib.util.module_from_spec(spec)
    -    spec.loader.exec_module(module)
    -
    -    names = []
    -    for name, cls in inspect.getmembers(module, lambda x: inspect.isclass(x)):
    -        if not issubclass(cls, PyPlugin) or cls == PyPlugin:
    -            continue
    -        cls.__name__ = name
    -        self.load(cls, args, template_dir)
    -        names.append(name)
    -    return names
    -
    def load_plugin_class(self, plugin_file, class_names) @@ -1024,114 +864,24 @@

    Returns

    directly to panda.pyplugins.register()

    This avoids the NameError: name 'PyPlugin' is not defined which you would get from directly doing import [class_name] from [plugin_file]

    -
    - -Expand source code - -
    def load_plugin_class(self, plugin_file, class_names):
    -    '''
    -    For backwards compatability with PyPlugins which subclass
    -    PyPlugin without importing it.
    -
    -    Given a path to a python file which has a class that subclasses
    -    PyPlugin, set up the imports correctly such that we can
    -    generate an uninstantiated instance of that class and return
    -    that object.
    -
    -    Note you can also just add `from pandare import PyPlugin` to
    -    the plugin file and then just import the class(es) you want and pass them
    -    directly to panda.pyplugins.register()
    -
    -    This avoids the `NameError: name 'PyPlugin' is not defined` which
    -    you would get from directly doing `import [class_name] from [plugin_file]`
    -    '''
    -    spec = importlib.util.spec_from_file_location(plugin_file.split("/")[-1], plugin_file)
    -    if spec is None:
    -        raise ValueError(f"Unable to resolve plugin {plugin_file}")
    -    plugin = importlib.util.module_from_spec(spec)
    -    plugin.PyPlugin = PyPlugin
    -    spec.loader.exec_module(plugin)
    -    classes = []
    -    success = False
    -    for class_name in class_names:
    -        if not hasattr(plugin, class_name):
    -            continue
    -
    -        cls = getattr(plugin, class_name)
    -        assert issubclass(cls, PyPlugin), f"Class {class_name} does not subclass PyPlugin"
    -        classes.append(cls)
    -        success = True
    -
    -    if not success:
    -        print(f"Warning: {plugin_file} does not contain any of the requested classes {class_names}")
    -
    -    return classes
    -
    def serve(self)
    -
    - -Expand source code - -
    def serve(self):
    -    assert(self.flask)
    -    assert(self.flask_thread is None)
    -    from threading import Thread
    -    self.flask_thread = Thread(target=self._do_serve, daemon=True)
    -    self.flask_thread.start() # TODO: shut down more gracefully?
    -
    def unload(self, pluginclass, do_del=True)

    Given an instance of a PyPlugin or its name, unload it

    -
    - -Expand source code - -
    def unload(self, pluginclass, do_del=True):
    -    '''
    -    Given an instance of a PyPlugin or its name, unload it
    -    '''
    -
    -    if isinstance(pluginclass, str) and pluginclass in self.plugins:
    -        pluginclass = self.plugins[pluginclass]
    -
    -    if not isinstance(pluginclass, PyPlugin):
    -        raise ValueError(f"Unload expects a name of a loaded pyplugin or a PyPlugin instance. Got {pluginclass} with plugin list: {self.plugins}")
    -
    -    # Call uninit method if it's present
    -    if callable(getattr(pluginclass, "uninit", None)):
    -        pluginclass.uninit()
    -
    -    # Remove by name
    -    if do_del:
    -        name = pluginclass.__class__.__name__
    -        del self.plugins[name]
    -
    def unload_all(self)

    Unload all PyPlugins

    -
    - -Expand source code - -
    def unload_all(self):
    -    '''
    -    Unload all PyPlugins
    -    '''
    -    while self.plugins:
    -        name, cls = self.plugins.popitem()
    -        self.unload(cls, do_del=False)
    -
    @@ -1148,8 +898,8 @@

    Returns

    - - + + - + + + @@ -399,90 +399,12 @@

    Static methods

    -
    - -Expand source code - -
    @staticmethod
    -def cli(target):
    -    q = Qcows.get_qcow_info(target)
    -    qcow = Qcows.get_qcow(target, _is_tty=stdout.isatty())
    -    arch = q.arch
    -    # User needs to have the specified arch in order to run the command.
    -    # But if they just want to download/delete files and we find another arch
    -    # we can fetch/delete the files print a warning about how the generatd command won't work.
    -
    -    build_dir = Qcows_cli._find_build_dir(arch) # will set find_executable
    -    panda_args = [path.join(build_dir, f"panda-system-{arch}")]
    -    biospath = path.realpath(path.join(build_dir, "pc-bios"))
    -    panda_args.extend(["-L", biospath])
    -    panda_args.extend(["-os", q.os])
    -
    -    if arch == 'mips64':
    -        panda_args.extend(["-drive", f"file={qcow},if=virtio"])
    -    else:
    -        panda_args.append(qcow)
    -
    -    panda_args.extend(['-m', q.default_mem])
    -
    -    if q.extra_args:
    -        extra_args = shlex_split(q.extra_args)
    -        for x in extra_args:
    -            if " " in x:
    -                panda_args.append(repr(x))
    -            else:
    -                panda_args.append(x)
    -
    -    panda_args.extend(['-loadvm', q.snapshot])
    -
    -    ret = " ".join(panda_args)
    -
    -    if "-display none" in ret:
    -        ret = ret.replace("-display none", "-nographic")
    -
    -    # Replace /home/username with ~ when we can (TTYs)
    -    if stdout.isatty() and 'HOME' in environ:
    -        ret = ret.replace(environ['HOME'], '~')
    -    return ret
    -
    def remove_image(target)
    -
    - -Expand source code - -
    @staticmethod
    -def remove_image(target):
    -    try:
    -        qcow = Qcows.get_qcow(target, download=False, _is_tty=stdout.isatty())
    -    except ValueError:
    -        # No QCOW, we're good!
    -        return
    -
    -    try:
    -        image_data = SUPPORTED_IMAGES[target]
    -    except ValueError:
    -        # Not a valid image? I guess we're good
    -        return
    -
    -    qc = image_data.qcow
    -    if not qc: # Default, get name from url
    -        qc = image_data.url.split("/")[-1]
    -    qcow_path = path.join(VM_DIR, qc)
    -    if path.isfile(qcow_path):
    -        print(f"Deleting {qcow_path}")
    -        remove(qcow_path)
    -
    -    for extra_file in image_data.extra_files or []:
    -        extra_file_path = path.join(VM_DIR, extra_file)
    -        if path.isfile(extra_file_path):
    -            print(f"Deleting {extra_file_path}")
    -            remove(extra_file_path)
    -
    @@ -499,8 +421,8 @@

    Static methods

    - - + + - + + + @@ -766,101 +766,12 @@

    Static methods

    Download the qcow described in the Image object in image_data Store to the output output_path. If the Image includes SHA1 hashes, validate the file was downloaded correctly, otherwise retry once

    -
    - -Expand source code - -
    @staticmethod
    -def download_qcow(image_data, output_path, _is_retry=False, _is_tty=True):
    -    '''
    -    Download the qcow described in the Image object in image_data
    -    Store to the output output_path.
    -    If the Image includes SHA1 hashes, validate the file was downloaded correctly, otherwise retry once
    -    '''
    -
    -    # Check if we have a hash for the base qcow. Then download and vlidate with that hash
    -    qcow_base = image_data.url.split("/")[-1] if '/' in image_data.url else image_data.url
    -    base_hash = None
    -
    -    if image_data.hashes is not None and qcow_base in image_data.hashes:
    -        base_hash = image_data.hashes[qcow_base]
    -
    -    Qcows.get_file([image_data.url] + (image_data.alternate_urls if image_data.alternate_urls is not None else []), output_path, base_hash, _is_tty=_is_tty)
    -
    -    # Download all extra files out of the same directory
    -    url_base = image_data.url[:image_data.url.rfind("/")] + "/"  # Truncate url to last /
    -    for extra_file in image_data.extra_files or []:
    -        extra_file_path = path.join(VM_DIR, extra_file)
    -        extra_hash = None
    -        if image_data.hashes is not None and extra_file in image_data.hashes:
    -            extra_hash = image_data.hashes[extra_file]
    -        Qcows.get_file([url_base + extra_file], extra_file_path, extra_hash, _is_tty=_is_tty) # TODO: support alternate URL here too? Won't work for some hosting options
    -
    def get_file(urls, output_path, sha1hash=None, do_retry=True)
    -
    - -Expand source code - -
    @staticmethod
    -def get_file(urls, output_path, sha1hash=None, do_retry=True, _is_tty=True):
    -    assert len(urls) > 0
    -    url = random.choice([x for x in urls if x is not None])
    -
    -    if _is_tty:
    -        print(f"Downloading required file: {url}")
    -        cmd = ["wget", '--show-progress', '--quiet', url, "-O", output_path+".tmp"]
    -    else:
    -        print(f"Please wait for download of required file: {url}", file=stderr)
    -        cmd = ["wget", "--quiet", url, "-O", output_path+".tmp"]
    -
    -    try:
    -        with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
    -            for line in p.stdout:
    -                print(line, end='')
    -
    -        if p.returncode != 0:
    -            raise CalledProcessError(p.returncode, p.args)
    -
    -        # Check hash if we have one
    -        if sha1hash is not None:
    -            if _is_tty:
    -                print(f"Validating file hash")
    -            sha1 = hashlib.sha1()
    -
    -            with open(output_path+".tmp", 'rb') as f:
    -                while True:
    -                    data = f.read(65536) #64kb chunks
    -                    if not data:
    -                        break
    -                    sha1.update(data)
    -            computed_hash = sha1.hexdigest()
    -            if computed_hash != sha1hash:
    -                raise ValueError(f"{url} has hash {computed_hash} vs expected hash {sha1hash}")
    -            # Hash matches, move .tmp file to actual path
    -            move(output_path+".tmp", output_path)
    -        else:
    -            # No hash, move .tmp file to actual path
    -            move(output_path+".tmp", output_path)
    -
    -
    -    except Exception as e:
    -        logger.info("Download failed, deleting partial file: %s", output_path)
    -        remove(output_path+".tmp")
    -
    -        if do_retry:
    -            if _is_tty and do_retry:
    -                print("Hash mismatch - retrying")
    -            Qcows.get_file([url], output_path, sha1hash, do_retry=False, _is_tty=_is_tty)
    -        else:
    -            # Not retrying again, fatal - leave any partial files though
    -            raise RuntimeError(f"Unable to download expeted file from {url} even after retrying: {e}") from None
    -    logger.debug("Downloaded %s to %s", url, output_path)
    -
    def get_qcow(name=None, download=True) @@ -886,63 +797,6 @@

    Raises

    RuntimeError
    if the architecture is unsupported or the necessary files could not be downloaded
    -
    - -Expand source code - -
    @staticmethod
    -def get_qcow(name=None, download=True, _is_tty=True):
    -    '''
    -    Given a generic name of a qcow in `pandare.qcows.SUPPORTED_IMAGES` or a path to a qcow, return the path. Defaults to i386
    -
    -    Args:
    -        name (str): generic name or path to qcow
    -        download (bool, default True): should the qcow be downloaded if necessary
    -
    -    Returns:
    -        string: Path to qcow
    -
    -    Raises:
    -        ValueError: if download is set to False and the qcow is not present
    -        RuntimeError: if the architecture is unsupported or the necessary files could not be downloaded
    -    '''
    -    if name is None:
    -        logger.warning("No qcow name provided. Defaulting to i386")
    -        name = "i386"
    -
    -    if path.isfile(name):
    -        logger.debug("Provided qcow name appears to be a path, returning it directly: %s", name)
    -        return name
    -
    -    name = name.lower() # Case insensitive. Assumes supported_images keys are lowercase
    -    if name not in SUPPORTED_IMAGES.keys():
    -        raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))
    -
    -    image_data = SUPPORTED_IMAGES[name]
    -    qc = image_data.qcow
    -    if not qc: # Default, get name from url
    -        qc = image_data.url.split("/")[-1]
    -    qcow_path = path.join(VM_DIR,qc)
    -    makedirs(VM_DIR, exist_ok=True)
    -
    -    # We need to downlaod if the QCOW or any extra files are missing
    -    # If the files are present on disk, assume they're okay
    -    needs_download = not path.isfile(qcow_path)
    -
    -    if not needs_download:
    -        for extra_file in image_data.extra_files or []:
    -            extra_file_path = path.join(VM_DIR, extra_file)
    -            if not path.isfile(extra_file_path):
    -                needs_download = True
    -                break
    -
    -    if needs_download and download:
    -        Qcows.download_qcow(image_data, qcow_path, _is_tty=_is_tty)
    -    elif needs_download:
    -        raise ValueError("Qcow is not on disk and download option is disabled")
    -
    -    return qcow_path
    -
    def get_qcow_info(name=None) @@ -959,36 +813,6 @@

    Returns

    Image
    Instance of the Image class for a qcow
    -
    - -Expand source code - -
    @staticmethod
    -def get_qcow_info(name=None):
    -    '''
    -    Get information about supported image as specified by name.
    -
    -    Args:
    -        name (str): String idenfifying a qcow supported
    -
    -    Returns:
    -        Image: Instance of the Image class for a qcow
    -    '''
    -    if name is None:
    -        logger.warning("No qcow name provided. Defaulting to i386")
    -        name = "i386"
    -
    -    if path.isfile(name):
    -        raise RuntimeError("TODO: can't automatically determine system info from custom qcows. Use one of: {}".format(", ".join(SUPPORTED_IMAGES.keys())))
    -
    -    name = name.lower() # Case insensitive. Assumes supported_arches keys are lowercase
    -    if name not in SUPPORTED_IMAGES.keys():
    -        raise RuntimeError("Architecture {} is not in list of supported names: {}".format(name, ", ".join(SUPPORTED_IMAGES.keys())))
    -
    -    r = SUPPORTED_IMAGES[name]
    -    # Move properties in .arch to being in the main object
    -    return r
    -
    def qcow_from_arg(idx=1) @@ -1005,63 +829,12 @@

    Returns

    string
    Path to qcow
    -
    - -Expand source code - -
    @staticmethod
    -def qcow_from_arg(idx=1):
    -    '''
    -    Given an index into argv, call get_qcow with that arg if it exists, else with None
    -
    -    Args:
    -        idx (int): an index into argv
    -
    -    Returns:
    -        string: Path to qcow
    -    '''
    -    from sys import argv
    -
    -    if len(argv) > idx:
    -        return Qcows.get_qcow(argv[idx])
    -    else:
    -        return Qcows.get_qcow()
    -
    def remove_image(target)
    -
    - -Expand source code - -
    @staticmethod
    -def remove_image(target):
    -    try:
    -        qcow = Qcows.get_qcow(target, download=False)
    -    except ValueError:
    -        # No QCOW, we're good!
    -        return
    -
    -    try:
    -        image_data = SUPPORTED_IMAGES[target]
    -    except ValueError:
    -        # Not a valid image? I guess we're good
    -        return
    -
    -    qc = image_data.qcow
    -    if not qc: # Default, get name from url
    -        qc = image_data.url.split("/")[-1]
    -    qcow_path = path.join(VM_DIR, qc)
    -    remove(qcow_path)
    -
    -    for extra_file in image_data.extra_files or []:
    -        extra_file_path = path.join(VM_DIR, extra_file)
    -        if path.isfile(extra_file_path):
    -            remove(extra_file_path)
    -
    @@ -1078,8 +851,8 @@

    Returns

    - - + + - + + + @@ -531,25 +531,6 @@

    Raises

    Exception
    no such log member
    -
    - -Expand source code - -
    def disable(self, name):
    -    """Disables the specified log level.
    -
    -    Args:
    -        name (str): name of log type (for list -d ?) or comma
    -        separated list of names.
    -
    -    Raises:
    -        Exception: no such log member
    -    """
    -    if name.upper() in self.log_members:
    -        self._unset_log_bit(self.log_members[name.upper()])
    -    else:
    -        raise Exception("no such log member: " + name)
    -
    def enable(self, name) @@ -567,42 +548,12 @@

    Raises

    Exception
    no such log member
    -
    - -Expand source code - -
    def enable(self, name):
    -    """Enables the specified log level.
    -
    -    Args:
    -        name (str): name of the log type (for list -d ?) or comma
    -        separated list of names.
    -
    -    Raises:
    -        Exception: no such log member
    -    """
    -    if name.upper() in self.log_members:
    -        self._set_log_bit(self.log_members[name.upper()])
    -    else:
    -        raise Exception("no such log member: " + name)
    -
    def log(self, msg)

    Output message to PANDA log

    -
    - -Expand source code - -
    def log(self, msg):
    -    """Output message to PANDA log"""
    -    # it's actually cheaper than asking if it is then setting it
    -    self.enable("panda")
    -    msg += "\n"
    -    self.panda.libpanda.qemu_log(msg.encode())
    -
    def output_to_file(self, file_name, append=True) @@ -617,69 +568,24 @@

    Args

    append : bool, optional
    File append setting
    -
    - -Expand source code - -
    def output_to_file(self, file_name, append=True):
    -    """Change qemu log file to file_name. If append is True, output will
    -    be appended to the file. Otherwise, the file will be overwritten.
    -
    -    Args:
    -        file_name ([str]): path to the file to output to
    -        append (bool, optional): File append setting
    -    """
    -    # qemu_logfile out previous file (if any)
    -    self.remove_log_file()
    -
    -    # open new file
    -    mode = b"a" if append else b"w"
    -    self.panda.libpanda.qemu_logfile = self.panda.libpanda.fopen(file_name.encode(),  mode)
    -
    def output_to_stderr(self)

    Removes the current log file and outputs to stderr

    -
    - -Expand source code - -
    def output_to_stderr(self):
    -    """Removes the current log file and outputs to stderr"""
    -    self.remove_log_file()
    -    self.panda.libpanda.qemu_logfile = self.panda.libpanda.stderr
    -
    def output_to_stout(self)

    Removes the current log file and outputs to stdout

    -
    - -Expand source code - -
    def output_to_stout(self):
    -    """Removes the current log file and outputs to stdout"""
    -    self.remove_log_file()
    -    self.panda.libpanda.qemu_logfile = self.panda.libpanda.stdout
    -
    def remove_log_file(self)

    Removes the current log file. By default outputs to stdout.

    -
    - -Expand source code - -
    def remove_log_file(self):
    -    """Removes the current log file. By default outputs to stdout."""
    -    self.panda.libpanda.qemu_log_close()
    -
    @@ -696,8 +602,8 @@

    Args

    - - + + - + + + @@ -270,30 +270,12 @@

    Methods

    -
    - -Expand source code - -
    def get_labels(self):
    -    ret = []
    -    for l in self:
    -        ret.append(l)
    -    #self.reset() # Reset so we can query again
    -    return ret
    -
    def reset(self)
    -
    - -Expand source code - -
    def reset(self):
    -    self.taint2.taint2_query_results_iter(self.qr)
    -
    @@ -310,8 +292,8 @@

    Methods

    - - + + - + + + @@ -181,8 +181,8 @@

    Sub-modules

    - - + + - + + + @@ -409,22 +409,6 @@

    Functions

    Decorator to ensure a function isn't run in the main thread

    -
    - -Expand source code - -
    def blocking(func):
    -    """
    -    Decorator to ensure a function isn't run in the main thread
    -    """
    -    @wraps(func)
    -    def wrapper(*args, **kwargs):
    -        assert (current_thread() is not main_thread()), "Blocking function run in main thread"
    -        return func(*args, **kwargs)
    -    wrapper.__blocking__ = True
    -    wrapper.__name__ = func.__name__ + " (with async thread)"
    -    return wrapper
    -
    def find_build_dir(arch_name=None, find_executable=False) @@ -441,68 +425,12 @@

    Functions

    3) Raise RuntimeError if we find nothing

    If arch_name is none, we'll search for any supported architecture and return the first one we find.

    -
    - -Expand source code - -
    def find_build_dir(arch_name=None, find_executable=False):
    -    '''
    -    Find directory containing the binaries we care about (i.e., ~git/panda/build). If
    -    find_executable is False, we're looking for [arch]-softmmu/libpanda-[arch].so. If
    -    find_executable is True, we're looking for [arch]-softmmu/panda-system-[arch] and we'll return
    -    the parent dir of the executable (i.e., ~/git/panda/build/x86_64-softmmu/)
    -
    -    We do this by searching paths in the following order:
    -        1) Check relative to file (in the case of installed packages)
    -        2) Check in../ ../../../build/
    -        2) Search path if user is looking for an executable instead of a library
    -        3) Raise RuntimeError if we find nothing
    -
    -    If arch_name is none, we'll search for any supported architecture and return the first
    -    one we find.
    -    '''
    -    arches = ['i386', 'x86_64', 'arm', 'aarch64', 'ppc', 'mips', 'mipsel', 'mips64']
    -
    -    if arch_name is None:
    -        e = None
    -        for arch in arches:
    -            try:
    -                return _find_build_dir(arch, find_executable)
    -            except RuntimeError as _e:
    -                e = _e
    -        if e:
    -            raise e
    -
    -    elif arch_name not in arches:
    -        raise ValueError(f"Unsupported architecture name: {arch_name}, allowed values are: {arches}")
    -    return _find_build_dir(arch_name, find_executable)
    -
    def make_iso(directory, iso_path)

    Generate an iso from a directory

    -
    - -Expand source code - -
    def make_iso(directory, iso_path):
    -    '''
    -    Generate an iso from a directory
    -    '''
    -    with open(devnull, "w") as DEVNULL:
    -        if platform.startswith('linux'):
    -            check_call([
    -                'genisoimage', '-RJ', '-max-iso9660-filenames', '-o', iso_path, directory
    -            ], stderr=STDOUT if debug else DEVNULL)
    -        elif platform == 'darwin':
    -            check_call([
    -                'hdiutil', 'makehybrid', '-hfs', '-joliet', '-iso', '-o', iso_path, directory
    -            ], stderr=STDOUT if debug else DEVNULL)
    -        else:
    -            raise NotImplementedError("Unsupported operating system!")
    -
    def progress(msg) @@ -510,75 +438,24 @@

    Functions

    Print a message with a green "[PYPANDA]" prefix if in a tty otherwise just print the message

    -
    - -Expand source code - -
    def progress(msg):
    -    """
    -    Print a message with a green "[PYPANDA]" prefix if in a tty
    -    otherwise just print the message
    -    """
    -    if stdout.isatty():
    -        print(Fore.GREEN + '[PYPANDA] ' + Fore.RESET + Style.BRIGHT + msg +Style.RESET_ALL)
    -    else:
    -        print(f"[PYPANDA] {msg}")
    -
    def rr2_contains_member(name, member)
    -
    - -Expand source code - -
    def rr2_contains_member(name, member):
    -    rr2_filename = rr2_name(name)
    -    contains_member = False
    -    if rr2_recording(rr2_filename):
    -        try:
    -            tar = tarfile.open(rr2_filename)
    -            tar.getmember(member)
    -            contains_member = True
    -        except (KeyError, IsADirectoryError, FileNotFoundError, tarfile.ReadError):
    -            pass
    -    return contains_member
    -
    def rr2_name(name)
    -
    - -Expand source code - -
    def rr2_name(name):
    -    return name if name.endswith(".rr2") else name + ".rr2"
    -
    def rr2_recording(name)
    -
    - -Expand source code - -
    def rr2_recording(name):
    -    def is_gzip(name):
    -        rr = open(name, "rb")
    -        return rr.read(2) == b'\x1f\x8b'
    -
    -    rr2_filename = rr2_name(name)
    -    if isfile(rr2_filename) and is_gzip(rr2_filename):
    -        return True
    -    return False
    -
    def telescope(panda, cpu, val) @@ -592,51 +469,6 @@

    Functions

    3) It's an invalid pointer 4) It's the 5th time we've done this, break

    TODO Should use memory protections to determine string/code/data

    -
    - -Expand source code - -
    def telescope(panda, cpu, val):
    -    '''
    -    Given a value, check if it's a pointer by seeing if we can map it to physical memory.
    -    If so, recursively print where it points
    -    to until
    -    1) It points to a string (then print the string)
    -    2) It's code (then disassembly the insn)
    -    3) It's an invalid pointer
    -    4) It's the 5th time we've done this, break
    -
    -    TODO Should use memory protections to determine string/code/data
    -    '''
    -    for _ in range(5): # Max chain of 5
    -        print("-> 0x{:0>8x}".format(val), end="\t")
    -
    -        if val == 0:
    -            print()
    -            return
    -        # Consider that val points to a string. Test and print
    -        try:
    -            str_data = panda.virtual_memory_read(cpu, val, 16)
    -        except ValueError:
    -            print()
    -            return
    -
    -        str_val = ""
    -        for d in str_data:
    -            if d >= 0x20 and d < 0x7F:
    -                str_val += chr(d)
    -            else:
    -                break
    -        if len(str_val) > 2:
    -            print("== \"{}\"".format(str_val))
    -            return
    -
    -
    -        data = str_data[:4] # Truncate to 4 bytes
    -        val = int.from_bytes(data, byteorder='little')
    -
    -    print("-> ...")
    -
    def warn(msg) @@ -644,20 +476,6 @@

    Functions

    Print a message with a red "[PYPANDA]" prefix if in a tty otherwise just print the message

    -
    - -Expand source code - -
    def warn(msg):
    -    """
    -    Print a message with a red "[PYPANDA]" prefix if in a tty
    -    otherwise just print the message
    -    """
    -    if stdout.isatty():
    -        print(Fore.RED + '[PYPANDA] ' + Fore.RESET + Style.BRIGHT + msg +Style.RESET_ALL)
    -    else:
    -        print(f"[PYPANDA] {msg}")
    -
    @@ -747,8 +565,8 @@

    Ancestors

    - - + +