diff --git a/Cargo.toml b/Cargo.toml index 760c956..8f3229b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1.0" simplelog = { version = "0.12", features = ["paris"] } [dependencies.windows] -version = "0.52" +version = "0.54" features = [ "Win32_Foundation", "Win32_System_Diagnostics_Debug", diff --git a/config.json b/config.json index 7ea05d5..77f1fc6 100644 --- a/config.json +++ b/config.json @@ -79,7 +79,7 @@ { "name": "dwForceCrouch", "module": "client.dll", - "pattern": "48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 4C 6B E3", + "pattern": "48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 83 C4 ? E9 ? ? ? ? CC CC CC CC CC CC CC CC CC CC 48 83 EC ? 66 C7 44 24", "operations": [ { "type": "rip" @@ -93,7 +93,7 @@ { "name": "dwForceForward", "module": "client.dll", - "pattern": "48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 48 8D 05 ? ? ? ? 48 89 45 ? 4C 6B E3", + "pattern": "48 8D 05 ? ? ? ? 48 89 05 ? ? ? ? 48 83 C4 ? E9 ? ? ? ? CC CC CC CC CC CC CC CC CC CC 48 81 EC", "operations": [ { "type": "rip" @@ -149,7 +149,7 @@ { "name": "dwGameEntitySystem", "module": "client.dll", - "pattern": "48 8B 1D ? ? ? ? 48 89 1D", + "pattern": "48 89 0D ? ? ? ? E9 ? ? ? ? CC", "operations": [ { "type": "rip" @@ -181,7 +181,7 @@ { "name": "dwGameTypes", "module": "matchmaking.dll", - "pattern": "48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 83 EC ? F0 81 0D", + "pattern": "48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? 33 D2", "operations": [ { "type": "rip" @@ -191,7 +191,7 @@ { "name": "dwGameTypes_mapName", "module": "matchmaking.dll", - "pattern": "48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 83 EC ? F0 81 0D", + "pattern": "48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? 33 D2", "operations": [ { "type": "rip" @@ -205,7 +205,7 @@ { "name": "dwGlobalVars", "module": "client.dll", - "pattern": "48 89 0D ? ? ? ? 48 89 41", + "pattern": "48 89 15 ? ? ? ? 48 8D 05 ? ? ? ? 48 85 D2", "operations": [ { "type": "rip" @@ -245,7 +245,7 @@ { "name": "dwLocalPlayerController", "module": "client.dll", - "pattern": "48 8B 05 ? ? ? ? 48 85 C0 74 4F", + "pattern": "48 8B 05 ? ? ? ? 48 85 C0 74 ? 8B 88", "operations": [ { "type": "rip" @@ -339,7 +339,7 @@ { "name": "dwSensitivity", "module": "client.dll", - "pattern": "48 8B 05 ? ? ? ? 48 8B 40 ? F3 0F 10 00 F3 0F 59 86", + "pattern": "48 8B 05 ? ? ? ? 48 8B 40 ? F3 41 0F 59 F4", "operations": [ { "type": "rip" diff --git a/src/builder/cpp_file_builder.rs b/src/builder/cpp_file_builder.rs index fbcfc87..38ad065 100644 --- a/src/builder/cpp_file_builder.rs +++ b/src/builder/cpp_file_builder.rs @@ -1,9 +1,7 @@ -use super::FileBuilder; - use std::io::{Result, Write}; -/// A structure representing a builder for C++ header files. -/// The builder implements the `FileBuilder` trait. +use super::FileBuilder; + #[derive(Clone, Debug, PartialEq)] pub struct CppFileBuilder; @@ -13,7 +11,10 @@ impl FileBuilder for CppFileBuilder { } fn write_top_level(&mut self, output: &mut dyn Write) -> Result<()> { - write!(output, "#pragma once\n\n#include \n\n") + writeln!(output, "#pragma once\n")?; + writeln!(output, "#include \n")?; + + Ok(()) } fn write_namespace( diff --git a/src/builder/csharp_file_builder.rs b/src/builder/csharp_file_builder.rs index d4bbb6e..5661e23 100644 --- a/src/builder/csharp_file_builder.rs +++ b/src/builder/csharp_file_builder.rs @@ -1,9 +1,7 @@ -use super::FileBuilder; - use std::io::{Result, Write}; -/// A structure representing a builder for C# files. -/// The builder implements the `FileBuilder` trait. +use super::FileBuilder; + #[derive(Clone, Debug, PartialEq)] pub struct CSharpFileBuilder; diff --git a/src/builder/file_builder.rs b/src/builder/file_builder.rs index 237ee9c..c089d71 100644 --- a/src/builder/file_builder.rs +++ b/src/builder/file_builder.rs @@ -1,42 +1,10 @@ use std::io::{Result, Write}; -/// A trait that defines the file builder operations. pub trait FileBuilder { - /// Returns the extension of the file. - /// - /// # Arguments - /// - /// * `&mut self` - A mutable reference to the `FileBuilder` struct. - /// - /// # Returns - /// - /// * `&str` - A string slice containing the extension of the file. fn extension(&mut self) -> &str; - /// Writes to the top level of the file. The output destination is `output`. - /// - /// # Arguments - /// - /// * `&mut self` - A mutable reference to the `FileBuilder` struct. - /// * `output` - An object implementing Write trait where the top level will be written. - /// - /// # Returns - /// - /// * `Result<()>` - A generic Result type indicating the operations outcome. fn write_top_level(&mut self, output: &mut dyn Write) -> Result<()>; - /// Writes a namespace to the output. - /// - /// # Arguments - /// - /// * `&mut self` - A mutable reference to the `FileBuilder` struct. - /// * `output` - An object implementing Write trait where the namespace will be written. - /// * `name` - The name of the namespace. - /// * `comment` - An optional comment. If present, this comment will be included in the output. - /// - /// # Returns - /// - /// * `Result<()>` - A generic Result type indicating the operations outcome. fn write_namespace( &mut self, output: &mut dyn Write, @@ -44,20 +12,6 @@ pub trait FileBuilder { comment: Option<&str>, ) -> Result<()>; - /// Writes a variable to the output. - /// - /// # Arguments - /// - /// * `&mut self` - A mutable reference to the `FileBuilder` struct. - /// * `output` - An object implementing Write trait where the variable will be written. - /// * `name` - The name of the variable. - /// * `value` - The value of the variable. - /// * `comment` - An optional comment. If present, this comment will be included in the output. - /// * `indentation` - An optional indentation value. If present, the variable will be written with the specified indentation. - /// - /// # Returns - /// - /// * `Result<()>` - A generic Result type indicating the operations outcome. fn write_variable( &mut self, output: &mut dyn Write, @@ -67,16 +21,5 @@ pub trait FileBuilder { indentation: Option, ) -> Result<()>; - /// Writes a closure to the output. - /// - /// # Arguments - /// - /// * `&mut self` - A mutable reference to the `FileBuilder` struct. - /// * `output` - An object implementing Write trait where the closure will be written. - /// * `eof` - A boolean value, if true, indicates that this is the last element to write to the output. - /// - /// # Returns - /// - /// * `Result<()>` - A generic Result type indicating the operations outcome. fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()>; } diff --git a/src/builder/json_file_builder.rs b/src/builder/json_file_builder.rs index 1010fed..969e33f 100644 --- a/src/builder/json_file_builder.rs +++ b/src/builder/json_file_builder.rs @@ -1,27 +1,22 @@ -use super::FileBuilder; - -use serde::Serialize; - use std::collections::BTreeMap; use std::io::{Result, Write}; -/// Represents a JSON offset value with an optional comment. +use serde::Serialize; + +use super::FileBuilder; + #[derive(Clone, Debug, Default, PartialEq, Serialize)] struct JsonOffsetValue { value: usize, comment: Option, } -/// Represents a JSON module, which contains data in the form of a `BTreeMap` of string keys and -/// `JsonOffsetValue` values, as well as an optional comment. #[derive(Clone, Debug, Default, PartialEq, Serialize)] struct JsonModule { data: BTreeMap, comment: Option, } -/// A structure representing a builder for JSON files. -/// The builder implements the `FileBuilder` trait. #[derive(Clone, Debug, Default, PartialEq)] pub struct JsonFileBuilder { data: BTreeMap, diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 3dd999f..7c5ed90 100644 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -6,7 +6,7 @@ pub use python_file_builder::PythonFileBuilder; pub use rust_file_builder::RustFileBuilder; pub use yaml_file_builder::YamlFileBuilder; -pub use std::io::{Result, Write}; +use std::io::{Result, Write}; pub mod cpp_file_builder; pub mod csharp_file_builder; @@ -16,26 +16,13 @@ pub mod python_file_builder; pub mod rust_file_builder; pub mod yaml_file_builder; -/// `FileBuilder` is an enum that defines different kinds of file builders. -/// Each variant corresponds to a builder for a particular type of file. #[derive(Clone, Debug, PartialEq)] pub enum FileBuilderEnum { - /// Represents a builder for C++ header files. CppFileBuilder(CppFileBuilder), - - /// Represents a builder for C# files. CSharpFileBuilder(CSharpFileBuilder), - - /// Represents a builder for JSON files. JsonFileBuilder(JsonFileBuilder), - - /// Represents a builder for Python files. PythonFileBuilder(PythonFileBuilder), - - /// Represents a builder for Rust files. RustFileBuilder(RustFileBuilder), - - /// Represents a builder for YAML files. YamlFileBuilder(YamlFileBuilder), } diff --git a/src/builder/python_file_builder.rs b/src/builder/python_file_builder.rs index 9e85e90..13d130c 100644 --- a/src/builder/python_file_builder.rs +++ b/src/builder/python_file_builder.rs @@ -1,9 +1,7 @@ -use super::FileBuilder; - use std::io::{Result, Write}; -/// A structure representing a builder for Python files. -/// The builder implements the `FileBuilder` trait. +use super::FileBuilder; + #[derive(Clone, Debug, PartialEq)] pub struct PythonFileBuilder; diff --git a/src/builder/rust_file_builder.rs b/src/builder/rust_file_builder.rs index f6cf2a9..cdf27e0 100644 --- a/src/builder/rust_file_builder.rs +++ b/src/builder/rust_file_builder.rs @@ -2,8 +2,6 @@ use super::FileBuilder; use std::io::{Result, Write}; -/// A structure representing a builder for Rust files. -/// The builder implements the `FileBuilder` trait. #[derive(Clone, Debug, Default, PartialEq)] pub struct RustFileBuilder; diff --git a/src/builder/yaml_file_builder.rs b/src/builder/yaml_file_builder.rs index 27eb34f..6604d81 100644 --- a/src/builder/yaml_file_builder.rs +++ b/src/builder/yaml_file_builder.rs @@ -1,9 +1,7 @@ -use super::FileBuilder; - use std::io::{Result, Write}; -/// A structure representing a builder for Yaml files. -/// The builder implements the `FileBuilder` trait. +use super::FileBuilder; + #[derive(Clone, Debug, Default, PartialEq)] pub struct YamlFileBuilder; diff --git a/src/config.rs b/src/config.rs index 31d5625..78d12e7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,72 +1,41 @@ use serde::{Deserialize, Serialize}; -/// Represents an operation that can be performed on a memory address. #[derive(Debug, Deserialize, Serialize)] #[serde(tag = "type", rename_all = "camelCase")] pub enum Operation { - /// Represents an `add` operation. - /// - /// `value` is the value to add. - Add { value: usize }, - - /// Represents a `dereference` operation. - /// - /// `times` is the number of times to dereference the address. If `None`, the number of times will be `1`. - /// `size` is the size of the resulting value. If `None`, the size will be `8`. + Add { + value: usize, + }, Deref { times: Option, size: Option, }, - - /// Represents an operation to resolve the absolute address of a relative call. - /// - /// `offset` is the offset of the displacement value. If `None`, the offset will be `0x1`. - /// `length` is the length of the instruction. If `None`, the length will be `0x5`. Jmp { offset: Option, length: Option, }, - - /// Represents an operation to resolve the absolute address of a RIP-relative address. - /// - /// `offset` is the offset of the displacement value. If `None`, the offset will be `0x3`. - /// `length` is the length of the instruction. If `None`, the length will be `0x7`. Rip { offset: Option, length: Option, }, - - /// Represents a `slice` operation. - /// - /// `start` is the start index of the slice. - /// `end` is the end index of the slice. - Slice { start: usize, end: usize }, - - /// Represents a `subtract` operation. - /// - /// `value` is the value to subtract. - Sub { value: usize }, + Slice { + start: usize, + end: usize, + }, + Sub { + value: usize, + }, } -/// Represents a signature specified in the `config.json` file. -#[derive(Debug, Deserialize, Serialize)] -pub struct Signature { - /// The name of the signature. - pub name: String, - - /// The name of the module. - pub module: String, - - /// The pattern of the signature. - pub pattern: String, - - /// The list of operations to perform on the target address. - pub operations: Vec, -} - -/// Represents the `config.json` file. #[derive(Debug, Deserialize, Serialize)] pub struct Config { - /// The list of signatures specified in the `config.json` file. pub signatures: Vec, } + +#[derive(Debug, Deserialize, Serialize)] +pub struct Signature { + pub name: String, + pub module: String, + pub pattern: String, + pub operations: Vec, +} diff --git a/src/dumper/interfaces.rs b/src/dumper/interfaces.rs index efd0801..c91578b 100644 --- a/src/dumper/interfaces.rs +++ b/src/dumper/interfaces.rs @@ -1,41 +1,25 @@ -use super::{generate_files, Entries, Entry}; - -use crate::builder::FileBuilderEnum; -use crate::util::{Address, Process}; +use std::ffi::c_char; +use std::mem::offset_of; use anyhow::Result; use simplelog::{debug, info}; -use std::ffi::c_char; -use std::mem::offset_of; +use super::{generate_files, Entries, Entry}; + +use crate::builder::FileBuilderEnum; +use crate::os::Process; -/// Represents a node in a linked list of interfaces. #[derive(Debug)] #[repr(C)] struct InterfaceNode { - /// Used to instantiate an instance of the interface. pub create_fn: *const (), - - /// Pointer to the name of the interface. pub name: *const c_char, - - /// Pointer to the next entry in the linked list. pub next: *mut InterfaceNode, } impl InterfaceNode { - /// Returns the instance of the interface. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `InterfaceNode` struct. - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result
` - A `Result` containing the instance of the interface if successful, or an error if the memory read fails. - fn instance(&self, process: &Process) -> Result
{ + fn instance(&self, process: &Process) -> Result { process .read_memory::( (self as *const _ as usize + offset_of!(InterfaceNode, create_fn)).into(), @@ -43,18 +27,6 @@ impl InterfaceNode { .map(|ptr| ptr.into()) } - /// Returns the name of the interface with the version number appended. - /// - /// E.g. "Source2Client002". - /// - /// # Arguments - /// - /// * `&self` - A reference to the `InterfaceNode` struct. - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the name of the interface if successful, or an error if the memory read fails. fn name(&self, process: &Process) -> Result { let name_ptr = process.read_memory::( (self as *const _ as usize + offset_of!(InterfaceNode, name)).into(), @@ -63,16 +35,6 @@ impl InterfaceNode { process.read_string(name_ptr.into()) } - /// Returns a pointer to the next `InterfaceNode` in the linked list. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `InterfaceNode` struct. - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result<*mut InterfaceNode>` - A `Result` containing a pointer to the next `InterfaceNode` if successful, or an error if the memory read fails. fn next(&self, process: &Process) -> Result<*mut InterfaceNode> { process.read_memory::<*mut InterfaceNode>( (self as *const _ as usize + offset_of!(InterfaceNode, next)).into(), @@ -80,18 +42,6 @@ impl InterfaceNode { } } -/// Dumps all interfaces and writes the results to a file. -/// -/// # Arguments -/// -/// * `process` - A reference to the `Process` struct. -/// * `builders` - A mutable reference to a vector of `FileBuilderEnum`. -/// * `file_path` - A string slice representing the path to the file to write the results to. -/// * `indent` - The number of spaces to use for indentation in the output file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. pub fn dump_interfaces( process: &Process, builders: &mut Vec, @@ -100,13 +50,12 @@ pub fn dump_interfaces( ) -> Result<()> { let mut entries = Entries::new(); - // Iterate over all modules in the process, excluding crashhandler64.dll. for module in process .modules()? .iter() .filter(|m| m.name != "crashhandler64.dll") { - if let Some(create_interface_export) = module.get_export_by_name("CreateInterface") { + if let Some(create_interface_export) = module.export_by_name("CreateInterface") { info!("Dumping interfaces in {}...", module.name); let create_interface_address; @@ -127,7 +76,6 @@ pub fn dump_interfaces( let mut node = process.read_memory::<*mut InterfaceNode>(create_interface_address)?; - // Iterate over each node in the linked list. while !node.is_null() { let instance = unsafe { (*node).instance(process) }?; let name = unsafe { (*node).name(process) }?; @@ -151,7 +99,6 @@ pub fn dump_interfaces( indent: Some(indent), }); - // Get the next node in the linked list. node = unsafe { (*node).next(process) }?; } } diff --git a/src/dumper/mod.rs b/src/dumper/mod.rs index 90f8099..dcd1484 100644 --- a/src/dumper/mod.rs +++ b/src/dumper/mod.rs @@ -1,9 +1,3 @@ -use crate::builder::{FileBuilder, FileBuilderEnum}; - -use anyhow::Result; - -use chrono::Utc; - pub use interfaces::dump_interfaces; pub use offsets::dump_offsets; pub use schemas::dump_schemas; @@ -12,51 +6,32 @@ use std::collections::BTreeMap; use std::fs::File; use std::io::Write; +use anyhow::Result; + +use chrono::Utc; + +use crate::builder::{FileBuilder, FileBuilderEnum}; + pub mod interfaces; pub mod offsets; pub mod schemas; -/// Represents an entry in the generated file. #[derive(Debug, PartialEq)] pub struct Entry { - /// The name of the entry. pub name: String, - - /// The value of the entry. pub value: usize, - - /// An optional comment associated with the entry. pub comment: Option, - - /// An optional indentation level for the entry. pub indent: Option, } -/// A container for entries, which consists of data and an optional comment. #[derive(Default)] pub struct EntriesContainer { - /// The data associated with the container. pub data: Vec, - - /// An optional comment associated with the container. pub comment: Option, } -/// A type alias for a `BTreeMap` that maps `String` keys to `EntriesContainer` values. pub type Entries = BTreeMap; -/// Generates a file using the given `builder`, `entries`, `file_path`, and `file_name`. -/// -/// # Arguments -/// -/// * `builder` - A mutable reference to the `FileBuilderEnum`. -/// * `entries` - A reference to the `Entries` struct. -/// * `file_path` - A string slice representing the path to the file. -/// * `file_name` - A string slice representing the name of the file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. pub fn generate_file( builder: &mut FileBuilderEnum, entries: &Entries, @@ -96,18 +71,6 @@ pub fn generate_file( Ok(()) } -/// Generate files using the given `builders`, `entries`, `file_path`, and `file_name`. -/// -/// # Arguments -/// -/// * `builders` - A mutable slice of `FileBuilderEnum` objects. -/// * `entries` - A reference to the `Entries` struct. -/// * `file_path` - A string slice representing the path to the file. -/// * `file_name` - A string slice representing the name of the file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. pub fn generate_files( builders: &mut [FileBuilderEnum], entries: &Entries, @@ -119,16 +82,6 @@ pub fn generate_files( .try_for_each(|builder| generate_file(builder, entries, file_path, file_name)) } -/// Writes the banner to the given file based on the file extension. -/// -/// # Arguments -/// -/// * `file` - A mutable reference to the file to write the banner to. -/// * `file_extension` - A string slice representing the file extension of the file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. fn write_banner_to_file(file: &mut File, file_extension: &str) -> Result<()> { const REPO_URL: &str = "https://github.com/a2x/cs2-dumper"; @@ -137,12 +90,12 @@ fn write_banner_to_file(file: &mut File, file_extension: &str) -> Result<()> { let banner = match file_extension { "json" => None, "py" => Some(format!( - "'''\nCreated using {}\n{}\n'''\n\n", + "'''\nGenerated using {}\n{}\n'''\n\n", REPO_URL, time_now )), "yaml" => None, _ => Some(format!( - "/*\n * Created using {}\n * {}\n */\n\n", + "/*\n * Generated using {}\n * {}\n */\n\n", REPO_URL, time_now )), }; diff --git a/src/dumper/offsets.rs b/src/dumper/offsets.rs index 59be15e..d098b2c 100644 --- a/src/dumper/offsets.rs +++ b/src/dumper/offsets.rs @@ -1,28 +1,16 @@ -use super::{generate_files, Entries, Entry}; - -use crate::builder::FileBuilderEnum; -use crate::config::Config; -use crate::config::Operation::*; -use crate::util::Process; +use std::fs::File; use anyhow::Result; use simplelog::{debug, error, info}; -use std::fs::File; +use super::{generate_files, Entries, Entry}; + +use crate::builder::FileBuilderEnum; +use crate::config::Config; +use crate::config::Operation::*; +use crate::os::Process; -/// Dumps all offsets specified in the `config.json` file and writes the results to a file. -/// -/// # Arguments -/// -/// * `process` - A reference to the `Process` struct. -/// * `builders` - A mutable reference to a vector of `FileBuilderEnum`. -/// * `file_path` - A string slice representing the path to the file to write the results to. -/// * `indent` - The number of spaces to use for indentation in the output file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. pub fn dump_offsets( process: &Process, builders: &mut Vec, @@ -64,11 +52,7 @@ pub fn dump_offsets( let size = size.unwrap_or(8); for _ in 0..times { - process.read_memory_raw( - address, - &mut address.0 as *mut _ as *mut _, - size, - )?; + process.read_memory_raw(address, &mut address as *mut _ as *mut _, size)?; } } Jmp { offset, length } => { @@ -81,7 +65,7 @@ pub fn dump_offsets( let mut result: usize = 0; process.read_memory_raw( - address.add(start.try_into().unwrap()), + address + start, &mut result as *mut _ as *mut _, end - start, )?; @@ -98,17 +82,17 @@ pub fn dump_offsets( signature.name, address ); - (signature.name, address.0) + (signature.name, address) } else { debug!( "Found {} @ {:#X} ({} + {:#X})", signature.name, address, signature.module, - address.sub(module.base().0) + address - module.base() ); - (signature.name, address.sub(module.base().0).0) + (signature.name, address - module.base()) }; if name == "dwBuildNumber" { @@ -165,8 +149,6 @@ mod tests { fn setup() -> Result { let mut process = Process::new("cs2.exe")?; - process.initialize()?; - Ok(process) } diff --git a/src/dumper/schemas.rs b/src/dumper/schemas.rs index 7f94ee2..8a34c26 100644 --- a/src/dumper/schemas.rs +++ b/src/dumper/schemas.rs @@ -1,25 +1,13 @@ -use super::{generate_files, Entries, Entry}; - -use crate::builder::FileBuilderEnum; -use crate::sdk::SchemaSystem; -use crate::util::Process; - use anyhow::Result; use simplelog::{debug, info}; -/// Dumps all schema system classes and writes the results to a file. -/// -/// # Arguments -/// -/// * `process` - A reference to the `Process` struct. -/// * `builders` - A mutable reference to a vector of `FileBuilderEnum`. -/// * `file_path` - A string slice representing the path to the file to write the results to. -/// * `indent` - The number of spaces to use for indentation in the output file. -/// -/// # Returns -/// -/// * `Result<()>` - A `Result` indicating the outcome of the operation. +use super::{generate_files, Entries, Entry}; + +use crate::builder::FileBuilderEnum; +use crate::os::Process; +use crate::sdk::SchemaSystem; + pub fn dump_schemas( process: &Process, builders: &mut Vec, diff --git a/src/main.rs b/src/main.rs index 8ee68a9..a3c63f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,44 +1,41 @@ #![allow(dead_code)] -use anyhow::{bail, Result}; - -use builder::*; - -use clap::Parser; - -use dumper::{dump_interfaces, dump_offsets, dump_schemas}; - -use log::LevelFilter; - -use simplelog::{info, ColorChoice, ConfigBuilder, TermLogger, TerminalMode}; - use std::fs; use std::path::Path; use std::time::Instant; -use util::Process; +use anyhow::{bail, Result}; + +use clap::Parser; + +use log::LevelFilter; + +use simplelog::{info, ColorChoice, ConfigBuilder, TermLogger, TerminalMode}; + +use builder::*; +use dumper::{dump_interfaces, dump_offsets, dump_schemas}; +use os::Process; mod builder; mod config; mod dumper; +mod os; mod sdk; -mod util; -/// Command line arguments for the program. #[derive(Debug, Parser)] #[command(name = "cs2-dumper")] #[command(author = "a2x")] #[command(version = "1.1.5")] struct Args { - /// Dump interfaces. + /// Whether to dump interfaces. #[arg(short, long)] interfaces: bool, - /// Dump offsets. + /// Whether to dump offsets. #[arg(short, long)] offsets: bool, - /// Dump schema system classes. + /// Whether to dump schema classes. #[arg(short, long)] schemas: bool, @@ -47,7 +44,7 @@ struct Args { #[arg( short, long, - value_parser = parse_extension, + value_parser = map_file_extension_to_builder, value_delimiter = ',', default_values = [".cs", ".hpp", ".json", ".py", ".rs", ".yaml"], )] @@ -89,7 +86,6 @@ fn main() -> Result<()> { TermLogger::init(log_level, config, TerminalMode::Mixed, ColorChoice::Auto)?; - // Check if the config file exists. if !Path::new("config.json").exists() { bail!("Missing config.json file"); } @@ -97,13 +93,8 @@ fn main() -> Result<()> { // Create the output directory if it doesn't exist. fs::create_dir_all(&output)?; - // Open a handle to the game process. let mut process = Process::new("cs2.exe")?; - process.initialize()?; - - /* - // Start the timer. let now = Instant::now(); let all = !(interfaces || offsets || schemas); @@ -120,26 +111,15 @@ fn main() -> Result<()> { dump_offsets(&mut process, &mut builders, &output, indent)?; } - // Stop the timer. info!( "Done! Time elapsed: {:?}", now.elapsed() - ); */ + ); Ok(()) } -/// Parses the given file extension and returns the corresponding `FileBuilderEnum`. -/// -/// # Arguments -/// -/// * `extension` - A string slice that represents the file extension. -/// -/// # Returns -/// -/// * `Ok(FileBuilderEnum)` - If the extension is valid, returns the corresponding `FileBuilderEnum`. -/// * `Err(&'static str)` - If the extension is invalid, returns an error message. -fn parse_extension(extension: &str) -> Result { +fn map_file_extension_to_builder(extension: &str) -> Result { match extension { ".cs" => Ok(FileBuilderEnum::CSharpFileBuilder(CSharpFileBuilder)), ".hpp" => Ok(FileBuilderEnum::CppFileBuilder(CppFileBuilder)), diff --git a/src/os/mod.rs b/src/os/mod.rs index 774eb38..cc86f99 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -1,8 +1,6 @@ -pub use address::Address; pub use module::Module; pub use module::ModuleEntry; pub use process::Process; -pub mod address; pub mod module; pub mod process; diff --git a/src/os/module.rs b/src/os/module.rs index 35d65a9..05272fa 100644 --- a/src/os/module.rs +++ b/src/os/module.rs @@ -1,5 +1,3 @@ -use super::Address; - use anyhow::Result; use goblin::pe::export::Export; @@ -16,7 +14,7 @@ use std::path::PathBuf; #[derive(Debug)] pub struct ModuleEntry { pub path: PathBuf, - pub start_addr: Address, + pub start_addr: usize, pub data: Vec, pub module_file_data: Vec, } @@ -28,8 +26,9 @@ pub struct Module<'a> { /// A reference to a slice of bytes containing the module data. #[cfg(target_os = "windows")] pub data: &'a [u8], - // #[cfg(target_os = "linux")] + #[cfg(target_os = "linux")] pub module_info: &'a ModuleEntry, + #[cfg(target_os = "windows")] /// The PE file format representation of the module. pub pe: PE<'a>, @@ -39,16 +38,6 @@ pub struct Module<'a> { } impl<'a> Module<'a> { - /// Parses the given module name and data and returns a `Result` containing a `Module` struct. - /// - /// # Arguments - /// - /// * `name` - A string slice that holds the name of the module. - /// * `data` - A byte slice that holds the data of the module. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing a `Module` instance if successful, or an error if the module could not be parsed. #[cfg(target_os = "windows")] pub fn parse(name: &'a str, data: &'a [u8]) -> Result { let pe = PE::parse_with_opts( @@ -73,40 +62,22 @@ impl<'a> Module<'a> { }) } - /// Returns the base address of the module. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `Address` - The base address of the module. #[inline] #[cfg(target_os = "windows")] - pub fn base(&self) -> Address { - self.pe.image_base.into() + pub fn base(&self) -> usize { + self.pe.image_base } #[inline] #[cfg(target_os = "linux")] - pub fn base(&self) -> Address { + pub fn base(&self) -> usize { self.module_info.start_addr } - /// Returns a slice of `Export` structs representing the exports of the module. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `&[Export]` - A slice of `Export` structs representing the exports of the module. #[inline] #[cfg(target_os = "windows")] - pub fn exports(&self) -> &'a [Export] { - self.pe.exports.as_slice() + pub fn exports(&self) -> &[Export] { + &self.pe.exports } #[inline] @@ -121,82 +92,10 @@ impl<'a> Module<'a> { exports } - /// Returns the address of the export with the given name, if it exists. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `Option
` - The address of the export with the given name, if it exists. #[inline] #[cfg(target_os = "windows")] - pub fn get_export_by_name(&self, name: &str) -> Option
{ - self.pe - .exports - .iter() - .find(|e| e.name.unwrap() == name) - .map(|e| (self.pe.image_base + e.rva).into()) - } - - #[inline] - #[cfg(target_os = "linux")] - pub fn get_export_by_name(&self, name: &str) -> Option
{ - let base_addr: usize = self.base().into(); - self.elf - .dynsyms - .iter() - .find(|sym| { - (sym.st_bind() == sym::STB_GLOBAL || sym.st_bind() == sym::STB_WEAK) - && self.elf.dynstrtab.get_at(sym.st_name) == Some(name) - }) - .map(|sym| ((base_addr as u64 + sym.st_value) as usize).into()) - } - - /// Returns the address of the imported function with the given name, if it exists. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `Option
` - The address of the imported function with the given name, if it exists. - #[inline] - #[cfg(target_os = "windows")] - pub fn get_import_by_name(&self, name: &str) -> Option
{ - self.pe - .imports - .iter() - .find(|i| i.name.to_string() == name) - .map(|i| (self.pe.image_base + i.rva).into()) - } - - #[inline] - #[cfg(target_os = "linux")] - pub fn get_import_by_name(&self, name: &str) -> Option
{ - let base_addr: usize = self.base().into(); - self.elf - .dynsyms - .iter() - .find(|sym| sym.is_import() && self.elf.dynstrtab.get_at(sym.st_name) == Some(name)) - .map(|sym| ((base_addr as u64 + sym.st_value) as usize).into()) - } - - /// Returns a slice of the imported functions of the module. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `&[Import]` - A slice of `Import` structs representing the imported functions of the module. - #[inline] - #[cfg(target_os = "windows")] - pub fn imports(&self) -> &'a [Import] { - self.pe.imports.as_slice() + pub fn imports(&self) -> &[Import] { + self.pe.imports } #[inline] @@ -211,19 +110,56 @@ impl<'a> Module<'a> { imports } - /// Returns a slice of the section table entries in the module's PE header. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `&[SectionTable]` - A slice of `SectionTable` structs representing the section table entries in the module's PE header. + #[inline] + #[cfg(target_os = "windows")] + pub fn export_by_name(&self, name: &str) -> Option { + self.pe + .exports + .iter() + .find(|e| e.name.unwrap() == name) + .map(|e| self.pe.image_base + e.rva) + } + + #[inline] + #[cfg(target_os = "linux")] + pub fn export_by_name(&self, name: &str) -> Option { + let base_addr: usize = self.base(); + self.elf + .dynsyms + .iter() + .find(|sym| { + (sym.st_bind() == sym::STB_GLOBAL || sym.st_bind() == sym::STB_WEAK) + && self.elf.dynstrtab.get_at(sym.st_name) == Some(name) + }) + .map(|sym| (base_addr as u64 + sym.st_value) as usize) + } + + #[inline] + #[cfg(target_os = "windows")] + #[inline] + pub fn import_by_name(&self, name: &str) -> Option { + self.pe + .imports + .iter() + .find(|i| i.name.to_string() == name) + .map(|i| self.pe.image_base + i.rva) + } + + #[inline] + #[cfg(target_os = "linux")] + pub fn get_import_by_name(&self, name: &str) -> Option { + let base_addr: usize = self.base().into(); + self.elf + .dynsyms + .iter() + .find(|sym| sym.is_import() && self.elf.dynstrtab.get_at(sym.st_name) == Some(name)) + .map(|sym| (base_addr as u64 + sym.st_value) as usize) + } + #[inline] #[cfg(target_os = "windows")] pub fn sections(&self) -> &[SectionTable] { - self.pe.sections.as_slice() + self.pe.sections } #[inline] @@ -232,22 +168,13 @@ impl<'a> Module<'a> { self.elf.section_headers.as_slice() } - /// Returns the size of the module. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Module` struct. - /// - /// # Returns - /// - /// * `u32` - The size of the module. #[inline] #[cfg(target_os = "windows")] pub fn size(&self) -> u32 { self.pe .header .optional_header - .unwrap() + .expect("optional header not found") .windows_fields .size_of_image } diff --git a/src/os/process.rs b/src/os/process.rs index 5a0eec1..d8e2624 100644 --- a/src/os/process.rs +++ b/src/os/process.rs @@ -1,4 +1,4 @@ -use super::{Address, Module, ModuleEntry}; +use super::{Module, ModuleEntry}; use anyhow::{bail, Result}; @@ -40,61 +40,31 @@ pub struct Process { } impl Process { - /// Creates a new `Process` instance with the given name. - /// - /// # Arguments - /// - /// * `name` - A string slice that holds the name of the process. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the `Process` instance if successful, or an error if the process could not be found. #[cfg(target_os = "windows")] pub fn new(name: &str) -> Result { let id = Self::get_process_id_by_name(name)?; let handle = unsafe { OpenProcess(PROCESS_ALL_ACCESS, false, id) }?; - Ok(Self { + let mut process = Self { id, handle, modules: HashMap::new(), - }) + }; + process.parse_loaded_modules()?; + Ok(process) } #[cfg(target_os = "linux")] pub fn new(process_name: &str) -> Result { let id = Self::get_process_id_by_name(process_name.strip_suffix(".exe").unwrap())?; - Ok(Self { + let mut process = Self { id, modules: HashMap::new(), - }) + }; + process.parse_loaded_modules()?; + Ok(process) } - - /// Initializes the `Process` instance by parsing all loaded modules in the process. - /// - /// # Arguments - /// - /// * `&self` - A mutable reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result<()>` - A `Result` indicating the outcome of the operation. - pub fn initialize(&mut self) -> Result<()> { - self.parse_loaded_modules() - } - - /// Searches for a pattern in the memory of a specified module and returns the address of the first occurrence. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `module_name` - A string slice that holds the name of the module to search in. - /// * `pattern` - A string slice that holds the pattern to search for. - /// - /// # Returns - /// - /// * `Option
` - The address of the first occurrence of the pattern if found, or `None` if the pattern was not found. #[cfg(target_os = "windows")] pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option
{ let module = self.get_module_by_name(module_name)?; @@ -115,7 +85,7 @@ impl Process { } #[cfg(target_os = "linux")] - pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option
{ + pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option { let module = self.get_module_by_name(module_name)?; let pattern_bytes = Self::pattern_to_bytes(pattern); @@ -138,60 +108,23 @@ impl Process { None } - /// Returns an optional `Module` instance by its name. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `name` - A string slice representing the name of the module to retrieve. - /// - /// # Returns - /// - /// * `Option` - An optional `Module` instance if the module was found, or `None` if the module was not found. pub fn get_module_by_name<'a>(&'a self, name: &'a str) -> Option> { self.modules .get(name) - .map(|entry| Module::parse(name, entry).unwrap()) - // Module::parse(name, self.modules.get_mut(name)?).ok() + .map(|data| Module::parse(name, data).unwrap()) } - /// Returns a vector of `Module` instances parsed from the process's loaded modules. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result>` - A `Result` containing a vector of `Module` instances if successful, or an error if the modules could not be parsed. pub fn modules(&self) -> Result> { let mut modules = Vec::new(); - for (name, entry) in &self.modules { - modules.push(Module::parse(name, entry)?); + for (name, data) in &self.modules { + modules.push(Module::parse(name, data)?); } Ok(modules) } - /// Reads the memory at the specified address and returns the value as type T. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address to read from. - /// - /// # Examples - /// - /// ``` - /// let process = Process::new(pid)?; - /// let value: i32 = process.read_memory(address)?; - /// ``` - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the value if successful, or an error if the memory read fails. - pub fn read_memory(&self, address: Address) -> Result { + pub fn read_memory(&self, address: usize) -> Result { let mut buffer: T = unsafe { mem::zeroed() }; self.read_memory_raw( @@ -203,30 +136,12 @@ impl Process { Ok(buffer) } - /// Reads the memory of a process and stores it in a buffer. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address to start reading from. - /// * `buffer` - A pointer to the buffer where the read data will be stored. - /// * `size` - The number of bytes to read. - /// - /// # Returns - /// - /// * `Result<()>` - A `Result` indicating the outcome of the operation. - #[cfg(target_os = "windows")] - pub fn read_memory_raw( - &self, - address: Address, - buffer: *mut c_void, - size: usize, - ) -> Result<()> { + pub fn read_memory_raw(&self, address: usize, buffer: *mut c_void, size: usize) -> Result<()> { unsafe { ReadProcessMemory( self.handle, - address.as_ptr(), + address as *mut _, buffer, size, Some(ptr::null_mut()), @@ -236,19 +151,12 @@ impl Process { } #[cfg(target_os = "linux")] - pub fn read_memory_raw( - &self, - address: Address, - buffer: *mut c_void, - size: usize, - ) -> Result<()> { + pub fn read_memory_raw(&self, address: usize, buffer: *mut c_void, size: usize) -> Result<()> { let proc_mem_path = format!("/proc/{}/mem", self.id); let mut mem_file = File::open(proc_mem_path)?; // Go to the start address - mem_file.seek(SeekFrom::Start( -
>::into(address) as u64 - ))?; + mem_file.seek(SeekFrom::Start(address as u64))?; let buffer_slice = unsafe { std::slice::from_raw_parts_mut(buffer as *mut u8, size) }; @@ -257,17 +165,7 @@ impl Process { Ok(()) } - /// Reads a null-terminated string from the process memory at the given address. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address in the process memory where the string is located. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the string read from the process memory if successful, or an error if the memory read fails or if the string contains invalid UTF-8. - pub fn read_string(&self, address: Address) -> Result { + pub fn read_string(&self, address: usize) -> Result { let mut buffer = Vec::new(); for i in 0.. { @@ -280,18 +178,7 @@ impl Process { Ok(String::from_utf8(buffer)?) } - /// Reads a string of the specified length from the process memory at the given address. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address to read the string from. - /// * `length` - The length of the string to read. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the string read from the process memory if successful, or an error if the memory read fails or if the string contains invalid UTF-8. - pub fn read_string_length(&self, address: Address, length: usize) -> Result { + pub fn read_string_length(&self, address: usize, length: usize) -> Result { let mut buffer = vec![0; length]; self.read_memory_raw(address, buffer.as_mut_ptr() as *mut _, length)?; @@ -303,69 +190,30 @@ impl Process { Ok(String::from_utf8(buffer)?) } - /// Resolves the absolute address of a relative call. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address of the jump instruction. - /// * `offset` - The offset of the displacement value. - /// * `length` - The length of the instruction. - /// - /// # Returns - /// - /// * `Result
` - A `Result` containing the absolute address if successful, or an error if the memory read fails. pub fn resolve_jmp( &self, - address: Address, + address: usize, offset: Option, length: Option, - ) -> Result
{ + ) -> Result { // The displacement value can be negative. - let displacement = - self.read_memory::(address.add(offset.unwrap_or(0x1).try_into().unwrap()))?; + let displacement = self.read_memory::(address + offset.unwrap_or(0x1))?; - Ok(address - .add(length.unwrap_or(0x5).try_into().unwrap()) - .add(displacement.into())) + Ok((address + displacement as usize) + length.unwrap_or(0x5)) } - /// Resolves the absolute address of a RIP-relative address. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `Process` struct. - /// * `address` - The address of the relative instruction pointer (RIP). - /// * `offset` - The offset of the displacement value. If `None`, the offset will be `0x3`. - /// * `length` - The length of the instruction. If `None`, the length will be `0x7`. - /// - /// # Returns - /// - /// * `Result
` - A `Result` containing the absolute address if successful, or an error if the memory read fails. pub fn resolve_rip( &self, - address: Address, + address: usize, offset: Option, length: Option, - ) -> Result
{ + ) -> Result { // The displacement value can be negative. - let displacement = - self.read_memory::(address.add(offset.unwrap_or(0x3).try_into().unwrap()))?; + let displacement = self.read_memory::(address + offset.unwrap_or(0x3))?; - Ok(address - .add(length.unwrap_or(0x7).try_into().unwrap()) - .add(displacement.into())) + Ok((address + displacement as usize) + length.unwrap_or(0x7)) } - /// Returns the process ID of the first process with the given name. - /// - /// # Arguments - /// - /// * `process_name` - A string slice that holds the name of the process to search for. - /// - /// # Returns - /// - /// * `Result` - A `Result` containing the process ID if successful, or an error if the process could not be found. #[cfg(target_os = "windows")] fn get_process_id_by_name(process_name: &str) -> Result { let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }?; @@ -410,16 +258,6 @@ impl Process { bail!("Process not found: {}", process_name); } - /// Parses the loaded modules of a process and stores them in a HashMap with the module name as the key and the module data as the value. - /// - /// # Arguments - /// - /// * `&self` - A mutable reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result<()>` - A `Result` indicating the outcome of the operation. - #[cfg(target_os = "windows")] fn parse_loaded_modules(&mut self) -> Result<()> { let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.id) }?; @@ -438,7 +276,7 @@ impl Process { let mut data = vec![0; entry.modBaseSize as usize]; if let Ok(_) = self.read_memory_raw( - entry.modBaseAddr.into(), + entry.modBaseAddr as _, data.as_mut_ptr() as *mut _, data.len(), ) { @@ -517,7 +355,7 @@ impl Process { module_name, ModuleEntry { path: path.clone(), - start_addr: (start as usize).into(), + start_addr: start as usize, data: data, module_file_data: Process::read_elf_file(&path)?, }, @@ -528,15 +366,6 @@ impl Process { Ok(()) } - /// Converts a pattern string to a vector of bytes. - /// - /// # Arguments - /// - /// * `pattern` - A string slice that represents the pattern to be converted. - /// - /// # Returns - /// - /// * `Vec` - A vector of bytes representing the pattern. fn pattern_to_bytes(pattern: &str) -> Vec { pattern .split_whitespace() @@ -551,9 +380,6 @@ impl Process { } } -/// Implements the `Drop` trait for the `Process` struct. -/// -/// When a `Process` instance goes out of scope, this implementation will automatically close the process handle if it is not invalid. #[cfg(target_os = "windows")] impl Drop for Process { fn drop(&mut self) { diff --git a/src/sdk/schema_class_field_data.rs b/src/sdk/schema_class_field_data.rs index efed0ac..4af5968 100644 --- a/src/sdk/schema_class_field_data.rs +++ b/src/sdk/schema_class_field_data.rs @@ -1,72 +1,31 @@ -use super::SchemaType; - -use crate::util::{Address, Process}; - use anyhow::Result; -/// Represents data for a field in a schema class. +use super::SchemaType; + +use crate::os::Process; + pub struct SchemaClassFieldData<'a> { process: &'a Process, - address: Address, + address: usize, } impl<'a> SchemaClassFieldData<'a> { - /// Creates a new `SchemaClassFieldData` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// * `address` - The address of the `SchemaClassFieldData` instance. - /// - /// # Returns - /// - /// * `SchemaClassFieldData` - The new `SchemaClassFieldData` instance. - pub fn new(process: &'a Process, address: Address) -> Self { + pub fn new(process: &'a Process, address: usize) -> Self { Self { process, address } } - /// Returns the name of the field. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassFieldData` struct. - /// - /// # Returns - /// - /// * `Result` - The name of the field. pub fn name(&self) -> Result { let name_ptr = self.process.read_memory::(self.address)?; self.process.read_string_length(name_ptr.into(), 64) } - /// Returns the `SchemaType` of the field. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassFieldData` struct. - /// - /// # Returns - /// - /// * `Result` - The `SchemaType` of the field. pub fn r#type(&self) -> Result { - Ok(SchemaType::new( - self.process, - self.process - .read_memory::(self.address + 0x8)? - .into(), - )) + let address = self.process.read_memory::(self.address + 0x8)?; + + Ok(SchemaType::new(self.process, address)) } - /// Returns the offset of the field. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassFieldData` struct. - /// - /// # Returns - /// - /// * `Result` - The offset of the field. pub fn offset(&self) -> Result { self.process.read_memory::(self.address + 0x10) } diff --git a/src/sdk/schema_class_info.rs b/src/sdk/schema_class_info.rs index 4d63e4e..9783d54 100644 --- a/src/sdk/schema_class_info.rs +++ b/src/sdk/schema_class_info.rs @@ -1,60 +1,29 @@ -use super::SchemaClassFieldData; - -use crate::util::{Address, Process}; - use anyhow::Result; -/// Represents information about a schema class. +use super::SchemaClassFieldData; + +use crate::os::Process; + pub struct SchemaClassInfo<'a> { process: &'a Process, - address: Address, - class_name: String, + address: usize, + name: String, } impl<'a> SchemaClassInfo<'a> { - /// Creates a new `SchemaClassInfo` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// * `address` - The address of the `SchemaClassInfo` instance. - /// * `class_name` - The name of the class. - /// - /// # Returns - /// - /// * `SchemaClassInfo` - The new `SchemaClassInfo` instance. - pub fn new(process: &'a Process, address: Address, class_name: &str) -> Self { + pub fn new(process: &'a Process, address: usize, name: &str) -> Self { Self { process, address, - class_name: class_name.to_string(), + name: name.to_string(), } } - /// Returns a reference to the name of the class. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassInfo` struct. - /// - /// # Returns - /// - /// * `&str` - A string slice containing the name of the class. #[inline] pub fn name(&self) -> &str { - &self.class_name + &self.name } - /// Returns a vector of `SchemaClassFieldData` representing the fields of the schema class. - /// If the address of the schema class is null, an empty vector is returned. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassInfo` struct. - /// - /// # Returns - /// - /// * `Result>` - A vector of `SchemaClassFieldData` representing the fields of the schema class. pub fn fields(&self) -> Result> { let address = self.process.read_memory::(self.address + 0x28)?; @@ -72,38 +41,18 @@ impl<'a> SchemaClassInfo<'a> { Ok(fields) } - /// Returns the number of fields in the class. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassInfo` struct. - /// - /// # Returns - /// - /// * `Result` - The number of fields in the class. pub fn fields_count(&self) -> Result { self.process.read_memory::(self.address + 0x1C) } - /// Returns the parent `SchemaClassInfo` of the current `SchemaClassInfo` instance. - /// If the parent is not found, returns `Ok(None)`. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaClassInfo` struct. - /// - /// # Returns - /// - /// * `Result>` - The parent `SchemaClassInfo` of the current `SchemaClassInfo` instance. pub fn parent(&self) -> Result> { - let address = Address::from(self.process.read_memory::(self.address + 0x38)?); + let address = self.process.read_memory::(self.address + 0x38)?; - if address.is_zero() { + if address == 0 { return Ok(None); } - let parent = Address::from(self.process.read_memory::(address + 0x8)?); - + let parent = self.process.read_memory::(address + 0x8)?; let name_ptr = self.process.read_memory::(parent + 0x8)?; let name = self.process.read_string(name_ptr.into())?; diff --git a/src/sdk/schema_system.rs b/src/sdk/schema_system.rs index fa34d0e..07dc8b2 100644 --- a/src/sdk/schema_system.rs +++ b/src/sdk/schema_system.rs @@ -1,52 +1,33 @@ -use super::SchemaSystemTypeScope; - -use crate::util::{Address, Process}; +use std::mem; use anyhow::{bail, Result}; -use std::mem; +use super::SchemaSystemTypeScope; + +use crate::os::Process; -/// Represents the schema system. pub struct SchemaSystem<'a> { process: &'a Process, - address: Address, + address: usize, } impl<'a> SchemaSystem<'a> { - /// Creates a new `SchemaSystem` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result` - The new `SchemaSystem` instance. pub fn new(process: &'a Process) -> Result { let mut address = process.find_pattern( "schemasystem.dll", "48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 8D 0D ? ? ? ? E9 ? ? ? ? CC CC CC CC 48 83 EC 28" - ).expect("Failed to find pattern for SchemaSystem"); + ).expect("unable to find schema system pattern"); address = process.resolve_rip(address, None, None)?; Ok(Self { process, address }) } - /// Returns a vector of `SchemaSystemTypeScope` objects. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaSystem` struct. - /// - /// # Returns - /// - /// * `Result>` - A vector of `SchemaSystemTypeScope` objects. pub fn type_scopes(&self) -> Result> { let size = self.process.read_memory::(self.address + 0x190)?; if size == 0 { - bail!("Type scopes size is 0"); + bail!("no type scopes found"); } let data = self.process.read_memory::(self.address + 0x198)?; @@ -61,7 +42,7 @@ impl<'a> SchemaSystem<'a> { let type_scopes: Vec = addresses .iter() - .map(|&address| SchemaSystemTypeScope::new(self.process, address.into())) + .map(|&address| SchemaSystemTypeScope::new(self.process, address)) .collect(); Ok(type_scopes) diff --git a/src/sdk/schema_system_type_scope.rs b/src/sdk/schema_system_type_scope.rs index 250c3e3..1db085e 100644 --- a/src/sdk/schema_system_type_scope.rs +++ b/src/sdk/schema_system_type_scope.rs @@ -1,40 +1,19 @@ -use super::{SchemaClassInfo, SchemaTypeDeclaredClass, UtlTsHash}; - -use crate::util::{Address, Process}; - use anyhow::Result; -/// Represents a system type scope in the schema. +use super::{SchemaClassInfo, SchemaTypeDeclaredClass, UtlTsHash}; + +use crate::os::Process; + pub struct SchemaSystemTypeScope<'a> { process: &'a Process, - address: Address, + address: usize, } impl<'a> SchemaSystemTypeScope<'a> { - /// Creates a new `SchemaSystemTypeScope` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// * `address` - The address of the `SchemaSystemTypeScope` instance. - /// * `class_name` - The name of the class. - /// - /// # Returns - /// - /// * `SchemaSystemTypeScope` - The new `SchemaSystemTypeScope` instance. - pub fn new(process: &'a Process, address: Address) -> Self { + pub fn new(process: &'a Process, address: usize) -> Self { Self { process, address } } - /// Returns a vector of `SchemaClassInfo` containing information about all the classes declared in the current scope. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaSystemTypeScope` struct. - /// - /// # Returns - /// - /// * `Result>` - A vector of `SchemaClassInfo` containing information about all the classes declared in the current scope. pub fn classes(&self) -> Result> { let declared_classes = self .process @@ -43,8 +22,8 @@ impl<'a> SchemaSystemTypeScope<'a> { let classes: Vec = declared_classes .elements(self.process)? .iter() - .filter_map(|&a| { - let address = Address::from(a as usize); + .filter_map(|&class_ptr| { + let address = class_ptr as usize; let declared_class = SchemaTypeDeclaredClass::new(self.process, address); @@ -58,15 +37,6 @@ impl<'a> SchemaSystemTypeScope<'a> { Ok(classes) } - /// Returns the name of the module associated with the current `SchemaSystemTypeScope` instance. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaSystemTypeScope` struct. - /// - /// # Returns - /// - /// * `Result` - The name of the module associated with the current `SchemaSystemTypeScope` instance. pub fn module_name(&self) -> Result { self.process.read_string_length(self.address + 0x8, 256) } diff --git a/src/sdk/schema_type.rs b/src/sdk/schema_type.rs index 040981a..00ee130 100644 --- a/src/sdk/schema_type.rs +++ b/src/sdk/schema_type.rs @@ -1,4 +1,4 @@ -use crate::util::{Address, Process}; +use std::collections::HashMap; use anyhow::Result; @@ -6,9 +6,8 @@ use lazy_static::lazy_static; use regex::Regex; -use std::collections::HashMap; +use crate::os::Process; -/// Map of type names to their `C` equivalents. const TYPE_MAP: &[(&'static str, &'static str)] = &[ ("uint8", "uint8_t"), ("uint16", "uint16_t"), @@ -23,8 +22,6 @@ const TYPE_MAP: &[(&'static str, &'static str)] = &[ ]; lazy_static! { - /// A static HashMap that maps a string to a Regex pattern. - /// The Regex pattern is created by wrapping the string with word boundaries `(\b)`. static ref REGEX_MAP: HashMap<&'static str, Regex> = { let mut map = HashMap::with_capacity(TYPE_MAP.len()); @@ -36,36 +33,16 @@ lazy_static! { }; } -/// Represents a type in the schema. pub struct SchemaType<'a> { process: &'a Process, - address: Address, + address: usize, } impl<'a> SchemaType<'a> { - /// Creates a new `SchemaType` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// * `address` - The address of the `SchemaType` instance. - /// - /// # Returns - /// - /// * `SchemaType` - The new `SchemaType` instance. - pub fn new(process: &'a Process, address: Address) -> Self { + pub fn new(process: &'a Process, address: usize) -> Self { Self { process, address } } - /// Returns the name of the schema type. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaType` instance. - /// - /// # Returns - /// - /// * `Result` - The name of the schema type, wrapped in a `Result` object. pub fn name(&self) -> Result { let name_ptr = self.process.read_memory::(self.address + 0x8)?; @@ -78,15 +55,6 @@ impl<'a> SchemaType<'a> { Ok(Self::convert_type_name(&name)) } - /// Converts a schema type name to its `C` equivalent. - /// - /// # Arguments - /// - /// * `type_name` - A string slice that holds the name of the schema type. - /// - /// # Returns - /// - /// * `String` - The `C` equivalent of the schema type name. fn convert_type_name(type_name: &str) -> String { let mut result = type_name.to_string(); diff --git a/src/sdk/schema_type_declared_class.rs b/src/sdk/schema_type_declared_class.rs index a50d0d2..6567002 100644 --- a/src/sdk/schema_type_declared_class.rs +++ b/src/sdk/schema_type_declared_class.rs @@ -1,40 +1,20 @@ -use crate::util::{Address, Process}; - use anyhow::Result; -/// Represents a declared class type in the schema. +use crate::os::Process; + pub struct SchemaTypeDeclaredClass<'a> { process: &'a Process, - address: Address, + address: usize, } impl<'a> SchemaTypeDeclaredClass<'a> { - /// Creates a new `SchemaTypeDeclaredClass` instance. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// * `address` - The address of the `SchemaTypeDeclaredClass` instance. - /// - /// # Returns - /// - /// * `SchemaTypeDeclaredClass` - The new `SchemaTypeDeclaredClass` instance. - pub fn new(process: &'a Process, address: Address) -> Self { + pub fn new(process: &'a Process, address: usize) -> Self { Self { process, address } } - /// Returns the name of the declared class. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `SchemaTypeDeclaredClass` struct. - /// - /// # Returns - /// - /// * `Result` - The name of the declared class. pub fn name(&self) -> Result { let name_ptr = self.process.read_memory::(self.address + 0x8)?; - self.process.read_string_length(name_ptr.into(), 64) + self.process.read_string_length(name_ptr, 64) } } diff --git a/src/sdk/utl_ts_hash.rs b/src/sdk/utl_ts_hash.rs index 14b1ccd..0794dd6 100644 --- a/src/sdk/utl_ts_hash.rs +++ b/src/sdk/utl_ts_hash.rs @@ -1,10 +1,9 @@ -use crate::util::Process; +use std::mem::offset_of; use anyhow::Result; -use std::mem::offset_of; +use crate::os::Process; -/// Represents the internal data of a hash table. #[derive(Debug)] #[repr(C)] struct HashFixedDataInternal { @@ -13,25 +12,14 @@ struct HashFixedDataInternal { data: T, // 0x0010 } -/// Implementation of HashFixedDataInternal struct with methods for reading the next element in the hash table. impl HashFixedDataInternal { - /// Reads the next element in the hash table. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// Returns a Result containing a pointer to the next element in the hash table if successful, or an error if unsuccessful. fn next(&self, process: &Process) -> Result<*mut HashFixedDataInternal> { process.read_memory::<*mut HashFixedDataInternal>( - (self as *const _ as usize + offset_of!(HashFixedDataInternal, next)).into(), + (self as *const _ as usize + offset_of!(HashFixedDataInternal, next)) as _, ) } } -/// Represents the internal data of a hash bucket. #[derive(Debug)] #[repr(C)] struct HashBucketDataInternal { @@ -41,23 +29,13 @@ struct HashBucketDataInternal { } impl HashBucketDataInternal { - /// Reads the next element in the hash table. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// Returns a Result containing a pointer to the next element in the hash table if successful, or an error if unsuccessful. fn next(&self, process: &Process) -> Result<*mut HashFixedDataInternal> { process.read_memory::<*mut HashFixedDataInternal>( - (self as *const _ as usize + offset_of!(HashBucketDataInternal, next)).into(), + (self as *const _ as usize + offset_of!(HashBucketDataInternal, next)) as _, ) } } -/// Represents allocated data in a hash table. #[derive(Debug)] #[repr(C)] pub struct HashAllocatedData { @@ -66,23 +44,13 @@ pub struct HashAllocatedData { } impl HashAllocatedData { - /// Reads the list of elements in the hash table. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// Returns a Result containing a list of elements in the hash table if successful, or an error if unsuccessful. fn list(&self, process: &Process) -> Result<[HashFixedDataInternal; 128]> { process.read_memory::<[HashFixedDataInternal; 128]>( - (self as *const _ as usize + offset_of!(HashAllocatedData, list)).into(), + (self as *const _ as usize + offset_of!(HashAllocatedData, list)) as _, ) } } -/// A struct representing unallocated data in a hash table. #[derive(Debug)] #[repr(C)] struct HashUnallocatedData { @@ -94,53 +62,25 @@ struct HashUnallocatedData { } impl HashUnallocatedData { - /// Reads the next `HashUnallocatedData` element in memory. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result<*mut HashUnallocatedData>` - A Result containing a pointer to the next `HashUnallocatedData` element in memory. fn next(&self, process: &Process) -> Result<*mut HashUnallocatedData> { process.read_memory::<*mut HashUnallocatedData>( - (self as *const _ as usize + offset_of!(HashUnallocatedData, next)).into(), + (self as *const _ as usize + offset_of!(HashUnallocatedData, next)) as _, ) } - /// Reads the UI key of the `HashUnallocatedData` element in memory. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result` - A Result containing the UI key of the `HashUnallocatedData` element in memory. fn ui_key(&self, process: &Process) -> Result { process.read_memory::( - (self as *const _ as usize + offset_of!(HashUnallocatedData, ui_key)).into(), + (self as *const _ as usize + offset_of!(HashUnallocatedData, ui_key)) as _, ) } - /// Reads the block list of the `HashUnallocatedData` element in memory. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result<[HashBucketDataInternal; 256]>` - A Result containing the block list of the `HashUnallocatedData` element in memory. fn block_list(&self, process: &Process) -> Result<[HashBucketDataInternal; 256]> { process.read_memory::<[HashBucketDataInternal; 256]>( - (self as *const _ as usize + offset_of!(HashUnallocatedData, block_list)).into(), + (self as *const _ as usize + offset_of!(HashUnallocatedData, block_list)) as _, ) } } -/// Represents a hash bucket. #[derive(Debug)] #[repr(C)] struct HashBucket { @@ -149,7 +89,6 @@ struct HashBucket { unallocated_data: *const HashUnallocatedData, // 0x0018 } -/// Represents a memory pool used by the `UtlTsHash` class. #[derive(Debug)] #[repr(C)] struct UtlMemoryPool { @@ -162,36 +101,17 @@ struct UtlMemoryPool { } impl UtlMemoryPool { - /// Returns the number of blocks per blob. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `UtlMemoryPool` struct. - /// - /// # Returns - /// - /// * `i32` - The number of blocks per blob. #[inline] fn block_size(&self) -> i32 { self.blocks_per_blob } - /// Returns the number of blocks allocated. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `UtlMemoryPool` struct. - /// - /// # Returns - /// - /// * `i32` - The number of blocks allocated. #[inline] fn count(&self) -> i32 { self.block_allocated_size } } -/// Represents a thread-safe hash table. #[derive(Debug)] #[repr(C)] pub struct UtlTsHash { @@ -203,43 +123,16 @@ impl UtlTsHash where T: Copy, { - /// Returns the number of blocks per blob. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `UtlTsHash` struct. - /// - /// # Returns - /// - /// * `i32` - The number of blocks per blob. #[inline] pub fn block_size(&self) -> i32 { self.entry_memory.block_size() } - /// Returns the number of blocks allocated. - /// - /// # Arguments - /// - /// * `&self` - A reference to the `UtlTsHash` struct. - /// - /// # Returns - /// - /// * `i32` - The number of blocks allocated. #[inline] pub fn count(&self) -> i32 { self.entry_memory.count() } - /// Returns a list of elements in the hash table. - /// - /// # Arguments - /// - /// * `process` - A reference to the `Process` struct. - /// - /// # Returns - /// - /// * `Result>` - A Result containing a list of elements in the hash table if successful, or an error if unsuccessful. pub fn elements(&self, process: &Process) -> Result> { let mut address = self.buckets.unallocated_data;