use crate::mem::read_guest_type;
use crate::plugin_import;
use crate::prelude::*;
use crate::GuestReadFail;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
mod osi_statics;
pub use osi_statics::*;
#[doc(inline)]
pub use panda_macros::osi_static;
pub use panda_macros::OsiType;
plugin_import! {
static OSI2: Osi2 = extern "cosi" {
fn kaslr_offset(cpu: &mut CPUState) -> target_ptr_t;
fn current_cpu_offset(cpu: &mut CPUState) -> target_ulong;
fn free_cosi_str(string: *mut c_char);
fn symbol_from_name(name: *const c_char) -> Option<&'static VolatilitySymbol>;
fn symbol_addr_from_name(name: *const c_char) -> target_ptr_t;
fn symbol_value_from_name(name: *const c_char) -> target_ptr_t;
fn addr_of_symbol(symbol: &VolatilitySymbol) -> target_ptr_t;
fn value_of_symbol(symbol: &VolatilitySymbol) -> target_ptr_t;
fn name_of_symbol(symbol: &VolatilitySymbol) -> *mut c_char;
fn type_from_name(name: *const c_char) -> Option<&'static VolatilityStruct>;
fn name_of_struct(ty: &VolatilityStruct) -> *mut c_char;
fn size_of_struct(vol_struct: &VolatilityStruct) -> target_ulong;
fn offset_of_field(
vol_struct: &VolatilityStruct,
name: *const c_char
) -> target_long;
fn type_of_field(
vol_struct: &VolatilityStruct,
name: *const c_char
) -> *mut c_char;
fn get_field_by_index(ty: &VolatilityStruct, index: usize) -> *mut c_char;
fn enum_from_name(name: *const c_char) -> Option<&'static VolatilityEnum>;
fn name_of_enum(ty: &VolatilityEnum) -> *mut c_char;
fn base_type_from_name(name: *const c_char) -> Option<&'static VolatilityBaseType>;
fn name_of_base_type(ty: &VolatilityBaseType) -> *mut c_char;
fn size_of_base_type(ty: &VolatilityBaseType) -> target_ptr_t;
fn is_base_type_signed(ty: &VolatilityBaseType) -> bool;
};
}
macro_rules! opaque_types {
($($(#[$meta:meta])* $name:ident),*) => {
$(
$(#[$meta])*
///
/// **Note:** This type is opaque due to having an undefined layout and thus
/// may only be accessed behind a reference.
pub struct $name {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
)*
};
}
opaque_types! {
VolatilityEnum,
VolatilityBaseType,
VolatilitySymbol,
VolatilityStruct
}
impl VolatilitySymbol {
pub fn addr(&self) -> target_ptr_t {
OSI2.addr_of_symbol(self)
}
pub fn raw_value(&self) -> target_ptr_t {
OSI2.value_of_symbol(self)
}
pub fn name(&self) -> Option<String> {
let name_ptr = OSI2.name_of_symbol(self);
if name_ptr.is_null() {
return None;
}
let name = unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.expect("Invalid volatility symbol name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(name_ptr);
Some(name)
}
}
impl VolatilityStruct {
pub fn size(&self) -> target_ulong {
OSI2.size_of_struct(self)
}
pub fn offset_of(&self, field: &str) -> target_long {
let field_name = CString::new(field).unwrap();
OSI2.offset_of_field(self, field_name.as_ptr())
}
pub fn type_of(&self, field: &str) -> String {
let field_name = CString::new(field).unwrap();
let type_ptr = OSI2.type_of_field(self, field_name.as_ptr());
if type_ptr.is_null() {
panic!("Failed to get type of VolatilityStruct field");
}
let type_name = unsafe { CStr::from_ptr(type_ptr) }
.to_str()
.expect("Invalid volatility struct field type name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(type_ptr);
type_name
}
pub fn name(&self) -> String {
let name_ptr = OSI2.name_of_struct(self);
if name_ptr.is_null() {
panic!("Failed to get name of VolatilityStruct");
}
let name = unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.expect("Invalid volatility struct name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(name_ptr);
name
}
pub fn fields(&self) -> VolatilityFieldIter<'_> {
VolatilityFieldIter(self, 0)
}
}
pub struct VolatilityFieldIter<'a>(&'a VolatilityStruct, usize);
impl Iterator for VolatilityFieldIter<'_> {
type Item = (String, target_ptr_t);
fn next(&mut self) -> Option<(String, target_ptr_t)> {
let name_ptr = OSI2.get_field_by_index(self.0, self.1);
self.1 += 1;
if name_ptr.is_null() {
return None;
}
let offset = OSI2.offset_of_field(self.0, name_ptr);
let name = unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.expect("Invalid volatility field name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(name_ptr);
Some((name, offset as target_ptr_t))
}
}
impl VolatilityEnum {
pub fn name(&self) -> String {
let name_ptr = OSI2.name_of_enum(self);
if name_ptr.is_null() {
panic!("Failed to get name of VolatilityEnum");
}
let name = unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.expect("Invalid volatility struct name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(name_ptr);
name
}
}
impl VolatilityBaseType {
pub fn name(&self) -> String {
let name_ptr = OSI2.name_of_base_type(self);
if name_ptr.is_null() {
panic!("Failed to get name of VolatilityBaseType");
}
let name = unsafe { CStr::from_ptr(name_ptr) }
.to_str()
.expect("Invalid volatility struct name, invalid UTF-8")
.to_owned();
OSI2.free_cosi_str(name_ptr);
name
}
pub fn size(&self) -> target_ptr_t {
OSI2.size_of_base_type(self)
}
pub fn signed(&self) -> bool {
OSI2.is_base_type_signed(self)
}
}
pub fn enum_from_name(name: &str) -> Option<&'static VolatilityEnum> {
let name = CString::new(name).unwrap();
OSI2.enum_from_name(name.as_ptr())
}
pub fn base_type_from_name(name: &str) -> Option<&'static VolatilityBaseType> {
let name = CString::new(name).unwrap();
OSI2.base_type_from_name(name.as_ptr())
}
pub fn symbol_from_name(name: &str) -> Option<&'static VolatilitySymbol> {
let name = CString::new(name).unwrap();
OSI2.symbol_from_name(name.as_ptr())
}
pub fn type_from_name(name: &str) -> Option<&'static VolatilityStruct> {
let name = CString::new(name).unwrap();
OSI2.type_from_name(name.as_ptr())
}
pub fn symbol_addr_from_name(name: &str) -> target_ptr_t {
let name = CString::new(name).unwrap();
OSI2.symbol_addr_from_name(name.as_ptr())
}
pub fn symbol_value_from_name(name: &str) -> target_ptr_t {
let name = CString::new(name).unwrap();
OSI2.symbol_value_from_name(name.as_ptr())
}
pub fn kaslr_offset(cpu: &mut CPUState) -> target_ptr_t {
OSI2.kaslr_offset(cpu)
}
pub fn current_cpu_offset(cpu: &mut CPUState) -> target_ulong {
OSI2.current_cpu_offset(cpu)
}
pub fn addr_of_symbol(symbol: &VolatilitySymbol) -> target_ptr_t {
OSI2.addr_of_symbol(symbol)
}
pub fn offset_of_field(vol_struct: &VolatilityStruct, name: &str) -> target_long {
let name = CString::new(name).unwrap();
OSI2.offset_of_field(vol_struct, name.as_ptr())
}
pub fn size_of_struct(vol_struct: &VolatilityStruct) -> target_ulong {
OSI2.size_of_struct(vol_struct)
}
pub fn find_per_cpu_address(
cpu: &mut CPUState,
symbol: &str,
) -> Result<target_ptr_t, GuestReadFail> {
let symbol_offset = symbol_value_from_name(symbol);
let ptr_to_ptr = current_cpu_offset(cpu) + symbol_offset;
read_guest_type(cpu, ptr_to_ptr)
}