mirror of
https://github.com/a2x/cs2-dumper.git
synced 2025-04-05 00:25:36 +08:00
Added unit tests
This commit is contained in:
parent
acf11ea6c1
commit
df7a1d75ca
@ -3,6 +3,7 @@ name = "cs2-dumper"
|
|||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
authors = ["a2x"]
|
authors = ["a2x"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
readme = "README.md"
|
||||||
repository = "https://github.com/a2x/cs2-dumper"
|
repository = "https://github.com/a2x/cs2-dumper"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
||||||
|
@ -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
|
# Generated Files
|
||||||
|
|
||||||
@ -8,6 +8,10 @@ Generated files are stored in the `generated` directory.
|
|||||||
|
|
||||||
📂 [Pre-generated Files](./generated)
|
📂 [Pre-generated Files](./generated)
|
||||||
|
|
||||||
|
# Running Tests
|
||||||
|
|
||||||
|
`cargo test -- --nocapture`
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
Please refer to the [LICENSE](./LICENSE) file for more details.
|
Please refer to the [LICENSE](./LICENSE) file for more details.
|
||||||
|
14
config.json
14
config.json
@ -25,34 +25,36 @@
|
|||||||
{
|
{
|
||||||
"name": "dwForceAttack",
|
"name": "dwForceAttack",
|
||||||
"module": "client.dll",
|
"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": [
|
"operations": [
|
||||||
{
|
{
|
||||||
"type": "ripRelative"
|
"type": "ripRelative"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "dereference"
|
"type": "subtract",
|
||||||
|
"value": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "add",
|
"type": "add",
|
||||||
"value": 440
|
"value": 48
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "dwForceAttack2",
|
"name": "dwForceAttack2",
|
||||||
"module": "client.dll",
|
"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": [
|
"operations": [
|
||||||
{
|
{
|
||||||
"type": "ripRelative"
|
"type": "ripRelative"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "dereference"
|
"type": "subtract",
|
||||||
|
"value": 8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "add",
|
"type": "add",
|
||||||
"value": 584
|
"value": 48
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,7 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
|
|||||||
interface_version,
|
interface_version,
|
||||||
interface_ptr,
|
interface_ptr,
|
||||||
module_name,
|
module_name,
|
||||||
interface_ptr - module.address()
|
interface_ptr - module.base()
|
||||||
);
|
);
|
||||||
|
|
||||||
entries
|
entries
|
||||||
@ -44,7 +44,7 @@ pub fn dump_interfaces(builders: &mut Vec<FileBuilderEnum>, process: &Process) -
|
|||||||
.or_default()
|
.or_default()
|
||||||
.push(Entry {
|
.push(Entry {
|
||||||
name: interface_version.clone(),
|
name: interface_version.clone(),
|
||||||
value: interface_ptr - module.address(),
|
value: interface_ptr - module.base(),
|
||||||
comment: None,
|
comment: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -9,6 +9,69 @@ use crate::remote::Process;
|
|||||||
|
|
||||||
use super::{generate_files, Entries};
|
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<()> {
|
pub fn dump_offsets(builders: &mut Vec<FileBuilderEnum>, process: &Process) -> Result<()> {
|
||||||
let file = File::open("config.json")?;
|
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);
|
log::debug!(" └─ {} @ {:#X}", signature.name, address.0);
|
||||||
|
|
||||||
(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,
|
signature.name,
|
||||||
address,
|
address,
|
||||||
signature.module,
|
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
|
entries
|
||||||
|
@ -24,7 +24,7 @@ pub struct Section {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Module<'a> {
|
pub struct Module<'a> {
|
||||||
address: usize,
|
base: usize,
|
||||||
nt_headers: &'a IMAGE_NT_HEADERS64,
|
nt_headers: &'a IMAGE_NT_HEADERS64,
|
||||||
size: u32,
|
size: u32,
|
||||||
exports: Vec<Export>,
|
exports: Vec<Export>,
|
||||||
@ -32,10 +32,10 @@ pub struct Module<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> 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];
|
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>() {
|
if headers.len() < mem::size_of::<IMAGE_DOS_HEADER>() {
|
||||||
return Err(Error::BufferSizeMismatch(
|
return Err(Error::BufferSizeMismatch(
|
||||||
@ -60,11 +60,11 @@ impl<'a> Module<'a> {
|
|||||||
|
|
||||||
let size = nt_headers.OptionalHeader.SizeOfImage;
|
let size = nt_headers.OptionalHeader.SizeOfImage;
|
||||||
|
|
||||||
let exports = unsafe { Self::parse_exports(process, address, size, nt_headers)? };
|
let exports = unsafe { Self::parse_exports(process, base, size, nt_headers)? };
|
||||||
let sections = unsafe { Self::parse_sections(address, nt_headers) };
|
let sections = unsafe { Self::parse_sections(base, nt_headers) };
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
address,
|
base,
|
||||||
nt_headers,
|
nt_headers,
|
||||||
size,
|
size,
|
||||||
exports,
|
exports,
|
||||||
@ -73,8 +73,8 @@ impl<'a> Module<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn address(&self) -> usize {
|
pub fn base(&self) -> usize {
|
||||||
self.address
|
self.base
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -35,7 +35,7 @@ impl Process {
|
|||||||
let mut module_data: Vec<u8> = vec![0; module.size() as usize];
|
let mut module_data: Vec<u8> = vec![0; module.size() as usize];
|
||||||
|
|
||||||
self.read_memory_raw(
|
self.read_memory_raw(
|
||||||
module.address(),
|
module.base(),
|
||||||
module_data.as_mut_ptr() as *mut _,
|
module_data.as_mut_ptr() as *mut _,
|
||||||
module_data.len(),
|
module_data.len(),
|
||||||
)?;
|
)?;
|
||||||
@ -54,7 +54,7 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
return Ok(module.address() + i);
|
return Ok(module.base() + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user