mirror of
https://github.com/a2x/cs2-dumper.git
synced 2025-10-07 16:30:01 +08:00
Add python file builder
This commit is contained in:
@@ -7,14 +7,14 @@ use super::FileBuilder;
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct JsonFileBuilder {
|
||||
json: Value,
|
||||
namespace: String,
|
||||
current_namespace: String,
|
||||
}
|
||||
|
||||
impl Default for JsonFileBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
json: Value::Object(Map::new()),
|
||||
namespace: String::new(),
|
||||
current_namespace: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ impl FileBuilder for JsonFileBuilder {
|
||||
}
|
||||
|
||||
fn write_namespace(&mut self, _output: &mut dyn Write, name: &str) -> Result<()> {
|
||||
self.namespace = name.to_string();
|
||||
self.current_namespace = name.to_string();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -42,7 +42,9 @@ impl FileBuilder for JsonFileBuilder {
|
||||
_comment: Option<&str>,
|
||||
) -> Result<()> {
|
||||
if let Some(map) = self.json.as_object_mut() {
|
||||
let entry = map.entry(&self.namespace).or_insert_with(|| json!({}));
|
||||
let entry = map
|
||||
.entry(&self.current_namespace)
|
||||
.or_insert_with(|| json!({}));
|
||||
|
||||
if let Some(object) = entry.as_object_mut() {
|
||||
object.insert(name.to_string(), json!(value));
|
||||
|
@@ -4,12 +4,14 @@ pub use cpp_file_builder::CppFileBuilder;
|
||||
pub use csharp_file_builder::CSharpFileBuilder;
|
||||
pub use file_builder::FileBuilder;
|
||||
pub use json_file_builder::JsonFileBuilder;
|
||||
pub use python_file_builder::PythonFileBuilder;
|
||||
pub use rust_file_builder::RustFileBuilder;
|
||||
|
||||
pub mod cpp_file_builder;
|
||||
pub mod csharp_file_builder;
|
||||
pub mod file_builder;
|
||||
pub mod json_file_builder;
|
||||
pub mod python_file_builder;
|
||||
pub mod rust_file_builder;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -17,6 +19,7 @@ pub enum FileBuilderEnum {
|
||||
CppFileBuilder(CppFileBuilder),
|
||||
CSharpFileBuilder(CSharpFileBuilder),
|
||||
JsonFileBuilder(JsonFileBuilder),
|
||||
PythonFileBuilder(PythonFileBuilder),
|
||||
RustFileBuilder(RustFileBuilder),
|
||||
}
|
||||
|
||||
@@ -54,6 +57,7 @@ impl FileBuilderEnum {
|
||||
FileBuilderEnum::CppFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::CSharpFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::JsonFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::PythonFileBuilder(builder) => builder,
|
||||
FileBuilderEnum::RustFileBuilder(builder) => builder,
|
||||
}
|
||||
}
|
||||
|
43
src/builder/python_file_builder.rs
Normal file
43
src/builder/python_file_builder.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::io::{Result, Write};
|
||||
|
||||
use super::FileBuilder;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PythonFileBuilder;
|
||||
|
||||
impl FileBuilder for PythonFileBuilder {
|
||||
fn extension(&mut self) -> &str {
|
||||
"py"
|
||||
}
|
||||
|
||||
fn write_top_level(&mut self, _output: &mut dyn Write) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_namespace(&mut self, output: &mut dyn Write, name: &str) -> Result<()> {
|
||||
write!(output, "class {}:\n", name)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_variable(
|
||||
&mut self,
|
||||
output: &mut dyn Write,
|
||||
name: &str,
|
||||
value: usize,
|
||||
comment: Option<&str>,
|
||||
) -> Result<()> {
|
||||
match comment {
|
||||
Some(comment) => write!(output, " {} = {:#X} # {}\n", name, value, comment),
|
||||
None => write!(output, " {} = {:#X}\n", name, value),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_closure(&mut self, output: &mut dyn Write, eof: bool) -> Result<()> {
|
||||
if !eof {
|
||||
write!(output, "\n")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@@ -1,51 +1,21 @@
|
||||
use std::ffi::c_char;
|
||||
use std::mem::offset_of;
|
||||
use convert_case::{Case, Casing};
|
||||
|
||||
use crate::builder::FileBuilderEnum;
|
||||
use crate::dumpers::Entry;
|
||||
use crate::error::Result;
|
||||
use crate::remote::Process;
|
||||
use crate::sdk::InterfaceReg;
|
||||
|
||||
use super::{generate_files, Entries};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
struct InterfaceReg {
|
||||
create_fn: *const (), // 0x0000
|
||||
name: *const c_char, // 0x0008
|
||||
next: *mut InterfaceReg, // 0x0010
|
||||
}
|
||||
|
||||
impl InterfaceReg {
|
||||
pub fn ptr(&self, process: &Process) -> Result<usize> {
|
||||
process
|
||||
.read_memory::<usize>(self as *const _ as usize + offset_of!(InterfaceReg, create_fn))
|
||||
}
|
||||
|
||||
pub fn name(&self, process: &Process) -> Result<String> {
|
||||
let name_ptr = process
|
||||
.read_memory::<usize>(self as *const _ as usize + offset_of!(InterfaceReg, name))?;
|
||||
|
||||
process.read_string(name_ptr)
|
||||
}
|
||||
|
||||
pub fn next(&self, process: &Process) -> Result<*mut InterfaceReg> {
|
||||
process.read_memory::<*mut InterfaceReg>(
|
||||
self as *const _ as usize + offset_of!(InterfaceReg, next),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> Result<()> {
|
||||
let module_names = process.get_loaded_modules()?;
|
||||
|
||||
let mut entries = Entries::new();
|
||||
|
||||
for module_name in module_names {
|
||||
if module_name == "crashhandler64.dll" {
|
||||
continue;
|
||||
}
|
||||
|
||||
for module_name in process
|
||||
.get_loaded_modules()?
|
||||
.into_iter()
|
||||
.filter(|module_name| *module_name != "crashhandler64.dll")
|
||||
{
|
||||
let module = process.get_module_by_name(&module_name)?;
|
||||
|
||||
if let Some(create_interface_export) = module.export("CreateInterface") {
|
||||
@@ -70,7 +40,12 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
|
||||
);
|
||||
|
||||
entries
|
||||
.entry(module_name.replace(".", "_"))
|
||||
.entry(
|
||||
module_name
|
||||
.replace(".", "_")
|
||||
.to_case(Case::Snake)
|
||||
.to_case(Case::Pascal),
|
||||
)
|
||||
.or_default()
|
||||
.push(Entry {
|
||||
name: name.clone(),
|
||||
|
@@ -2,6 +2,8 @@ use std::collections::BTreeMap;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use chrono::Utc;
|
||||
|
||||
use crate::builder::{FileBuilder, FileBuilderEnum};
|
||||
use crate::error::Result;
|
||||
|
||||
@@ -34,29 +36,23 @@ pub fn generate_file(
|
||||
|
||||
let mut file = File::create(file_path)?;
|
||||
|
||||
builder.write_top_level(&mut file)?;
|
||||
write_banner_to_file(&mut file, builder.extension())?;
|
||||
|
||||
if builder.extension() != "json" {
|
||||
write!(
|
||||
file,
|
||||
"/*\n * https://github.com/a2x/cs2-dumper\n * {}\n */\n\n",
|
||||
chrono::Utc::now()
|
||||
)?;
|
||||
}
|
||||
builder.write_top_level(&mut file)?;
|
||||
|
||||
let len = entries.len();
|
||||
|
||||
for (i, pair) in entries.iter().enumerate() {
|
||||
builder.write_namespace(&mut file, pair.0)?;
|
||||
|
||||
for entry in pair.1 {
|
||||
pair.1.iter().try_for_each(|entry| {
|
||||
builder.write_variable(
|
||||
&mut file,
|
||||
&entry.name,
|
||||
entry.value,
|
||||
entry.comment.as_deref(),
|
||||
)?;
|
||||
}
|
||||
)
|
||||
})?;
|
||||
|
||||
builder.write_closure(&mut file, i == len - 1)?;
|
||||
}
|
||||
@@ -65,12 +61,28 @@ pub fn generate_file(
|
||||
}
|
||||
|
||||
pub fn generate_files(
|
||||
builders: &mut Vec<FileBuilderEnum>,
|
||||
builders: &mut [FileBuilderEnum],
|
||||
entries: &Entries,
|
||||
file_name: &str,
|
||||
) -> Result<()> {
|
||||
for builder in builders {
|
||||
generate_file(builder, &entries, file_name)?;
|
||||
builders
|
||||
.iter_mut()
|
||||
.try_for_each(|builder| generate_file(builder, entries, file_name))
|
||||
}
|
||||
|
||||
fn write_banner_to_file(file: &mut File, extension: &str) -> Result<()> {
|
||||
let chrono_now = Utc::now();
|
||||
|
||||
const URL: &str = "https://github.com/a2x/cs2-dumper";
|
||||
|
||||
let banner = match extension {
|
||||
"json" => None,
|
||||
"py" => Some(format!("'''\n{}\n{}\n'''\n\n", URL, chrono_now)),
|
||||
_ => Some(format!("/*\n * {}\n * {}\n */\n\n", URL, chrono_now)),
|
||||
};
|
||||
|
||||
if let Some(banner) = banner {
|
||||
write!(file, "{}", banner)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@@ -1,5 +1,7 @@
|
||||
use std::fs::File;
|
||||
|
||||
use convert_case::{Case, Casing};
|
||||
|
||||
use crate::builder::FileBuilderEnum;
|
||||
use crate::config::{Config, Operation::*};
|
||||
use crate::dumpers::Entry;
|
||||
@@ -84,72 +86,81 @@ pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
|
||||
for signature in config.signatures {
|
||||
let module = process.get_module_by_name(&signature.module)?;
|
||||
|
||||
if let Ok(address) = process.find_pattern(&signature.module, &signature.pattern) {
|
||||
let mut address = Address::from(address);
|
||||
let mut address = match process.find_pattern(&signature.module, &signature.pattern) {
|
||||
Ok(a) => Address::from(a),
|
||||
Err(_) => {
|
||||
log::error!("Failed to find pattern for {}.", signature.name);
|
||||
|
||||
for operation in signature.operations {
|
||||
match operation {
|
||||
Add { value } => address += value,
|
||||
Dereference { times, size } => {
|
||||
let times = times.unwrap_or(1);
|
||||
let size = size.unwrap_or(8);
|
||||
|
||||
for _ in 0..times {
|
||||
process.read_memory_raw(
|
||||
address.0,
|
||||
&mut address.0 as *mut _ as *mut _,
|
||||
size,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Jmp { offset, length } => {
|
||||
address = process.resolve_jmp(address.0, offset, length)?.into()
|
||||
}
|
||||
RipRelative { offset, length } => {
|
||||
address = process.resolve_rip(address.0, offset, length)?.into()
|
||||
}
|
||||
Slice { start, end } => {
|
||||
let mut result: usize = 0;
|
||||
|
||||
process.read_memory_raw(
|
||||
address.add(start).0,
|
||||
&mut result as *mut _ as *mut _,
|
||||
end - start,
|
||||
)?;
|
||||
|
||||
address = result.into();
|
||||
}
|
||||
Subtract { value } => address -= value,
|
||||
}
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let (name, value) = if address.0 < module.base() {
|
||||
log::debug!(" └─ {} @ {:#X}", signature.name, address.0);
|
||||
for operation in signature.operations {
|
||||
match operation {
|
||||
Add { value } => address += value,
|
||||
Dereference { times, size } => {
|
||||
let times = times.unwrap_or(1);
|
||||
let size = size.unwrap_or(8);
|
||||
|
||||
(signature.name, address.0)
|
||||
} else {
|
||||
log::debug!(
|
||||
" └─ {} @ {:#X} ({} + {:#X})",
|
||||
signature.name,
|
||||
address,
|
||||
signature.module,
|
||||
address.sub(module.base())
|
||||
);
|
||||
for _ in 0..times {
|
||||
process.read_memory_raw(
|
||||
address.0,
|
||||
&mut address.0 as *mut _ as *mut _,
|
||||
size,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Jmp { offset, length } => {
|
||||
address = process.resolve_jmp(address.0, offset, length)?.into()
|
||||
}
|
||||
RipRelative { offset, length } => {
|
||||
address = process.resolve_rip(address.0, offset, length)?.into()
|
||||
}
|
||||
Slice { start, end } => {
|
||||
let mut result: usize = 0;
|
||||
|
||||
(signature.name, address.sub(module.base()).0)
|
||||
};
|
||||
process.read_memory_raw(
|
||||
address.add(start).0,
|
||||
&mut result as *mut _ as *mut _,
|
||||
end - start,
|
||||
)?;
|
||||
|
||||
entries
|
||||
.entry(signature.module.replace(".", "_"))
|
||||
.or_default()
|
||||
.push(Entry {
|
||||
name,
|
||||
value,
|
||||
comment: None,
|
||||
});
|
||||
} else {
|
||||
log::error!("Failed to find pattern for {}.", signature.name);
|
||||
address = result.into();
|
||||
}
|
||||
Subtract { value } => address -= value,
|
||||
}
|
||||
}
|
||||
|
||||
let (name, value) = if address.0 < module.base() {
|
||||
log::debug!(" └─ {} @ {:#X}", signature.name, address.0);
|
||||
|
||||
(signature.name, address.0)
|
||||
} else {
|
||||
log::debug!(
|
||||
" └─ {} @ {:#X} ({} + {:#X})",
|
||||
signature.name,
|
||||
address,
|
||||
signature.module,
|
||||
address.sub(module.base())
|
||||
);
|
||||
|
||||
(signature.name, address.sub(module.base()).0)
|
||||
};
|
||||
|
||||
entries
|
||||
.entry(
|
||||
signature
|
||||
.module
|
||||
.replace(".", "_")
|
||||
.to_case(Case::Snake)
|
||||
.to_case(Case::Pascal),
|
||||
)
|
||||
.or_default()
|
||||
.push(Entry {
|
||||
name,
|
||||
value,
|
||||
comment: None,
|
||||
});
|
||||
}
|
||||
|
||||
generate_files(builders, &entries, "offsets")?;
|
||||
|
14
src/error.rs
14
src/error.rs
@@ -9,7 +9,7 @@ use windows::core::Error as WindowsError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("Buffer size mismatch: expected {0}, got {1}")]
|
||||
#[error("Buffer size mismatch. Expected {0}, got {1}")]
|
||||
BufferSizeMismatch(usize, usize),
|
||||
|
||||
#[error("Invalid magic: {0:#X}")]
|
||||
@@ -18,14 +18,14 @@ pub enum Error {
|
||||
#[error("IO error: {0}")]
|
||||
IOError(#[from] io::Error),
|
||||
|
||||
#[error("Module not found")]
|
||||
ModuleNotFound,
|
||||
#[error("Module not found: {0}")]
|
||||
ModuleNotFound(String),
|
||||
|
||||
#[error("Pattern not found")]
|
||||
PatternNotFound,
|
||||
#[error("Pattern not found: {0}")]
|
||||
PatternNotFound(String),
|
||||
|
||||
#[error("Process not found")]
|
||||
ProcessNotFound,
|
||||
#[error("Process not found: {0}")]
|
||||
ProcessNotFound(String),
|
||||
|
||||
#[error("Serde error: {0}")]
|
||||
SerdeError(#[from] SerdeError),
|
||||
|
@@ -67,6 +67,7 @@ fn main() -> Result<()> {
|
||||
FileBuilderEnum::CppFileBuilder(CppFileBuilder),
|
||||
FileBuilderEnum::CSharpFileBuilder(CSharpFileBuilder),
|
||||
FileBuilderEnum::JsonFileBuilder(JsonFileBuilder::default()),
|
||||
FileBuilderEnum::PythonFileBuilder(PythonFileBuilder),
|
||||
FileBuilderEnum::RustFileBuilder(RustFileBuilder),
|
||||
];
|
||||
|
||||
|
@@ -58,7 +58,7 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::PatternNotFound)
|
||||
Err(Error::PatternNotFound(pattern.to_owned()))
|
||||
}
|
||||
|
||||
pub fn get_loaded_modules(&self) -> Result<Vec<String>> {
|
||||
@@ -108,7 +108,7 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ModuleNotFound)
|
||||
Err(Error::ModuleNotFound(module_name.to_owned()))
|
||||
}
|
||||
|
||||
pub fn read_memory_raw(&self, address: usize, buffer: *mut c_void, size: usize) -> Result<()> {
|
||||
@@ -215,7 +215,7 @@ impl Process {
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::ProcessNotFound)
|
||||
Err(Error::ProcessNotFound(process_name.to_owned()))
|
||||
}
|
||||
|
||||
fn pattern_to_bytes(pattern: &str) -> Vec<i32> {
|
||||
|
33
src/sdk/interface.rs
Normal file
33
src/sdk/interface.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::ffi::c_char;
|
||||
use std::mem::offset_of;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::remote::Process;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub struct InterfaceReg {
|
||||
pub create_fn: *const (), // 0x0000
|
||||
pub name: *const c_char, // 0x0008
|
||||
pub next: *mut InterfaceReg, // 0x0010
|
||||
}
|
||||
|
||||
impl InterfaceReg {
|
||||
pub fn ptr(&self, process: &Process) -> Result<usize> {
|
||||
process
|
||||
.read_memory::<usize>(self as *const _ as usize + offset_of!(InterfaceReg, create_fn))
|
||||
}
|
||||
|
||||
pub fn name(&self, process: &Process) -> Result<String> {
|
||||
let name_ptr = process
|
||||
.read_memory::<usize>(self as *const _ as usize + offset_of!(InterfaceReg, name))?;
|
||||
|
||||
process.read_string(name_ptr)
|
||||
}
|
||||
|
||||
pub fn next(&self, process: &Process) -> Result<*mut InterfaceReg> {
|
||||
process.read_memory::<*mut InterfaceReg>(
|
||||
self as *const _ as usize + offset_of!(InterfaceReg, next),
|
||||
)
|
||||
}
|
||||
}
|
@@ -1,3 +1,4 @@
|
||||
pub use interface::InterfaceReg;
|
||||
pub use schema_class_field_data::SchemaClassFieldData;
|
||||
pub use schema_class_info::SchemaClassInfo;
|
||||
pub use schema_system::SchemaSystem;
|
||||
@@ -6,6 +7,7 @@ pub use schema_type::SchemaType;
|
||||
pub use schema_type_declared_class::SchemaTypeDeclaredClass;
|
||||
pub use utl_ts_hash::UtlTsHash;
|
||||
|
||||
pub mod interface;
|
||||
pub mod schema_class_field_data;
|
||||
pub mod schema_class_info;
|
||||
pub mod schema_system;
|
||||
|
Reference in New Issue
Block a user