memory_tool_4_cheat/
lib.rs

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; // Assuming standard page size
11
12#[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        //get the pid
134        
135        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(); // 获取带 \0 的字节切片
189
190        // 确保目标缓冲区足够大(包括 \0)
191        if bytes.len() > args.name.len() {
192            panic!("Source string too large for destination buffer");
193        }
194
195        // 逐个字节拷贝(转换为 c_char)
196        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(); // 获取带 \0 的字节切片
290
291        // 确保目标缓冲区足够大(包括 \0)
292        if bytes.len() > args.name.len() {
293            panic!("Source string too large for destination buffer");
294        }
295
296        // 逐个字节拷贝(转换为 c_char)
297        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    // 构造命令字符串
329    let cmd = format!("pidof {}", name);
330
331    // 执行命令并获取输出
332    let output = Command::new("sh").arg("-c").arg(cmd).output();
333    match output {
334        Ok(output) => {
335            // 检查命令是否成功执行
336            if !output.status.success() {
337                Err(format!("pidof {name} command exec failed."))
338            } else {
339                // 将命令输出转换为字符串
340                let stdout = String::from_utf8(output.stdout);
341
342                // 尝试从输出中解析 PID
343                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}