Remove redundant doc comments

This commit is contained in:
a2x 2024-03-02 01:02:58 +10:00
parent c731cab0a0
commit 127cd110bb
24 changed files with 103 additions and 969 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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<usize>,
) -> 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<()>;
}

View File

@ -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<String>,
}
/// 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<String, JsonOffsetValue>,
comment: Option<String>,
}
/// A structure representing a builder for JSON files.
/// The builder implements the `FileBuilder` trait.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct JsonFileBuilder {
data: BTreeMap<String, JsonModule>,

View File

@ -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),
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<usize>,
size: Option<usize>,
},
/// 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<usize>,
length: Option<usize>,
},
/// 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<usize>,
length: Option<usize>,
},
/// 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<Operation>,
}
/// 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<Signature>,
}

View File

@ -1,40 +1,24 @@
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::util::{Address, 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<Address>` - A `Result` containing the instance of the interface if successful, or an error if the memory read fails.
fn instance(&self, process: &Process) -> Result<Address> {
process
.read_memory::<usize>(
@ -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<String>` - A `Result` containing the name of the interface if successful, or an error if the memory read fails.
fn name(&self, process: &Process) -> Result<String> {
let name_ptr = process.read_memory::<usize>(
(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<FileBuilderEnum>,
@ -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 <blue>{}</>...", module.name);
let create_interface_address =
@ -114,7 +63,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) }?;
@ -138,7 +86,6 @@ pub fn dump_interfaces(
indent: Some(indent),
});
// Get the next node in the linked list.
node = unsafe { (*node).next(process) }?;
}
}

View File

@ -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<String>,
/// An optional indentation level for the entry.
pub indent: Option<usize>,
}
/// 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<Entry>,
/// An optional comment associated with the container.
pub comment: Option<String>,
}
/// A type alias for a `BTreeMap` that maps `String` keys to `EntriesContainer` values.
pub type Entries = BTreeMap<String, EntriesContainer>;
/// 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
)),
};

View File

@ -1,3 +1,9 @@
use std::fs::File;
use anyhow::Result;
use simplelog::{debug, error, info};
use super::{generate_files, Entries, Entry};
use crate::builder::FileBuilderEnum;
@ -5,24 +11,6 @@ use crate::config::Config;
use crate::config::Operation::*;
use crate::util::Process;
use anyhow::Result;
use simplelog::{debug, error, info};
use std::fs::File;
/// 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<FileBuilderEnum>,

View File

@ -1,25 +1,13 @@
use anyhow::Result;
use simplelog::{debug, info};
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.
pub fn dump_schemas(
process: &Process,
builders: &mut Vec<FileBuilderEnum>,

View File

@ -1,21 +1,19 @@
#![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 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 util::Process;
mod builder;
@ -24,21 +22,20 @@ mod dumper;
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,12 +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);
@ -119,7 +111,6 @@ fn main() -> Result<()> {
dump_offsets(&mut process, &mut builders, &output, indent)?;
}
// Stop the timer.
info!(
"<on-green>Done!</> <green>Time elapsed: <b>{:?}</></>",
now.elapsed()
@ -128,18 +119,8 @@ fn main() -> Result<()> {
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<FileBuilderEnum, &'static str> {
match extension {
fn map_file_extension_to_builder(ext: &str) -> Result<FileBuilderEnum, &'static str> {
match ext {
".cs" => Ok(FileBuilderEnum::CSharpFileBuilder(CSharpFileBuilder)),
".hpp" => Ok(FileBuilderEnum::CppFileBuilder(CppFileBuilder)),
".json" => Ok(FileBuilderEnum::JsonFileBuilder(JsonFileBuilder::default())),

View File

@ -1,54 +1,25 @@
use anyhow::Result;
use super::SchemaType;
use crate::util::{Address, Process};
use anyhow::Result;
/// Represents data for a field in a schema class.
pub struct SchemaClassFieldData<'a> {
process: &'a Process,
address: Address,
}
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 {
Self { process, address }
}
/// Returns the name of the field.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaClassFieldData` struct.
///
/// # Returns
///
/// * `Result<String>` - The name of the field.
pub fn name(&self) -> Result<String> {
let name_ptr = self.process.read_memory::<usize>(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<SchemaType>` - The `SchemaType` of the field.
pub fn r#type(&self) -> Result<SchemaType> {
Ok(SchemaType::new(
self.process,
@ -58,15 +29,6 @@ impl<'a> SchemaClassFieldData<'a> {
))
}
/// Returns the offset of the field.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaClassFieldData` struct.
///
/// # Returns
///
/// * `Result<u16>` - The offset of the field.
pub fn offset(&self) -> Result<u16> {
self.process.read_memory::<u16>(self.address + 0x10)
}

View File

@ -1,10 +1,9 @@
use anyhow::Result;
use super::SchemaClassFieldData;
use crate::util::{Address, Process};
use anyhow::Result;
/// Represents information about a schema class.
pub struct SchemaClassInfo<'a> {
process: &'a Process,
address: Address,
@ -12,17 +11,6 @@ pub struct SchemaClassInfo<'a> {
}
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 {
Self {
process,
@ -31,30 +19,11 @@ impl<'a> SchemaClassInfo<'a> {
}
}
/// 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
}
/// 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<Vec<SchemaClassFieldData>>` - A vector of `SchemaClassFieldData` representing the fields of the schema class.
pub fn fields(&self) -> Result<Vec<SchemaClassFieldData>> {
let address = self.process.read_memory::<usize>(self.address + 0x28)?;
@ -72,29 +41,10 @@ impl<'a> SchemaClassInfo<'a> {
Ok(fields)
}
/// Returns the number of fields in the class.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaClassInfo` struct.
///
/// # Returns
///
/// * `Result<u16>` - The number of fields in the class.
pub fn fields_count(&self) -> Result<u16> {
self.process.read_memory::<u16>(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<Option<SchemaClassInfo>>` - The parent `SchemaClassInfo` of the current `SchemaClassInfo` instance.
pub fn parent(&self) -> Result<Option<SchemaClassInfo>> {
let address = Address::from(self.process.read_memory::<usize>(self.address + 0x38)?);

View File

@ -1,27 +1,17 @@
use std::mem;
use anyhow::{bail, Result};
use super::SchemaSystemTypeScope;
use crate::util::{Address, Process};
use anyhow::{bail, Result};
use std::mem;
/// Represents the schema system.
pub struct SchemaSystem<'a> {
process: &'a Process,
address: Address,
}
impl<'a> SchemaSystem<'a> {
/// Creates a new `SchemaSystem` instance.
///
/// # Arguments
///
/// * `process` - A reference to the `Process` struct.
///
/// # Returns
///
/// * `Result<SchemaSystem>` - The new `SchemaSystem` instance.
pub fn new(process: &'a Process) -> Result<Self> {
let mut address = process.find_pattern(
"schemasystem.dll",
@ -33,15 +23,6 @@ impl<'a> SchemaSystem<'a> {
Ok(Self { process, address })
}
/// Returns a vector of `SchemaSystemTypeScope` objects.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaSystem` struct.
///
/// # Returns
///
/// * `Result<Vec<SchemaSystemTypeScope>>` - A vector of `SchemaSystemTypeScope` objects.
pub fn type_scopes(&self) -> Result<Vec<SchemaSystemTypeScope>> {
let size = self.process.read_memory::<u32>(self.address + 0x190)?;

View File

@ -1,40 +1,19 @@
use anyhow::Result;
use super::{SchemaClassInfo, SchemaTypeDeclaredClass, UtlTsHash};
use crate::util::{Address, Process};
use anyhow::Result;
/// Represents a system type scope in the schema.
pub struct SchemaSystemTypeScope<'a> {
process: &'a Process,
address: Address,
}
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 {
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<Vec<SchemaClassInfo>>` - A vector of `SchemaClassInfo` containing information about all the classes declared in the current scope.
pub fn classes(&self) -> Result<Vec<SchemaClassInfo>> {
let declared_classes = self
.process
@ -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<String>` - The name of the module associated with the current `SchemaSystemTypeScope` instance.
pub fn module_name(&self) -> Result<String> {
self.process.read_string_length(self.address + 0x8, 256)
}

View File

@ -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::util::{Address, 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,
}
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 {
Self { process, address }
}
/// Returns the name of the schema type.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaType` instance.
///
/// # Returns
///
/// * `Result<String>` - The name of the schema type, wrapped in a `Result` object.
pub fn name(&self) -> Result<String> {
let name_ptr = self.process.read_memory::<usize>(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();

View File

@ -1,37 +1,17 @@
use crate::util::{Address, Process};
use anyhow::Result;
/// Represents a declared class type in the schema.
use crate::util::{Address, Process};
pub struct SchemaTypeDeclaredClass<'a> {
process: &'a Process,
address: Address,
}
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 {
Self { process, address }
}
/// Returns the name of the declared class.
///
/// # Arguments
///
/// * `&self` - A reference to the `SchemaTypeDeclaredClass` struct.
///
/// # Returns
///
/// * `Result<String>` - The name of the declared class.
pub fn name(&self) -> Result<String> {
let name_ptr = self.process.read_memory::<usize>(self.address + 0x8)?;

View File

@ -1,10 +1,9 @@
use crate::util::Process;
use std::mem::offset_of;
use anyhow::Result;
use std::mem::offset_of;
use crate::util::Process;
/// Represents the internal data of a hash table.
#[derive(Debug)]
#[repr(C)]
struct HashFixedDataInternal<T, K> {
@ -13,17 +12,7 @@ struct HashFixedDataInternal<T, K> {
data: T, // 0x0010
}
/// Implementation of HashFixedDataInternal struct with methods for reading the next element in the hash table.
impl<T, K> HashFixedDataInternal<T, K> {
/// 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<T, K>> {
process.read_memory::<*mut HashFixedDataInternal<T, K>>(
(self as *const _ as usize + offset_of!(HashFixedDataInternal<T, K>, next)).into(),
@ -31,7 +20,6 @@ impl<T, K> HashFixedDataInternal<T, K> {
}
}
/// Represents the internal data of a hash bucket.
#[derive(Debug)]
#[repr(C)]
struct HashBucketDataInternal<T, K> {
@ -41,15 +29,6 @@ struct HashBucketDataInternal<T, K> {
}
impl<T, K> HashBucketDataInternal<T, K> {
/// 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<T, K>> {
process.read_memory::<*mut HashFixedDataInternal<T, K>>(
(self as *const _ as usize + offset_of!(HashBucketDataInternal<T, K>, next)).into(),
@ -57,7 +36,6 @@ impl<T, K> HashBucketDataInternal<T, K> {
}
}
/// Represents allocated data in a hash table.
#[derive(Debug)]
#[repr(C)]
pub struct HashAllocatedData<T, K> {
@ -66,15 +44,6 @@ pub struct HashAllocatedData<T, K> {
}
impl<T, K> HashAllocatedData<T, K> {
/// 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<T, K>; 128]> {
process.read_memory::<[HashFixedDataInternal<T, K>; 128]>(
(self as *const _ as usize + offset_of!(HashAllocatedData<T, K>, list)).into(),
@ -82,7 +51,6 @@ impl<T, K> HashAllocatedData<T, K> {
}
}
/// A struct representing unallocated data in a hash table.
#[derive(Debug)]
#[repr(C)]
struct HashUnallocatedData<T, K> {
@ -94,45 +62,18 @@ struct HashUnallocatedData<T, K> {
}
impl<T, K> HashUnallocatedData<T, K> {
/// Reads the next `HashUnallocatedData` element in memory.
///
/// # Arguments
///
/// * `process` - A reference to the `Process` struct.
///
/// # Returns
///
/// * `Result<*mut HashUnallocatedData<T, K>>` - A Result containing a pointer to the next `HashUnallocatedData` element in memory.
fn next(&self, process: &Process) -> Result<*mut HashUnallocatedData<T, K>> {
process.read_memory::<*mut HashUnallocatedData<T, K>>(
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, next)).into(),
)
}
/// Reads the UI key of the `HashUnallocatedData` element in memory.
///
/// # Arguments
///
/// * `process` - A reference to the `Process` struct.
///
/// # Returns
///
/// * `Result<K>` - A Result containing the UI key of the `HashUnallocatedData` element in memory.
fn ui_key(&self, process: &Process) -> Result<K> {
process.read_memory::<K>(
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, ui_key)).into(),
)
}
/// Reads the block list of the `HashUnallocatedData` element in memory.
///
/// # Arguments
///
/// * `process` - A reference to the `Process` struct.
///
/// # Returns
///
/// * `Result<[HashBucketDataInternal<T, K>; 256]>` - A Result containing the block list of the `HashUnallocatedData` element in memory.
fn block_list(&self, process: &Process) -> Result<[HashBucketDataInternal<T, K>; 256]> {
process.read_memory::<[HashBucketDataInternal<T, K>; 256]>(
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, block_list)).into(),
@ -140,7 +81,6 @@ impl<T, K> HashUnallocatedData<T, K> {
}
}
/// Represents a hash bucket.
#[derive(Debug)]
#[repr(C)]
struct HashBucket<T, K> {
@ -149,7 +89,6 @@ struct HashBucket<T, K> {
unallocated_data: *const HashUnallocatedData<T, K>, // 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<T, K = u64> {
@ -203,43 +123,16 @@ impl<T, K> UtlTsHash<T, K>
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<Vec<T>>` - 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<Vec<T>> {
let mut address = self.buckets.unallocated_data;

View File

@ -1,210 +1,136 @@
use std::fmt::{LowerHex, UpperHex};
use std::ops::{Add, AddAssign, Sub, SubAssign};
/// Represents a memory address.
#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Address(pub usize);
impl Address {
/// Adds the given value to the current address and returns a new `Address`.
///
/// # Arguments
///
/// * `&self` - A reference to the `Address` struct.
/// * `value` - The value to add to the current address.
///
/// # Returns
///
/// * `Address` - A new `Address` struct with the value of the current address plus the given value.
#[inline]
pub fn add(&self, value: usize) -> Self {
Self(self.0 + value)
}
/// Returns true if the value of the address is zero.
///
/// # Arguments
///
/// * `&self` - A reference to the `Address` struct.
///
/// # Returns
///
/// * `bool` - True if the value of the address is zero, false otherwise.
#[inline]
pub fn is_zero(&self) -> bool {
self.0 == 0
}
/// Subtracts a value from the current address and returns a new `Address`.
///
/// # Arguments
///
/// * `&self` - A reference to the `Address` struct.
/// * `value` - The value to subtract from the current address.
///
/// # Returns
///
/// * `Address` - A new `Address` struct with the value of the current address minus the given value.
#[inline]
pub fn sub(&self, value: usize) -> Self {
Self(self.0 - value)
}
/// Returns a raw pointer to the underlying data as a `*const T`.
///
/// # Arguments
///
/// * `&self` - A reference to the `Address` struct.
///
/// # Returns
///
/// * `*const T` - A raw pointer to the underlying data as a `*const T`.
#[inline]
pub fn as_ptr<T>(&self) -> *const T {
self.0 as *const T
}
/// Returns a mutable pointer to the underlying data.
///
/// # Arguments
///
/// * `&self` - A reference to the `Address` struct.
///
/// # Returns
///
/// * `*mut T` - A mutable pointer to the underlying data.
#[inline]
pub fn as_mut_ptr<T>(&self) -> *mut T {
self.0 as *mut T
}
}
/// Converts a `usize` value to an `Address` struct.
impl From<usize> for Address {
fn from(value: usize) -> Self {
Self(value)
}
}
/// Converts a raw pointer to a `usize` value and creates an `Address` instance from it.
impl From<*const u8> for Address {
fn from(value: *const u8) -> Self {
Self(value as usize)
}
}
/// Converts a raw pointer to a `usize` value and wraps it in an `Address` struct.
impl From<*mut u8> for Address {
fn from(value: *mut u8) -> Self {
Self(value as usize)
}
}
/// Converts an `Address` struct to a `usize` value.
impl From<Address> for usize {
fn from(value: Address) -> Self {
value.0
}
}
/// Converts an `Address` struct to a raw pointer to an unsigned 8-bit integer.
impl From<Address> for *const u8 {
fn from(value: Address) -> Self {
value.0 as *const u8
}
}
/// Converts an `Address` struct to a raw pointer to a mutable unsigned 8-bit integer.
impl From<Address> for *mut u8 {
fn from(value: Address) -> Self {
value.0 as *mut u8
}
}
/// Implements the addition of a `usize` value to an `Address` value.
impl Add<usize> for Address {
type Output = Self;
/// Adds a `usize` value to an `Address` value and returns the result.
fn add(self, rhs: usize) -> Self::Output {
Self(self.0 + rhs)
}
}
/// Implements the addition of two `Address` instances.
impl Add<Address> for Address {
type Output = Self;
/// Adds two `Address` instances and returns a new `Address`.
fn add(self, rhs: Address) -> Self::Output {
Self(self.0 + rhs.0)
}
}
/// Implements the `AddAssign` trait for `Address` struct.
impl AddAssign<usize> for Address {
/// Adds the given `rhs` value to the `Address` struct.
fn add_assign(&mut self, rhs: usize) {
self.0 += rhs;
}
}
/// Implements the `AddAssign` trait for `Address`.
impl AddAssign<Address> for Address {
/// Adds the given `rhs` value to the `Address` struct.
fn add_assign(&mut self, rhs: Address) {
self.0 += rhs.0;
}
}
/// Implements the subtraction of a `usize` from an `Address`.
impl Sub<usize> for Address {
type Output = Self;
/// Subtracts the given `rhs` from `&self`.
fn sub(self, rhs: usize) -> Self::Output {
Self(self.0 - rhs)
}
}
/// Implements the subtraction operation for `Address`.
impl Sub<Address> for Address {
type Output = Self;
/// Subtracts the given `rhs` from `&self`.
fn sub(self, rhs: Address) -> Self::Output {
Self(self.0 - rhs.0)
}
}
/// Implements the subtraction assignment operation for `Address` and `usize`.
impl SubAssign<usize> for Address {
/// Subtracts the given `rhs` from the `Address`.
fn sub_assign(&mut self, rhs: usize) {
self.0 -= rhs;
}
}
/// Implements the subtraction assignment operator for `Address`.
impl SubAssign<Address> for Address {
/// Subtracts the value of `rhs` from `&self`.
fn sub_assign(&mut self, rhs: Address) {
self.0 -= rhs.0;
}
}
/// Implements the `UpperHex` trait for the `Address` struct.
impl UpperHex for Address {
/// Formats the value of `&self` using the `write!` macro and the `UpperHex` format specifier.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#X}", self.0)
}
}
/// Implements the `LowerHex` trait for the `Address` struct.
impl LowerHex for Address {
/// Formats the value of `&self` using the `write!` macro and the `LowerHex` format specifier.
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#x}", self.0)
}

View File

@ -1,5 +1,3 @@
use super::Address;
use anyhow::Result;
use goblin::pe::export::Export;
@ -8,29 +6,15 @@ use goblin::pe::options::ParseOptions;
use goblin::pe::section_table::SectionTable;
use goblin::pe::PE;
/// Represents a module loaded in a Windows process.
use super::Address;
pub struct Module<'a> {
/// The name of the module.
pub name: &'a str,
/// A reference to a slice of bytes containing the module data.
pub data: &'a [u8],
/// The PE file format representation of the module.
pub pe: PE<'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<Self>` - A `Result` containing a `Module` instance if successful, or an error if the module could not be parsed.
pub fn parse(name: &'a str, data: &'a [u8]) -> Result<Self> {
let pe = PE::parse_with_opts(
data,
@ -43,45 +27,23 @@ impl<'a> Module<'a> {
Ok(Self { name, data, pe })
}
/// Returns the base address of the module.
///
/// # Arguments
///
/// * `&self` - A reference to the `Module` struct.
///
/// # Returns
///
/// * `Address` - The base address of the module.
#[inline]
pub fn base(&self) -> Address {
self.pe.image_base.into()
}
/// 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]
pub fn exports(&self) -> &'a [Export] {
self.pe.exports.as_slice()
pub fn exports(&self) -> &[Export] {
&self.pe.exports
}
/// Returns the address of the export with the given name, if it exists.
///
/// # Arguments
///
/// * `&self` - A reference to the `Module` struct.
///
/// # Returns
///
/// * `Option<Address>` - The address of the export with the given name, if it exists.
#[inline]
pub fn get_export_by_name(&self, name: &str) -> Option<Address> {
pub fn imports(&self) -> &[Import] {
&self.pe.imports
}
#[inline]
pub fn export_by_name(&self, name: &str) -> Option<Address> {
self.pe
.exports
.iter()
@ -89,17 +51,8 @@ impl<'a> Module<'a> {
.map(|e| (self.pe.image_base + e.rva).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<Address>` - The address of the imported function with the given name, if it exists.
#[inline]
pub fn get_import_by_name(&self, name: &str) -> Option<Address> {
pub fn import_by_name(&self, name: &str) -> Option<Address> {
self.pe
.imports
.iter()
@ -107,49 +60,17 @@ impl<'a> Module<'a> {
.map(|i| (self.pe.image_base + i.rva).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]
pub fn imports(&self) -> &'a [Import] {
self.pe.imports.as_slice()
}
/// 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]
pub fn sections(&self) -> &[SectionTable] {
self.pe.sections.as_slice()
&self.pe.sections
}
/// Returns the size of the module.
///
/// # Arguments
///
/// * `&self` - A reference to the `Module` struct.
///
/// # Returns
///
/// * `u32` - The size of the module.
#[inline]
pub fn size(&self) -> u32 {
self.pe
.header
.optional_header
.unwrap()
.expect("optional header not found")
.windows_fields
.size_of_image
}

View File

@ -1,76 +1,41 @@
use super::{Address, Module};
use anyhow::{bail, Result};
use std::collections::HashMap;
use std::ffi::{c_void, CStr};
use std::mem;
use std::ptr;
use anyhow::{bail, Result};
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory;
use windows::Win32::System::Diagnostics::ToolHelp::*;
use windows::Win32::System::Threading::{OpenProcess, PROCESS_ALL_ACCESS};
/// Represents a Windows process.
use super::{Address, Module};
#[derive(Debug)]
pub struct Process {
/// ID of the process.
id: u32,
/// Handle to the process.
handle: HANDLE,
/// A HashMap containing the name of each module and its corresponding raw data.
modules: HashMap<String, Vec<u8>>,
}
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<Self>` - A `Result` containing the `Process` instance if successful, or an error if the process could not be found.
pub fn new(name: &str) -> Result<Self> {
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)
}
/// 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<Address>` - The address of the first occurrence of the pattern if found, or `None` if the pattern was not found.
pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option<Address> {
let module = self.get_module_by_name(module_name)?;
@ -89,31 +54,12 @@ 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<Module>` - 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<Module<'a>> {
self.modules
.get(name)
.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<Vec<Module>>` - 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<Vec<Module>> {
let mut modules = Vec::new();
@ -124,23 +70,6 @@ impl Process {
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<T>` - A `Result` containing the value if successful, or an error if the memory read fails.
pub fn read_memory<T>(&self, address: Address) -> Result<T> {
let mut buffer: T = unsafe { mem::zeroed() };
@ -153,18 +82,6 @@ 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.
pub fn read_memory_raw(
&self,
address: Address,
@ -183,16 +100,6 @@ impl Process {
.map_err(|e| e.into())
}
/// 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<String>` - 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<String> {
let mut buffer = Vec::new();
@ -206,17 +113,6 @@ 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<String>` - 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<String> {
let mut buffer = vec![0; length];
@ -229,18 +125,6 @@ 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<Address>` - A `Result` containing the absolute address if successful, or an error if the memory read fails.
pub fn resolve_jmp(
&self,
address: Address,
@ -255,18 +139,6 @@ impl Process {
.add(displacement as usize))
}
/// 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<Address>` - A `Result` containing the absolute address if successful, or an error if the memory read fails.
pub fn resolve_rip(
&self,
address: Address,
@ -281,15 +153,6 @@ impl Process {
.add(displacement as usize))
}
/// 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<u32>` - A `Result` containing the process ID if successful, or an error if the process could not be found.
fn get_process_id_by_name(process_name: &str) -> Result<u32> {
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }?;
@ -313,15 +176,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.
fn parse_loaded_modules(&mut self) -> Result<()> {
let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.id) }?;
@ -351,15 +205,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<i32>` - A vector of bytes representing the pattern.
fn pattern_to_bytes(pattern: &str) -> Vec<i32> {
pattern
.split_whitespace()
@ -374,9 +219,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.
impl Drop for Process {
fn drop(&mut self) {
if !self.handle.is_invalid() {