mirror of
https://github.com/a2x/cs2-dumper.git
synced 2025-04-24 19:25:34 +08:00
Merge branch 'main' into linux
This commit is contained in:
commit
41fbb8b4b6
@ -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",
|
||||
|
16
config.json
16
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"
|
||||
|
@ -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 <cstddef>\n\n")
|
||||
writeln!(output, "#pragma once\n")?;
|
||||
writeln!(output, "#include <cstddef>\n")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_namespace(
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<()>;
|
||||
}
|
||||
|
@ -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>,
|
||||
|
@ -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),
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct Signature {
|
||||
pub name: String,
|
||||
pub module: String,
|
||||
pub pattern: String,
|
||||
pub operations: Vec<Operation>,
|
||||
}
|
||||
|
@ -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<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> {
|
||||
fn instance(&self, process: &Process) -> Result<usize> {
|
||||
process
|
||||
.read_memory::<usize>(
|
||||
(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<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;
|
||||
@ -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) }?;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)),
|
||||
};
|
||||
|
@ -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<FileBuilderEnum>,
|
||||
@ -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 <bright-yellow>{}</> @ <bright-magenta>{:#X}</> (<blue>{}</> + <bright-blue>{:#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<Process> {
|
||||
let mut process = Process::new("cs2.exe")?;
|
||||
|
||||
process.initialize()?;
|
||||
|
||||
Ok(process)
|
||||
}
|
||||
|
||||
|
@ -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<FileBuilderEnum>,
|
||||
|
56
src/main.rs
56
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!(
|
||||
"<on-green>Done!</> <green>Time elapsed: <b>{:?}</></>",
|
||||
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<FileBuilderEnum, &'static str> {
|
||||
fn map_file_extension_to_builder(extension: &str) -> Result<FileBuilderEnum, &'static str> {
|
||||
match extension {
|
||||
".cs" => Ok(FileBuilderEnum::CSharpFileBuilder(CSharpFileBuilder)),
|
||||
".hpp" => Ok(FileBuilderEnum::CppFileBuilder(CppFileBuilder)),
|
||||
|
@ -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;
|
||||
|
189
src/os/module.rs
189
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<u8>,
|
||||
pub module_file_data: Vec<u8>,
|
||||
}
|
||||
@ -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<Self>` - 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<Self> {
|
||||
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<Address>` - 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<Address> {
|
||||
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<Address> {
|
||||
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<Address>` - 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<Address> {
|
||||
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<Address> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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
|
||||
}
|
||||
|
@ -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<Self>` - 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<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)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn new(process_name: &str) -> Result<Self> {
|
||||
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<Address>` - 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<Address> {
|
||||
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<Address> {
|
||||
pub fn find_pattern(&self, module_name: &str, pattern: &str) -> Option<usize> {
|
||||
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<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(|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<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();
|
||||
|
||||
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<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> {
|
||||
pub fn read_memory<T>(&self, address: usize) -> Result<T> {
|
||||
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(
|
||||
<Address as Into<usize>>::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<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> {
|
||||
pub fn read_string(&self, address: usize) -> Result<String> {
|
||||
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<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> {
|
||||
pub fn read_string_length(&self, address: usize, length: usize) -> Result<String> {
|
||||
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<Address>` - 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<usize>,
|
||||
length: Option<usize>,
|
||||
) -> Result<Address> {
|
||||
) -> Result<usize> {
|
||||
// The displacement value can be negative.
|
||||
let displacement =
|
||||
self.read_memory::<i32>(address.add(offset.unwrap_or(0x1).try_into().unwrap()))?;
|
||||
let displacement = self.read_memory::<i32>(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<Address>` - 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<usize>,
|
||||
length: Option<usize>,
|
||||
) -> Result<Address> {
|
||||
) -> Result<usize> {
|
||||
// The displacement value can be negative.
|
||||
let displacement =
|
||||
self.read_memory::<i32>(address.add(offset.unwrap_or(0x3).try_into().unwrap()))?;
|
||||
let displacement = self.read_memory::<i32>(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<u32>` - 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<u32> {
|
||||
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<i32>` - A vector of bytes representing the pattern.
|
||||
fn pattern_to_bytes(pattern: &str) -> Vec<i32> {
|
||||
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) {
|
||||
|
@ -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<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,
|
||||
self.process
|
||||
.read_memory::<usize>(self.address + 0x8)?
|
||||
.into(),
|
||||
))
|
||||
let address = self.process.read_memory::<usize>(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<u16>` - The offset of the field.
|
||||
pub fn offset(&self) -> Result<u16> {
|
||||
self.process.read_memory::<u16>(self.address + 0x10)
|
||||
}
|
||||
|
@ -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<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,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<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)?);
|
||||
let address = self.process.read_memory::<usize>(self.address + 0x38)?;
|
||||
|
||||
if address.is_zero() {
|
||||
if address == 0 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let parent = Address::from(self.process.read_memory::<usize>(address + 0x8)?);
|
||||
|
||||
let parent = self.process.read_memory::<usize>(address + 0x8)?;
|
||||
let name_ptr = self.process.read_memory::<usize>(parent + 0x8)?;
|
||||
let name = self.process.read_string(name_ptr.into())?;
|
||||
|
||||
|
@ -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<SchemaSystem>` - The new `SchemaSystem` instance.
|
||||
pub fn new(process: &'a Process) -> Result<Self> {
|
||||
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<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)?;
|
||||
|
||||
if size == 0 {
|
||||
bail!("Type scopes size is 0");
|
||||
bail!("no type scopes found");
|
||||
}
|
||||
|
||||
let data = self.process.read_memory::<usize>(self.address + 0x198)?;
|
||||
@ -61,7 +42,7 @@ impl<'a> SchemaSystem<'a> {
|
||||
|
||||
let type_scopes: Vec<SchemaSystemTypeScope> = addresses
|
||||
.iter()
|
||||
.map(|&address| SchemaSystemTypeScope::new(self.process, address.into()))
|
||||
.map(|&address| SchemaSystemTypeScope::new(self.process, address))
|
||||
.collect();
|
||||
|
||||
Ok(type_scopes)
|
||||
|
@ -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<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
|
||||
@ -43,8 +22,8 @@ impl<'a> SchemaSystemTypeScope<'a> {
|
||||
let classes: Vec<SchemaClassInfo> = 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<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)
|
||||
}
|
||||
|
@ -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<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();
|
||||
|
||||
|
@ -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<String>` - The name of the declared class.
|
||||
pub fn name(&self) -> Result<String> {
|
||||
let name_ptr = self.process.read_memory::<usize>(self.address + 0x8)?;
|
||||
|
||||
self.process.read_string_length(name_ptr.into(), 64)
|
||||
self.process.read_string_length(name_ptr, 64)
|
||||
}
|
||||
}
|
||||
|
@ -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<T, K> {
|
||||
@ -13,25 +12,14 @@ 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(),
|
||||
(self as *const _ as usize + offset_of!(HashFixedDataInternal<T, K>, next)) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the internal data of a hash bucket.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct HashBucketDataInternal<T, K> {
|
||||
@ -41,23 +29,13 @@ 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(),
|
||||
(self as *const _ as usize + offset_of!(HashBucketDataInternal<T, K>, next)) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents allocated data in a hash table.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct HashAllocatedData<T, K> {
|
||||
@ -66,23 +44,13 @@ 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(),
|
||||
(self as *const _ as usize + offset_of!(HashAllocatedData<T, K>, list)) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct representing unallocated data in a hash table.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct HashUnallocatedData<T, K> {
|
||||
@ -94,53 +62,25 @@ 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(),
|
||||
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, next)) as _,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, ui_key)) as _,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
(self as *const _ as usize + offset_of!(HashUnallocatedData<T, K>, block_list)) as _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user