pdb helper for finding UE4 globals
This commit is contained in:
parent
953bfa10ef
commit
679f97397a
|
@ -1,2 +1,3 @@
|
|||
[workspace]
|
||||
members = ["sdk-serializer", "unreal-sdk", "sdk-generator"]
|
||||
members = ["sdk-serializer", "unreal-sdk", "sdk-generator", "pdb-helper"]
|
||||
resolver = "2"
|
21
pdb-helper/Cargo.toml
Normal file
21
pdb-helper/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
|||
[package]
|
||||
name = "pdb-helper"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.0"
|
||||
anyhow = "1.0"
|
||||
once_cell = "1.17.1"
|
||||
pdb = "0.8.0"
|
||||
|
||||
[dependencies.windows]
|
||||
version = "0.44"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
189
pdb-helper/src/lib.rs
Normal file
189
pdb-helper/src/lib.rs
Normal file
|
@ -0,0 +1,189 @@
|
|||
use std::{ptr::NonNull, sync::Mutex};
|
||||
|
||||
use anyhow::Context;
|
||||
use once_cell::sync::OnceCell;
|
||||
use pdb::{DataSymbol, FallibleIterator, ProcedureReferenceSymbol, ProcedureSymbol, SymbolData};
|
||||
use windows::{core::PCWSTR, Win32::System::LibraryLoader::GetModuleHandleW};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Globals {
|
||||
pub names: NonNull<()>,
|
||||
pub objects: NonNull<()>,
|
||||
pub process_event: usize,
|
||||
}
|
||||
|
||||
static PDB: OnceCell<Mutex<PdbCtx>> = OnceCell::new();
|
||||
|
||||
pub fn get_pdb() -> std::sync::MutexGuard<'static, PdbCtx> {
|
||||
PDB.get_or_init(|| Mutex::new(PdbCtx::new().expect("failed to create PDB helper.")))
|
||||
.lock()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PdbCtx {
|
||||
base: usize,
|
||||
pdb: pdb::PDB<'static, std::fs::File>,
|
||||
}
|
||||
|
||||
// i dont care i need this to be safe and its stored as a dyn so any Send trait is removed
|
||||
unsafe impl Send for PdbCtx {}
|
||||
|
||||
impl PdbCtx {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let base = unsafe {
|
||||
GetModuleHandleW(PCWSTR::null())
|
||||
.context("could not get process base")?
|
||||
.0 as usize
|
||||
};
|
||||
|
||||
let pdb_path = std::env::current_exe()
|
||||
.context("current exe")?
|
||||
.with_file_name("ShooterGame.pdb");
|
||||
let pdb_file = std::fs::File::open(&pdb_path).context("pdb file open")?;
|
||||
|
||||
let pdb = pdb::PDB::open(pdb_file).context("parse pdb file")?;
|
||||
|
||||
Ok(Self { base, pdb })
|
||||
}
|
||||
|
||||
/// returns the address of the function
|
||||
pub fn find_function(&mut self, function_name: &str) -> anyhow::Result<usize> {
|
||||
let globals = self.pdb.global_symbols().context("global symbols")?;
|
||||
let mut iter = globals.iter();
|
||||
|
||||
while let Some(symbol) = iter.next()? {
|
||||
match symbol.parse() {
|
||||
Ok(pdb::SymbolData::ProcedureReference(data))
|
||||
if data.name.map(|name| name.to_string() == function_name) == Some(true) =>
|
||||
{
|
||||
log::trace!("{data:?}");
|
||||
let dbg_info = self.pdb.debug_information()?;
|
||||
let mods = dbg_info
|
||||
.modules()?
|
||||
.nth(data.module.context("module idx none")?)
|
||||
.context("module not found")?
|
||||
.context("module found but none")?;
|
||||
let a = self
|
||||
.pdb
|
||||
.module_info(&mods)
|
||||
.context("module info not found")?
|
||||
.context("module info found but none")?;
|
||||
let sym = a
|
||||
.symbols_at(data.symbol_index)
|
||||
.context("symbol not found")?
|
||||
.next()
|
||||
.context("symbol failed to read")?
|
||||
.context("symbol read but none")?
|
||||
.parse()
|
||||
.context("symbol failed to parse")?;
|
||||
|
||||
log::debug!("actual symbol: {sym:?}");
|
||||
|
||||
if let SymbolData::Procedure(ProcedureSymbol { offset, .. }) = sym {
|
||||
// asdf
|
||||
let get_names = offset
|
||||
.to_rva(&self.pdb.address_map()?)
|
||||
.context("rva none")?;
|
||||
return Ok(self.base + get_names.0 as usize);
|
||||
} else {
|
||||
return Err(anyhow::anyhow!("no proceduresymbol found"));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
return Err(anyhow::anyhow!("no procedureref found"));
|
||||
}
|
||||
|
||||
pub fn parse_names(&mut self, data: ProcedureReferenceSymbol) -> anyhow::Result<usize> {
|
||||
log::trace!("{data:?}");
|
||||
let dbg_info = self.pdb.debug_information()?;
|
||||
let mods = dbg_info
|
||||
.modules()?
|
||||
.nth(data.module.context("module idx none")?)
|
||||
.context("module not found")?
|
||||
.context("module found but none")?;
|
||||
let a = self
|
||||
.pdb
|
||||
.module_info(&mods)
|
||||
.context("module info not found")?
|
||||
.context("module info found but none")?;
|
||||
let sym = a
|
||||
.symbols_at(data.symbol_index)
|
||||
.context("symbol not found")?
|
||||
.next()
|
||||
.context("symbol failed to read")?
|
||||
.context("symbol read but none")?
|
||||
.parse()
|
||||
.context("symbol failed to parse")?;
|
||||
|
||||
log::debug!("actual symbol: {sym:?}");
|
||||
|
||||
if let SymbolData::Procedure(ProcedureSymbol { offset, .. }) = sym {
|
||||
// asdf
|
||||
let get_names = offset
|
||||
.to_rva(&self.pdb.address_map()?)
|
||||
.context("rva none")?;
|
||||
let get_names = unsafe {
|
||||
core::mem::transmute::<_, unsafe extern "win64" fn() -> *mut ()>(
|
||||
self.base + get_names.0 as usize,
|
||||
)
|
||||
};
|
||||
|
||||
let names = unsafe { get_names() };
|
||||
|
||||
log::debug!("names: {:?}", names);
|
||||
|
||||
Ok(names as usize)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("no proceduresymbol found"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_objects(&mut self, data: DataSymbol) -> anyhow::Result<usize> {
|
||||
let get_names = data
|
||||
.offset
|
||||
.to_rva(&self.pdb.address_map()?)
|
||||
.context("rva none")?;
|
||||
let objects = self.base + get_names.0 as usize;
|
||||
|
||||
Ok(objects)
|
||||
}
|
||||
|
||||
pub fn find_ue_globals(&mut self) -> anyhow::Result<Globals> {
|
||||
let globals = self.pdb.global_symbols().context("global symbols")?;
|
||||
let mut iter = globals.iter();
|
||||
|
||||
let mut names = None;
|
||||
let mut objects = None;
|
||||
|
||||
while let Some(symbol) = iter.next()? {
|
||||
match symbol.parse() {
|
||||
Ok(pdb::SymbolData::Data(data)) if data.name.to_string() == "GUObjectArray" => {
|
||||
objects = Some(self.parse_objects(data)?);
|
||||
}
|
||||
Ok(pdb::SymbolData::ProcedureReference(data))
|
||||
if data.name.map(|name| name.to_string() == "FName::GetNames")
|
||||
== Some(true) =>
|
||||
{
|
||||
names = Some(self.parse_names(data)?);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let process_event = self
|
||||
.find_function("UObject::ProcessEvent")
|
||||
.context("could not find UObject::ProcessEvent!")?;
|
||||
|
||||
let names = names.context("could not find names!")?;
|
||||
let objects = objects.context("could not find objects!")?;
|
||||
|
||||
Ok(Globals {
|
||||
names: NonNull::new(names as _).context("GNames was null!")?,
|
||||
objects: NonNull::new(objects as _).context("GObjects was null!")?,
|
||||
process_event,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue