Added unit tests

This commit is contained in:
a2x 2023-10-14 15:18:52 +10:00
parent acf11ea6c1
commit df7a1d75ca
7 changed files with 93 additions and 23 deletions

View File

@ -3,6 +3,7 @@ name = "cs2-dumper"
version = "1.1.0"
authors = ["a2x"]
edition = "2021"
readme = "README.md"
repository = "https://github.com/a2x/cs2-dumper"
license = "MIT"

View File

@ -1,6 +1,6 @@
# CS2 Dumper
# cs2-dumper
Tool to automatically dump offsets and interfaces for Counter-Strike: 2.
External offsets/interfaces dumper for Counter-Strike: 2, written in Rust.
# Generated Files
@ -8,6 +8,10 @@ Generated files are stored in the `generated` directory.
📂 [Pre-generated Files](./generated)
# Running Tests
`cargo test -- --nocapture`
# License
Please refer to the [LICENSE](./LICENSE) file for more details.

View File

@ -25,34 +25,36 @@
{
"name": "dwForceAttack",
"module": "client.dll",
"pattern": "48 8B 15 ? ? ? ? 48 8D 0D ? ? ? ? E9 7D 69 FD 00",
"pattern": "48 8D 0D ? ? ? ? E9 D4 4B B4 FF",
"operations": [
{
"type": "ripRelative"
},
{
"type": "dereference"
"type": "subtract",
"value": 8
},
{
"type": "add",
"value": 440
"value": 48
}
]
},
{
"name": "dwForceAttack2",
"module": "client.dll",
"pattern": "48 8B 15 ? ? ? ? 48 8D 0D ? ? ? ? E9 7D 69 FD 00",
"pattern": "48 8D 0D ? ? ? ? E9 E4 4B B4 FF",
"operations": [
{
"type": "ripRelative"
},
{
"type": "dereference"
"type": "subtract",
"value": 8
},
{
"type": "add",
"value": 584
"value": 48
}
]
},

View File

@ -36,7 +36,7 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
interface_version,
interface_ptr,
module_name,
interface_ptr - module.address()
interface_ptr - module.base()
);
entries
@ -44,7 +44,7 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
.or_default()
.push(Entry {
name: interface_version.clone(),
value: interface_ptr - module.address(),
value: interface_ptr - module.base(),
comment: None,
});

View File

@ -9,6 +9,69 @@ use crate::remote::Process;
use super::{generate_files, Entries};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_number() -> Result<()> {
let process = Process::new("cs2.exe")?;
let engine_base = process.get_module_by_name("engine2.dll")?.base();
let build_number = process.read_memory::<u32>(engine_base + 0x487514)?;
println!("Build number: {}", build_number);
Ok(())
}
#[test]
fn global_vars() -> Result<()> {
let process = Process::new("cs2.exe")?;
let client_base = process.get_module_by_name("client.dll")?.base();
let global_vars = process.read_memory::<usize>(client_base + 0x1692EE8)?;
let current_map_name =
process.read_string(process.read_memory::<usize>(global_vars + 0x188)?)?;
println!("Current map name: {}", current_map_name);
Ok(())
}
#[test]
fn local_player() -> Result<()> {
let process = Process::new("cs2.exe")?;
let client_base = process.get_module_by_name("client.dll")?.base();
let local_player_controller = process.read_memory::<usize>(client_base + 0x17DE508)?;
let player_name = process.read_string(local_player_controller + 0x610)?;
println!("Name: {}", player_name);
Ok(())
}
#[test]
fn window_size() -> Result<()> {
let process = Process::new("cs2.exe")?;
let engine_base = process.get_module_by_name("engine2.dll")?.base();
let window_width = process.read_memory::<u32>(engine_base + 0x538670)?;
let window_height = process.read_memory::<u32>(engine_base + 0x538674)?;
println!("Window size: {}x{}", window_width, window_height);
Ok(())
}
}
pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> Result<()> {
let file = File::open("config.json")?;
@ -60,7 +123,7 @@ pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
}
}
let (name, value) = if address.0 < module.address() {
let (name, value) = if address.0 < module.base() {
log::debug!(" └─ {} @ {:#X}", signature.name, address.0);
(signature.name, address.0)
@ -70,10 +133,10 @@ pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> R
signature.name,
address,
signature.module,
address.sub(module.address())
address.sub(module.base())
);
(signature.name, address.sub(module.address()).0)
(signature.name, address.sub(module.base()).0)
};
entries

View File

@ -24,7 +24,7 @@ pub struct Section {
}
pub struct Module<'a> {
address: usize,
base: usize,
nt_headers: &'a IMAGE_NT_HEADERS64,
size: u32,
exports: Vec<Export>,
@ -32,10 +32,10 @@ pub struct Module<'a> {
}
impl<'a> Module<'a> {
pub fn new(process: &'a Process, address: usize) -> Result<Self> {
pub fn new(process: &'a Process, base: usize) -> Result<Self> {
let mut headers: [u8; 0x1000] = [0; 0x1000];
process.read_memory_raw(address, headers.as_mut_ptr() as *mut _, headers.len())?;
process.read_memory_raw(base, headers.as_mut_ptr() as *mut _, headers.len())?;
if headers.len() < mem::size_of::<IMAGE_DOS_HEADER>() {
return Err(Error::BufferSizeMismatch(
@ -60,11 +60,11 @@ impl<'a> Module<'a> {
let size = nt_headers.OptionalHeader.SizeOfImage;
let exports = unsafe { Self::parse_exports(process, address, size, nt_headers)? };
let sections = unsafe { Self::parse_sections(address, nt_headers) };
let exports = unsafe { Self::parse_exports(process, base, size, nt_headers)? };
let sections = unsafe { Self::parse_sections(base, nt_headers) };
Ok(Self {
address,
base,
nt_headers,
size,
exports,
@ -73,8 +73,8 @@ impl<'a> Module<'a> {
}
#[inline]
pub fn address(&self) -> usize {
self.address
pub fn base(&self) -> usize {
self.base
}
#[inline]

View File

@ -35,7 +35,7 @@ impl Process {
let mut module_data: Vec<u8> = vec![0; module.size() as usize];
self.read_memory_raw(
module.address(),
module.base(),
module_data.as_mut_ptr() as *mut _,
module_data.len(),
)?;
@ -54,7 +54,7 @@ impl Process {
}
if found {
return Ok(module.address() + i);
return Ok(module.base() + i);
}
}