#![allow(non_snake_case, non_camel_case_types)]
use core::{ffi::c_void, slice::from_raw_parts};
use dinvk::{types::*, helper::PE};
pub const UNW_FLAG_EHANDLER: u8 = 0x1;
pub const UNW_FLAG_CHAININFO: u8 = 0x4;
#[derive(Debug)]
pub struct Unwind {
pub pe: PE,
}
impl Unwind {
pub fn new(pe: PE) -> Self {
Unwind { pe }
}
pub fn entries(&self) -> Option<&[IMAGE_RUNTIME_FUNCTION]> {
let nt = self.pe.nt_header()?;
let dir = unsafe {
(*nt).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]
};
if dir.VirtualAddress == 0 || dir.Size == 0 {
return None;
}
let addr = (self.pe.base as usize + dir.VirtualAddress as usize) as *const IMAGE_RUNTIME_FUNCTION;
let len = dir.Size as usize / size_of::<IMAGE_RUNTIME_FUNCTION>();
Some(unsafe { from_raw_parts(addr, len) })
}
pub fn function_by_offset(&self, offset: u32) -> Option<&IMAGE_RUNTIME_FUNCTION> {
self.entries()?.iter().find(|f| f.BeginAddress == offset)
}
#[cfg(feature = "desync")]
pub fn function_size(&self, func: *mut c_void) -> Option<u64> {
let offset = (func as usize - self.pe.base as usize) as u32;
let entry = self.function_by_offset(offset)?;
let start = self.pe.base as u64 + entry.BeginAddress as u64;
let end = self.pe.base as u64 + entry.EndAddress as u64;
Some(end - start)
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Config {
pub rtl_user_addr: *const c_void,
pub rtl_user_thread_size: u64,
pub base_thread_addr: *const c_void,
pub base_thread_size: u64,
pub first_frame_fp: *const c_void,
pub second_frame_fp: *const c_void,
pub jmp_rbx_gadget: *const c_void,
pub add_rsp_gadget: *const c_void,
pub first_frame_size: u64,
pub second_frame_size: u64,
pub jmp_rbx_frame_size: u64,
pub add_rsp_frame_size: u64,
pub rbp_stack_offset: u64,
pub spoof_function: *const c_void,
pub return_address: *const c_void,
pub is_syscall: u32,
pub ssn: u16,
pub number_args: u32,
pub arg01: *const c_void,
pub arg02: *const c_void,
pub arg03: *const c_void,
pub arg04: *const c_void,
pub arg05: *const c_void,
pub arg06: *const c_void,
pub arg07: *const c_void,
pub arg08: *const c_void,
pub arg09: *const c_void,
pub arg10: *const c_void,
pub arg11: *const c_void,
}
impl Default for Config {
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
#[allow(dead_code)]
pub enum Registers {
Rax = 0,
Rcx,
Rdx,
Rbx,
Rsp,
Rbp,
Rsi,
Rdi,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
}
impl PartialEq<usize> for Registers {
fn eq(&self, other: &usize) -> bool {
*self as usize == *other
}
}
#[repr(C)]
pub union UNWIND_CODE {
pub FrameOffset: u16,
pub Anonymous: UNWIND_CODE_0,
}
bitfield::bitfield! {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct UNWIND_CODE_0(u16);
pub u8, CodeOffset, SetCodeOffset: 7, 0;
pub u8, UnwindOp, SetUnwindOp: 11, 8;
pub u8, OpInfo, SetOpInfo: 15, 12;
}
#[repr(C)]
pub union UNWIND_INFO_0 {
pub ExceptionHandler: u32,
pub FunctionEntry: u32,
}
bitfield::bitfield! {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct UNWIND_VERSION_FLAGS(u8);
pub u8, Version, SetVersion: 2, 0;
pub u8, Flags, SetFlags: 7, 3;
}
bitfield::bitfield! {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct UNWIND_FRAME_INFO(u8);
pub u8, FrameRegister, SetFrameRegister: 3, 0;
pub u8, FrameOffset, SetFrameOffset: 7, 4;
}
#[repr(C)]
pub struct UNWIND_INFO {
pub VersionFlags: UNWIND_VERSION_FLAGS,
pub SizeOfProlog: u8,
pub CountOfCodes: u8,
pub FrameInfo: UNWIND_FRAME_INFO,
pub UnwindCode: UNWIND_CODE,
pub Anonymous: UNWIND_INFO_0,
pub ExceptionData: u32,
}
#[repr(u8)]
#[allow(dead_code)]
pub enum UNWIND_OP_CODES {
UWOP_PUSH_NONVOL = 0,
UWOP_ALLOC_LARGE = 1,
UWOP_ALLOC_SMALL = 2,
UWOP_SET_FPREG = 3,
UWOP_SAVE_NONVOL = 4,
UWOP_SAVE_NONVOL_BIG = 5,
UWOP_EPILOG = 6,
UWOP_SPARE_CODE = 7,
UWOP_SAVE_XMM128 = 8,
UWOP_SAVE_XMM128BIG = 9,
UWOP_PUSH_MACH_FRAME = 10,
}
impl TryFrom<u8> for UNWIND_OP_CODES {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0..=10 => Ok(unsafe { core::mem::transmute::<u8, UNWIND_OP_CODES>(value) }),
_ => Err(()),
}
}
}