mirror of
				https://github.com/a2x/cs2-dumper.git
				synced 2025-11-04 10:00:06 +08:00 
			
		
		
		
	Add tests for offsets
This commit is contained in:
		
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							@@ -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)).
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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)?;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user