一秒扫雷
可以直接用CE进行雷总数修改,下面是通过C#直接修改雷总数内存地址
/// PROCESS_ALL_ACCESS -> (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
public const int PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED| (SYNCHRONIZE | 4095));
/// STANDARD_RIGHTS_REQUIRED -> (0x000F0000L)
public const int STANDARD_RIGHTS_REQUIRED = 983040;
/// SYNCHRONIZE -> (0x00100000L)
public const int SYNCHRONIZE = 1048576;
/// https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
/// Return Type: HANDLE->void*
///dwDesiredAccess: DWORD->unsigned int
///bInheritHandle: BOOL->int
///dwProcessId: DWORD->unsigned int
[System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern System.IntPtr OpenProcess(uint dwDesiredAccess, [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId);
/// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
/// Return Type: BOOL->int
///hProcess: HANDLE->void*
///lpBaseAddress: LPVOID->void*
///lpBuffer: LPCVOID->void*
///nSize: SIZE_T->ULONG_PTR->unsigned int
///lpNumberOfBytesWritten: SIZE_T*
[System.Runtime.InteropServices.DllImportAttribute("kernel32.dll", EntryPoint = "WriteProcessMemory")]
[return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool WriteProcessMemory(System.IntPtr hProcess, System.IntPtr lpBaseAddress, System.IntPtr lpBuffer, uint nSize, ref uint lpNumberOfBytesWritten);
private int GetProcessId(string name)
{
var process = Process.GetProcesses().ToList().FirstOrDefault(f => f.ProcessName == name);
return process.Id;
}
private void button1_Click(object sender, EventArgs e)
{
var pid = GetProcessId("MineSweeper");
var hProcess = NativeApiCall.OpenProcess(NativeApiCall.PROCESS_ALL_ACCESS, false, (uint)pid);
int address = 0x06241858;// 类总数内存地址
byte[] rBytes = new byte[] { 0x00, 0x00, 0x00, 0x00 };
uint lpNumberOfBytesWrite = 0;
var lpBuffer = Marshal.UnsafeAddrOfPinnedArrayElement(rBytes, 0);
NativeApiCall.WriteProcessMemory(hProcess, (IntPtr)address, lpBuffer, 4, ref lpNumberOfBytesWrite);
}
关于基址寻找
寻找雷总数内存中的地址
首先打开扫雷游戏,然后打开CE,并从CE中打开扫雷进程;在弹出的进程列表中有三个选项,应用程序、进程、窗口这三个任意选哪个都可以。
现在,回到扫雷游戏,我们发现扫雷游戏的界面上有计时器,雷总数还有一些可以点击的小格子等等;现在假设下,如果扫雷是咱们写代码开发的话,他界面上显示的这些东西,实际上在代码中是不是都有对应的一个地址和一个内存空间,而我们现在就是要找这个地址;当然我们没办直接知道这个地址在什么位置,所以只能根据他对应的一些特征把他找出来;比如根据他的值进行查找;在我的游戏界面中,雷的总数显示的是99
所以现在我们用CE对游戏进行扫描,把存有99的地址都找出来;
现在游戏中存储有99的地址都显示出来了,哇,这一堆堆的哪个才是呢?我们知道new出来的内存空间是不会变化的(除非销毁),但是存储在内存空间里的值是会根据程序运行和用户操作发生变化;现在我们用排除法,先让雷的总数发生一些变化,把总数修改成40个,而其他都不动。
现在我们再根据40这个值进行扫描,发现只剩下一个了;(如果你的界面中还是有很多个值或者一个都没有了,你还可以重复上面的操作,或者根据不同的条件再进行扫描)
那么现在扫出来的这个地址是不是我们想要的雷总数的地址呢?先把他复制到地址列表中,然后我们对他的值进行修改,看看会不会对游戏中的总数有影响
在CE中,把06990968地址对应的值改成1,然后回到游戏,我们发现雷的总数已经变成了1,说明我们找的地址是对的
寻找基址
然而我们上面找到的地址在关闭游戏后就会失效,那如何做到每次打开游戏如何直接找到这个地址呢?并且这个地址不一定每次都存雷的总数,因为每次游戏载入内存的启始地址不是固定的,既然这样,那每次还怎么找雷总数的地址呢?
还记得每次写程序的时候,我们的程序都有一个main函数么,这不就是你程序的起点,而代码中的变量,函数...是不是按你调用的顺序载入到内存中,所以回到游戏来说,游戏载入内存的时候也有一个起点,而存储雷总数的代码也是相对起点存储在某个位置,除非你把游戏的代码改了。
现在在看下我们手里有什么了,扫雷游戏在内存中的起始地址,还有雷总数的地址,雷总数的位置相对起始位置是固定的;那么我们每次打开游戏,雷总数的地址不就是起始地址进行相对距离的偏移了么。所以现在我们要把这个相对距离找出来。
还是利用我们之前找到的雷总数的地址,然后进行扫描
这里有很多参数,相关的解释可以去https://www.cheatengine.org/官网找找
点击ok后,会需要我们选择一个扫描结果保存目录,随便找个目录起个名字就可以
(由于扫描的过程中把游戏关闭了,所以我这里重新扫雷总数的地址,大家看的时候雷总数地址和之前会对不起来,06990968-069D2858)当CE扫完后,我这里有1000多个结果,那么哪个才是呢?
还是使用排除法,把雷总数修改后再进行重新扫描,这里我把雷又改成了99个
这次扫完后只剩了300多个,还是有点多,重复上面的动作,继续改雷个数继续扫
发现重复了好多次还是有一两百个,我们可以换成用地址扫
但是还是有很多结果,怎么办呢,其实到这里已经很接近答案了,我们可以试着随便双击几个结果,复制到地址列表中,尝试修改它的值,如果游戏中的地址也跟着发生变化,那么这个基址和偏移值基本就是我们要找的结果
现在,你可以试试重启扫雷,然后用CE再打开进程,看看这些地址是否还能对应到总数的