use alloc::vec::Vec;
use core::ffi::c_void;
use dinvk::pe::PE;
use dinvk::{
data::{CONTEXT, IMAGE_RUNTIME_FUNCTION},
};
use super::config::Config;
const JMP_GADGETS: &[(&[u8], Reg)] = &[
(&[0xFF, 0xE7], Reg::Rdi),
(&[0x41, 0xFF, 0xE2], Reg::R10),
(&[0x41, 0xFF, 0xE3], Reg::R11),
(&[0x41, 0xFF, 0xE4], Reg::R12),
(&[0x41, 0xFF, 0xE5], Reg::R13),
(&[0x41, 0xFF, 0xE6], Reg::R14),
(&[0x41, 0xFF, 0xE7], Reg::R15),
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Reg {
Rdi,
R10,
R11,
R12,
R13,
R14,
R15,
}
#[derive(Debug, Clone, Copy)]
pub struct Gadget {
pub addr: u64,
pub reg: Reg,
}
impl Gadget {
fn apply(&self, ctx: &mut CONTEXT, target: u64) {
ctx.Rip = self.addr;
match self.reg {
Reg::Rdi => ctx.Rdi = target,
Reg::R10 => ctx.R10 = target,
Reg::R11 => ctx.R11 = target,
Reg::R12 => ctx.R12 = target,
Reg::R13 => ctx.R13 = target,
Reg::R14 => ctx.R14 = target,
Reg::R15 => ctx.R15 = target,
}
}
pub fn resolve(cfg: &Config) -> Gadget {
let mut gadgets = Vec::new();
let modules = [
cfg.modules.ntdll.as_ptr() as *const u8,
cfg.modules.kernel32.as_ptr() as *const u8,
cfg.modules.kernelbase.as_ptr() as *const u8,
];
for &base in modules.iter() {
if let Some(range) = Self::get_text_section(base as *mut c_void) {
if let Some(gadget) = Self::find(base, range).first().copied() {
gadgets.push(gadget);
}
}
}
shuffle(&mut gadgets);
if let Some(gadget) = gadgets.first().copied() {
gadget
} else {
unsafe { core::hint::unreachable_unchecked() }
}
}
fn find<B>(base: *const u8, region: &B) -> Vec<Gadget>
where
B: ?Sized + AsRef<[u8]>,
{
let mut gadgets = Vec::new();
let mut seen = [false; JMP_GADGETS.len()];
for (i, (pattern, reg)) in JMP_GADGETS.iter().enumerate() {
if seen[i] {
continue;
}
if let Some(pos) = memchr::memmem::find(region.as_ref(), pattern) {
gadgets.push(Gadget {
addr: base as u64 + (region.as_ref().as_ptr() as usize - base as usize + pos) as u64,
reg: *reg,
});
seen[i] = true;
}
}
gadgets
}
pub fn scan_runtime<B>(
module: *mut c_void,
pattern: &B,
runtime_table: &[IMAGE_RUNTIME_FUNCTION]
) -> Option<(*mut u8, u32)>
where
B: ?Sized + AsRef<[u8]>,
{
unsafe {
let mut gadgets = Vec::new();
for runtime in runtime_table {
let start = module as u64 + runtime.BeginAddress as u64;
let end = module as u64 + runtime.EndAddress as u64;
let size = end - start;
let bytes = core::slice::from_raw_parts(start as *const u8, size as usize);
if let Some(pos) = memchr::memmem::find(bytes, pattern.as_ref()) {
let addr = (start as *mut u8).add(pos);
if let Some(size) = uwd::StackFrame::ignoring_set_fpreg(module, runtime) {
if size != 0 {
gadgets.push((addr, size))
}
}
}
}
if gadgets.is_empty() {
return None;
}
shuffle(&mut gadgets);
gadgets.first().copied()
}
}
pub fn get_text_section(base: *mut c_void) -> Option<&'static [u8]> {
if base.is_null() {
return None;
}
unsafe {
let pe = PE::parse(base);
let section = pe.section_by_name(obfstr::obfstr!(".text"))?;
let ptr = base.add(section.VirtualAddress as usize);
Some(core::slice::from_raw_parts(ptr.cast(), section.Misc.VirtualSize as usize))
}
}
}
pub trait GadgetContext {
fn jmp(&mut self, cfg: &Config, target: u64);
}
impl GadgetContext for CONTEXT {
fn jmp(&mut self, cfg: &Config, target: u64) {
let gadget = Gadget::resolve(cfg);
gadget.apply(self, target);
}
}
pub fn shuffle<T>(list: &mut [T]) {
let mut seed = unsafe { core::arch::x86_64::_rdtsc() };
for i in (1..list.len()).rev() {
seed = seed.wrapping_mul(1103515245).wrapping_add(12345);
let j = seed as usize % (i + 1);
list.swap(i, j);
}
}