Money reveal + writing memory
This commit is contained in:
parent
d6719b0d54
commit
5f99e03b4c
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/target
|
||||
/src/dma/cs2dumper/offsets_mod.rs
|
||||
/src/dma/cs2dumper/client_mod.rs
|
||||
/src/dma/cs2dumper/engine2_mod.rs
|
||||
/src/dma/cs2dumper/engine2_mod.rs
|
||||
dump.sh
|
||||
|
@ -6,7 +6,7 @@ use memflow::plugins::Inventory;
|
||||
use crate::dma::Connector;
|
||||
const PORT_RANGE: std::ops::RangeInclusive<usize> = 8000..=65535;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[derive(Parser, Clone)]
|
||||
#[command(author, version = version(), about, long_about = None)]
|
||||
pub struct Cli {
|
||||
/// Specifies the connector type for DMA
|
||||
|
50
src/comms.rs
50
src/comms.rs
@ -51,6 +51,20 @@ pub enum EntityData {
|
||||
Bomb(BombData)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CheatOptions {
|
||||
#[serde(rename = "revealMoney")]
|
||||
pub reveal_money: bool,
|
||||
}
|
||||
|
||||
impl Default for CheatOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
reveal_money: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RadarData {
|
||||
freq: usize,
|
||||
@ -83,13 +97,30 @@ pub struct RadarData {
|
||||
#[serde(rename(serialize = "entityData"))]
|
||||
player_data: Vec<EntityData>,
|
||||
|
||||
//#[serde(rename(serialize = "localYaw"))]
|
||||
//local_yaw: f32,
|
||||
#[serde(rename = "options")]
|
||||
options: CheatOptions,
|
||||
|
||||
#[serde(skip)]
|
||||
pub money_reveal_enabled: bool,
|
||||
}
|
||||
|
||||
impl RadarData {
|
||||
pub fn new(ingame: bool, map_name: String, player_data: Vec<EntityData>, freq: usize, bomb_planted: bool, bomb_cannot_defuse: bool, bomb_defuse_timeleft: f32, bomb_exploded: bool, bomb_being_defused: bool, bomb_defuse_length: f32, bomb_defuse_end: f32) -> RadarData {
|
||||
RadarData { ingame, map_name, player_data, freq, bomb_planted, bomb_can_defuse: bomb_cannot_defuse, bomb_defuse_timeleft, bomb_exploded, bomb_being_defused, bomb_defuse_length, bomb_defuse_end }
|
||||
RadarData {
|
||||
ingame,
|
||||
map_name,
|
||||
player_data,
|
||||
freq,
|
||||
bomb_planted,
|
||||
bomb_can_defuse: bomb_cannot_defuse,
|
||||
bomb_defuse_timeleft,
|
||||
bomb_exploded,
|
||||
bomb_being_defused,
|
||||
bomb_defuse_length,
|
||||
bomb_defuse_end,
|
||||
options: CheatOptions::default(),
|
||||
money_reveal_enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns empty RadarData, it's also the same data that gets sent to clients when not ingame
|
||||
@ -105,9 +136,20 @@ impl RadarData {
|
||||
bomb_exploded: false,
|
||||
bomb_being_defused: false,
|
||||
bomb_defuse_length: 0.0,
|
||||
bomb_defuse_end: 0.0
|
||||
bomb_defuse_end: 0.0,
|
||||
options: CheatOptions::default(),
|
||||
money_reveal_enabled: false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reveal_money(&mut self, value: bool) {
|
||||
self.options.reveal_money = value;
|
||||
self.money_reveal_enabled = value;
|
||||
}
|
||||
|
||||
pub fn get_reveal_money(&self) -> bool {
|
||||
self.options.reveal_money
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for RadarData {}
|
||||
|
@ -4,17 +4,28 @@ use memflow::{mem::MemoryView, os::Process, types::Address};
|
||||
|
||||
use crate::{enums::PlayerType, comms::{EntityData, PlayerData, RadarData, ArcRwlockRadarData, BombData}};
|
||||
|
||||
use crate::money_reveal::MoneyReveal;
|
||||
|
||||
use self::{context::DmaCtx, threaddata::CsData};
|
||||
|
||||
mod context;
|
||||
mod threaddata;
|
||||
pub mod context;
|
||||
pub mod threaddata;
|
||||
mod cs2dumper;
|
||||
|
||||
pub use context::Connector;
|
||||
|
||||
pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_device: String, skip_version: bool) -> anyhow::Result<()> {
|
||||
let mut ctx = DmaCtx::setup(connector, pcileech_device, skip_version)?;
|
||||
let mut data = CsData { recheck_bomb_holder: true, ..Default::default() };
|
||||
let mut data = CsData {
|
||||
recheck_bomb_holder: true,
|
||||
money_reveal_enabled: false,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut money_reveal = MoneyReveal::new();
|
||||
if let Err(e) = money_reveal.init(&mut ctx.process, &ctx.client_module) {
|
||||
log::warn!("Failed to initialize money reveal: {}", e);
|
||||
}
|
||||
|
||||
// For read timing
|
||||
let mut last_bomb_dropped = false;
|
||||
@ -47,6 +58,17 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
|
||||
|
||||
data.update_common(&mut ctx);
|
||||
|
||||
{
|
||||
let radar = radar_data.read().await;
|
||||
if radar.money_reveal_enabled != data.money_reveal_enabled {
|
||||
data.money_reveal_enabled = radar.money_reveal_enabled;
|
||||
|
||||
if let Err(e) = money_reveal.toggle(&mut ctx.process) {
|
||||
log::warn!("Failed to toggle money reveal: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bomb update
|
||||
if (data.bomb_dropped && !last_bomb_dropped) || (data.bomb_planted && !last_bomb_planted) {
|
||||
data.update_bomb(&mut ctx);
|
||||
@ -247,9 +269,12 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
|
||||
data.bomb_defuse_length,
|
||||
bomb_defuse_end
|
||||
);
|
||||
|
||||
radar.money_reveal_enabled = data.money_reveal_enabled;
|
||||
} else {
|
||||
let mut radar = radar_data.write().await;
|
||||
*radar = RadarData::empty(freq);
|
||||
radar.money_reveal_enabled = data.money_reveal_enabled;
|
||||
}
|
||||
|
||||
last_tick_count = data.tick_count;
|
||||
@ -264,5 +289,10 @@ pub async fn run(radar_data: ArcRwlockRadarData, connector: Connector, pcileech_
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
}
|
||||
|
||||
let cleanup_result = money_reveal.ensure_disabled(&mut ctx.process);
|
||||
if let Err(e) = cleanup_result {
|
||||
log::warn!("Failed to cleanup money reveal: {}", e);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -38,6 +38,7 @@ pub struct CsData {
|
||||
pub bomb_defuse_length: f32,
|
||||
pub bomb_exploded: bool,
|
||||
pub bomb_defused: bool,
|
||||
pub money_reveal_enabled: bool,
|
||||
}
|
||||
|
||||
|
||||
|
15
src/main.rs
15
src/main.rs
@ -13,6 +13,9 @@ mod comms;
|
||||
mod dma;
|
||||
mod websocket;
|
||||
|
||||
mod pattern;
|
||||
mod money_reveal;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let cli: Cli = Cli::parse();
|
||||
@ -29,6 +32,7 @@ async fn main() -> anyhow::Result<()> {
|
||||
);
|
||||
|
||||
let radar_clone = radar_data.clone();
|
||||
|
||||
let dma_handle = tokio::spawn(async move {
|
||||
if let Err(err) = dma::run(radar_clone, cli.connector, cli.pcileech_device, cli.skip_version).await {
|
||||
log::error!("Error in dma thread: [{}]", err.to_string());
|
||||
@ -37,20 +41,23 @@ async fn main() -> anyhow::Result<()> {
|
||||
}
|
||||
});
|
||||
|
||||
let web_path = cli.web_path.clone();
|
||||
let port = cli.port;
|
||||
|
||||
let _websocket_handle = tokio::spawn(async move {
|
||||
if let Ok(my_local_ip) = local_ip_address::local_ip() {
|
||||
let address = format!("http://{}:{}", my_local_ip, cli.port);
|
||||
let address = format!("http://{}:{}", my_local_ip, port);
|
||||
println!("Launched webserver at {}", address);
|
||||
} else {
|
||||
let address = format!("http://0.0.0.0:{}", cli.port);
|
||||
let address = format!("http://0.0.0.0:{}", port);
|
||||
println!("launched webserver at {}", address);
|
||||
}
|
||||
|
||||
if let Err(err) = websocket::run(cli.web_path, cli.port, radar_data).await {
|
||||
if let Err(err) = websocket::run(web_path, port, radar_data).await {
|
||||
log::error!("Error in ws server: [{}]", err.to_string());
|
||||
}
|
||||
});
|
||||
|
||||
dma_handle.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
96
src/money_reveal.rs
Normal file
96
src/money_reveal.rs
Normal file
@ -0,0 +1,96 @@
|
||||
use memflow::{mem::MemoryView, types::Address, os::ModuleInfo};
|
||||
use crate::pattern::pattern_scan;
|
||||
|
||||
const BUF_SIZE: usize = 3;
|
||||
|
||||
pub struct MoneyReveal {
|
||||
pub is_enabled: bool,
|
||||
pub address: Option<Address>,
|
||||
original_bytes: Option<[u8; BUF_SIZE]>,
|
||||
}
|
||||
|
||||
impl MoneyReveal {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
is_enabled: false,
|
||||
address: None,
|
||||
original_bytes: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(&mut self, mem: &mut impl MemoryView, client_module: &ModuleInfo) -> anyhow::Result<()> {
|
||||
self.address = self.find_function(mem, client_module)?;
|
||||
log::info!("Money reveal function found at: {:?}", self.address);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn toggle(&mut self, mem: &mut impl MemoryView) -> anyhow::Result<bool> {
|
||||
if let Some(addr) = self.address {
|
||||
if self.is_enabled {
|
||||
if let Some(original) = self.original_bytes {
|
||||
self.restore(mem, addr, original)?;
|
||||
self.original_bytes = None;
|
||||
self.is_enabled = false;
|
||||
}
|
||||
} else {
|
||||
let original = self.patch(mem, addr)?;
|
||||
self.original_bytes = Some(original);
|
||||
self.is_enabled = true;
|
||||
}
|
||||
Ok(self.is_enabled)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Money reveal not initialized"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_disabled(&mut self, mem: &mut impl MemoryView) -> anyhow::Result<()> {
|
||||
if self.is_enabled {
|
||||
if let Some(addr) = self.address {
|
||||
if let Some(original) = self.original_bytes {
|
||||
self.restore(mem, addr, original)?;
|
||||
self.is_enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_function(&self, mem: &mut impl MemoryView, module: &ModuleInfo) -> anyhow::Result<Option<Address>> {
|
||||
let is_hltv = pattern_scan(
|
||||
mem,
|
||||
module,
|
||||
"48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 84 C0 75 0D"
|
||||
)?;
|
||||
|
||||
if is_hltv.is_none() {
|
||||
Ok(pattern_scan(
|
||||
mem,
|
||||
module,
|
||||
"B0 01 C3 28 48 8B 0D ?? ?? ?? ?? 48 8B 01 FF 90 ?? ?? ?? ?? 84 C0 75 0D"
|
||||
)?)
|
||||
} else {
|
||||
Ok(is_hltv)
|
||||
}
|
||||
}
|
||||
|
||||
fn patch(&self, mem: &mut impl MemoryView, location: Address) -> anyhow::Result<[u8; BUF_SIZE]> {
|
||||
let mut original_buf = [0u8; BUF_SIZE];
|
||||
mem.read_into(location, &mut original_buf)?;
|
||||
|
||||
let new_buf: [u8; BUF_SIZE] = [
|
||||
0xB0, 0x01, // MOV AL,1
|
||||
0xC3 // RET
|
||||
];
|
||||
|
||||
log::debug!("Patching memory for money reveal");
|
||||
mem.write(location, &new_buf)?;
|
||||
|
||||
Ok(original_buf)
|
||||
}
|
||||
|
||||
fn restore(&self, mem: &mut impl MemoryView, location: Address, original: [u8; BUF_SIZE]) -> anyhow::Result<()> {
|
||||
log::debug!("Restoring memory for money reveal");
|
||||
mem.write(location, &original)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
51
src/pattern.rs
Normal file
51
src/pattern.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use std::io::Read;
|
||||
use log::warn;
|
||||
use memflow::{os::ModuleInfo, mem::MemoryView, types::Address};
|
||||
|
||||
fn pattern_to_bytes(pattern: String) -> Vec<Option<u8>> {
|
||||
pattern.split(' ')
|
||||
.fold(Vec::new(), |mut accum, str| {
|
||||
if str == "??" {
|
||||
accum.push(None);
|
||||
} else {
|
||||
let byte = u8::from_str_radix(str, 16).unwrap();
|
||||
accum.push(Some(byte));
|
||||
}
|
||||
|
||||
accum
|
||||
})
|
||||
}
|
||||
|
||||
pub fn pattern_scan(mem: &mut impl MemoryView, module: &ModuleInfo, sig: &str) -> anyhow::Result<Option<Address>> {
|
||||
let pattern_bytes = pattern_to_bytes(sig.to_owned());
|
||||
let mut cursor = mem.cursor();
|
||||
|
||||
log::debug!("Searching \"{}\" for pattern: \"{}\"", module.name, sig);
|
||||
|
||||
for i in 0..module.size {
|
||||
let mut found = true;
|
||||
cursor.set_address(module.base + i);
|
||||
|
||||
let mut buf = vec![0u8; pattern_bytes.len()];
|
||||
|
||||
if let Err(_) = cursor.read(&mut buf) {
|
||||
warn!("Encountered read error while scanning for pattern");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (idx, byte) in buf.iter().enumerate() {
|
||||
if let Some(pat_byte) = pattern_bytes[idx] {
|
||||
if *byte != pat_byte {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
return Ok(Some(module.base + i));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
@ -25,29 +25,43 @@ async fn ws_handler(ws: WebSocketUpgrade, State(state): State<AppState>) -> Resp
|
||||
async fn handle_socket(mut socket: WebSocket, state: AppState) {
|
||||
while let Some(msg) = socket.recv().await {
|
||||
if let Ok(msg) = msg {
|
||||
if msg == Message::Text("requestInfo".to_string()) {
|
||||
let str = {
|
||||
let data = state.data_lock.read().await;
|
||||
if let Ok(text) = msg.to_text() {
|
||||
if text == "requestInfo" {
|
||||
let str = {
|
||||
let data = state.data_lock.read().await;
|
||||
|
||||
match serde_json::to_string(&*data) {
|
||||
Ok(json) => json,
|
||||
Err(e) => {
|
||||
log::error!("Could not serialize data into json: {}", e.to_string());
|
||||
log::error!("Sending \"error\" instead");
|
||||
"error".to_string()
|
||||
},
|
||||
match serde_json::to_string(&*data) {
|
||||
Ok(json) => json,
|
||||
Err(e) => {
|
||||
log::error!("Could not serialize data into json: {}", e.to_string());
|
||||
log::error!("Sending \"error\" instead");
|
||||
"error".to_string()
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if socket.send(Message::Text(str)).await.is_err() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
} else if text == "toggleMoneyReveal" {
|
||||
let new_value = {
|
||||
let mut data = state.data_lock.write().await;
|
||||
data.money_reveal_enabled = !data.money_reveal_enabled;
|
||||
data.money_reveal_enabled
|
||||
};
|
||||
|
||||
//println!("{str}");
|
||||
let response = serde_json::json!({
|
||||
"action": "toggleMoneyReveal",
|
||||
"status": "success",
|
||||
"enabled": new_value
|
||||
});
|
||||
|
||||
if socket.send(Message::Text(str)).await.is_err() {
|
||||
// client disconnected
|
||||
return;
|
||||
if socket.send(Message::Text(response.to_string())).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// client disconnected
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,10 @@
|
||||
<input type="checkbox" onclick="toggleGuns()" id="gunsCheck" name="guns"/>
|
||||
<label for="gunsCheck">Weapons</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" onclick="toggleMoneyReveal()" id="moneyReveal" name="money"/>
|
||||
<label for="moneyReveal">Money Reveal (DANGEROUS!)</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="canvas"></canvas>
|
||||
|
@ -652,28 +652,36 @@ function connect() {
|
||||
if (event.data == "error") {
|
||||
console.log("[radarflow] Server had an unknown error")
|
||||
} else {
|
||||
let data = JSON.parse(event.data);
|
||||
radarData = data;
|
||||
freq = data.freq;
|
||||
try {
|
||||
let data = JSON.parse(event.data);
|
||||
radarData = data;
|
||||
freq = data.freq;
|
||||
|
||||
if (data.ingame == false) {
|
||||
mapName = null
|
||||
entityData = null
|
||||
|
||||
if (loaded)
|
||||
unloadMap()
|
||||
} else {
|
||||
if (!loaded) {
|
||||
mapName = data.mapName
|
||||
entityData = data.entityData
|
||||
loadMap(mapName)
|
||||
} else {
|
||||
entityData = data.entityData
|
||||
if (data.money_reveal_enabled !== undefined) {
|
||||
document.getElementById("moneyReveal").checked = data.money_reveal_enabled;
|
||||
}
|
||||
}
|
||||
|
||||
update = true
|
||||
requestAnimationFrame(render);
|
||||
if (data.ingame == false) {
|
||||
mapName = null
|
||||
entityData = null
|
||||
|
||||
if (loaded)
|
||||
unloadMap()
|
||||
} else {
|
||||
if (!loaded) {
|
||||
mapName = data.mapName
|
||||
entityData = data.entityData
|
||||
loadMap(mapName)
|
||||
} else {
|
||||
entityData = data.entityData
|
||||
}
|
||||
}
|
||||
|
||||
update = true
|
||||
requestAnimationFrame(render);
|
||||
} catch (e) {
|
||||
console.error("[radarflow] Error parsing server message:", e, event.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -710,6 +718,7 @@ addEventListener("DOMContentLoaded", (e) => {
|
||||
document.getElementById("statsCheck").checked = true;
|
||||
document.getElementById("namesCheck").checked = true;
|
||||
document.getElementById("gunsCheck").checked = true;
|
||||
document.getElementById("moneyReveal").checked = false;
|
||||
|
||||
canvas = document.getElementById('canvas');
|
||||
canvas.width = 1024;
|
||||
@ -735,4 +744,10 @@ function toggleNames() {
|
||||
|
||||
function toggleGuns() {
|
||||
drawGuns = !drawGuns
|
||||
}
|
||||
|
||||
function toggleMoneyReveal() {
|
||||
if (websocket && websocket.readyState === WebSocket.OPEN) {
|
||||
websocket.send("toggleMoneyReveal");
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user