mirror of
https://github.com/a2x/cs2-dumper.git
synced 2026-03-07 17:57:22 +08:00
0.1.3
* Bug fixes
This commit is contained in:
@@ -11,7 +11,7 @@ use pelite::pe64::{Pe, PeView};
|
||||
|
||||
use crate::source2::KeyButton;
|
||||
|
||||
pub type ButtonMap = BTreeMap<String, imem>;
|
||||
pub type ButtonMap = BTreeMap<String, umem>;
|
||||
|
||||
pub fn buttons<P: Process + MemoryView>(process: &mut P) -> Result<ButtonMap> {
|
||||
let module = process.module_by_name("client.dll")?;
|
||||
@@ -31,35 +31,40 @@ pub fn buttons<P: Process + MemoryView>(process: &mut P) -> Result<ButtonMap> {
|
||||
bail!("outdated button list pattern");
|
||||
}
|
||||
|
||||
read_buttons(process, &module, module.base + save[1])
|
||||
let list_head = process.read_addr64(module.base + save[1]).data_part()?;
|
||||
|
||||
read_buttons(process, &module, list_head)
|
||||
}
|
||||
|
||||
fn read_buttons(
|
||||
mem: &mut impl MemoryView,
|
||||
module: &ModuleInfo,
|
||||
list_addr: Address,
|
||||
list_head: Address,
|
||||
) -> Result<ButtonMap> {
|
||||
let mut map = ButtonMap::new();
|
||||
let mut result = ButtonMap::new();
|
||||
|
||||
let mut cur_button = Pointer64::<KeyButton>::from(mem.read_addr64(list_addr).data_part()?);
|
||||
let mut button_ptr = Pointer64::<KeyButton>::from(list_head);
|
||||
|
||||
while !cur_button.is_null() {
|
||||
let button = mem.read_ptr(cur_button).data_part()?;
|
||||
let name = mem.read_utf8(button.name.address(), 32).data_part()?;
|
||||
let rva = (cur_button.address() - module.base) + offset_of!(KeyButton.state) as imem;
|
||||
while !button_ptr.is_null() {
|
||||
let button = mem.read_ptr(button_ptr).data_part()?;
|
||||
let name = mem.read_utf8_lossy(button.name.address(), 32).data_part()?;
|
||||
|
||||
debug!(
|
||||
"found button: {} at {:#X} ({} + {:#X})",
|
||||
name,
|
||||
cur_button.to_umem() + offset_of!(KeyButton.state) as umem,
|
||||
module.name,
|
||||
rva
|
||||
);
|
||||
let state_addr = button_ptr.address() + offset_of!(KeyButton.state);
|
||||
|
||||
map.insert(name, rva);
|
||||
if let Some(state_rva) = state_addr.to_umem().checked_sub(module.base.to_umem()) {
|
||||
debug!(
|
||||
"found \"{}\" at {:#X} ({} + {:#X})",
|
||||
name,
|
||||
state_addr.to_umem(),
|
||||
module.name,
|
||||
state_rva
|
||||
);
|
||||
|
||||
cur_button = button.next;
|
||||
result.insert(name, state_rva);
|
||||
}
|
||||
|
||||
button_ptr = button.next;
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use memflow::prelude::v1::*;
|
||||
use pelite::pe64::exports::Export;
|
||||
use pelite::pe64::{Pe, PeView};
|
||||
|
||||
use crate::memory::address;
|
||||
use crate::source2::InterfaceReg;
|
||||
|
||||
pub type InterfaceMap = BTreeMap<String, BTreeMap<String, umem>>;
|
||||
@@ -17,7 +18,6 @@ pub fn interfaces<P: Process + MemoryView>(process: &mut P) -> Result<InterfaceM
|
||||
process
|
||||
.module_list()?
|
||||
.iter()
|
||||
.filter(|module| module.name.as_ref() != "crashandler64.dll")
|
||||
.filter_map(|module| {
|
||||
let buf = process
|
||||
.read_raw(module.base, module.size as _)
|
||||
@@ -34,16 +34,18 @@ pub fn interfaces<P: Process + MemoryView>(process: &mut P) -> Result<InterfaceM
|
||||
.name("CreateInterface")
|
||||
.ok()?;
|
||||
|
||||
if let Export::Symbol(symbol) = ci_export {
|
||||
let list_addr = read_addr64_rip(process, module.base + symbol).ok()?;
|
||||
match ci_export {
|
||||
Export::Symbol(symbol) => {
|
||||
let list_ptr = address::resolve_rip(process, module.base + symbol).ok()?;
|
||||
let list_head = process.read_addr64(list_ptr).data_part().ok()?;
|
||||
|
||||
return read_interfaces(process, module, list_addr)
|
||||
.ok()
|
||||
.filter(|ifaces| !ifaces.is_empty())
|
||||
.map(|ifaces| Ok((module.name.to_string(), ifaces)));
|
||||
return read_interfaces(process, module, list_head)
|
||||
.ok()
|
||||
.filter(|ifaces| !ifaces.is_empty())
|
||||
.map(|ifaces| Ok((module.name.to_string(), ifaces)));
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
@@ -51,36 +53,32 @@ pub fn interfaces<P: Process + MemoryView>(process: &mut P) -> Result<InterfaceM
|
||||
fn read_interfaces(
|
||||
mem: &mut impl MemoryView,
|
||||
module: &ModuleInfo,
|
||||
list_addr: Address,
|
||||
list_head: Address,
|
||||
) -> Result<BTreeMap<String, umem>> {
|
||||
let mut ifaces = BTreeMap::new();
|
||||
let mut result = BTreeMap::new();
|
||||
|
||||
let mut cur_reg = Pointer64::<InterfaceReg>::from(mem.read_addr64(list_addr).data_part()?);
|
||||
let mut reg_ptr = Pointer64::<InterfaceReg>::from(list_head);
|
||||
|
||||
while !cur_reg.is_null() {
|
||||
let reg = mem.read_ptr(cur_reg).data_part()?;
|
||||
let name = mem.read_utf8(reg.name.address(), 128).data_part()?;
|
||||
let instance = read_addr64_rip(mem, reg.create_fn.address())?;
|
||||
let value = instance.wrapping_sub(module.base).to_umem();
|
||||
while !reg_ptr.is_null() {
|
||||
let reg = mem.read_ptr(reg_ptr).data_part()?;
|
||||
let name = mem.read_utf8_lossy(reg.name.address(), 128).data_part()?;
|
||||
|
||||
debug!(
|
||||
"found interface: {} at {:#X} ({} + {:#X})",
|
||||
name,
|
||||
value.wrapping_add(module.base.to_umem()),
|
||||
module.name,
|
||||
value
|
||||
);
|
||||
let instance_addr = address::resolve_rip(mem, reg.create_fn.address())?;
|
||||
|
||||
ifaces.insert(name, value);
|
||||
if let Some(instance_rva) = instance_addr.to_umem().checked_sub(module.base.to_umem()) {
|
||||
debug!(
|
||||
"found \"{}\" at {:#X} ({} + {:#X})",
|
||||
name,
|
||||
instance_addr.to_umem(),
|
||||
module.name,
|
||||
instance_rva
|
||||
);
|
||||
|
||||
cur_reg = reg.next;
|
||||
result.insert(name, instance_rva);
|
||||
}
|
||||
|
||||
reg_ptr = reg.next;
|
||||
}
|
||||
|
||||
Ok(ifaces)
|
||||
}
|
||||
|
||||
fn read_addr64_rip(mem: &mut impl MemoryView, addr: Address) -> Result<Address> {
|
||||
let disp = mem.read::<i32>(addr + 0x3).data_part()?;
|
||||
|
||||
Ok(addr + 0x7 + disp)
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ macro_rules! pattern_map {
|
||||
|
||||
for (name, value) in &map {
|
||||
debug!(
|
||||
"found offset: {} at {:#X} ({}.dll + {:#X})",
|
||||
"found \"{}\" at {:#X} ({}.dll + {:#X})",
|
||||
name,
|
||||
*value as u64 + view.optional_header().ImageBase,
|
||||
stringify!($module),
|
||||
@@ -154,12 +154,145 @@ pub fn offsets<P: Process + MemoryView>(process: &mut P) -> Result<OffsetMap> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use std::sync::Once;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use simplelog::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn build_number() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let engine_base = process.module_by_name("engine2.dll")?.base;
|
||||
|
||||
let offset = read_offset("engine2.dll", "dwBuildNumber").unwrap();
|
||||
|
||||
let build_number: u32 = process.read(engine_base + offset).data_part()?;
|
||||
|
||||
debug!("build number: {}", build_number);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_vars() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let offset = read_offset("client.dll", "dwGlobalVars").unwrap();
|
||||
|
||||
let global_vars: u64 = process.read(client_base + offset).data_part()?;
|
||||
|
||||
let map_name_addr = process
|
||||
.read_addr64((global_vars + 0x180).into())
|
||||
.data_part()?;
|
||||
|
||||
let map_name = process.read_utf8(map_name_addr, 128).data_part()?;
|
||||
|
||||
debug!("[global vars] map name: \"{}\"", map_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_controller() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let local_controller_offset = read_offset("client.dll", "dwLocalPlayerController").unwrap();
|
||||
|
||||
let player_name_offset =
|
||||
read_class_field("client.dll", "CBasePlayerController", "m_iszPlayerName").unwrap();
|
||||
|
||||
let local_controller: u64 = process
|
||||
.read(client_base + local_controller_offset)
|
||||
.data_part()?;
|
||||
|
||||
let player_name = process
|
||||
.read_utf8((local_controller + player_name_offset).into(), 128)
|
||||
.data_part()?;
|
||||
|
||||
debug!("[local controller] name: \"{}\"", player_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_pawn() -> Result<()> {
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
struct Vector3D {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let local_player_pawn_offset = read_offset("client.dll", "dwLocalPlayerPawn").unwrap();
|
||||
|
||||
let game_scene_node_offset =
|
||||
read_class_field("client.dll", "C_BaseEntity", "m_pGameSceneNode").unwrap();
|
||||
|
||||
let origin_offset =
|
||||
read_class_field("client.dll", "CGameSceneNode", "m_vecAbsOrigin").unwrap();
|
||||
|
||||
let local_player_pawn: u64 = process
|
||||
.read(client_base + local_player_pawn_offset)
|
||||
.data_part()?;
|
||||
|
||||
let game_scene_node: u64 = process
|
||||
.read((local_player_pawn + game_scene_node_offset).into())
|
||||
.data_part()?;
|
||||
|
||||
let origin: Vector3D = process
|
||||
.read((game_scene_node + origin_offset).into())
|
||||
.data_part()?;
|
||||
|
||||
debug!(
|
||||
"[local pawn] origin: {:.2}, y: {:.2}, z: {:.2}",
|
||||
origin.x, origin.y, origin.z
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let engine_base = process.module_by_name("engine2.dll")?.base;
|
||||
|
||||
let window_width_offset = read_offset("engine2.dll", "dwWindowWidth").unwrap();
|
||||
let window_height_offset = read_offset("engine2.dll", "dwWindowHeight").unwrap();
|
||||
|
||||
let window_width: u32 = process
|
||||
.read(engine_base + window_width_offset)
|
||||
.data_part()?;
|
||||
|
||||
let window_height: u32 = process
|
||||
.read(engine_base + window_height_offset)
|
||||
.data_part()?;
|
||||
|
||||
debug!("window size: {}x{}", window_width, window_height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup() -> Result<IntoProcessInstanceArcBox<'static>> {
|
||||
static LOGGER: Once = Once::new();
|
||||
|
||||
LOGGER.call_once(|| {
|
||||
SimpleLogger::init(LevelFilter::Trace, Config::default()).ok();
|
||||
});
|
||||
|
||||
let os = memflow_native::create_os(&OsArgs::default(), LibArc::default())?;
|
||||
|
||||
let process = os.into_process_by_name("cs2.exe")?;
|
||||
@@ -167,7 +300,7 @@ mod tests {
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
fn get_class_field_value(module_name: &str, class_name: &str, field_name: &str) -> Option<u64> {
|
||||
fn read_class_field(module_name: &str, class_name: &str, field_name: &str) -> Option<u64> {
|
||||
let content =
|
||||
fs::read_to_string(format!("output/{}.json", module_name.replace(".", "_"))).ok()?;
|
||||
|
||||
@@ -182,7 +315,7 @@ mod tests {
|
||||
.as_u64()
|
||||
}
|
||||
|
||||
fn get_offset_value(module_name: &str, offset_name: &str) -> Option<u64> {
|
||||
fn read_offset(module_name: &str, offset_name: &str) -> Option<u64> {
|
||||
let content = fs::read_to_string("output/offsets.json").ok()?;
|
||||
let value: Value = serde_json::from_str(&content).ok()?;
|
||||
|
||||
@@ -190,129 +323,4 @@ mod tests {
|
||||
|
||||
offset.as_u64()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_number() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let engine_base = process.module_by_name("engine2.dll")?.base;
|
||||
|
||||
let offset = get_offset_value("engine2.dll", "dwBuildNumber").unwrap();
|
||||
|
||||
let build_number: u32 = process.read(engine_base + offset).data_part()?;
|
||||
|
||||
println!("build number: {}", build_number);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_vars() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let offset = get_offset_value("client.dll", "dwGlobalVars").unwrap();
|
||||
|
||||
let global_vars: u64 = process.read(client_base + offset).data_part()?;
|
||||
|
||||
let cur_map_name = {
|
||||
let addr = process
|
||||
.read_addr64((global_vars + 0x180).into())
|
||||
.data_part()?;
|
||||
|
||||
process.read_utf8(addr, 128).data_part()?
|
||||
};
|
||||
|
||||
println!("current map name: {}", cur_map_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_player_controller() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let local_player_controller_offset =
|
||||
get_offset_value("client.dll", "dwLocalPlayerController").unwrap();
|
||||
|
||||
let player_name_offset =
|
||||
get_class_field_value("client.dll", "CBasePlayerController", "m_iszPlayerName")
|
||||
.unwrap();
|
||||
|
||||
let local_player_controller: u64 = process
|
||||
.read(client_base + local_player_controller_offset)
|
||||
.data_part()?;
|
||||
|
||||
let player_name = process
|
||||
.read_utf8((local_player_controller + player_name_offset).into(), 4096)
|
||||
.data_part()?;
|
||||
|
||||
println!("local player name: {}", player_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_player_pawn() -> Result<()> {
|
||||
#[derive(Debug, Pod)]
|
||||
#[repr(C)]
|
||||
struct Vector3D {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32,
|
||||
}
|
||||
|
||||
let mut process = setup()?;
|
||||
|
||||
let client_base = process.module_by_name("client.dll")?.base;
|
||||
|
||||
let local_player_pawn_offset = get_offset_value("client.dll", "dwLocalPlayerPawn").unwrap();
|
||||
|
||||
let game_scene_node_offset =
|
||||
get_class_field_value("client.dll", "C_BaseEntity", "m_pGameSceneNode").unwrap();
|
||||
|
||||
let vec_abs_origin_offset =
|
||||
get_class_field_value("client.dll", "CGameSceneNode", "m_vecAbsOrigin").unwrap();
|
||||
|
||||
let local_player_pawn: u64 = process
|
||||
.read(client_base + local_player_pawn_offset)
|
||||
.data_part()?;
|
||||
|
||||
let game_scene_node: u64 = process
|
||||
.read((local_player_pawn + game_scene_node_offset).into())
|
||||
.data_part()?;
|
||||
|
||||
let vec_abs_origin: Vector3D = process
|
||||
.read((game_scene_node + vec_abs_origin_offset).into())
|
||||
.data_part()?;
|
||||
|
||||
println!("local player origin: {:?}", vec_abs_origin);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn window_size() -> Result<()> {
|
||||
let mut process = setup()?;
|
||||
|
||||
let engine_base = process.module_by_name("engine2.dll")?.base;
|
||||
|
||||
let window_width_offset = get_offset_value("engine2.dll", "dwWindowWidth").unwrap();
|
||||
let window_height_offset = get_offset_value("engine2.dll", "dwWindowHeight").unwrap();
|
||||
|
||||
let window_width: u32 = process
|
||||
.read(engine_base + window_width_offset)
|
||||
.data_part()?;
|
||||
|
||||
let window_height: u32 = process
|
||||
.read(engine_base + window_height_offset)
|
||||
.data_part()?;
|
||||
|
||||
println!("window size: {}x{}", window_width, window_height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ pub enum ClassMetadata {
|
||||
pub struct Class {
|
||||
pub name: String,
|
||||
pub module_name: String,
|
||||
pub parent: Option<Box<Class>>,
|
||||
pub parent_name: Option<String>,
|
||||
pub metadata: Vec<ClassMetadata>,
|
||||
pub fields: Vec<ClassField>,
|
||||
}
|
||||
@@ -64,7 +64,7 @@ pub fn schemas<P: Process + MemoryView>(process: &mut P) -> Result<SchemaMap> {
|
||||
let schema_system = read_schema_system(process)?;
|
||||
let type_scopes = read_type_scopes(process, &schema_system)?;
|
||||
|
||||
let map = type_scopes
|
||||
Ok(type_scopes
|
||||
.into_iter()
|
||||
.map(|type_scope| {
|
||||
(
|
||||
@@ -72,9 +72,7 @@ pub fn schemas<P: Process + MemoryView>(process: &mut P) -> Result<SchemaMap> {
|
||||
(type_scope.classes, type_scope.enums),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(map)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn read_class_binding(
|
||||
@@ -86,51 +84,35 @@ fn read_class_binding(
|
||||
let module_name = mem
|
||||
.read_utf8_lossy(binding.module_name.address(), 128)
|
||||
.data_part()
|
||||
.map(|s| format!("{}.dll", s))?;
|
||||
.map(|m| format!("{}.dll", m))?;
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(binding.name.address(), 4096)
|
||||
.read_utf8_lossy(binding.name.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
if name.is_empty() {
|
||||
bail!("empty class name");
|
||||
bail!("invalid class name");
|
||||
}
|
||||
|
||||
let parent = binding.base_classes.non_null().and_then(|ptr| {
|
||||
let parent_name = binding.base_classes.non_null().and_then(|ptr| {
|
||||
let base_class = mem.read_ptr(ptr).data_part().ok()?;
|
||||
let parent_class = mem.read_ptr(base_class.prev).data_part().ok()?;
|
||||
let parent_class = mem.read_ptr(base_class.class).data_part().ok()?;
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(parent_class.name.address(), 4096)
|
||||
let parent_name = mem
|
||||
.read_utf8_lossy(parent_class.name.address(), 128)
|
||||
.data_part()
|
||||
.ok()?;
|
||||
|
||||
Some(Box::new(Class {
|
||||
name,
|
||||
module_name: String::new(),
|
||||
parent: None,
|
||||
metadata: Vec::new(),
|
||||
fields: Vec::new(),
|
||||
}))
|
||||
(!parent_name.is_empty()).then_some(parent_name)
|
||||
});
|
||||
|
||||
let fields = read_class_binding_fields(mem, &binding)?;
|
||||
let metadata = read_class_binding_metadata(mem, &binding)?;
|
||||
|
||||
debug!(
|
||||
"found class: {} at {:#X} (module name: {}) (parent name: {:?}) (metadata count: {}) (field count: {})",
|
||||
name,
|
||||
binding_ptr.to_umem(),
|
||||
module_name,
|
||||
parent.as_ref().map(|p| p.name.clone()),
|
||||
metadata.len(),
|
||||
fields.len(),
|
||||
);
|
||||
|
||||
Ok(Class {
|
||||
name,
|
||||
module_name,
|
||||
parent,
|
||||
parent_name,
|
||||
metadata,
|
||||
fields,
|
||||
})
|
||||
@@ -151,13 +133,9 @@ fn read_class_binding_fields(
|
||||
return Ok(acc);
|
||||
}
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(field.name.address(), 4096)
|
||||
.data_part()?;
|
||||
|
||||
let name = mem.read_utf8_lossy(field.name.address(), 128).data_part()?;
|
||||
let r#type = mem.read_ptr(field.r#type).data_part()?;
|
||||
|
||||
// TODO: Parse this properly.
|
||||
let type_name = mem
|
||||
.read_utf8_lossy(r#type.name.address(), 128)
|
||||
.data_part()?
|
||||
@@ -191,7 +169,7 @@ fn read_class_binding_metadata(
|
||||
}
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(metadata.name.address(), 4096)
|
||||
.read_utf8_lossy(metadata.name.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
let network_value = mem.read_ptr(metadata.network_value).data_part()?;
|
||||
@@ -199,7 +177,7 @@ fn read_class_binding_metadata(
|
||||
let metadata = match name.as_str() {
|
||||
"MNetworkChangeCallback" => unsafe {
|
||||
let name = mem
|
||||
.read_utf8_lossy(network_value.value.name_ptr.address(), 4096)
|
||||
.read_utf8_lossy(network_value.value.name_ptr.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
ClassMetadata::NetworkChangeCallback { name }
|
||||
@@ -208,7 +186,7 @@ fn read_class_binding_metadata(
|
||||
let var_value = network_value.value.var_value;
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(var_value.name.address(), 4096)
|
||||
.read_utf8_lossy(var_value.name.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
let type_name = mem
|
||||
@@ -234,27 +212,19 @@ fn read_enum_binding(
|
||||
let binding = mem.read_ptr(binding_ptr).data_part()?;
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(binding.name.address(), 4096)
|
||||
.read_utf8_lossy(binding.name.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
if name.is_empty() {
|
||||
bail!("empty enum name");
|
||||
bail!("invalid enum name");
|
||||
}
|
||||
|
||||
let members = read_enum_binding_members(mem, &binding)?;
|
||||
|
||||
debug!(
|
||||
"found enum: {} at {:#X} (alignment: {}) (member count: {})",
|
||||
name,
|
||||
binding_ptr.to_umem(),
|
||||
binding.align_of,
|
||||
binding.size,
|
||||
);
|
||||
|
||||
Ok(Enum {
|
||||
name,
|
||||
alignment: binding.align_of,
|
||||
size: binding.enum_count,
|
||||
alignment: binding.alignment,
|
||||
size: binding.enumerator_count,
|
||||
members,
|
||||
})
|
||||
}
|
||||
@@ -263,15 +233,15 @@ fn read_enum_binding_members(
|
||||
mem: &mut impl MemoryView,
|
||||
binding: &SchemaEnumBinding,
|
||||
) -> Result<Vec<EnumMember>> {
|
||||
if binding.enums.is_null() {
|
||||
if binding.enumerators.is_null() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
(0..binding.enum_count).try_fold(Vec::new(), |mut acc, i| {
|
||||
let r#enum = mem.read_ptr(binding.enums.at(i as _)).data_part()?;
|
||||
(0..binding.enumerator_count).try_fold(Vec::new(), |mut acc, i| {
|
||||
let r#enum = mem.read_ptr(binding.enumerators.at(i as _)).data_part()?;
|
||||
|
||||
let name = mem
|
||||
.read_utf8_lossy(r#enum.name.address(), 4096)
|
||||
.read_utf8_lossy(r#enum.name.address(), 128)
|
||||
.data_part()?;
|
||||
|
||||
acc.push(EnumMember {
|
||||
@@ -296,15 +266,15 @@ fn read_schema_system<P: Process + MemoryView>(process: &mut P) -> Result<Schema
|
||||
|
||||
if !view
|
||||
.scanner()
|
||||
.finds_code(pattern!("488905${'} 4c8d0d${} 33c0 48c705[8] 8905"), &mut save)
|
||||
.finds_code(pattern!("4c8d35${'} 0f2845"), &mut save)
|
||||
{
|
||||
bail!("outdated schema system pattern");
|
||||
}
|
||||
|
||||
let schema_system: SchemaSystem = process.read(module.base + save[1]).data_part()?;
|
||||
|
||||
if schema_system.num_registrations == 0 {
|
||||
bail!("no schema system registrations found");
|
||||
if schema_system.registration_count == 0 {
|
||||
bail!("no schema registrations");
|
||||
}
|
||||
|
||||
Ok(schema_system)
|
||||
@@ -316,7 +286,7 @@ fn read_type_scopes(
|
||||
) -> Result<Vec<TypeScope>> {
|
||||
let type_scopes = &schema_system.type_scopes;
|
||||
|
||||
(0..type_scopes.size).try_fold(Vec::new(), |mut acc, i| {
|
||||
(0..type_scopes.count).try_fold(Vec::new(), |mut acc, i| {
|
||||
let type_scope_ptr = type_scopes.element(mem, i as _)?;
|
||||
let type_scope = mem.read_ptr(type_scope_ptr).data_part()?;
|
||||
|
||||
@@ -326,14 +296,14 @@ fn read_type_scopes(
|
||||
|
||||
let classes: Vec<_> = type_scope
|
||||
.class_bindings
|
||||
.elements(mem)?
|
||||
.elements(mem)
|
||||
.iter()
|
||||
.filter_map(|ptr| read_class_binding(mem, *ptr).ok())
|
||||
.collect();
|
||||
|
||||
let enums: Vec<_> = type_scope
|
||||
.enum_bindings
|
||||
.elements(mem)?
|
||||
.elements(mem)
|
||||
.iter()
|
||||
.filter_map(|ptr| read_enum_binding(mem, *ptr).ok())
|
||||
.collect();
|
||||
@@ -343,9 +313,8 @@ fn read_type_scopes(
|
||||
}
|
||||
|
||||
debug!(
|
||||
"found type scope: {} at {:#X} (class count: {}) (enum count: {})",
|
||||
"module \"{}\" contains {} class(es) and {} enum(s)",
|
||||
module_name,
|
||||
type_scope_ptr.to_umem(),
|
||||
classes.len(),
|
||||
enums.len(),
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![feature(once_cell_try)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
@@ -19,6 +20,7 @@ use simplelog::*;
|
||||
use output::Output;
|
||||
|
||||
mod analysis;
|
||||
mod memory;
|
||||
mod output;
|
||||
mod source2;
|
||||
|
||||
|
||||
24
src/memory/address.rs
Normal file
24
src/memory/address.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use memflow::prelude::*;
|
||||
|
||||
#[inline]
|
||||
pub fn follow_call(mem: &mut impl MemoryView, base: Address) -> Result<Address> {
|
||||
rel32_target(mem, base, 0x1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn follow_jmp(mem: &mut impl MemoryView, base: Address) -> Result<Address> {
|
||||
rel32_target(mem, base, 0x1)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn resolve_rip(mem: &mut impl MemoryView, base: Address) -> Result<Address> {
|
||||
rel32_target(mem, base, 0x3)
|
||||
}
|
||||
|
||||
fn rel32_target(mem: &mut impl MemoryView, base: Address, offset: usize) -> Result<Address> {
|
||||
let rel32: i32 = mem.read(base + offset).data_part()?; // RIP-relative displacement.
|
||||
let instr_end = (base + offset + size_of::<i32>()).to_umem() as i64;
|
||||
let target_addr = instr_end.wrapping_add(rel32 as i64);
|
||||
|
||||
Ok(target_addr.into())
|
||||
}
|
||||
1
src/memory/mod.rs
Normal file
1
src/memory/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod address;
|
||||
@@ -20,7 +20,8 @@ impl CodeWriter for ButtonMap {
|
||||
|
||||
fn write_hpp(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(fmt, "#pragma once\n")?;
|
||||
writeln!(fmt, "#include <cstddef>\n")?;
|
||||
writeln!(fmt, "#include <cstddef>")?;
|
||||
writeln!(fmt, "#include <cstdint>\n")?;
|
||||
|
||||
fmt.block("namespace cs2_dumper", false, |fmt| {
|
||||
writeln!(fmt, "// Module: client.dll")?;
|
||||
|
||||
@@ -38,7 +38,8 @@ impl CodeWriter for InterfaceMap {
|
||||
|
||||
fn write_hpp(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(fmt, "#pragma once\n")?;
|
||||
writeln!(fmt, "#include <cstddef>\n")?;
|
||||
writeln!(fmt, "#include <cstddef>")?;
|
||||
writeln!(fmt, "#include <cstdint>\n")?;
|
||||
|
||||
fmt.block("namespace cs2_dumper", false, |fmt| {
|
||||
fmt.block("namespace interfaces", false, |fmt| {
|
||||
|
||||
@@ -29,7 +29,8 @@ impl CodeWriter for OffsetMap {
|
||||
|
||||
fn write_hpp(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(fmt, "#pragma once\n")?;
|
||||
writeln!(fmt, "#include <cstddef>\n")?;
|
||||
writeln!(fmt, "#include <cstddef>")?;
|
||||
writeln!(fmt, "#include <cstdint>\n")?;
|
||||
|
||||
fmt.block("namespace cs2_dumper", false, |fmt| {
|
||||
fmt.block("namespace offsets", false, |fmt| {
|
||||
|
||||
@@ -41,17 +41,17 @@ impl CodeWriter for SchemaMap {
|
||||
.members
|
||||
.iter()
|
||||
.map(|member| {
|
||||
let hex = if member.value < 0
|
||||
|| member.value > i32::MAX as i64
|
||||
{
|
||||
format!(
|
||||
"unchecked(({}){})",
|
||||
type_name, member.value
|
||||
)
|
||||
} else {
|
||||
format!("{:#X}", member.value)
|
||||
};
|
||||
format!("{} = {}", member.name, hex)
|
||||
let formatted_value =
|
||||
if (0..=i32::MAX as i64).contains(&member.value) {
|
||||
format!("{:#X}", member.value)
|
||||
} else {
|
||||
format!(
|
||||
"unchecked(({}){})",
|
||||
type_name, member.value
|
||||
)
|
||||
};
|
||||
|
||||
format!("{} = {}", member.name, formatted_value)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
@@ -63,10 +63,10 @@ impl CodeWriter for SchemaMap {
|
||||
|
||||
for class in classes {
|
||||
let parent_name = class
|
||||
.parent
|
||||
.as_ref()
|
||||
.map(|parent| slugify(&parent.name))
|
||||
.unwrap_or_else(|| String::from("None"));
|
||||
.parent_name
|
||||
.as_deref()
|
||||
.map(slugify)
|
||||
.unwrap_or("None".to_string());
|
||||
|
||||
writeln!(fmt, "// Parent: {}", parent_name)?;
|
||||
writeln!(fmt, "// Field count: {}", class.fields.len())?;
|
||||
@@ -101,7 +101,8 @@ impl CodeWriter for SchemaMap {
|
||||
|
||||
fn write_hpp(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
writeln!(fmt, "#pragma once\n")?;
|
||||
writeln!(fmt, "#include <cstddef>\n")?;
|
||||
writeln!(fmt, "#include <cstddef>")?;
|
||||
writeln!(fmt, "#include <cstdint>\n")?;
|
||||
|
||||
fmt.block("namespace cs2_dumper", false, |fmt| {
|
||||
fmt.block("namespace schemas", false, |fmt| {
|
||||
@@ -134,7 +135,23 @@ impl CodeWriter for SchemaMap {
|
||||
.members
|
||||
.iter()
|
||||
.map(|member| {
|
||||
format!("{} = {:#X}", member.name, member.value)
|
||||
let formatted_value = if (0..=i32::MAX as i64)
|
||||
.contains(&member.value)
|
||||
{
|
||||
format!("{:#X}", member.value)
|
||||
} else {
|
||||
let max_value = match type_name {
|
||||
"uint8_t" => 0xFFu64,
|
||||
"uint16_t" => 0xFFFFu64,
|
||||
"uint32_t" => 0xFFFFFFFFu64,
|
||||
"uint64_t" => 0xFFFFFFFFFFFFFFFFu64,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
format!("{:#X}", max_value)
|
||||
};
|
||||
|
||||
format!("{} = {}", member.name, formatted_value)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
@@ -146,10 +163,10 @@ impl CodeWriter for SchemaMap {
|
||||
|
||||
for class in classes {
|
||||
let parent_name = class
|
||||
.parent
|
||||
.as_ref()
|
||||
.map(|parent| slugify(&parent.name))
|
||||
.unwrap_or_else(|| String::from("None"));
|
||||
.parent_name
|
||||
.as_deref()
|
||||
.map(slugify)
|
||||
.unwrap_or("None".to_string());
|
||||
|
||||
writeln!(fmt, "// Parent: {}", parent_name)?;
|
||||
writeln!(fmt, "// Field count: {}", class.fields.len())?;
|
||||
@@ -219,7 +236,7 @@ impl CodeWriter for SchemaMap {
|
||||
(
|
||||
slugify(&class.name),
|
||||
json!({
|
||||
"parent": class.parent.as_ref().map(|parent| &parent.name),
|
||||
"parent": class.parent_name,
|
||||
"fields": fields,
|
||||
"metadata": metadata
|
||||
}),
|
||||
@@ -311,18 +328,21 @@ impl CodeWriter for SchemaMap {
|
||||
.members
|
||||
.iter()
|
||||
.filter_map(|member| {
|
||||
// Filter out duplicate values.
|
||||
if used_values.insert(member.value) {
|
||||
let value = if member.value == -1 {
|
||||
format!("{}::MAX", type_name)
|
||||
} else {
|
||||
format!("{:#X}", member.value)
|
||||
};
|
||||
|
||||
Some(format!("{} = {}", member.name, value))
|
||||
} else {
|
||||
None
|
||||
// Skip duplicate values.
|
||||
if !used_values.insert(member.value) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let formatted_value = if member.value == -1 {
|
||||
format!("{}::MAX", type_name)
|
||||
} else {
|
||||
format!("{:#X}", member.value)
|
||||
};
|
||||
|
||||
Some(format!(
|
||||
"{} = {}",
|
||||
member.name, formatted_value
|
||||
))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
@@ -334,10 +354,10 @@ impl CodeWriter for SchemaMap {
|
||||
|
||||
for class in classes {
|
||||
let parent_name = class
|
||||
.parent
|
||||
.as_ref()
|
||||
.map(|parent| slugify(&parent.name))
|
||||
.unwrap_or_else(|| String::from("None"));
|
||||
.parent_name
|
||||
.as_deref()
|
||||
.map(slugify)
|
||||
.unwrap_or("None".to_string());
|
||||
|
||||
writeln!(fmt, "// Parent: {}", parent_name)?;
|
||||
writeln!(fmt, "// Field count: {}", class.fields.len())?;
|
||||
|
||||
@@ -3,10 +3,10 @@ use memflow::prelude::v1::*;
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
pub struct KeyButton {
|
||||
pad_0000: [u8; 0x8], // 0x0000
|
||||
pad_0: [u8; 0x8], // 0x0000
|
||||
pub name: Pointer64<ReprCString>, // 0x0008
|
||||
pad_0010: [u8; 0x20], // 0x0010
|
||||
pad_1: [u8; 0x20], // 0x0010
|
||||
pub state: u32, // 0x0030
|
||||
pad_0034: [u8; 0x54], // 0x0034
|
||||
pad_2: [u8; 0x54], // 0x0034
|
||||
pub next: Pointer64<KeyButton>, // 0x0088
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
pub use client::*;
|
||||
pub use schema_system::*;
|
||||
pub use tier0::*;
|
||||
pub use tier1::*;
|
||||
|
||||
pub mod client;
|
||||
pub mod schema_system;
|
||||
pub mod tier0;
|
||||
pub mod tier1;
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
use memflow::prelude::v1::*;
|
||||
|
||||
use super::SchemaClassInfoData;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SchemaBaseClassInfoData {
|
||||
pad_0000: [u8; 0x18], // 0x0000
|
||||
pub prev: Pointer64<SchemaBaseClass>, // 0x0018
|
||||
pad_0: [u8; 0x18], // 0x0000
|
||||
pub class: Pointer64<SchemaBaseClass>, // 0x0018
|
||||
}
|
||||
|
||||
unsafe impl Pod for SchemaBaseClassInfoData {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SchemaBaseClass {
|
||||
pad_0000: [u8; 0x10], // 0x0000
|
||||
pad_0: [u8; 0x10], // 0x0000
|
||||
pub name: Pointer64<ReprCString>, // 0x0010
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@ pub struct SchemaClassInfoData {
|
||||
pub size: i32, // 0x0018
|
||||
pub field_count: i16, // 0x001C
|
||||
pub static_metadata_count: i16, // 0x001E
|
||||
pad_0020: [u8; 0x2], // 0x0020
|
||||
pub align_of: u8, // 0x0022
|
||||
pad_0: [u8; 0x2], // 0x0020
|
||||
pub alignment: u8, // 0x0022
|
||||
pub has_base_class: u8, // 0x0023
|
||||
pub total_class_size: i16, // 0x0024
|
||||
pub derived_class_size: i16, // 0x0026
|
||||
pub fields: Pointer64<[SchemaClassFieldData]>, // 0x0028
|
||||
pad_0038: [u8; 0x8], // 0x0030
|
||||
pad_1: [u8; 0x8], // 0x0030
|
||||
pub base_classes: Pointer64<SchemaBaseClassInfoData>, // 0x0038
|
||||
pub static_metadata: Pointer64<[SchemaMetadataEntryData]>, // 0x0040
|
||||
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0050
|
||||
pub r#type: Pointer64<SchemaType>, // 0x0058
|
||||
pad_0060: [u8; 0x10], // 0x0060
|
||||
pad_2: [u8; 0x10], // 0x0060
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ pub struct SchemaEnumInfoData {
|
||||
pub name: Pointer64<ReprCString>, // 0x0008
|
||||
pub module_name: Pointer64<ReprCString>, // 0x0010
|
||||
pub size: u8, // 0x0018
|
||||
pub align_of: u8, // 0x0019
|
||||
pad_001a: [u8; 0x2], // 0x001A
|
||||
pub enum_count: u16, // 0x001C
|
||||
pub alignment: u8, // 0x0019
|
||||
pub flags: u8, // 0x001A
|
||||
pad_0: [u8; 0x1], // 0x001B
|
||||
pub enumerator_count: u16, // 0x001C
|
||||
pub static_metadata_count: u16, // 0x001E
|
||||
pub enums: Pointer64<[SchemaEnumeratorInfoData]>, // 0x0020
|
||||
pub enumerators: Pointer64<[SchemaEnumeratorInfoData]>, // 0x0020
|
||||
pub static_metadata: Pointer64<SchemaMetadataEntryData>, // 0x0028
|
||||
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0030
|
||||
pub min_enum_value: i64, // 0x0038
|
||||
pub max_enum_value: i64, // 0x0040
|
||||
pub min_enumerator_value: i64, // 0x0038
|
||||
pub max_enumerator_value: i64, // 0x0040
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct SchemaEnumeratorInfoData {
|
||||
pub name: Pointer64<ReprCString>, // 0x0000
|
||||
pub value: SchemaEnumeratorInfoDataUnion, // 0x0008
|
||||
pub metadata_count: i32, // 0x0010
|
||||
pad_0014: [u8; 0x4], // 0x0014
|
||||
pad_0: [u8; 0x4], // 0x0014
|
||||
pub metadata: Pointer64<SchemaMetadataEntryData>, // 0x0018
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub union SchemaNetworkValueUnion {
|
||||
pub name_ptr: Pointer64<ReprCString>,
|
||||
pub int_value: i32,
|
||||
pub float_value: f32,
|
||||
pub ptr: Pointer64<()>,
|
||||
pub ptr_value: Pointer64<()>,
|
||||
pub var_value: SchemaVarName,
|
||||
pub name_value: [c_char; 32],
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ use crate::source2::UtlVector;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SchemaSystem {
|
||||
pad_0000: [u8; 0x190], // 0x0000
|
||||
pad_0: [u8; 0x190], // 0x0000
|
||||
pub type_scopes: UtlVector<Pointer64<SchemaSystemTypeScope>>, // 0x0190
|
||||
pad_0198: [u8; 0xE0], // 0x01A0
|
||||
pub num_registrations: i32, // 0x0280
|
||||
pad_1: [u8; 0xE0], // 0x01A0
|
||||
pub registration_count: i32, // 0x0280
|
||||
}
|
||||
|
||||
unsafe impl Pod for SchemaSystem {}
|
||||
|
||||
@@ -9,10 +9,10 @@ use crate::source2::UtlTsHash;
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaSystemTypeScope {
|
||||
pad_0000: [u8; 0x8], // 0x0000
|
||||
pad_0: [u8; 0x8], // 0x0000
|
||||
pub name: [c_char; 256], // 0x0008
|
||||
pub global_scope: Pointer64<SchemaSystemTypeScope>, // 0x0108
|
||||
pad_0110: [u8; 0x430], // 0x0110
|
||||
pub class_bindings: UtlTsHash<SchemaClassBinding>, // 0x0540
|
||||
pad_1: [u8; 0x450], // 0x0110
|
||||
pub class_bindings: UtlTsHash<SchemaClassBinding>, // 0x0560
|
||||
pub enum_bindings: UtlTsHash<SchemaEnumBinding>, // 0x1DD0
|
||||
}
|
||||
|
||||
@@ -30,36 +30,36 @@ pub enum SchemaTypeCategory {
|
||||
#[repr(C)]
|
||||
pub struct SchemaArrayT {
|
||||
pub array_size: u32, // 0x0000
|
||||
pad_0004: [u8; 0x4], // 0x0004
|
||||
pad_0: [u8; 0x4], // 0x0004
|
||||
pub element: Pointer64<SchemaType>, // 0x0008
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaAtomicI {
|
||||
pad_0000: [u8; 0x10], // 0x0000
|
||||
pub value: u64, // 0x0010
|
||||
pad_0: [u8; 0x10], // 0x0000
|
||||
pub value: u64, // 0x0010
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaAtomicT {
|
||||
pub element: Pointer64<SchemaType>, // 0x0000
|
||||
pad_0008: [u8; 0x8], // 0x0008
|
||||
pad_0: [u8; 0x8], // 0x0008
|
||||
pub template: Pointer64<SchemaType>, // 0x0010
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaAtomicTT {
|
||||
pad_0000: [u8; 0x10], // 0x0000
|
||||
pad_0: [u8; 0x10], // 0x0000
|
||||
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaAtomicTF {
|
||||
pad_0000: [u8; 0x10], // 0x0000
|
||||
pad_0: [u8; 0x10], // 0x0000
|
||||
pub template: Pointer64<SchemaType>, // 0x0010
|
||||
pub size: i32, // 0x0018
|
||||
}
|
||||
@@ -67,14 +67,14 @@ pub struct SchemaAtomicTF {
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct SchemaAtomicTTF {
|
||||
pad_0000: [u8; 0x10], // 0x0000
|
||||
pad_0: [u8; 0x10], // 0x0000
|
||||
pub templates: [Pointer64<SchemaType>; 2], // 0x0010
|
||||
pub size: i32, // 0x0020
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SchemaType {
|
||||
pad_0000: [u8; 0x8], // 0x0000
|
||||
pad_0: [u8; 0x8], // 0x0000
|
||||
pub name: Pointer64<ReprCString>, // 0x0008
|
||||
pub type_scope: Pointer64<SchemaSystemTypeScope>, // 0x0010
|
||||
pub type_category: SchemaTypeCategory, // 0x0018
|
||||
|
||||
3
src/source2/tier0/mod.rs
Normal file
3
src/source2/tier0/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub use ts_list::*;
|
||||
|
||||
pub mod ts_list;
|
||||
14
src/source2/tier0/ts_list.rs
Normal file
14
src/source2/tier0/ts_list.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use memflow::types::Pointer64;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TsListNode;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TsListHead {
|
||||
pub next: Pointer64<TsListNode>, // 0x0000
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TsListBase {
|
||||
pub head: TsListHead, // 0x0000
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
pub use interface::InterfaceReg;
|
||||
pub use utl_memory::UtlMemory;
|
||||
pub use utl_memory_pool::UtlMemoryPoolBase;
|
||||
pub use utl_ts_hash::UtlTsHash;
|
||||
pub use utl_vector::UtlVector;
|
||||
pub use interface::*;
|
||||
pub use utl_memory::*;
|
||||
pub use utl_memory_pool::*;
|
||||
pub use utl_ts_hash::*;
|
||||
pub use utl_vector::*;
|
||||
|
||||
pub mod interface;
|
||||
pub mod utl_memory;
|
||||
|
||||
@@ -2,9 +2,9 @@ use memflow::prelude::v1::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UtlMemory<T> {
|
||||
pub mem: Pointer64<[T]>, // 0x0000
|
||||
pub alloc_count: i32, // 0x0008
|
||||
pub grow_size: i32, // 0x000C
|
||||
pub data: Pointer64<[T]>, // 0x0000
|
||||
pub count: i32, // 0x0008
|
||||
pub grow_size: i32, // 0x000C
|
||||
}
|
||||
|
||||
impl<T: Pod> UtlMemory<T> {
|
||||
@@ -14,10 +14,10 @@ impl<T: Pod> UtlMemory<T> {
|
||||
}
|
||||
|
||||
pub fn element(&self, mem: &mut impl MemoryView, index: usize) -> Result<T> {
|
||||
if index >= self.alloc_count as usize {
|
||||
if index >= self.count as usize {
|
||||
return Err(ErrorKind::OutOfBounds.into());
|
||||
}
|
||||
|
||||
mem.read_ptr(self.mem.at(index as _)).data_part()
|
||||
mem.read_ptr(self.data.at(index as _)).data_part()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
use memflow::prelude::v1::*;
|
||||
|
||||
use crate::source2::TsListBase;
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum MemoryPoolGrowType {
|
||||
None = 0,
|
||||
Fast,
|
||||
Slow,
|
||||
None = 0, // Doesn't allocate new blobs.
|
||||
Fast, // New blobs will grow in size.
|
||||
Slow, // New blobs will stay the same size.
|
||||
}
|
||||
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
pub struct Blob {
|
||||
pub next: Pointer64<Blob>, // 0x0000
|
||||
pub num_bytes: i32, // 0x0008
|
||||
pub data: [u8; 1], // 0x000C
|
||||
pad_000d: [u8; 3], // 0x000D
|
||||
}
|
||||
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
pub struct FreeList {
|
||||
pub next: Pointer64<FreeList>, // 0x0000
|
||||
pub struct UtlMemoryPoolBlob {
|
||||
pub next: Pointer64<UtlMemoryPoolBlob>, // 0x0000
|
||||
pub size: i32, // 0x0008
|
||||
pub data: [u8; 1], // 0x000C
|
||||
pad_0: [u8; 0x3], // 0x000D
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UtlMemoryPoolBase {
|
||||
pub block_size: i32, // 0x0000
|
||||
pub blocks_per_blob: i32, // 0x0004
|
||||
pub grow_mode: MemoryPoolGrowType, // 0x0008
|
||||
pub blocks_alloc: i32, // 0x000C
|
||||
pub peak_alloc: i32, // 0x0010
|
||||
pub align_of: u16, // 0x0014
|
||||
pub blob_count: u16, // 0x0016
|
||||
pub free_list_tail: Pointer64<Pointer64<FreeList>>, // 0x0018
|
||||
pub free_list_head: Pointer64<FreeList>, // 0x0020
|
||||
pad_0028: [u8; 0x44], // 0x0028
|
||||
pub blob_head: Pointer64<Blob>, // 0x0070
|
||||
pub total_size: i32, // 0x0078
|
||||
pad_007c: [u8; 0x4], // 0x007C
|
||||
pub struct UtlMemoryPool {
|
||||
pub block_size: i32, // 0x0000
|
||||
pub blocks_per_blob: i32, // 0x0004
|
||||
pub grow_mode: MemoryPoolGrowType, // 0x0008
|
||||
pub blocks_allocated: i32, // 0x000C
|
||||
pub peak_allocated: i32, // 0x0010
|
||||
pub alignment: u16, // 0x0014
|
||||
pub blob_count: u16, // 0x0016
|
||||
pad_0: [u8; 0x2], // 0x0018
|
||||
pub free_blocks: TsListBase, // 0x0020
|
||||
pad_1: [u8; 0x20], // 0x0028
|
||||
pub blob_head: Pointer64<UtlMemoryPoolBlob>, // 0x0048
|
||||
pub total_size: i32, // 0x0050
|
||||
pad_2: [u8; 0xC], // 0x0054
|
||||
}
|
||||
|
||||
@@ -1,109 +1,119 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use memflow::prelude::v1::*;
|
||||
|
||||
use super::UtlMemoryPoolBase;
|
||||
use super::UtlMemoryPool;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HashAllocatedBlob<D> {
|
||||
pub next: Pointer64<HashAllocatedBlob<D>>, // 0x0000
|
||||
pad_0008: [u8; 0x8], // 0x0008
|
||||
pub data: Pointer64<D>, // 0x0010
|
||||
pad_0018: [u8; 0x8], // 0x0018
|
||||
pub struct UtlTsHashAllocatedBlob<D> {
|
||||
pub next: Pointer64<UtlTsHashAllocatedBlob<D>>, // 0x0000
|
||||
pad_0: [u8; 0x8], // 0x0008
|
||||
pub data: Pointer64<D>, // 0x0010
|
||||
pad_1: [u8; 0x18], // 0x0018
|
||||
}
|
||||
|
||||
unsafe impl<D: 'static> Pod for HashAllocatedBlob<D> {}
|
||||
unsafe impl<D: 'static> Pod for UtlTsHashAllocatedBlob<D> {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RTL_SRWLOCK
|
||||
{
|
||||
pub struct UtlTsHashFixedData<D, K> {
|
||||
pub ui_key: K, // 0x0000
|
||||
pub next: Pointer64<UtlTsHashFixedData<D, K>>, // 0x0008
|
||||
pub data: Pointer64<D>, // 0x0010
|
||||
}
|
||||
|
||||
unsafe impl<D: 'static, K: 'static> Pod for UtlTsHashFixedData<D, K> {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HashBucket<D, K> {
|
||||
pub lock: Pointer64<RTL_SRWLOCK>, // 0x0000
|
||||
pub first: Pointer64<HashFixedDataInternal<D, K>>, // 0x0008
|
||||
pub first_uncommitted: Pointer64<HashFixedDataInternal<D, K>>, // 0x0010
|
||||
pub struct UtlTsHashBucket<D, K> {
|
||||
pub add_lock: usize, // 0x0000
|
||||
pub first: Pointer64<UtlTsHashFixedData<D, K>>, // 0x0008
|
||||
pub first_uncommitted: Pointer64<UtlTsHashFixedData<D, K>>, // 0x0010
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct HashFixedDataInternal<D, K> {
|
||||
pub ui_key: K, // 0x0000
|
||||
pub next: Pointer64<HashFixedDataInternal<D, K>>, // 0x0008
|
||||
pub data: Pointer64<D>, // 0x0010
|
||||
}
|
||||
|
||||
unsafe impl<D: 'static, K: 'static> Pod for HashFixedDataInternal<D, K> {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UtlTsHash<D, const C: usize = 256, K = u64> {
|
||||
pub entry_mem: UtlMemoryPoolBase, // 0x0000
|
||||
pub buckets: [HashBucket<D, K>; C], // 0x0080
|
||||
pub needs_commit: bool, // 0x1880
|
||||
pad_2881: [u8; 0xF], // 0x1881
|
||||
pub entry_mem: UtlMemoryPool, // 0x0000
|
||||
pub buckets: [UtlTsHashBucket<D, K>; C], // 0x0060
|
||||
pub needs_commit: bool, // 0x1860
|
||||
pad_0: [u8; 0x3], // 0x1861
|
||||
pub contention_check: i32, // 0x1864
|
||||
pad_1: [u8; 0x8], // 0x1868
|
||||
}
|
||||
|
||||
impl<D: Pod, const C: usize, K: Pod> UtlTsHash<D, C, K> {
|
||||
#[inline]
|
||||
pub fn blocks_alloc(&self) -> i32 {
|
||||
self.entry_mem.blocks_alloc
|
||||
pub fn elements(&self, mem: &mut impl MemoryView) -> Vec<Pointer64<D>> {
|
||||
let allocated = self.allocated_elements(mem);
|
||||
let unallocated = self.unallocated_elements(mem);
|
||||
|
||||
let mut result = Vec::with_capacity(allocated.len() + unallocated.len());
|
||||
|
||||
result.extend(allocated);
|
||||
result.extend(unallocated);
|
||||
|
||||
let mut seen = HashSet::with_capacity(result.capacity());
|
||||
|
||||
// Remove duplicate pointers that exist in both lists.
|
||||
result.retain(|ptr| seen.insert(ptr.address().to_umem()));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn block_size(&self) -> i32 {
|
||||
self.entry_mem.block_size
|
||||
}
|
||||
fn allocated_elements(&self, mem: &mut impl MemoryView) -> Vec<Pointer64<D>> {
|
||||
let used_count = self.entry_mem.blocks_allocated as usize;
|
||||
|
||||
#[inline]
|
||||
pub fn peak_count(&self) -> i32 {
|
||||
self.entry_mem.peak_alloc
|
||||
}
|
||||
|
||||
pub fn elements(&self, mem: &mut impl MemoryView) -> Result<Vec<Pointer64<D>>> {
|
||||
let blocks_alloc = self.blocks_alloc() as usize;
|
||||
let peak_alloc = self.peak_count() as usize;
|
||||
|
||||
let mut allocated_list = Vec::with_capacity(peak_alloc);
|
||||
let mut unallocated_list = Vec::with_capacity(blocks_alloc);
|
||||
let mut elements = Vec::with_capacity(used_count);
|
||||
|
||||
for bucket in &self.buckets {
|
||||
let mut cur_element = bucket.first_uncommitted;
|
||||
let mut node_ptr = bucket.first_uncommitted;
|
||||
|
||||
while !cur_element.is_null() {
|
||||
let element = mem.read_ptr(cur_element).data_part()?;
|
||||
while !node_ptr.is_null() {
|
||||
let node = match mem.read_ptr(node_ptr).data_part() {
|
||||
Ok(n) => n,
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
if !element.data.is_null() {
|
||||
allocated_list.push(element.data);
|
||||
if !node.data.is_null() {
|
||||
elements.push(node.data);
|
||||
}
|
||||
|
||||
if allocated_list.len() >= blocks_alloc {
|
||||
if elements.len() >= used_count {
|
||||
break;
|
||||
}
|
||||
|
||||
cur_element = element.next;
|
||||
node_ptr = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
let mut cur_blob =
|
||||
Pointer64::<HashAllocatedBlob<D>>::from(self.entry_mem.free_list_head.address());
|
||||
elements
|
||||
}
|
||||
|
||||
while !cur_blob.is_null() {
|
||||
let blob = mem.read_ptr(cur_blob).data_part()?;
|
||||
fn unallocated_elements(&self, mem: &mut impl MemoryView) -> Vec<Pointer64<D>> {
|
||||
let free_count = self.entry_mem.peak_allocated as usize;
|
||||
|
||||
let mut elements = Vec::with_capacity(free_count);
|
||||
|
||||
let mut blob_ptr = Pointer64::<UtlTsHashAllocatedBlob<D>>::from(
|
||||
self.entry_mem.free_blocks.head.next.address(),
|
||||
);
|
||||
|
||||
while !blob_ptr.is_null() {
|
||||
let blob = match mem.read_ptr(blob_ptr).data_part() {
|
||||
Ok(b) => b,
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
if !blob.data.is_null() {
|
||||
unallocated_list.push(blob.data);
|
||||
elements.push(blob.data);
|
||||
}
|
||||
|
||||
if unallocated_list.len() >= peak_alloc {
|
||||
if elements.len() >= free_count {
|
||||
break;
|
||||
}
|
||||
|
||||
cur_blob = blob.next;
|
||||
blob_ptr = blob.next;
|
||||
}
|
||||
|
||||
Ok(if unallocated_list.len() > allocated_list.len() {
|
||||
unallocated_list
|
||||
} else {
|
||||
allocated_list
|
||||
})
|
||||
elements
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,18 @@ use memflow::prelude::v1::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UtlVector<T> {
|
||||
pub size: i32, // 0x0000
|
||||
pad_0004: [u8; 0x4], // 0x0004
|
||||
pub mem: Pointer64<[T]>, // 0x0008
|
||||
pub count: i32, // 0x0000
|
||||
pad_0: [u8; 0x4], // 0x0004
|
||||
pub data: Pointer64<[T]>, // 0x0008
|
||||
}
|
||||
|
||||
impl<T: Pod> UtlVector<T> {
|
||||
pub fn element(&self, mem: &mut impl MemoryView, index: usize) -> Result<T> {
|
||||
if index >= self.size as usize {
|
||||
if index >= self.count as usize {
|
||||
return Err(ErrorKind::OutOfBounds.into());
|
||||
}
|
||||
|
||||
mem.read_ptr(self.mem.at(index as _)).data_part()
|
||||
mem.read_ptr(self.data.at(index as _)).data_part()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user