Rust自制内存作弊器

1,562 阅读3分钟

前言

Cheat Engine是一款专注于游戏的作弊器。它可以用来扫描游戏中的内存,并允许修改它们。这个工具也许没有听说过,但是十几年前大名鼎鼎的金山游侠想必每个人都用过。

偶然机会下看到了一个用Rust写的项目,喜欢于他的语法简练,聚集很多编程语言优点于一身。于是借着空闲时间去学习Rust,想到很多年没用的WinAPI正好借着这个机会复习下,便有了以下的内容。

搭建Rust环境

这块教程网上一大堆,这里就不详细说了 www.runoob.com/rust/rust-s…

枚举进程

这里使用的是CE自带的demo 在这里插入图片描述

枚举进程,让用户输入进程PID来选择修改的进程

这里由于是在windows平台需要用到winapi

添加Cargo.toml依赖

[dependencies]
winapi = { version = "0.3.9", features = ["psapi","processthreadsapi","handleapi","memoryapi"] }
//这里将所有所需的API添加上去了

使用EnumProcesses获取所有进程

pub fn enum_proc() -> io::Result<Vec<u32>> {
    let mut size = 0; //返回的结构体大小
    let mut pids = Vec::<DWORD>::with_capacity(2048); //分配pid数组大小
    if unsafe {
        /**
             BOOL EnumProcesses(
            [out] DWORD   *lpidProcess,
            [in]  DWORD   cb,
            [out] LPDWORD lpcbNeeded
            );
         */
        winapi::um::psapi::EnumProcesses(
            pids.as_mut_ptr(),
            (pids.capacity() * std::mem::size_of::<DWORD>()) as u32,
            &mut size,
        )
    } == FALSE
    {
        return Err(io::Error::last_os_error());
    }
    
    //计算获取到的进程数,使用set_len设置数组个数
    let count = size as usize / std::mem::size_of::<DWORD>();
    unsafe { pids.set_len(count) }
    Ok(pids)
}

打开进程

    pub fn open(pid: u32) -> io::Result<Self> {
        NonNull::new(unsafe {
            /*
            HANDLE OpenProcess(
                [in] DWORD dwDesiredAccess,
                [in] BOOL  bInheritHandle,
                [in] DWORD dwProcessId
            );
             */
            winapi::um::processthreadsapi::OpenProcess(
                winnt::PROCESS_QUERY_INFORMATION
                    | winnt::PROCESS_VM_READ
                    | winnt::PROCESS_VM_WRITE
                    | winnt::PROCESS_VM_OPERATION,
                FALSE,
                pid,
            )
        })
        .map(|handle| Self { pid, handle })
        .ok_or_else(io::Error::last_os_error)
    }

获取进程名

拿到进程pid后但是并不清楚进程名是什么,以下代码获取进程名 在这里插入图片描述

pub fn name(&self) -> io::Result<String> {
        let mut module = MaybeUninit::<HMODULE>::uninit();
        let mut size = 0;
        if unsafe {
            /*
            BOOL EnumProcessModules(
              [in]  HANDLE  hProcess,
              [out] HMODULE *lphModule,
              [in]  DWORD   cb,
              [out] LPDWORD lpcbNeeded
            );
             */
            winapi::um::psapi::EnumProcessModules(
                self.handle.as_ptr(),
                module.as_mut_ptr(),
                size_of::<HMODULE>() as u32,
                &mut size,
            )
        } == FALSE
        {
            return Err(io::Error::last_os_error());
        }
        
        
        let module = unsafe { module.assume_init() };

        //进程名最长1024
        let mut buffer = Vec::<u8>::with_capacity(1024);
        let length = unsafe {
            /*
                DWORD GetModuleBaseNameA(
                [in]           HANDLE  hProcess,
                [in, optional] HMODULE hModule,
                [out]          LPSTR   lpBaseName,
                [in]           DWORD   nSize
                );
             */
            winapi::um::psapi::GetModuleBaseNameA(
                self.handle.as_ptr(),
                module,
                buffer.as_mut_ptr().cast(),
                buffer.capacity() as u32,
            )
        };
        if length == 0 {
            return Err(io::Error::last_os_error());
        }

        //中文显示未修复
        unsafe { buffer.set_len(length as usize) };
        return match String::from_utf8(buffer) {
            Ok(s) => Ok(s),
            Err(e) => Ok("".to_string()),
        };
}

获取内存区域

为了提高首次搜索的速度,先获取内存区域

    pub fn memory_regions(&self) -> Vec<MEMORY_BASIC_INFORMATION> {
        let mut base = 0;

        let mut regions = Vec::new();
        let mut info = MaybeUninit::uninit();

        loop {
            /*
             SIZE_T VirtualQueryEx(
                [in]           HANDLE                    hProcess,
                [in, optional] LPCVOID                   lpAddress,
                [out]          PMEMORY_BASIC_INFORMATION lpBuffer,
                [in]           SIZE_T                    dwLength
             );
             */
            let written = unsafe {
                winapi::um::memoryapi::VirtualQueryEx(
                    self.handle.as_ptr(),
                    base as *const _,
                    info.as_mut_ptr(),
                    size_of::<MEMORY_BASIC_INFORMATION>(),
                )
            };

            //结束
            if written == 0 {
                break;
            }
            let info = unsafe { info.assume_init() };

            //计算下一块区域base
            base = info.BaseAddress as usize + info.RegionSize;
            
            //保存
            regions.push(info);
        }

        let mask = winnt::PAGE_EXECUTE_READWRITE
        | winnt::PAGE_EXECUTE_WRITECOPY
        | winnt::PAGE_READWRITE
        | winnt::PAGE_WRITECOPY;

        //过滤掉系统模块,无效区域
        return regions.into_iter().filter(|x|!(x.BaseAddress as u32 > 0x70000000 
        && (x.BaseAddress as u32) < 0x80000000)).filter(|p| (p.Protect & mask) != 0).collect();
    }

首次搜索

我们搜索这个100

得到262个地址

在这里插入图片描述

代码如下:

    //获取所有内存区域
    let regions:Vec<MEMORY_BASIC_INFORMATION> = process.memory_regions();
    
    println!("Scanning {} memory regions", regions.len());
    println!("Which exact value to scan for?");
    
    //得到输入值转bytes
    let mut input = String::new();
    stdin().read_line(&mut input).unwrap();
    let target: u32 = input.trim().parse::<u32>().unwrap();
    let target = target.to_ne_bytes();
    
    //存储位置数组    
    let mut locations = Vec::new();

    
    for region in regions {
        
        match process.read_memory(region.BaseAddress as _, region.RegionSize){
            Ok(mem)=>{
                mem.windows(target.len()).enumerate().for_each(|(offset, window)| {
                    //是否为输入的值
                    if window == target {
                        locations.push(region.BaseAddress as usize + offset);
                    }
                })
            },
            Err(e)=>continue
        }
    }

再次搜索,直到找到结果

点击[hit me]结果变成97了,我们再去访问刚刚搜索的地址看哪个不是97就将他删除掉 在这里插入图片描述

    //只剩一个结果跳出循环
    while locations.len() != 1 {
        println!("Next Scan value:");
        
        //输入的值
        let mut input = String::new();
        stdin().read_line(&mut input).unwrap();
        let target: u32 = input.trim().parse::<u32>().unwrap();
        let target = target.to_ne_bytes();

        //使用retain删除不满足要求的结果
        locations.retain(|addr| match process.read_memory(*addr, target.len()) {
            Ok(memory) => memory == target,
            Err(_) => false,
        });
    }

修改内存数据

经过多次查找只剩下一个地址,我们可以修改成任意值达到作弊的效果(如定时器一直修改锁血)

在这里插入图片描述

修改成比较大的数再点击Hit me,Health就发生了变化 在这里插入图片描述

pub fn write_memory(&self, address: usize, value: &[u8]) {
        let mut write = 0;
        let mut old_protect: u32 = 0;
        if unsafe {
            //使用VirtualProtectEx解除内存保护
            /*
            BOOL VirtualProtectEx(
                [in]  HANDLE hProcess,
                [in]  LPVOID lpAddress,
                [in]  SIZE_T dwSize,
                [in]  DWORD  flNewProtect,
                [out] PDWORD lpflOldProtect
                );
            */
            winapi::um::memoryapi::VirtualProtectEx(
                self.handle.as_ptr(),
                address as LPVOID,
                value.len(),
                PAGE_EXECUTE_READWRITE,
                &mut old_protect,
            )
        } == FALSE
        {
            return;
        }
        if unsafe {
            //WriteProcessMemory写入特定的数据
            winapi::um::memoryapi::WriteProcessMemory(
                self.handle.as_ptr(),
                address as LPVOID,
                value.as_ptr().cast(),
                value.len(),
                &mut write,
            )
        } == FALSE
        {
            return;
        }
    }

代码下载github.com/Mrack/rust_… 关注我的技术公众号 不定期分析各种技术文章 在这里插入图片描述