using RainOpsMini.Helpers; using System.Diagnostics; using System.Runtime.InteropServices; public class MemoryExtraction { /// /// 阵营1基址 (由TicketQueryPlugin配置管理) /// public static List team1offsets = new List { 0x158, 0x408, 0x8, 0x2C0, 0x2B0 }; /// /// 阵营2基址 (由TicketQueryPlugin配置管理) /// public static List team2offsets = new List { 0x158, 0x408, 0x10, 0x2C0, 0x2B0 }; /// /// 基础偏移量 (由TicketQueryPlugin配置管理) /// public static int baseoffset = 0x0BAF53B8; /// /// 主程序运行目录 (由TicketQueryPlugin配置管理) /// public static string path = ""; /// /// 更新偏移量配置 /// public static void UpdateOffsets(int baseOff, List t1, List t2) { baseoffset = baseOff; team1offsets = t1; team2offsets = t2; } // 定义 Windows API 函数 [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll")] private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out uint lpNumberOfBytesRead); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); // 打开进程并获取进程句柄 [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId); // 进程访问权限标志 [Flags] private enum ProcessAccessFlags : uint { All = 0x1F0FFF, QueryInformation = 0x0400, VirtualMemoryRead = 0x0010, } /// /// 修改票数 /// /// /// 值为0代表查询 /// public static int TickChange(int TeamID, int Tick = 0) { try { //进程ID int PID = GetSquadPID(path); //获取PID失败了 if (PID <= 0) { RainOpsLog.Log("【无法读取票数】找不到:" + path + "(绝对路径)文件对应的PID"); return 0; } int teamtick = ChangeTeamTick(PID, TeamID, Tick); return teamtick; } catch (Exception e) { return 0; } } /// /// 读取游戏指定路径的进程ID /// /// private static int GetSquadPID(string path) { string processName = Path.GetFileName(path); if (string.IsNullOrEmpty(processName)) return -1; try { Process[] processes = Process.GetProcesses(); foreach (var process in processes) { try { // 获取进程的路径 if (process.MainModule != null && string.Equals(process.MainModule.FileName, path, StringComparison.OrdinalIgnoreCase)) { return process.Id; } } catch { // 忽略无法访问的进程 } } } catch (Exception ex) { Console.WriteLine($"发生错误: {ex.Message}"); } return -1; } /// /// 修改票数 /// /// 进程ID /// 队伍ID /// /// private static int ChangeTeamTick(int PID, int teamID, int Tick) { try { //进程ID var targetProcessId = PID; // 获取目标进程的模块信息 var targetProcess = Process.GetProcessById(PID); if (targetProcess == null) { Console.WriteLine("未找到目标进程"); return 0; } // 遍历进程模块来获取特定模块的基址 foreach (ProcessModule module in targetProcess.Modules) { //主程序地址 IntPtr hProcess = MemoryHelper.OpenProcess(0x1F0FFF, false, targetProcess.Id); IntPtr baseAddress = module.BaseAddress; //Console.WriteLine($"PID:" + PID.ToString("X")); //Console.WriteLine($"基址:" + baseAddress.ToString("X")); // 读取该地址的值 if (hProcess == IntPtr.Zero) { Console.WriteLine("无法打开进程!"); return 0; } if (module.ModuleName != null && module.ModuleName.StartsWith("SquadGameServer")) { // 计算最终地址 + 基础偏移 IntPtr finalAddress = baseAddress + baseoffset; //Console.WriteLine($" 计算最终地址 + 基础偏移:" + finalAddress.ToString("X")); var value = ReadMemoryValue(hProcess, finalAddress); // 将字符串解析为整数 long baseValue = Convert.ToInt64(value, 16); long fullAddress = baseValue; // 使用示例 List offsets = new List(); if (teamID == 1) { offsets = team1offsets; } else if (teamID == 2) { offsets = team2offsets; } // 转换为 IntPtr IntPtr addressPtr = new IntPtr(fullAddress); List readVal = new List(); for (int i = 0; i < offsets.Count; i++) { var temp_addressPtr = new IntPtr(fullAddress); addressPtr = new IntPtr(fullAddress); addressPtr = new IntPtr(addressPtr.ToInt64() + offsets[i]); var readMemoryValue = ReadMemoryValue(hProcess, addressPtr); if (!string.IsNullOrEmpty(readMemoryValue)) { readMemoryValue = readMemoryValue.ToString().TrimStart('0'); } //Console.WriteLine("【" + (i + 1) + "】信息:" + temp_addressPtr.ToString("X") + "+" + offsets[i].ToString("X") + "\t" + readMemoryValue); //偏移指针地址 fullAddress = Convert.ToInt64(readMemoryValue, 16); } //当前票数 int now_tick = Convert.ToInt32(fullAddress); if (Tick != 0) { WriteMemoryValue(hProcess, addressPtr, Tick); } return now_tick; } } } catch (Exception e) { RainOpsLog.Log("阵营(" + teamID + ")票数查询失败:" + e.Message); return 0; } return 0; } /// /// 读取内存地址值 /// /// /// /// /// public static string ReadMemoryValue(IntPtr hProcess, IntPtr finalAddress, int length = 8) // 默认读取8字节 { ulong value = 0; // 使用无符号长整型 byte[] buffer = new byte[length]; // 读取8字节(ulong) if (MemoryHelper.ReadProcessMemory(hProcess, finalAddress, buffer, buffer.Length, out int bytesRead)) { // 使用 BitConverter.ToUInt64 来确保读取的是无符号长整型 value = BitConverter.ToUInt64(buffer, 0); } else { // 读取失败 Console.WriteLine("读取失败!"); return "0"; } // 输出十六进制格式,确保与 CE 一致 return value.ToString("X16"); // 保证 16 位宽度,适应64位数据 } /// /// 根据内存地址写入数据 /// /// /// /// /// /// /// [DllImport("kernel32.dll")] private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out uint lpNumberOfBytesWritten); /// /// 写入内存值 /// /// 进程句柄 /// 内存地址 /// 要写入的值 /// 写入的字节长度 /// 是否成功 private static bool WriteMemoryValue(IntPtr hProcess, IntPtr finalAddress, int value, int length = 4) { // 将整数值转换为字节数组 byte[] buffer = BitConverter.GetBytes(value); // 确保字节数组长度和指定长度一致 if (buffer.Length != length) { Array.Resize(ref buffer, length); } uint bytesWritten; // 使用 WriteProcessMemory 函数写入内存 var res = WriteProcessMemory(hProcess, finalAddress, buffer, (uint)buffer.Length, out bytesWritten); return res; } } public class MemoryHelper { [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead); [DllImport("kernel32.dll", SetLastError = true)] public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesWritten); [DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr hObject); }