Add tests for offsets

This commit is contained in:
a2x 2024-06-18 12:47:28 +10:00
parent 3590d4412c
commit bfc2ceb13d
3 changed files with 178 additions and 5 deletions

View File

@ -1,6 +1,7 @@
# cs2-dumper # cs2-dumper
An external offset/interface dumper for Counter-Strike 2, with support for both Windows & Linux. Powered by [memflow](https://github.com/memflow/memflow). An external offset/interface dumper for Counter-Strike 2, with support for both Windows & Linux. Powered
by [memflow](https://github.com/memflow/memflow).
The native Linux version is available in the [linux](https://github.com/a2x/cs2-dumper/tree/linux) branch. The native Linux version is available in the [linux](https://github.com/a2x/cs2-dumper/tree/linux) branch.
@ -15,7 +16,10 @@ toolchain must be installed.
1. Ensure the game process is running (Being in the main menu should suffice). 1. Ensure the game process is running (Being in the main menu should suffice).
2. Run the `cs2-dumper` executable (Note that some memflow connectors may require elevated privileges). 2. Run the `cs2-dumper` executable (Note that some memflow connectors may require elevated privileges).
When running the executable without providing an optional memflow connector name, it will default to using the [memflow-native](https://github.com/memflow/memflow-native) cross-platform OS layer to read the memory of the game process. If you wish to use an existing memflow connector instead, pass the `connector` and optional `connector-args` arguments to the program. _Note:_ If you run the executable without specifying an optional memflow connector name, it will automatically use the
[memflow-native](https://github.com/memflow/memflow-native) OS layer to read the memory of the game process. If you
wish to use an existing memflow connector instead, you can pass the `connector` and optional `connector-args` arguments
to the program.
E.g. `./cs2-dumper -c pcileech -a device=fpga -vvv` E.g. `./cs2-dumper -c pcileech -a device=fpga -vvv`
@ -31,6 +35,10 @@ E.g. `./cs2-dumper -c pcileech -a device=fpga -vvv`
- `-h, --help`: Print help. - `-h, --help`: Print help.
- `-V, --version`: Print version. - `-V, --version`: Print version.
## Running Tests
To run tests, use the following command: `cargo test -- --nocapture`.
## License ## License
Licensed under the MIT license ([LICENSE](./LICENSE)). Licensed under the MIT license ([LICENSE](./LICENSE)).

View File

@ -148,3 +148,167 @@ pub fn offsets(process: &mut IntoProcessInstanceArcBox<'_>) -> Result<OffsetMap>
Ok(map) Ok(map)
} }
#[cfg(test)]
mod tests {
use std::fs;
use serde_json::Value;
use super::*;
fn setup() -> Result<IntoProcessInstanceArcBox<'static>> {
let os = memflow_native::create_os(&OsArgs::default(), LibArc::default())?;
let process = os.into_process_by_name("cs2.exe")?;
Ok(process)
}
fn get_class_field_value(module_name: &str, class_name: &str, field_name: &str) -> Option<u64> {
let content = fs::read_to_string(format!("output/{}.json", module_name)).ok()?;
let value: Value = serde_json::from_str(&content).ok()?;
value
.get(module_name)?
.get("classes")?
.get(class_name)?
.get("fields")?
.get(field_name)?
.as_u64()
}
fn get_offset_value(module_name: &str, offset_name: &str) -> Option<u64> {
let content = fs::read_to_string("output/offsets.json").ok()?;
let value: Value = serde_json::from_str(&content).ok()?;
let offset = value.get(module_name)?.get(offset_name)?;
offset.as_u64()
}
#[test]
fn build_number() -> Result<()> {
let mut process = setup()?;
let engine_base = process.module_by_name("engine2.dll")?.base;
let offset = get_offset_value("engine2.dll", "dwBuildNumber").unwrap();
let build_number: u32 = process.read(engine_base + offset).data_part()?;
println!("build number: {}", build_number);
Ok(())
}
#[test]
fn global_vars() -> Result<()> {
let mut process = setup()?;
let client_base = process.module_by_name("client.dll")?.base;
let offset = get_offset_value("client.dll", "dwGlobalVars").unwrap();
let global_vars: u64 = process.read(client_base + offset).data_part()?;
let cur_map_name = {
let addr = process
.read_addr64((global_vars + 0x1B8).into())
.data_part()?;
process.read_char_string(addr).data_part()?
};
println!("current map name: {}", cur_map_name);
Ok(())
}
#[test]
fn local_player_controller() -> Result<()> {
let mut process = setup()?;
let client_base = process.module_by_name("client.dll")?.base;
let local_player_controller_offset =
get_offset_value("client.dll", "dwLocalPlayerController").unwrap();
let player_name_offset =
get_class_field_value("client.dll", "CBasePlayerController", "m_iszPlayerName")
.unwrap();
let local_player_controller: u64 = process
.read(client_base + local_player_controller_offset)
.data_part()?;
let player_name = process
.read_char_string((local_player_controller + player_name_offset).into())
.data_part()?;
println!("local player name: {}", player_name);
Ok(())
}
#[test]
fn local_player_pawn() -> Result<()> {
#[derive(Debug, Pod)]
#[repr(C)]
struct Vector3D {
x: f32,
y: f32,
z: f32,
}
let mut process = setup()?;
let client_base = process.module_by_name("client.dll")?.base;
let local_player_pawn_offset = get_offset_value("client.dll", "dwLocalPlayerPawn").unwrap();
let game_scene_node_offset =
get_class_field_value("client.dll", "C_BaseEntity", "m_pGameSceneNode").unwrap();
let vec_abs_origin_offset =
get_class_field_value("client.dll", "CGameSceneNode", "m_vecAbsOrigin").unwrap();
let local_player_pawn: u64 = process
.read(client_base + local_player_pawn_offset)
.data_part()?;
let game_scene_node: u64 = process
.read((local_player_pawn + game_scene_node_offset).into())
.data_part()?;
let vec_abs_origin: Vector3D = process
.read((game_scene_node + vec_abs_origin_offset).into())
.data_part()?;
println!("local player origin: {:?}", vec_abs_origin);
Ok(())
}
#[test]
fn window_size() -> Result<()> {
let mut process = setup()?;
let engine_base = process.module_by_name("engine2.dll")?.base;
let window_width_offset = get_offset_value("engine2.dll", "dwWindowWidth").unwrap();
let window_height_offset = get_offset_value("engine2.dll", "dwWindowHeight").unwrap();
let window_width: u32 = process
.read(engine_base + window_width_offset)
.data_part()?;
let window_height: u32 = process
.read(engine_base + window_height_offset)
.data_part()?;
println!("window size: {}x{}", window_width, window_height);
Ok(())
}
}

View File

@ -8,7 +8,7 @@ use log::{info, Level};
use memflow::prelude::v1::*; use memflow::prelude::v1::*;
use simplelog::{ColorChoice, TermLogger}; use simplelog::{ColorChoice, Config, TermLogger, TerminalMode};
use error::Result; use error::Result;
use output::Output; use output::Output;
@ -65,8 +65,8 @@ fn main() -> Result<()> {
TermLogger::init( TermLogger::init(
log_level.to_level_filter(), log_level.to_level_filter(),
Default::default(), Config::default(),
Default::default(), TerminalMode::Mixed,
ColorChoice::Auto, ColorChoice::Auto,
) )
.unwrap(); .unwrap();
@ -93,6 +93,7 @@ fn main() -> Result<()> {
let mut process = os.into_process_by_name(&args.process_name)?; let mut process = os.into_process_by_name(&args.process_name)?;
let result = analysis::analyze_all(&mut process)?; let result = analysis::analyze_all(&mut process)?;
let output = Output::new(&args.file_types, args.indent_size, &args.output, &result)?; let output = Output::new(&args.file_types, args.indent_size, &args.output, &result)?;
output.dump_all(&mut process)?; output.dump_all(&mut process)?;