Open Einfachirgendwa1 opened 1 week ago
Oh, nvm, I'm stupid, this just only builds on windows: From https://stackoverflow.com/questions/77218919/the-compiler-cant-find-um-in-winapi:
The modules of winapi are only available if cfg(windows) is present due to its [conditional compilation](https://github.com/retep998/winapi-rs/blob/0.3.6/src/lib.rs#L7). If you target windows but write the source code on a different platform, make sure to setup cross compilation as noted by iceburg in the comment section.
yeah i couldnt find out how to do process memory reading cross platform, so i picked a windows-only lib for simplicity. iirc i even modified the lib locally (i seriously had no idea what i was doing), so its very well possible that this repo actually never built on windows either lol
if you're interested in this project, i could port it to windows-rs so it at least compiles on windows, but idk how to make it work on other platforms
yeah, making it cross platform would probably be a huge pain as the game only includes a native windows build, so you would have to find a way to access memory managed by proton. But tbh I have no idea why you can just access other programs memory regions without a segfault in general, I have never done something like this. I guess if you do find a way, you could just make a bunch of #[cfg(windows)] and #[cfg(not(windows))]? its also not like i actually use linux to play this game, i just like linux for programming.
btw, compiling on windows also doesnt work, because of
error[E0554]: `#![feature]` may not be used on the stable release channel
--> /home/[name]/.cargo/registry/src/index.crates.io-6f17d22bba15001f/win_mem-0.2.2/src/lib.rs:1:1
|
1 | #![feature(min_const_generics)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the attribute
|
= help: the feature `min_const_generics` has been stable since `1.51.0` and no longer requires an attribute to enable
You could probably just cargo vendor and then just remove that attribute, but that seems like a very temporary solution
I have no idea why you can just access other programs memory regions without a segfault in general
A direct memory access in the form of a deref would indeed segfault. what im doing ultimately boils down to this function from the Windows API, so I'd need something similar for each platform. Given that even CheatEngine works under wine, its certainly possible, I'm just lacking the necessary know-how
cargo vendor
nah, if i'm gonna fix it, then i'd do it properly and get rid of the win-mem
dependency entirely. And looking at the code now I might as well just rewrite the whole thing. I don't even know what modifications I made to win-mem
back then, and figuring that out would likely be just as much effort
Given that even CheatEngine works under wine, its certainly possible
Really? That's cool, I didn't know that.
Do you think something like this might work or are there problems with vram?
fn read_process_memory(pid: u64, address: u64, size: usize) -> Vec<u8> {
let mut mem_file = File::open(&format!("/proc/{pid}/mem")).unwrap();
mem_file.seek(SeekFrom::Start(address)).unwrap();
let mut buf = vec![0; size];
mem_file.read(&mut buf).unwrap();
buf
}
I might as well just rewrite the whole thing
Good luck!
no idea, i'm not that familiar with other platforms. but thats not even the issue. the hard part is finding the concrete addresses, since the pointer is relative to UnityPlayer.dll
This should find the dll:
use std::{fs::File, io::Read};
use sysinfo::System;
fn main() {
for (pid, proc) in System::new_all().processes() {
if proc.name() == "EXO ONE.exe" {
let mut maps_file = File::open(&format!("/proc/{pid}/maps")).unwrap();
let mut maps = String::new();
maps_file.read_to_string(&mut maps).unwrap();
let unity_player = maps
.split('\n')
.filter(|line| !line.is_empty())
.map(str::split_whitespace)
.find_map(|mut line| {
let address = line.next().unwrap().split_once('-').unwrap().0;
let path: String = line.skip(4).collect();
if path.ends_with("/UnityPlayer.dll") {
Some(address);
} else {
None
}
})
.expect("UnityPlayer.dll not found.");
let unity_player = u64::from_str_radix(unity_player, 16).unwrap() as *const u8;
println!("{unity_player:p}");
break;
}
}
}
~/dev/rust/linux_read_process_memory $ sudo target/debug/linux_read_process_memory
0x6ffffc150000
but tbh i dont really get your code to find the value. can you explain to me what these offsets are?
regardless, i just finished it and it actually works!
use std::{
fs::File,
io::{Read, Seek, SeekFrom},
mem::forget,
};
use nalgebra::Vector3;
use sysinfo::System;
struct Game {
mem_file: File,
}
impl Game {
fn read_mem<T>(&mut self, address: usize) -> &'static T {
let mut buf = vec![0; size_of::<T>()];
self.mem_file.seek(SeekFrom::Start(address as u64)).unwrap();
self.mem_file.read(&mut buf).unwrap();
let static_ref = unsafe { &*(buf.as_ptr() as *const T) };
forget(buf);
static_ref
}
fn resolve_multilevel_pointer(&mut self, base: usize, offsets: &[usize]) -> usize {
let mut address = base;
for offset in offsets {
address = self.read_mem::<usize>(address) + offset;
}
address
}
}
fn main() {
for (pid, proc) in System::new_all().processes() {
if proc.name() == "EXO ONE.exe" {
let mut game = Game {
mem_file: File::open(format!("/proc/{pid}/mem")).unwrap(),
};
let mut maps_file = File::open(format!("/proc/{pid}/maps")).unwrap();
let mut maps = String::new();
maps_file.read_to_string(&mut maps).unwrap();
let unity_player = maps
.split('\n')
.filter(|line| !line.is_empty())
.map(str::split_whitespace)
.find_map(|mut line| {
let address = line.next().unwrap().split_once('-').unwrap().0;
let path: String = line.skip(4).collect();
if path.ends_with("/UnityPlayer.dll") {
Some(address)
} else {
None
}
})
.expect("UnityPlayer.dll not found.");
let unity_player = usize::from_str_radix(unity_player, 16).unwrap();
let address = game.resolve_multilevel_pointer(
unity_player + 0x01A03D00, //0x0156C900
&[0x58, 0x158, 0x28, 0xA0], //0x3F8, 0x1A8, 0x28, 0xA0
);
let velocity = game.read_mem::<Vector3<f32>>(address + 0x30);
println!("{velocity}");
return;
}
}
panic!("Exo One is not running!");
}
It panics when the game doesn't run, show [0, 0, 0] when in the main menu and some values when in a level. I'm sure you can make this way better than I did it as I'm still learning rust, but at least it works.
More specifically,
win_mem
fails to build. The last change towin_mem
was approx. 4 years ago and the github repo listed on crates.io is gone (https://crates.io/crates/win_mem links to https://github.com/nevalackin/win_mem, which doesn't exist) and I couldn't find anything about it online. I don't know if and how much you still care about this project, but I'm definitly going to investigate this myself and maybe make a PR if I find a fix. Or maybe I'm just dumb and made a stupid mistake.