#cheat #memory #windows #gamehack

toy-arms

一个旨在简化 Rust 游戏破解的 crate

18 个版本 (6 个破坏性更新)

0.9.4 2022 年 3 月 24 日
0.9.1 2021 年 11 月 16 日

#423开发工具

MIT 许可证

58KB
896

Crates.io Docs.rs

⚔️ toy-arms

非常感谢我的朋友为这个头部做的贡献 @suzuharuR

使用方法 | 示例 | 文档

什么是 toy-arms?

这是一个工具包,专为那些厌倦了用 C++ 编写游戏破解,但仍想以优雅的方式完成的人而设计。由于这个库封装了许多常用的 Windows API,您可以构建破解而无需与它们苦苦挣扎。通过使用它,您在编写底层代码时的许多压力都不会出现。

但请注意,由于我既不是专业的 Rust 编程人员,也不是专业的游戏破解人员,这个库可能仍然包含一些有缺陷的代码,这些代码可能导致意外的行为。如果您能通过发现它们并提交 PR 或 issue 来帮助我改进 toy-arms,我将不胜感激。

📌 目录

💕 支持我

通过 GitHub Sponsors 赞助我 是支持我和这个项目的最佳方式。您也可以通过 收藏此项目 或任何类型的 PR(无论是重构此项目还是添加新功能)来支持我,这将让我感到非常兴奋!

🔥 开始使用

但在实际测试示例之前,我将向您展示一些您应该了解的准备工作。

步骤1

首先,在 Cargo.toml 中将 toy-arms 包含在依赖项表中。

目前,toy-arms 有两个功能:internalexternal。默认情况下,internal 功能标志是开启的,所以当您想使用 external 时,您必须指定它。

内部使用

[dependencies]
toy-arms = "0.9.4"

# This annotation below is to tell the compiler to compile this into dll. MUST.
[lib]
crate-type = ["cdylib"]

外部使用

[dependencies]
toy-arms = {version = "0.9.4", features = ["external"]}

步骤2

其次,由于大多数测试都是针对 "csgo.exe(x86)" 游戏的,您可能需要根据示例构建 x86 架构的代码。您可以在 .cargo/config.toml 中指定如下:

[build]
target = "i686-pc-windows-msvc"

或者每次构建代码时都使用 --target i686-pc-windows-msvc 标志。

📜 实际示例

在本节中,我将向您展示各种不同情况下内部和外部功能的示例。找到适合您目的的一个。

请注意,所有这些示例都是针对 CSGO:反恐精英:全球攻势 的,所以请确保您拥有它并使用它进行测试。

内部

欢迎来到内部黑客示例。通过构建这些示例,将生成一个.dll文件,使用您拥有的任何.dll注入器注入它。

最简单的.dll(内部)

使用这个crate,创建最小的可注入.dll就像这样简单

cargob --examplein_simplest_dll --targeti686-pc-windows-msvc

// A neat macro which defines entry point instead of you.
// Also, you dont have to alloc/free console by yourself, console will show up when u compile into debug build.
toy_arms::create_entrypoint!(hack_main_thread);

// Main thread
fn hack_main_thread() {
  // YOUR STUNNING CODE'S SUPPOSED TO BE HERE;
  for i in 0..30000 {
    println!("using toy-arms {}", i);
  }
}

自动射击(内部)

这是覆盖csgo.exe中DW_FORCE_ATTACK值的代码,每次循环将其设置为0x5。注意,您必须检查DW_FORCE_ATTACK地址是否最新。

cargob --examplein_auto_shoot --targeti686-pc-windows-msvc

use toy_arms::VirtualKeyCode;
use toy_arms::external::Process;
use toy_arms::external::{ read, write };

fn main() {
  // This const has to be up to date.
  const DW_CLIENT_STATE: usize = 0x58CFC4;
  const DW_CLIENT_STATE_STATE: usize = 0x108;
  const DW_FORCE_ATTACK: usize = 0x31FE33C;
  // Getting process information
  let process = Process::from_process_name("csgo.exe");
  println!(
    "process id = {}, \nprocess handle = {:?}",
    process.process_id, process.process_handle
  );

  // You can get module information by using get_module_info
  let module_info = process.get_module_info("client.dll").unwrap();
  println!("{}", module_info.module_name);

  // read fetches the value at where the address is pointing.
  // U have to specify the type of the value with turbofish
  println!(
    "{:x}",
    read::<i32>(process.process_handle, read::<u32>(process.process_handle, process.get_module_base("engine.dll").unwrap() + DW_CLIENT_STATE).unwrap() as usize + DW_CLIENT_STATE_STATE).unwrap()
  );

  loop {
    // write helps you tamper with the value.
    write::<u32>(
      process.process_handle,
      process.get_module_base("client.dll").unwrap() + DW_FORCE_ATTACK as usize,
      &mut 0x5,
    )
            .unwrap();

    // Exit this loop by pressing INSERT
    if toy_arms::detect_keypress(VirtualKeyCode::VK_INSERT) {
      break;
    }
  }
}

获取本地玩家生命值(内部)

以下代码将检索csgo.exe中LocalPlayer对象的健康值。注意,您必须更新DW_LOCAL_PLAYER的偏移量。

cargob --examplein_localplayer_health --targeti686-pc-windows-msvc

use toy_arms::GameObject;
use toy_arms::{cast, create_entrypoint, VirtualKeyCode};
use toy_arms::internal::Module;
use toy_arms_derive::GameObject;

create_entrypoint!(hack_main_thread);

// This macro provides from_raw() func that ensures the base address is not null.
#[derive(GameObject)]
struct LocalPlayer {
  pointer: *const usize, // Denote the base address of LocalPlayer to use it later in get_health() function.
}

impl LocalPlayer {
  unsafe fn get_health(&self) -> u16 {
    *cast!(self.pointer as usize + 0x100, u16)
  }
}

// This offset has to be up to date.
const DW_LOCAL_PLAYER: i32 = 0xDB25DC;

fn hack_main_thread() {
  let module = Module::from_module_name("client.dll").unwrap();
  unsafe {
    //let dw_local_player = memory.read_mut::<LocalPlayer>(0xDA244C);
    loop {
      if let Some(i) = LocalPlayer::from_raw(module.read(DW_LOCAL_PLAYER)) {
        println!("health = {:?}", (*i).get_health());
      };
      if toy_arms::detect_keypress(VirtualKeyCode::VK_INSERT) {
        break;
      }
    }
  }
}

模式扫描(内部)

这是模式扫描示例,其中模式是针对csgo中的dwForceAttack。

cargob --examplein_pattern_scanning --targeti686-pc-windows-msvc

use toy_arms::{
  detect_keypress,
  internal::{
    Module
  },
  VirtualKeyCode
};
toy_arms::create_entrypoint!(hack_main_thread);

const DW_FORCE_ATTACK_PATTERN: &str = "89 0D ? ? ? ? 8B 0D ? ? ? ? 8B F2 8B C1 83 CE 04";

fn hack_main_thread() {
  let mut once = false;

  let client = Module::from_module_name("client.dll").unwrap();

  match client.find_pattern(DW_FORCE_ATTACK_PATTERN) {
    Some(i) => println!("address: 0x{:x}", i),
    None => println!("Pattern not found"),
  }

  match client.pattern_scan(
    DW_FORCE_ATTACK_PATTERN,
    2,
    0,
  ) {
    Some(i) => println!("address: 0x{:x}", i),
    None => println!("Offset not found"),
  }

  loop {
    if !once {
      println!("Press INSERT to exit...");
      once = !once;
    }
    // To exit this hack loop when you input INSEERT KEY
    if detect_keypress(VirtualKeyCode::VK_INSERT) {
      break;
    }
  }
}

外部

另一方面,以下代码展示了如何在外部篡改内存。

自动射击(外部)

这是覆盖csgo.exe中DW_FORCE_ATTACK值的代码,每次循环将其设置为0x5。注意,您必须检查DW_FORCE_ATTACK地址是否最新。

cargor --exampleex_auto_shoot --功能外部 --no-default-features

use toy_arms::VirtualKeyCode;
use toy_arms::external::Process;
use toy_arms::external::{ read, write };

fn main() {
  // This const has to be up to date.
  const DW_CLIENT_STATE: usize = 0x58CFC4;
  const DW_CLIENT_STATE_STATE: usize = 0x108;
  const DW_FORCE_ATTACK: usize = 0x31FE33C;
  // Getting process information
  let process = Process::from_process_name("csgo.exe");
  println!(
    "process id = {}, \nprocess handle = {:?}",
    process.process_id, process.process_handle
  );

  // You can get module information by using get_module_info
  let module_info = process.get_module_info("client.dll").unwrap();
  println!("{}", module_info.module_name);

  // read fetches the value at where the address is pointing.
  // U have to specify the type of the value with turbofish
  println!(
    "{:x}",
    read::<i32>(process.process_handle, read::<u32>(process.process_handle, process.get_module_base("engine.dll").unwrap() + DW_CLIENT_STATE).unwrap() as usize + DW_CLIENT_STATE_STATE).unwrap()
  );

  loop {
    // write helps you tamper with the value.
    write::<u32>(
      process.process_handle,
      process.get_module_base("client.dll").unwrap() + DW_FORCE_ATTACK as usize,
      &mut 0x5,
    )
            .unwrap();

    // Exit this loop by pressing INSERT
    if toy_arms::detect_keypress(VirtualKeyCode::VK_INSERT) {
      break;
    }
  }
}

获取本地玩家生命值(外部)

以下是获取本地玩家生命值演示。快捷提示:您不想使用usize作为指针类型,因为外部程序本身就是64位,因此缓冲区大小将是8字节,而实际指针是4字节。使用u32或DWORD。

cargor --exampleex_get_localplayer_health --功能外部 --no-default-features

use toy_arms::external::{Module, Process, read};
use toy_arms::external::error::TAExternalError::ReadMemoryFailed;

const DW_LOCAL_PLAYER: u32 = 0xDB35DC;

fn main() {
  let csgo: Process;
  match Process::from_process_name("csgo.exe") {
    Ok(p) => csgo = p,
    Err(e) => {
      println!("{}", e);
      return;
    },
  }
  let client: Module;
  match csgo.get_module_info("client.dll") {
    Ok(m) => client = m,
    Err(e) => {
      println!("{}", e);
      return;
    },
  }

  println!("module_base: {:x}", client.module_base_address);
  println!("localplayer pointer: 0x{:x}", client.module_base_address + DW_LOCAL_PLAYER as usize);
  let localplayer = read::<u32>(csgo.process_handle, client.module_base_address + DW_LOCAL_PLAYER as usize);

  match localplayer {
    Ok(l) => {
      println!("localplayer address: 0x{:x}", l);
      // 0x100 is the offset of the health in player entity class.
      let health = read::<u16>(csgo.process_handle, l as usize + 0x100);
      match health {
        // This is what we wanted.
        Ok(h) => println!("localplayer's health: {}", h),
        Err(ReadMemoryFailed(e)) => println!("{}", e),
        Err(_) => println!("some error"),
      }
    },
    Err(e) => println!("error: {}", e),
  }
}

模式扫描(外部)

这是模式扫描示例,其中模式是针对csgo中的dwForceAttack。

cargor --exampleex_pattern_scanning --功能外部 --no-default-features

use toy_arms::{ VirtualKeyCode };
use toy_arms::external::Process;

const DW_FORCE_ATTACK_PATTERN: &str = "89 0D ? ? ? ? 8B 0D ? ? ? ? 8B F2 8B C1 83 CE 04";

fn main() {
  let mut once = false;

  // Getting process information
  let process = Process::from_process_name("csgo.exe");
  // You can get module information by using get_client
  let client = process.get_module_info("client.dll").unwrap();


  let address = client.find_pattern(DW_FORCE_ATTACK_PATTERN);
  match address {
    Some(i) => println!("found pattern at 0x{:x}", i),
    None => println!("NOTHING FOUND"),
  }

  let offset = client.pattern_scan(
    DW_FORCE_ATTACK_PATTERN,
    2,
    0,
  );
  match offset {
    Some(i) => println!("found offset at 0x{:x}", i),
    None => println!("NOTHING FOUND"),
  }

  loop {
    if !once {
      println!("Press INSERT to exit...");
      once = !once;
    }
    // Exit this loop by pressing INSERT
    if toy_arms::detect_keydown!(VirtualKeyCode::VK_INSERT) {
      break;
    }
  }
}

🗃️ 其他示例?

是的!查看示例目录,您将看到更多示例!

然而,您可能需要手动更新一些示例中的偏移量。

始终参考hazedumper以获取最新的CSGO偏移量。

在x86架构中构建示例

cargo build --example EXAMPLE_NAME --target i686-pc-windows-msvc

依赖关系

~1.1–1.7MB
~37K SLoC