use lief_ffi as ffi;
use std::marker::PhantomData;
use super::exception::ExceptionInfo;
use crate::common::{FromFFI, into_optional};
use crate::{declare_fwd_iterator, to_opt, to_slice};
use bitflags::bitflags;
pub struct RuntimeFunction<'a> {
ptr: cxx::UniquePtr<ffi::PE_RuntimeFunctionX64>,
_owner: PhantomData<&'a ffi::PE_Binary>,
}
impl FromFFI<ffi::PE_RuntimeFunctionX64> for RuntimeFunction<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_RuntimeFunctionX64>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl std::fmt::Debug for RuntimeFunction<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let base = self as &dyn ExceptionInfo;
f.debug_struct("RuntimeFunction(x86_64)")
.field("rva_start", &base.rva_start())
.field("rva_end", &self.rva_end())
.field("unwind_rva", &self.unwind_rva())
.field("size", &self.size())
.field("unwind_info", &self.unwind_info())
.finish()
}
}
impl ExceptionInfo for RuntimeFunction<'_> {
fn as_generic(&self) -> &ffi::PE_ExceptionInfo {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl RuntimeFunction<'_> {
pub fn rva_end(&self) -> u32 {
self.ptr.rva_end()
}
pub fn unwind_rva(&self) -> u32 {
self.ptr.unwind_rva()
}
pub fn size(&self) -> u32 {
self.ptr.size()
}
pub fn unwind_info(&self) -> Option<UnwindInfo<'_>> {
into_optional(self.ptr.unwind_info())
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum UnwindOpcodes {
PUSH_NONVOL,
ALLOC_LARGE,
ALLOC_SMALL,
SET_FPREG,
SAVE_NONVOL,
SAVE_NONVOL_FAR,
EPILOG,
SPARE,
SAVE_XMM128,
SAVE_XMM128_FAR,
PUSH_MACHFRAME,
UNKNOWN(u32),
}
impl From<u32> for UnwindOpcodes {
fn from(value: u32) -> Self {
match value {
0x00000000 => UnwindOpcodes::PUSH_NONVOL,
0x00000001 => UnwindOpcodes::ALLOC_LARGE,
0x00000002 => UnwindOpcodes::ALLOC_SMALL,
0x00000003 => UnwindOpcodes::SET_FPREG,
0x00000004 => UnwindOpcodes::SAVE_NONVOL,
0x00000005 => UnwindOpcodes::SAVE_NONVOL_FAR,
0x00000006 => UnwindOpcodes::EPILOG,
0x00000007 => UnwindOpcodes::SPARE,
0x00000008 => UnwindOpcodes::SAVE_XMM128,
0x00000009 => UnwindOpcodes::SAVE_XMM128_FAR,
0x0000000a => UnwindOpcodes::PUSH_MACHFRAME,
_ => UnwindOpcodes::UNKNOWN(value),
}
}
}
impl From<UnwindOpcodes> for u32 {
fn from(value: UnwindOpcodes) -> u32 {
match value {
UnwindOpcodes::PUSH_NONVOL => 0x00000000,
UnwindOpcodes::ALLOC_LARGE => 0x00000001,
UnwindOpcodes::ALLOC_SMALL => 0x00000002,
UnwindOpcodes::SET_FPREG => 0x00000003,
UnwindOpcodes::SAVE_NONVOL => 0x00000004,
UnwindOpcodes::SAVE_NONVOL_FAR => 0x00000005,
UnwindOpcodes::EPILOG => 0x00000006,
UnwindOpcodes::SPARE => 0x00000007,
UnwindOpcodes::SAVE_XMM128 => 0x00000008,
UnwindOpcodes::SAVE_XMM128_FAR => 0x00000009,
UnwindOpcodes::PUSH_MACHFRAME => 0x0000000a,
UnwindOpcodes::UNKNOWN(value) => value,
}
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum UnwindReg {
RAX,
RCX,
RDX,
RBX,
RSP,
RBP,
RSI,
RDI,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
UNKNOWN(u32),
}
impl From<u32> for UnwindReg {
fn from(value: u32) -> Self {
match value {
0x00000000 => UnwindReg::RAX,
0x00000001 => UnwindReg::RCX,
0x00000002 => UnwindReg::RDX,
0x00000003 => UnwindReg::RBX,
0x00000004 => UnwindReg::RSP,
0x00000005 => UnwindReg::RBP,
0x00000006 => UnwindReg::RSI,
0x00000007 => UnwindReg::RDI,
0x00000008 => UnwindReg::R8,
0x00000009 => UnwindReg::R9,
0x0000000a => UnwindReg::R10,
0x0000000b => UnwindReg::R11,
0x0000000c => UnwindReg::R12,
0x0000000d => UnwindReg::R13,
0x0000000e => UnwindReg::R14,
0x0000000f => UnwindReg::R15,
_ => UnwindReg::UNKNOWN(value),
}
}
}
impl From<UnwindReg> for u32 {
fn from(value: UnwindReg) -> u32 {
match value {
UnwindReg::RAX => 0x00000000,
UnwindReg::RCX => 0x00000001,
UnwindReg::RDX => 0x00000002,
UnwindReg::RBX => 0x00000003,
UnwindReg::RSP => 0x00000004,
UnwindReg::RBP => 0x00000005,
UnwindReg::RSI => 0x00000006,
UnwindReg::RDI => 0x00000007,
UnwindReg::R8 => 0x00000008,
UnwindReg::R9 => 0x00000009,
UnwindReg::R10 => 0x0000000a,
UnwindReg::R11 => 0x0000000b,
UnwindReg::R12 => 0x0000000c,
UnwindReg::R13 => 0x0000000d,
UnwindReg::R14 => 0x0000000e,
UnwindReg::R15 => 0x0000000f,
UnwindReg::UNKNOWN(value) => value,
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnwindFlags: u8 {
const EXCEPTION_HANDLER = 0x1;
const TERMINATE_HANDLER = 0x2;
const CHAIN_INFO = 0x4;
}
}
impl From<u8> for UnwindFlags {
fn from(value: u8) -> Self {
UnwindFlags::from_bits_truncate(value)
}
}
impl From<UnwindFlags> for u8 {
fn from(value: UnwindFlags) -> Self {
value.bits()
}
}
impl std::fmt::Display for UnwindFlags {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
pub struct UnwindInfo<'a> {
ptr: cxx::UniquePtr<ffi::PE_RuntimeFunctionX64_unwind_info_t>,
_owner: PhantomData<&'a ffi::PE_Binary>,
}
impl FromFFI<ffi::PE_RuntimeFunctionX64_unwind_info_t> for UnwindInfo<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_RuntimeFunctionX64_unwind_info_t>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl UnwindInfo<'_> {
pub fn version(&self) -> u8 {
self.ptr.version()
}
pub fn flags(&self) -> UnwindFlags {
UnwindFlags::from(self.ptr.flags())
}
pub fn sizeof_prologue(&self) -> u8 {
self.ptr.sizeof_prologue()
}
pub fn count_opcodes(&self) -> u8 {
self.ptr.count_opcodes()
}
pub fn frame_reg(&self) -> u8 {
self.ptr.frame_reg()
}
pub fn frame_reg_offset(&self) -> u8 {
self.ptr.frame_reg_offset()
}
pub fn raw_opcodes(&self) -> &[u8] {
to_slice!(self.ptr.raw_opcodes());
}
pub fn opcodes(&self) -> OpcodesIterator<'_> {
OpcodesIterator::new(self.ptr.opcodes())
}
pub fn handler(&self) -> Option<u32> {
to_opt!(&ffi::PE_RuntimeFunctionX64_unwind_info_t::handler, &self);
}
pub fn chained(&self) -> Option<RuntimeFunction<'_>> {
into_optional(self.ptr.chained())
}
}
impl std::fmt::Debug for UnwindInfo<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("UnwindInfo")
.field("version", &self.version())
.field("flags", &self.flags())
.field("sizeof_prologue", &self.sizeof_prologue())
.field("count_opcodes", &self.count_opcodes())
.field("frame_reg", &self.frame_reg())
.field("frame_reg_offset", &self.frame_reg_offset())
.field("handler", &self.handler())
.finish()
}
}
impl std::fmt::Display for UnwindInfo<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.ptr.to_string())
}
}
pub trait Opcode {
#[doc(hidden)]
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code;
fn position(&self) -> u32 {
self.as_generic().position()
}
fn opcode(&self) -> UnwindOpcodes {
UnwindOpcodes::from(self.as_generic().opcode())
}
}
impl std::fmt::Display for &dyn Opcode {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_generic().to_string())
}
}
pub enum Opcodes<'a> {
Alloc(Alloc<'a>),
PushNonVol(PushNonVol<'a>),
PushMachFrame(PushMachFrame<'a>),
SetFPReg(SetFPReg<'a>),
SaveNonVolatile(SaveNonVolatile<'a>),
SaveXMM128(SaveXMM128<'a>),
Epilog(Epilog<'a>),
Spare(Spare<'a>),
}
impl<'a> FromFFI<ffi::PE_unwind_x64_Code> for Opcodes<'a> {
fn from_ffi(ffi_entry: cxx::UniquePtr<ffi::PE_unwind_x64_Code>) -> Self {
unsafe {
let code_ref = ffi_entry.as_ref().unwrap();
if ffi::PE_unwind_x64_Alloc::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_Alloc>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::Alloc(Alloc::from_ffi(raw))
} else if ffi::PE_unwind_x64_PushNonVol::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_PushNonVol>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::PushNonVol(PushNonVol::from_ffi(raw))
} else if ffi::PE_unwind_x64_PushMachFrame::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_PushMachFrame>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::PushMachFrame(PushMachFrame::from_ffi(raw))
} else if ffi::PE_unwind_x64_SetFPReg::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_SetFPReg>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::SetFPReg(SetFPReg::from_ffi(raw))
} else if ffi::PE_unwind_x64_SaveNonVolatile::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_SaveNonVolatile>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::SaveNonVolatile(SaveNonVolatile::from_ffi(raw))
} else if ffi::PE_unwind_x64_SaveXMM128::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_SaveXMM128>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::SaveXMM128(SaveXMM128::from_ffi(raw))
} else if ffi::PE_unwind_x64_Epilog::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_Epilog>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::Epilog(Epilog::from_ffi(raw))
} else if ffi::PE_unwind_x64_Spare::classof(code_ref) {
let raw = {
type From = cxx::UniquePtr<ffi::PE_unwind_x64_Code>;
type To = cxx::UniquePtr<ffi::PE_unwind_x64_Spare>;
std::mem::transmute::<From, To>(ffi_entry)
};
Opcodes::Spare(Spare::from_ffi(raw))
} else {
panic!("Unknown opcode");
}
}
}
}
pub struct Alloc<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Alloc>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_Alloc> for Alloc<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Alloc>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for Alloc<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl Alloc<'_> {
pub fn size(&self) -> u32 {
self.ptr.size()
}
}
pub struct PushNonVol<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_PushNonVol>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_PushNonVol> for PushNonVol<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_PushNonVol>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for PushNonVol<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl PushNonVol<'_> {
pub fn reg(&self) -> UnwindReg {
UnwindReg::from(self.ptr.reg())
}
}
pub struct PushMachFrame<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_PushMachFrame>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_PushMachFrame> for PushMachFrame<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_PushMachFrame>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for PushMachFrame<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl PushMachFrame<'_> {
pub fn value(&self) -> u8 {
self.ptr.value()
}
}
pub struct SetFPReg<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SetFPReg>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_SetFPReg> for SetFPReg<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SetFPReg>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for SetFPReg<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl SetFPReg<'_> {
pub fn reg(&self) -> UnwindReg {
UnwindReg::from(self.ptr.reg())
}
}
pub struct SaveNonVolatile<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SaveNonVolatile>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_SaveNonVolatile> for SaveNonVolatile<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SaveNonVolatile>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for SaveNonVolatile<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl SaveNonVolatile<'_> {
pub fn reg(&self) -> UnwindReg {
UnwindReg::from(self.ptr.reg())
}
pub fn offset(&self) -> u32 {
self.ptr.offset()
}
}
pub struct SaveXMM128<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SaveXMM128>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_SaveXMM128> for SaveXMM128<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_SaveXMM128>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for SaveXMM128<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl SaveXMM128<'_> {
pub fn num(&self) -> u8 {
self.ptr.num()
}
pub fn offset(&self) -> u32 {
self.ptr.offset()
}
}
pub struct Epilog<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Epilog>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_Epilog> for Epilog<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Epilog>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for Epilog<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
impl Epilog<'_> {
pub fn flags(&self) -> u8 {
self.ptr.flags()
}
pub fn size(&self) -> u32 {
self.ptr.size()
}
}
pub struct Spare<'a> {
ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Spare>,
_owner: PhantomData<&'a ffi::PE_RuntimeFunctionX64_unwind_info_t>,
}
impl FromFFI<ffi::PE_unwind_x64_Spare> for Spare<'_> {
fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_unwind_x64_Spare>) -> Self {
Self {
ptr,
_owner: PhantomData,
}
}
}
impl Opcode for Spare<'_> {
fn as_generic(&self) -> &ffi::PE_unwind_x64_Code {
self.ptr.as_ref().unwrap().as_ref()
}
}
declare_fwd_iterator!(
OpcodesIterator,
Opcodes<'a>,
ffi::PE_unwind_x64_Code,
ffi::PE_RuntimeFunctionX64_unwind_info_t,
ffi::PE_RuntimeFunctionX64_unwind_info_t_it_opcodes
);