Module pandare.cosi
Expand source code
# TODO: rename to cosi
COSI = 'cosi'
from dataclasses import dataclass
class VolatilitySymbol:
'''
A reference to an entry in the volatility symbol table
'''
def __init__(self, panda, raw_ptr):
self.panda = panda
self.inner = raw_ptr
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 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)
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
@dataclass
class VolatilityStructField:
'''
A single field in a volatility struct
'''
name: str
offset: int
type_name: str
class VolatilityStruct:
'''
A reference to a struct in the volatility symbol table
'''
def __init__(self, panda, raw_ptr):
self.panda = panda
self.inner = raw_ptr
def __getitem__(self, item):
if type(item) is int:
name = self.get_field_by_index(item)
if name:
offset = self.offset_of_field(name)
type_name = self.type_of_field(name)
return VolatilityStructField(
name=name,
offset=offset,
type_name=type_name
)
else:
raise IndexError("Index {item} is out of range of the length of the struct fields")
elif type(item) is str:
name = item
offset = self.offset_of_field(name)
type_name = self.type_of_field(name)
return VolatilityStructField(
name=name,
offset=offset,
type_name=type_name
)
else:
raise Exception(f"Invalid type {type(item)} for indexing VolatilityStruct")
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
'''
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
'''
name = name.encode('utf8')
name = self.panda.ffi.new("char[]", name)
return self.panda.plugins[COSI].offset_of_field(self.inner, name)
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
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 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 at(self, ptr):
'''
Get a CosiGuestPointer of this type
'''
type_name = self.name()
return CosiGuestPointer(self.panda, type_name, ptr)
class VolatilityBaseType:
'''
A reference to a base type in the volatility symbol table
'''
def __init__(self, panda, raw_ptr):
self.panda = panda
self.inner = raw_ptr
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
'''
return self.panda.plugins[COSI].size_of_base_type(self.inner)
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)
class Cosi:
'''
Object to interact with the `cosi` PANDA plugin. An instance can be foudn at
`panda.cosi`, where `panda` is a `Panda` object.
'''
def __init__(self, panda):
self.panda = panda
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_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 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 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 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 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)
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 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):
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)
# ============= Task API =============
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 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 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 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)
class CosiFiles:
def __init__(self, panda, files):
self.inner = files
self.panda = panda
def __del__(self):
self.panda.plugins[COSI].free_cosi_files(self.inner)
self.inner = None
def __len__(self) -> int:
return self.panda.plugins[COSI].cosi_files_len(self.inner)
def __getitem__(self, key: int):
if not type(key) is int:
raise TypeError("CosiFiles must be indexed with an integer")
file_ptr = self.panda.plugins[COSI].cosi_files_get(self.inner, key)
if file_ptr == self.panda.ffi.NULL:
raise IndexError("Integer {} out of bounds of CosiFiles len")
return CosiFile(self.panda, file_ptr, hold_ref=self)
def __iter__(self):
for i in range(len(self)):
yield self[i]
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)
class CosiFile:
def __init__(self, panda, file_ptr, hold_ref=None):
self.inner = file_ptr
self.panda = panda
self.hold_ref = hold_ref
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')
def __getattr__(self, key):
if key == "name":
return self.get_name()
attr = getattr(self.inner, key, None)
if not attr is None:
return attr
return getattr(self.inner.file_struct, key)
class CosiThread:
def __init__(self, panda, thread):
self.inner = thread
self.panda = panda
def __del__(self):
self.panda.plugins[COSI].free_thread(self.inner)
self.inner = None
def __getattr__(self, key):
return getattr(self.inner, key)
class CosiProcess:
def __init__(self, panda, proc, hold_ref=None):
self.panda = panda
self.inner = proc
self.hold_ref = hold_ref
def __del__(self):
if self.hold_ref is None:
self.panda.plugins[COSI].free_process(self.inner)
self.inner = None
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 __getattr__(self, key):
if key == "name":
return self.get_name()
attr = getattr(self.inner, key, None)
if not attr is None:
return attr
return getattr(self.inner.task, key)
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)
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 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)
class CosiProcList:
def __init__(self, panda, inner):
self.inner = inner
self.panda = panda
def __del__(self):
self.panda.plugins[COSI].cosi_free_proc_list(self.inner)
self.inner = None
def __len__(self) -> int:
return self.panda.plugins[COSI].cosi_proc_list_len(self.inner)
def __getitem__(self, key: int):
if not type(key) is int:
raise TypeError("CosiProcList must be indexed with an integer")
file_ptr = self.panda.plugins[COSI].cosi_proc_list_get(self.inner, key)
if file_ptr == self.panda.ffi.NULL:
raise IndexError("Integer {} out of bounds of CosiProcList length")
return CosiProcess(self.panda, file_ptr, hold_ref=self)
def __iter__(self):
for i in range(len(self)):
yield self[i]
class CosiModule:
def __init__(self, panda, proc, hold_ref=None):
self.panda = panda
self.inner = proc
self.hold_ref = hold_ref
def __del__(self):
if self.hold_ref is None:
pass # TODO: free module if it's not a reference
self.inner = None
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')
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 __getattr__(self, key):
if key == "name":
return self.get_name()
elif key == "file":
return self.get_file()
attr = getattr(self.inner, key, None)
if not attr is None:
return attr
return getattr(self.inner.task, key)
class CosiMappings:
def __init__(self, panda, inner):
self.inner = inner
self.panda = panda
def __del__(self):
self.panda.plugins[COSI].cosi_free_mappings(self.inner)
self.inner = None
def __len__(self) -> int:
return self.panda.plugins[COSI].cosi_mappings_len(self.inner)
def __getitem__(self, key: int):
if not type(key) is int:
raise TypeError("CosiMappings must be indexed with an integer")
file_ptr = self.panda.plugins[COSI].cosi_mappings_get(self.inner, key)
if file_ptr == self.panda.ffi.NULL:
raise IndexError("Integer {} out of bounds of CosiProcList length")
return CosiModule(self.panda, file_ptr, hold_ref=self)
def __iter__(self):
for i in range(len(self)):
yield self[i]
import struct
struct_types = {
'float': ('f', 4),
'double': ('d', 8),
'char': ('c', 1),
'_Bool': ('?', 1),
'short': ('h', 2),
'unsigned short': ('H', 2),
'int': ('i', 4),
'unsigned int': ('I', 4),
'long': ('l', 4),
'unsigned long': ('L', 4),
'long long': ('q', 8),
'unsigned long long': ('Q', 8),
}
types_from_size = {
1: 'b',
2: 'h',
4: 'i',
8: 'q'
}
class CosiIntrusiveListAccessor:
def __init__(self, panda, ptr):
self._panda = panda
self._ptr = ptr
def __getattr__(self, name):
list_head = getattr(self._ptr, name)
if list_head._type_name != 'list_head':
raise Exception(f"Field {self._ptr._type_name}.{name} not a list_head*")
field_offset = self._ptr._type[name].offset
ptrs = []
head = list_head.next.prev
current = list_head.next
while head._ptr != current._ptr:
addr = current._ptr - field_offset
ptrs.append(CosiGuestPointer(self._panda, self._ptr._type_name, addr))
current = current.next
return ptrs
class CosiGuestPointer:
'''
A type representing a pointer for a data structure in the kernel
'''
def __init__(self, panda, type_name, ptr, parent=None):
self._panda = panda
self._type_name = type_name
self._ptr = ptr
self._type = panda.cosi.type_from_name(type_name)
self._parent = parent
def __repr__(self):
return f'<CosiGuestPointer of type {self._type_name} at {hex(self._ptr)}>'
def _read_type(self, field_ptr: int, field_type: str, fallback=False):
panda = self._panda
cpu = panda.get_cpu()
# pointer
if field_type[-1] == '*':
pointer = panda.virtual_memory_read(cpu, field_ptr, panda.bits // 8, fmt='int')
inner_type = field_type[:-1]
if inner_type.startswith('struct '):
inner_type = inner_type[len('struct '):]
return CosiGuestPointer(panda, inner_type, pointer, parent=self)
# inline struct
elif field_type.startswith('struct '):
field_type_inner = field_type[len('struct '):]
return CosiGuestPointer(panda, field_type_inner, field_ptr, parent=self)
# char array
elif field_type.startswith('char[') and field_type[-1] == ']':
length = int(field_type[len('char['):-1])
data = panda.virtual_memory_read(cpu, field_ptr, length)
try:
length = data.index(b'\0')
data = data[:length]
except ValueError:
pass
return data.decode('utf8')
# non-char array
elif field_type[-1] == ']':
return CosiGuestPointer(panda, field_type, field_ptr, parent=self)
# basic type
elif field_type in struct_types:
specifier, size = struct_types[field_type]
val_bytes = panda.virtual_memory_read(cpu, field_ptr, size)
return struct.unpack(
('<' if panda.endianness == 'little' else '>') + specifier, val_bytes
)[0]
# platform-specific base types
elif base_type := panda.cosi.base_type_from_name(field_type):
size = base_type.size()
is_signed = base_type.is_signed()
specifier = types_from_size[size]
if not is_signed:
specifier = specifier.upper()
val_bytes = panda.virtual_memory_read(cpu, field_ptr, size)
return struct.unpack(
('<' if panda.endianness == 'little' else '>') + specifier, val_bytes
)[0]
elif fallback:
inner_type = field_type
if inner_type.startswith('struct '):
inner_type = inner_type[len('struct '):]
return CosiGuestPointer(panda, inner_type, field_ptr, parent=self)
else:
raise Exception(f"Dereferencing type {field_type} is unsupported")
def _type_len(self, inner_type):
panda = self._panda
# array of pointer
if inner_type[-1] == '*':
inner_type_size = panda.bits // 8
# array of primitive
elif inner_type in struct_types:
inner_type_size = struct_types[inner_type][1]
# array of platform-specific base types
elif base_type := panda.cosi.base_type_from_name(inner_type):
inner_type_size = base_type.size()
# array of structs
elif struct_type := panda.cosi.type_from_name(inner_type):
inner_type_size = struct_type.size()
else:
raise ValueError(f'sizeof({inner_type}) cannot be determined')
return inner_type_size
def __getattr__(self, name):
field = self._type[name]
field_ptr = self._ptr + field.offset
field_type = field.type_name
return self._read_type(field_ptr, field_type)
def __getitem__(self, item):
panda = self._panda
# pointer to array
if self._type_name[-1] == ']':
inner_type, count = self._type_name[:-1].split('[')
length = int(count)
inner_type_size = self._type_len(inner_type)
# indexing a single field
if type(item) is int:
index = item
offset = inner_type_size * index
return self._read_type(self._ptr + offset, inner_type, fallback=True)
# slicing the array
elif type(item) is slice:
out = []
for i in range(0, length)[item]:
offset = inner_type_size * i
item = self._read_type(self._ptr + offset, inner_type, fallback=True)
out.append(item)
return out
else:
raise ValueError(f'CosiGuestPointer cannot be sliced with type of {type(item)}')
# indexing pointer
elif type(item) is int:
index = item
inner_type_size = self._type_len(self._type_name)
offset = inner_type_size * index
return self._read_type(self._ptr + offset, self._type_name, fallback=True)
# slicing pointer
elif type(item) is slice:
if item.stop is None or item.stop < 0:
raise ValueError("Cannot slice a pointer without an end")
inner_type_size = self._type_len(self._type_name)
out = []
for i in range(item.start or 0, item.stop, item.step or 1):
offset = inner_type_size * i
item = self._read_type(self._ptr + offset, self._type_name, fallback=True)
out.append(item)
return out
# accessing field
else:
return self.__getattr__(item)
def __dir__(self):
if self._type is None:
return ['null_terminated', 'get_raw_ptr', 'cast']
else:
return [field[2] for field in self._type.fields()]
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 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:
'''
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 deref(self):
return self[0]
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}*")
def get_raw_ptr(self) -> int:
'''
Get the address in memory this points to
'''
return self._ptr
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
'''
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)
Classes
class Cosi (panda)
-
Object to interact with the
cosi
PANDA plugin. An instance can be foudn atpanda.cosi
, wherepanda
is aPanda
object.Expand source code
class Cosi: ''' Object to interact with the `cosi` PANDA plugin. An instance can be foudn at `panda.cosi`, where `panda` is a `Panda` object. ''' def __init__(self, panda): self.panda = panda 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_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 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 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 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 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) 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 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): 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) # ============= Task API ============= 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 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 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 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)
Methods
def base_type_from_name(self, name: str) ‑> VolatilityBaseType
-
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
-
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
-
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)
class CosiFile (panda, file_ptr, hold_ref=None)
-
Expand source code
class CosiFile: def __init__(self, panda, file_ptr, hold_ref=None): self.inner = file_ptr self.panda = panda self.hold_ref = hold_ref 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') def __getattr__(self, key): if key == "name": return self.get_name() attr = getattr(self.inner, key, None) if not attr is None: return attr return getattr(self.inner.file_struct, key)
Methods
def get_name(self) ‑> str
-
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')
class CosiFiles (panda, files)
-
Expand source code
class CosiFiles: def __init__(self, panda, files): self.inner = files self.panda = panda def __del__(self): self.panda.plugins[COSI].free_cosi_files(self.inner) self.inner = None def __len__(self) -> int: return self.panda.plugins[COSI].cosi_files_len(self.inner) def __getitem__(self, key: int): if not type(key) is int: raise TypeError("CosiFiles must be indexed with an integer") file_ptr = self.panda.plugins[COSI].cosi_files_get(self.inner, key) if file_ptr == self.panda.ffi.NULL: raise IndexError("Integer {} out of bounds of CosiFiles len") return CosiFile(self.panda, file_ptr, hold_ref=self) def __iter__(self): for i in range(len(self)): yield self[i] 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)
Methods
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.
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)
class CosiGuestPointer (panda, type_name, ptr, parent=None)
-
A type representing a pointer for a data structure in the kernel
Expand source code
class CosiGuestPointer: ''' A type representing a pointer for a data structure in the kernel ''' def __init__(self, panda, type_name, ptr, parent=None): self._panda = panda self._type_name = type_name self._ptr = ptr self._type = panda.cosi.type_from_name(type_name) self._parent = parent def __repr__(self): return f'<CosiGuestPointer of type {self._type_name} at {hex(self._ptr)}>' def _read_type(self, field_ptr: int, field_type: str, fallback=False): panda = self._panda cpu = panda.get_cpu() # pointer if field_type[-1] == '*': pointer = panda.virtual_memory_read(cpu, field_ptr, panda.bits // 8, fmt='int') inner_type = field_type[:-1] if inner_type.startswith('struct '): inner_type = inner_type[len('struct '):] return CosiGuestPointer(panda, inner_type, pointer, parent=self) # inline struct elif field_type.startswith('struct '): field_type_inner = field_type[len('struct '):] return CosiGuestPointer(panda, field_type_inner, field_ptr, parent=self) # char array elif field_type.startswith('char[') and field_type[-1] == ']': length = int(field_type[len('char['):-1]) data = panda.virtual_memory_read(cpu, field_ptr, length) try: length = data.index(b'\0') data = data[:length] except ValueError: pass return data.decode('utf8') # non-char array elif field_type[-1] == ']': return CosiGuestPointer(panda, field_type, field_ptr, parent=self) # basic type elif field_type in struct_types: specifier, size = struct_types[field_type] val_bytes = panda.virtual_memory_read(cpu, field_ptr, size) return struct.unpack( ('<' if panda.endianness == 'little' else '>') + specifier, val_bytes )[0] # platform-specific base types elif base_type := panda.cosi.base_type_from_name(field_type): size = base_type.size() is_signed = base_type.is_signed() specifier = types_from_size[size] if not is_signed: specifier = specifier.upper() val_bytes = panda.virtual_memory_read(cpu, field_ptr, size) return struct.unpack( ('<' if panda.endianness == 'little' else '>') + specifier, val_bytes )[0] elif fallback: inner_type = field_type if inner_type.startswith('struct '): inner_type = inner_type[len('struct '):] return CosiGuestPointer(panda, inner_type, field_ptr, parent=self) else: raise Exception(f"Dereferencing type {field_type} is unsupported") def _type_len(self, inner_type): panda = self._panda # array of pointer if inner_type[-1] == '*': inner_type_size = panda.bits // 8 # array of primitive elif inner_type in struct_types: inner_type_size = struct_types[inner_type][1] # array of platform-specific base types elif base_type := panda.cosi.base_type_from_name(inner_type): inner_type_size = base_type.size() # array of structs elif struct_type := panda.cosi.type_from_name(inner_type): inner_type_size = struct_type.size() else: raise ValueError(f'sizeof({inner_type}) cannot be determined') return inner_type_size def __getattr__(self, name): field = self._type[name] field_ptr = self._ptr + field.offset field_type = field.type_name return self._read_type(field_ptr, field_type) def __getitem__(self, item): panda = self._panda # pointer to array if self._type_name[-1] == ']': inner_type, count = self._type_name[:-1].split('[') length = int(count) inner_type_size = self._type_len(inner_type) # indexing a single field if type(item) is int: index = item offset = inner_type_size * index return self._read_type(self._ptr + offset, inner_type, fallback=True) # slicing the array elif type(item) is slice: out = [] for i in range(0, length)[item]: offset = inner_type_size * i item = self._read_type(self._ptr + offset, inner_type, fallback=True) out.append(item) return out else: raise ValueError(f'CosiGuestPointer cannot be sliced with type of {type(item)}') # indexing pointer elif type(item) is int: index = item inner_type_size = self._type_len(self._type_name) offset = inner_type_size * index return self._read_type(self._ptr + offset, self._type_name, fallback=True) # slicing pointer elif type(item) is slice: if item.stop is None or item.stop < 0: raise ValueError("Cannot slice a pointer without an end") inner_type_size = self._type_len(self._type_name) out = [] for i in range(item.start or 0, item.stop, item.step or 1): offset = inner_type_size * i item = self._read_type(self._ptr + offset, self._type_name, fallback=True) out.append(item) return out # accessing field else: return self.__getattr__(item) def __dir__(self): if self._type is None: return ['null_terminated', 'get_raw_ptr', 'cast'] else: return [field[2] for field in self._type.fields()] 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 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: ''' 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 deref(self): return self[0] 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}*") def get_raw_ptr(self) -> int: ''' Get the address in memory this points to ''' return self._ptr 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 ''' 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)
Methods
def after(self, type_name: str)
-
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
-
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 thelist_head*
came from.For example, if current_task.children is a
list_head*
, the parent would becurrent_task
(of typetask_struct
), so the list would default to being a list oftask_struct
(of whichsibling
should be passed a value of"sibling"
).So if one does
current_task.children.as_linux_list("sibling")
it will return a list ofCosiGuestPointer
s pointing totask_struct
s.Expand source code
def as_linux_list(self, sibling: str, list_entry_type=None) -> list: ''' Takes a list_head* and reads it into a list. If no `list_entry_type` is provided, it is assumed to be equivelant to the parent struct the `list_head*` came from. For example, if current_task.children is a `list_head*`, the parent would be `current_task` (of type `task_struct`), so the list would default to being a list of `task_struct` (of which `sibling` should be passed a value of `"sibling"`). So if one does `current_task.children.as_linux_list("sibling")` it will return a list of `CosiGuestPointer`s pointing to `task_struct`s. ''' if type(list_entry_type) is str: list_type_name = list_entry_type list_entry_type = self._panda.cosi.type_from_name(list_entry_type) elif list_entry_type is None: parent = self._parent if parent is None: raise ValueError("The list_head has no parent and `list_entry_type` was not provided") list_entry_type = parent._type list_type_name = parent._type_name field_offset = list_entry_type.offset_of_field(sibling) ptrs = [] head = self.next current = head while head._ptr != current.next._ptr and current._ptr != self._ptr: addr = current._ptr - field_offset ptrs.append(CosiGuestPointer(self._panda, list_type_name, addr)) current = current.next return ptrs
def before(self, type_name: str)
-
Returns a pointer to the data following the current pointer of type
type_name
Expand source code
def before(self, type_name: str): ''' Returns a pointer to the data following the current pointer of type `type_name` ''' size = self._panda.cosi.type_from_name(type_name).size() print(size) ptr = self._ptr - size return CosiGuestPointer(self._panda, type_name, ptr)
def cast(self, cast_to: str)
-
Cast to a pointer of another type
Expand source code
def cast(self, cast_to: str): ''' Cast to a pointer of another type ''' return CosiGuestPointer(self._panda, cast_to, self._ptr, parent=self._parent)
def container_of(self, type_name: str, field_name: str)
-
Get a pointer to the struct containing this type
Expand source code
def container_of(self, type_name: str, field_name: str): ''' Get a pointer to the struct containing this type ''' container_type = self._panda.cosi.type_from_name(type_name) offset_in_container = container_type.offset_of_field(field_name) return container_type.at(self._ptr - offset_in_container)
def deref(self)
-
Expand source code
def deref(self): return self[0]
def get_raw_ptr(self) ‑> int
-
Get the address in memory this points to
Expand source code
def get_raw_ptr(self) -> int: ''' Get the address in memory this points to ''' return self._ptr
def null_terminated(self) ‑> str
-
Read a CosiGuestPointer for a
char*
as a null-terminated stringExpand 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}*")
class CosiIntrusiveListAccessor (panda, ptr)
-
Expand source code
class CosiIntrusiveListAccessor: def __init__(self, panda, ptr): self._panda = panda self._ptr = ptr def __getattr__(self, name): list_head = getattr(self._ptr, name) if list_head._type_name != 'list_head': raise Exception(f"Field {self._ptr._type_name}.{name} not a list_head*") field_offset = self._ptr._type[name].offset ptrs = [] head = list_head.next.prev current = list_head.next while head._ptr != current._ptr: addr = current._ptr - field_offset ptrs.append(CosiGuestPointer(self._panda, self._ptr._type_name, addr)) current = current.next return ptrs
class CosiMappings (panda, inner)
-
Expand source code
class CosiMappings: def __init__(self, panda, inner): self.inner = inner self.panda = panda def __del__(self): self.panda.plugins[COSI].cosi_free_mappings(self.inner) self.inner = None def __len__(self) -> int: return self.panda.plugins[COSI].cosi_mappings_len(self.inner) def __getitem__(self, key: int): if not type(key) is int: raise TypeError("CosiMappings must be indexed with an integer") file_ptr = self.panda.plugins[COSI].cosi_mappings_get(self.inner, key) if file_ptr == self.panda.ffi.NULL: raise IndexError("Integer {} out of bounds of CosiProcList length") return CosiModule(self.panda, file_ptr, hold_ref=self) def __iter__(self): for i in range(len(self)): yield self[i]
class CosiModule (panda, proc, hold_ref=None)
-
Expand source code
class CosiModule: def __init__(self, panda, proc, hold_ref=None): self.panda = panda self.inner = proc self.hold_ref = hold_ref def __del__(self): if self.hold_ref is None: pass # TODO: free module if it's not a reference self.inner = None 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') 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 __getattr__(self, key): if key == "name": return self.get_name() elif key == "file": return self.get_file() attr = getattr(self.inner, key, None) if not attr is None: return attr return getattr(self.inner.task, key)
Methods
def get_file(self)
-
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')
class CosiProcList (panda, inner)
-
Expand source code
class CosiProcList: def __init__(self, panda, inner): self.inner = inner self.panda = panda def __del__(self): self.panda.plugins[COSI].cosi_free_proc_list(self.inner) self.inner = None def __len__(self) -> int: return self.panda.plugins[COSI].cosi_proc_list_len(self.inner) def __getitem__(self, key: int): if not type(key) is int: raise TypeError("CosiProcList must be indexed with an integer") file_ptr = self.panda.plugins[COSI].cosi_proc_list_get(self.inner, key) if file_ptr == self.panda.ffi.NULL: raise IndexError("Integer {} out of bounds of CosiProcList length") return CosiProcess(self.panda, file_ptr, hold_ref=self) def __iter__(self): for i in range(len(self)): yield self[i]
class CosiProcess (panda, proc, hold_ref=None)
-
Expand source code
class CosiProcess: def __init__(self, panda, proc, hold_ref=None): self.panda = panda self.inner = proc self.hold_ref = hold_ref def __del__(self): if self.hold_ref is None: self.panda.plugins[COSI].free_process(self.inner) self.inner = None 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 __getattr__(self, key): if key == "name": return self.get_name() attr = getattr(self.inner, key, None) if not attr is None: return attr return getattr(self.inner.task, key) 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) 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 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)
Methods
def children(self)
-
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)
class CosiThread (panda, thread)
-
Expand source code
class CosiThread: def __init__(self, panda, thread): self.inner = thread self.panda = panda def __del__(self): self.panda.plugins[COSI].free_thread(self.inner) self.inner = None def __getattr__(self, key): return getattr(self.inner, key)
class VolatilityBaseType (panda, raw_ptr)
-
A reference to a base type in the volatility symbol table
Expand source code
class VolatilityBaseType: ''' A reference to a base type in the volatility symbol table ''' def __init__(self, panda, raw_ptr): self.panda = panda self.inner = raw_ptr 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 ''' return self.panda.plugins[COSI].size_of_base_type(self.inner) 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)
Methods
def is_signed(self) ‑> bool
-
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)
class VolatilityStruct (panda, raw_ptr)
-
A reference to a struct in the volatility symbol table
Expand source code
class VolatilityStruct: ''' A reference to a struct in the volatility symbol table ''' def __init__(self, panda, raw_ptr): self.panda = panda self.inner = raw_ptr def __getitem__(self, item): if type(item) is int: name = self.get_field_by_index(item) if name: offset = self.offset_of_field(name) type_name = self.type_of_field(name) return VolatilityStructField( name=name, offset=offset, type_name=type_name ) else: raise IndexError("Index {item} is out of range of the length of the struct fields") elif type(item) is str: name = item offset = self.offset_of_field(name) type_name = self.type_of_field(name) return VolatilityStructField( name=name, offset=offset, type_name=type_name ) else: raise Exception(f"Invalid type {type(item)} for indexing VolatilityStruct") 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 ''' 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 ''' name = name.encode('utf8') name = self.panda.ffi.new("char[]", name) return self.panda.plugins[COSI].offset_of_field(self.inner, name) 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 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 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 at(self, ptr): ''' Get a CosiGuestPointer of this type ''' type_name = self.name() return CosiGuestPointer(self.panda, type_name, ptr)
Methods
def at(self, ptr)
-
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)
-
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
-
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
class VolatilityStructField (name: str, offset: int, type_name: str)
-
A single field in a volatility struct
Expand source code
class VolatilityStructField: ''' A single field in a volatility struct ''' name: str offset: int type_name: str
Class variables
var name : str
var offset : int
var type_name : str
class VolatilitySymbol (panda, raw_ptr)
-
A reference to an entry in the volatility symbol table
Expand source code
class VolatilitySymbol: ''' A reference to an entry in the volatility symbol table ''' def __init__(self, panda, raw_ptr): self.panda = panda self.inner = raw_ptr 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 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) 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
Methods
def addr(self) ‑> int
-
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)