我的外挂之路【二】读取内存

系列文章目录


我的外挂之路一按键精灵

我的外挂之路二读取内存

我的外挂之路三远程调用


前言

在按键精灵篇使用取色和模拟按钮,已经实现了对一个游戏自动吃血脚本,但是也留下了一些不足,本篇博客将从读取游戏内存的方式去实现对一个游戏的自动加血


正文开始

本篇记录了使用 Cheat Engine寻找到血量内存地址,使用C#语言循环读取血量内存,监测血量变化,然后模拟按键按键,来达到精准补血的操作

具体步骤为:

  • 找到血量值的地址(变化的地址)
  • 找到血量地址的基址(固定的地址+偏移值 = 变化的地址)
  • 外部程序读取该地址的值
  • 循环读取,监测血量变化
  • 根据条件模拟按键操作道具

1.Cheat Engine

Cheat Engine 也就是大名鼎鼎的CE修改器,功能非常强大,界面如下
在这里插入图片描述

1.1 读取游戏内存

打开CE载入游戏进程,搜索血量值1223,会出现一很多搜索结果,然后脱下自己的装备(脱下装备会导致血量值变换)发现自己血量变成942,然后在搜索942,还是有很多搜索结果,这时在穿上装备,如此往复,直到搜索结果变得很少,然后切换一下地图,可以然后观察一下CE那个和血量保持一致的就是要找的血量存储地址,但是这个地址还不能拿来直接用,因为这个地址是一个动态地址,关闭游戏重开一下就没有用了,因此还要去找到人物的基址
在这里插入图片描述
在这里插入图片描述

1.2 查找游戏基址

关于找基址有两种方法,一种是一层一层向上查,另一种是指针扫描,这里就用一层一层向上找的方法把,因为很简单
在这里插入图片描述

在这里插入图片描述
然后去ce搜 03501758
在这里插入图片描述
要勾上十六进制
在这里插入图片描述
绿色就要找的基址了, 可以看到血量的地址是由 [neuz.exe+4E99D8] + 820个偏移得到
这样基址就找到了

1.3 验证基址

可以在ce中手动添加一下基址,然后填入对应的偏移值,就知道是否正确了,如果是正确的即使退出游戏,重新运行也会是正确的数据,到这里CE部分就已经完成了
在这里插入图片描述

2.读取内存值

这里使用C#来读取游戏的数据,当然大多数外怪程序都是用易语言编写的,其实使用什么语言来编写应该都是可以的,重要的是知道流程和方法就行,想想刚才在CE手动添加基址的过程,其实就是一个程序读取另一个程序数据的过程,只需要把对应的操作还原成代码就可以了

2.1 根据游戏名称查找进程Id

使用 Process.GetProcessesByName() 去查询进程Id


// 进程名称
string processName = "Neuz";

// 查找进程,如果多开就会有多个
Process[] arrayProcess = Process.GetProcessesByName(processName);

// 得到进程Id
mProcessId = arrayProcess[0].Id;

2.2 读取某个进程中的数据

具体流程为:

先创建出一个内存空间

然后打开进程句柄

然后把该进程句柄下某个地址的数据,拷贝到刚刚创建出来的内存空间上

具体代码为:

// 创建一个内存空间
byte[] buffer = new byte[4];

// 获取刚刚创建的内存空间在内存中的地址
IntPtr byteAddress = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);

// 最高权限获取进程句柄
IntPtr hProcess = WinAPI.OpenProcess(0x1F0FFF, false, processId);

//将指定内存中的值读入内存空间
WinAPI.ReadProcessMemory(hProcess, (IntPtr)pAddreass, byteAddress, 4, IntPtr.Zero);

//关闭操作
WinAPI.CloseHandle(hProcess);

//从非托管内存中读取一个 32 位带符号整数。 还原成int数据
Marshal.ReadInt32(byteAddress);

因为这段代码会频繁用到,可以把它抽取成一个方法,方便调用

/// <summary>
/// 读取一个int数据
/// </summary>
/// <param name="pAddreass">要读取的地址</param>
/// <param name="processId">要读取的进程Id</param>
/// <returns></returns>
public static int ReadMemoryValue(int pAddreass, int processId)
{
    try
    {
        // 创建一个内存空间
        byte[] buffer = new byte[4];

        // 获取刚刚创建的内存空间在内存中的地址
        IntPtr byteAddress = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);

        // 最高权限获取进程句柄
        IntPtr hProcess = WinAPI.OpenProcess(0x1F0FFF, false, processId);

        //将指定内存中的值读入内存空间
        WinAPI.ReadProcessMemory(hProcess, (IntPtr)pAddreass, byteAddress, 4, IntPtr.Zero);

        //关闭操作
        WinAPI.CloseHandle(hProcess);

        //从非托管内存中读取一个 32 位带符号整数。 还原成int数据
        return Marshal.ReadInt32(byteAddress);
    }
    catch
    {
        return 0;
    }
}

2.3 读取游戏中的血量数据

这里根据 先读基址数据,先添加上血量的偏移就得到了血量的地址,在去读一下血量的地址,就得到了游戏中实时运行的血量了


// 因为这个游戏的入口地址是变化的,因此需要先去找到进程的入口
var baseaddress = mProcess.MainModule.BaseAddress.ToInt32();

// 得到基址的数据
address_user = WinAPI.ReadMemoryValue(baseaddress + offset_user, mProcessId);
  
// 得到血量数据
TotalHP = WinAPI.ReadMemoryValue(address_user + offset_hp, mProcessId);

2.4 读取游戏中的血量数据

搭一个界面,看看效果,这样就实现了读取血量数据了
在这里插入图片描述
只需要加一个定时器,循环读取,那么就能一直知道血量的变化

3.模拟键盘输入

使用user32中的向进程发送消息的方法来模拟 键盘按下

[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
public static extern int PostMessage(IntPtr hWnd, int Msg, System.Windows.Forms.Keys wParam, int lParam);

模拟按一下 F1

 // 按下f1
 WinAPI.PostMessage(formHandler.MainWindowHandle, WM_KEYDOWN, Keys.F1, 0);
 Thread.Sleep(60);
 WinAPI.PostMessage(formHandler.MainWindowHandle, WM_KEYUP, Keys.F1, 0);

当血量减少后,就按一下

4.完成效果

在这里插入图片描述

总结

以上就是本篇博客讲述的内容了,本文简单的讲解了使用ce查找游戏数据,找基址,使用C#读取游戏内存地址的过程,还是有点复杂的,但是完成了也很有成就感!

回顾本文中的一些的知识点

  • 根据进程名称获取进程Id

    Process[] arrayProcess = Process.GetProcessesByName(processName);;

  • 根据进程id获取进程句柄

    Process process= Process.GetProcessById(mProcessId);

  • 读取某个进程中某个地址的数据

    // 创建一个内存空间
    byte[] buffer = new byte[4];
    
    // 获取刚刚创建的内存空间在内存中的地址
    IntPtr byteAddress = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
    
    // 最高权限获取进程句柄
    IntPtr hProcess = WinAPI.OpenProcess(0x1F0FFF, false, processId);
    
    //将指定内存中的值读入内存空间
    WinAPI.ReadProcessMemory(hProcess, (IntPtr)pAddreass, byteAddress, 4, IntPtr.Zero);
    
    //关闭操作
    WinAPI.CloseHandle(hProcess);
    
    //从非托管内存中读取一个 32 位带符号整数。 还原成int数据
    Marshal.ReadInt32(byteAddress);
    
  • 向某个进程发送消息(模拟键盘)

     WinAPI.PostMessage(formHandler.MainWindowHandle, WM_KEYDOWN, Keys.F1, 0);
     Thread.Sleep(40);
     WinAPI.PostMessage(formHandler.MainWindowHandle, WM_KEYUP, Keys.F1, 0);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值