MemoryUtils.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. using Mir2Assistant.Common.Models;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Text;
  5. namespace Mir2Assistant.Common.Utils;
  6. public class MemoryUtils
  7. {
  8. #region API
  9. //从指定内存中读取字节集数据
  10. [DllImport("kernel32.dll", EntryPoint = "ReadProcessMemory")]
  11. private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, IntPtr lpNumberOfBytesRead);
  12. //从指定内存中写入字节集数据
  13. [DllImport("kernel32.dll", EntryPoint = "WriteProcessMemory")]
  14. private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, IntPtr lpNumberOfBytesWritten);
  15. //打开一个已存在的进程对象,并返回进程的句柄
  16. [DllImport("kernel32.dll", EntryPoint = "OpenProcess")]
  17. private static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
  18. //关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。
  19. [DllImport("kernel32.dll")]
  20. private static extern void CloseHandle(IntPtr hObject);
  21. #endregion
  22. // byte (字节 1字节)
  23. // int16 (short 2字节),
  24. // int32 (int 4字节),
  25. // int64 (long 8字节)
  26. // float (单浮点 Single 4字节
  27. // double (双浮点 8字节)
  28. // string (字符串)
  29. // byte[] (字节数组)
  30. private readonly MirGameInstanceModel _gameInstance;
  31. private nint _handle;
  32. public MemoryUtils(MirGameInstanceModel gameInstance)
  33. {
  34. _gameInstance = gameInstance;
  35. _handle = OpenProcess(0x1F0FFF, false, gameInstance.MirPid);
  36. }
  37. ~MemoryUtils()
  38. {
  39. CloseHandle(_handle);
  40. }
  41. #region Read
  42. public byte[] ReadToBytes(IntPtr address, int size)
  43. {
  44. lock (_gameInstance)
  45. {
  46. byte[] buffer = new byte[size];
  47. ReadProcessMemory(_handle, address, buffer, size, IntPtr.Zero);
  48. return buffer;
  49. }
  50. }
  51. public T ReadObject<T>(IntPtr address) where T : struct
  52. {
  53. var buffer = ReadToBytes(address, Marshal.SizeOf(typeof(T)));
  54. var bufferAddress = Marshal.AllocHGlobal(buffer.Length);
  55. Marshal.Copy(buffer, 0, bufferAddress, buffer.Length);
  56. var structure = (T)Marshal.PtrToStructure(bufferAddress, typeof(T))!;
  57. Marshal.FreeHGlobal(bufferAddress);
  58. return structure;
  59. }
  60. public char ReadToChar(IntPtr address)
  61. {
  62. byte[] buffer = ReadToBytes(address, sizeof(char));
  63. return BitConverter.ToChar(buffer, 0);
  64. }
  65. public short ReadToShort(IntPtr address)
  66. {
  67. byte[] buffer = ReadToBytes(address, sizeof(short));
  68. return BitConverter.ToInt16(buffer, 0);
  69. }
  70. public int ReadToInt(IntPtr address)
  71. {
  72. byte[] buffer = ReadToBytes(address, sizeof(int));
  73. return BitConverter.ToInt32(buffer, 0);
  74. }
  75. public long ReadToLong(IntPtr address)
  76. {
  77. byte[] buffer = ReadToBytes(address, sizeof(long));
  78. return BitConverter.ToInt64(buffer, 0);
  79. }
  80. public float ReadToFloat(IntPtr address)
  81. {
  82. byte[] buffer = ReadToBytes(address, sizeof(float));
  83. return BitConverter.ToSingle(buffer, 0);
  84. }
  85. public double ReadToDouble(IntPtr address)
  86. {
  87. byte[] buffer = ReadToBytes(address, sizeof(double));
  88. return BitConverter.ToDouble(buffer, 0);
  89. }
  90. public string ReadToString(IntPtr address, int stringSize = 20)
  91. {
  92. byte[] buffer = ReadToBytes(address, stringSize);
  93. string asciiString = Encoding.GetEncoding("gb2312").GetString(buffer).Split('\0')[0].TrimEnd(); // 获取ASCII字符串并去除结尾的空字符
  94. return asciiString;
  95. }
  96. #endregion
  97. #region Write
  98. public bool WriteByteArray(IntPtr address, byte[] byteData)
  99. {
  100. return WriteProcessMemory(_handle, address, byteData, byteData.Length, IntPtr.Zero);
  101. }
  102. public bool WriteChar(IntPtr address, char value)
  103. {
  104. return WriteByteArray(address, BitConverter.GetBytes(value));
  105. }
  106. public bool WriteShort(IntPtr address, short value)
  107. {
  108. return WriteByteArray(address, BitConverter.GetBytes(value));
  109. }
  110. public bool WriteInt(IntPtr address, int value)
  111. {
  112. return WriteByteArray(address, BitConverter.GetBytes(value));
  113. }
  114. public bool WriteLong(IntPtr address, long value)
  115. {
  116. return WriteByteArray(address, BitConverter.GetBytes(value));
  117. }
  118. public bool WriteFloat(IntPtr address, float value)
  119. {
  120. return WriteByteArray(address, BitConverter.GetBytes(value));
  121. }
  122. public bool WriteDouble(IntPtr address, double value)
  123. {
  124. return WriteByteArray(address, BitConverter.GetBytes(value));
  125. }
  126. public bool WriteString(IntPtr address, string value)
  127. {
  128. return WriteByteArray(address, Encoding.Default.GetBytes(value));
  129. }
  130. #endregion
  131. #region Utils
  132. /// <summary>
  133. /// 根据 进程名 获取 PID
  134. /// </summary>
  135. public static int GetPidByProcessName(string processName)
  136. {
  137. return GetProcessByProcessName(processName)?.Id ?? 0;
  138. }
  139. /// <summary>
  140. /// 通过 进程名(不加exe后缀) 获取 进程对象
  141. /// </summary>
  142. public static Process? GetProcessByProcessName(string processName)
  143. {
  144. var processArr = Process.GetProcessesByName(processName);
  145. if (processArr.Length > 0)
  146. {
  147. return processArr[0];
  148. }
  149. return null;
  150. }
  151. /// <summary>
  152. /// 根据窗体标题查找窗口句柄(支持模糊匹配)
  153. /// </summary>
  154. public static IntPtr FindWindow(string title)
  155. {
  156. var processArray = Process.GetProcesses();
  157. foreach (var item in processArray)
  158. {
  159. if (item.MainWindowTitle.IndexOf(title) != -1)
  160. {
  161. return item.MainWindowHandle;
  162. }
  163. }
  164. return IntPtr.Zero;
  165. }
  166. /// <summary>
  167. /// 获取进程中模块的基址 (例如: Game.dll / War3.exe)
  168. /// </summary>
  169. public IntPtr GetModuleBaseAddress(string moduleName)
  170. {
  171. var process = Process.GetProcessById(_gameInstance.MirPid);
  172. IntPtr baseAddress = default;
  173. for (int i = 0; i < process.Modules.Count; i++)
  174. {
  175. var item = process.Modules[i];
  176. if (item.ModuleName == moduleName)
  177. {
  178. baseAddress = item.BaseAddress;
  179. break;
  180. }
  181. }
  182. return baseAddress;
  183. }
  184. /// <summary>
  185. /// 计算地址偏移
  186. /// </summary>
  187. public IntPtr GetMemoryAddress(string moduleName, params nint[] offsetArray)
  188. {
  189. if ((offsetArray?.Length ?? 0) == 0)
  190. {
  191. throw new Exception("至少需要一个偏移");
  192. }
  193. IntPtr addr = IntPtr.Zero;
  194. // 模块的地址
  195. var addrVal = GetModuleBaseAddress(moduleName);
  196. // 计算剩下的多级偏移
  197. for (int i = 0; i < offsetArray!.Length; i++)
  198. {
  199. addr = IntPtr.Add(addrVal, (int)offsetArray[i]);
  200. addrVal = (IntPtr)ReadToInt(addr);
  201. }
  202. // 最终的地址 (只是一个地址, 需要手动去读里面的值)
  203. return addr;
  204. }
  205. public IntPtr GetMemoryAddress(params nint[] offsetArray)
  206. {
  207. return GetMemoryAddress("mir1", offsetArray);
  208. }
  209. /// <summary>
  210. /// 通过进程名 获取窗口句柄
  211. /// </summary>
  212. /// <param name="processName"></param>
  213. /// <returns></returns>
  214. /// <exception cref="Exception"></exception>
  215. public static IntPtr GetWindowHwndByProcessName(string processName)
  216. {
  217. var hwnd = MemoryUtils.GetProcessByProcessName(processName)?.MainWindowHandle ?? IntPtr.Zero;
  218. if (hwnd == IntPtr.Zero)
  219. {
  220. throw new Exception($"没有找到[{processName}]进程..");
  221. }
  222. return hwnd;
  223. }
  224. #endregion
  225. #region 进制转换
  226. /// <summary>
  227. /// 16进制(0x41)转为10进制(65)
  228. /// </summary>
  229. public static int ConvertFrom16To10(string value)
  230. {
  231. return Convert.ToInt32(value, 16);
  232. }
  233. /// <summary>
  234. /// 16进制(0x41)转为10进制(65)
  235. /// </summary>
  236. public static int ConvertFrom16To10(IntPtr value)
  237. {
  238. return Convert.ToInt32(Convert.ToString(value));
  239. }
  240. /// <summary>
  241. /// 10进制(65)转为16进制(0x41)
  242. /// </summary>
  243. public static string ConvertFrom10To16(IntPtr value)
  244. {
  245. //x4 0补齐4位
  246. //x8 0补齐8位
  247. return value.ToString("x");
  248. }
  249. /// <summary>
  250. /// 16/10进制转为2进制
  251. /// </summary>
  252. public static string ConvertFrom16Or10To2(IntPtr value)
  253. {
  254. return Convert.ToString(value);
  255. }
  256. /// <summary>
  257. /// 2进制(1010)到10进制(2)
  258. /// </summary>
  259. public static int ConvertFrom2To10(string value)
  260. {
  261. return Convert.ToInt32(value, 2);
  262. }
  263. #endregion
  264. }