1mod arch;
2mod perf;
3use libc::{c_void, size_t};
4use perf::PerfMap;
5use std::os::raw::c_char;
6use std::ptr::copy_nonoverlapping;
7
8pub const GET_MODULE_BASE_MAX_MODULE_NAME_LEN: usize = 100 + 1;
9pub const OP_MEMORY_MAX_OFFSETS_COUNT: usize = 10;
10pub const PAGE_SIZE: usize = 4096; #[repr(C)]
13#[derive(Debug, Copy, Clone)]
14pub struct OpMemoryArgs {
15 pub addr: u64,
16 pub buffer: *mut c_void,
17 pub size: size_t,
18 pub offsets_count: size_t,
19 pub offsets: [u64; OP_MEMORY_MAX_OFFSETS_COUNT],
20}
21impl OpMemoryArgs {
22 fn set_offsets(&mut self, off: &[u64]) {
23 unsafe {
24 copy_nonoverlapping(off.as_ptr(), self.offsets.as_mut_ptr(), off.len());
25 }
26 }
27}
28pub const MIN_MMAP_SIZE: usize = std::mem::size_of::<OpMemoryArgs>();
29
30#[repr(C)]
31#[derive(Debug, Copy, Clone)]
32pub struct GetModuleArgs {
33 pub name: [c_char; GET_MODULE_BASE_MAX_MODULE_NAME_LEN],
34 pub result: u64,
35}
36#[repr(C)]
37#[derive(Debug, Copy, Clone)]
38pub struct GetTidArgs {
39 pub name: [c_char; GET_MODULE_BASE_MAX_MODULE_NAME_LEN],
40 pub result: i32,
41}
42
43#[repr(C)]
44pub union OpArgs {
45 pub op_mem_args: OpMemoryArgs,
46 pub get_module_args: GetModuleArgs,
47 pub get_tid_args:GetTidArgs,
48}
49
50#[repr(u32)]
51#[derive(Debug, Copy, Clone, PartialEq, Eq)]
52pub enum CheatOperations {
53 OpReadMemory = 0x901,
54 OpWriteMemory = 0x902,
55 OpGetModuleBase = 0x903,
56 OpStartGuard = 0x904,
57 OpStopGuard = 0x905,
58 OpGetTid = 0x906,
59}
60
61#[repr(C)]
62pub struct OpT {
63 pub op_type: CheatOperations,
64 pub args: OpArgs,
65}
66impl OpT {
67 fn get_op_mem_args(&mut self) -> &mut OpMemoryArgs {
68 unsafe { &mut self.args.op_mem_args }
69 }
70 fn get_module_args(&mut self) -> &mut GetModuleArgs {
71 unsafe { &mut self.args.get_module_args }
72 }
73 fn get_tid_args(&mut self)->&mut GetTidArgs{
74 unsafe { &mut self.args.get_tid_args }
75 }
76}
77
78pub const OP_ARGS_MIN_BUFFER_PAGE: usize = {
79 let size = std::mem::size_of::<OpT>();
80 if size % PAGE_SIZE == 0 {
81 size / PAGE_SIZE
82 } else {
83 size / PAGE_SIZE + 1
84 }
85};
86
87pub const OP_ARGS_MIN_BUFFER_LEN: usize = OP_ARGS_MIN_BUFFER_PAGE * PAGE_SIZE;
88
89const CMD_GET_CHEAT_HANDLE: u64 = 14;
90
91pub struct GameMem {
92 fd: i32,
93 mmap_priv: *mut c_void,
94 additional_offset: u64,
95 is_additional_offset_negative: bool,
96 need_additional_offset: bool,
97}
98
99impl GameMem {
100 pub fn init_via_pid(pid:i32)->Result<Self, &'static str>{
101 Self::new(pid)
102 }
103 pub fn probe<F: FnMut(perf::SampleData)->Option<u64>>(&self,tid:i32,addr:u64,condition:F)->Option<u64>{
104 self.start_guard();
105 let perf = PerfMap::new(perf_event_open_sys::bindings::HW_BREAKPOINT_X, addr, 8, tid, 4, false).unwrap();
106 let res = perf.events(condition);
107 perf.close();
108 self.stop_guard();
109 res
110 }
111 pub fn init_via_process_name(process_name: &str)->Result<Self, &'static str>{
112 let pid = loop {
113 let pid: i32=
114 match get_name_pid(process_name) {
115 Ok(pid) => {
116 pid
117 }
118 Err(msg) => {
119 println!("{}", msg);
120 std::thread::sleep(Duration::from_secs(3));
121 0
122 }
123 };
124 if pid == 0 {
125 continue;
126 } else {
127 break pid;
128 }
129 };
130 Self::new(pid)
131 }
132 fn new(pid:i32) -> Result<Self, &'static str> {
133 let mut fd = -1;
136 unsafe {
137 libc::prctl(0xdeadbeefu32 as i32, CMD_GET_CHEAT_HANDLE, &pid, 0,&mut fd);
138 }
139 if fd == -1 {
140 return Err("open driver failed!");
141 }
142 let mmaped = unsafe {
143 libc::mmap(
144 std::ptr::null_mut(),
145 OP_ARGS_MIN_BUFFER_LEN,
146 libc::PROT_READ | libc::PROT_WRITE,
147 libc::MAP_SHARED,
148 fd,
149 0,
150 )
151 };
152 if mmaped.is_null() {
153 return Err("mmap failed!");
154 }
155 Ok(Self {
156 fd,
157 mmap_priv: mmaped,
158 additional_offset: 0,
159 is_additional_offset_negative: false,
160 need_additional_offset: false,
161 })
162 }
163 fn get_op_t(&self) -> *mut OpT {
164 self.mmap_priv as *mut OpT
165 }
166 fn blocking_request(&self) -> bool {
167 unsafe { libc::fdatasync(self.fd) == 0 }
168 }
169 fn start_guard(&self)->bool{
170 let op_t = self.get_op_t();
171 let op_t = unsafe{
172 op_t.as_mut().unwrap()
173 };
174 op_t.op_type = CheatOperations::OpStartGuard;
175 self.blocking_request()
176 }
177 pub fn get_tid_by_name(&self,name:&str)->Option<i32>{
178 let op_t = self.get_op_t();
179 let op_t = unsafe{
180 op_t.as_mut().unwrap()
181 };
182 op_t.op_type = CheatOperations::OpGetTid;
183
184 let args = op_t.get_tid_args();
185
186 let c_name = std::ffi::CString::new(name).unwrap();
187
188 let bytes = c_name.as_bytes_with_nul(); if bytes.len() > args.name.len() {
192 panic!("Source string too large for destination buffer");
193 }
194
195 for (i, &byte) in bytes.iter().enumerate() {
197 args.name[i] = byte as c_char;
198 }
199
200 if self.blocking_request() {
201 Some(args.result)
202 } else {
203 None
204 }
205 }
206 fn stop_guard(&self)->bool{
207 let op_t = self.get_op_t();
208 let op_t = unsafe{
209 op_t.as_mut().unwrap()
210 };
211 op_t.op_type = CheatOperations::OpStopGuard;
212 self.blocking_request()
213 }
214 pub fn read_memory_with_offsets<T: Default>(
215 &self,
216 addr: u64,
217 buffer: *mut T,
218 offsets: &[u64],
219 ) -> bool {
220 let op_t = self.get_op_t();
221 let op_t = unsafe{
222 op_t.as_mut().unwrap()
223 };
224 op_t.op_type = CheatOperations::OpReadMemory;
225 let args = op_t.get_op_mem_args();
226 args.addr = addr;
227 args.buffer = buffer as _;
228 args.offsets_count = offsets.len();
229 args.size = size_of::<T>();
230 args.set_offsets(offsets);
231
232 if self.need_additional_offset {
233 if self.is_additional_offset_negative {
234 args.offsets[args.offsets_count - 1] -= self.additional_offset;
235 } else {
236 args.offsets[args.offsets_count - 1] += self.additional_offset;
237 }
238 }
239 if self.blocking_request() {
240 return true;
241 }
242 false
243 }
244 pub fn read_memory_with_length_and_offsets(
245 &self,
246 addr: u64,
247 buffer: *mut libc::c_void,
248 length: usize,
249 offsets: &[u64],
250 ) -> bool {
251 let op_t = self.get_op_t();
252 let op_t = unsafe{
253 op_t.as_mut().unwrap()
254 };
255 op_t.op_type = CheatOperations::OpReadMemory;
256
257 let args = op_t.get_op_mem_args();
258
259 args.addr = addr;
260 args.buffer = buffer as _;
261 args.offsets_count = offsets.len();
262 args.size = length;
263 args.set_offsets(offsets);
264
265 if self.need_additional_offset {
266 if self.is_additional_offset_negative {
267 args.offsets[args.offsets_count - 1] -= self.additional_offset;
268 } else {
269 args.offsets[args.offsets_count - 1] += self.additional_offset;
270 }
271 }
272 if self.blocking_request() {
273 return true;
274 }
275 false
276 }
277
278 pub fn get_module_base(&self, name: &str) -> Option<u64> {
279 let op_t = self.get_op_t();
280 let op_t = unsafe{
281 op_t.as_mut().unwrap()
282 };
283 op_t.op_type = CheatOperations::OpGetModuleBase;
284
285 let args = op_t.get_module_args();
286
287 let c_name = std::ffi::CString::new(name).unwrap();
288
289 let bytes = c_name.as_bytes_with_nul(); if bytes.len() > args.name.len() {
293 panic!("Source string too large for destination buffer");
294 }
295
296 for (i, &byte) in bytes.iter().enumerate() {
298 args.name[i] = byte as c_char;
299 }
300
301 if self.blocking_request() {
302 Some(args.result)
303 } else {
304 None
305 }
306 }
307
308 pub fn read_with_offsets<T: Default>(&mut self, addr: u64, offsets: &[u64]) -> T {
309 let mut res = T::default();
310 if self.read_memory_with_offsets(addr, &mut res, offsets) {
311 return res;
312 }
313 Default::default()
314 }
315 pub fn set_additional_offset(&mut self, offset: u64, negative: bool) {
316 self.additional_offset = offset;
317 self.is_additional_offset_negative = negative;
318 self.need_additional_offset = true;
319 }
320 pub fn un_set_additional_offset(&mut self) {
321 self.need_additional_offset = false;
322 }
323}
324#[allow(unused_imports)]
325use std::process::Command;
326use std::time::Duration;
327fn get_name_pid(name: &str) -> Result<i32, String> {
328 let cmd = format!("pidof {}", name);
330
331 let output = Command::new("sh").arg("-c").arg(cmd).output();
333 match output {
334 Ok(output) => {
335 if !output.status.success() {
337 Err(format!("pidof {name} command exec failed."))
338 } else {
339 let stdout = String::from_utf8(output.stdout);
341
342 let pid = stdout.unwrap_or_default().trim().parse::<i32>().ok();
344 if let Some(pid) = pid {
345 Ok(pid)
346 } else {
347 Err("cannot find process,waiting for opening game".to_string())
348 }
349 }
350 }
351 _ => Err(format!("pidof {name} command exec failed.")),
352 }
353}