PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/NovoAnimus/MemScan.cs

https://gitlab.com/wh1t3rabbit/NovoAnimus
C# | 491 lines | 438 code | 50 blank | 3 comment | 53 complexity | a5f36c5fd097be2ca12c475687d26940 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. namespace NovoAnimus
  10. {
  11. public class MemScan
  12. {
  13. #region Memory Imports
  14. #region structs
  15. [Flags]
  16. public enum ACCESS : uint
  17. {
  18. All = 0x001F0FFF,
  19. Terminate = 0x00000001,
  20. CreateThread = 0x00000002,
  21. VMOperation = 0x00000008,
  22. VMRead = 0x00000010,
  23. VMWrite = 0x00000020,
  24. DuplicateHandle = 0x00000040,
  25. CreateProcess = 0x000000080,
  26. SetQuota = 0x00000100,
  27. SetInformation = 0x00000200,
  28. QueryInformation = 0x00000400,
  29. QueryLimitedInformation = 0x00001000,
  30. Synchronize = 0x00100000
  31. }
  32. [Flags]
  33. public enum PROTECT : uint
  34. {
  35. EXECUTE = 0x00000010,
  36. EXECUTE_READ = 0x00000020,
  37. EXECUTE_READWRITE = 0x00000040,
  38. EXECUTE_WRITECOPY = 0x00000080,
  39. NOACCESS = 0x00000001,
  40. READONLY = 0x00000002,
  41. READWRITE = 0x00000004,
  42. WRITECOPY = 0x00000008,
  43. GUARD = 0x00000100,
  44. NOCACHE = 0x00000200,
  45. WRITECOMBINE = 0x00000400
  46. }
  47. public struct MEMORY_BASIC_INFORMATION
  48. {
  49. public IntPtr BaseAddress;
  50. public IntPtr AllocationBase;
  51. public uint AllocationProtect;
  52. public uint __alignment1;
  53. public IntPtr RegionSize;
  54. public uint State;
  55. public PROTECT Protect;
  56. public uint lType;
  57. public uint __alignment2;
  58. }
  59. public struct SYSTEM_INFO
  60. {
  61. public ushort processorArchitecture;
  62. ushort reserved;
  63. public uint pageSize;
  64. public IntPtr minimumApplicationAddress;
  65. public IntPtr maximumApplicationAddress;
  66. public IntPtr activeProcessorMask;
  67. public uint numberOfProcessors;
  68. public uint processorType;
  69. public uint allocationGranularity;
  70. public ushort processorLevel;
  71. public ushort processorRevision;
  72. }
  73. #endregion structs
  74. #region consts
  75. const uint szMemBasicInfo = 48; //28; //sizeof(MEMORY_BASIC_INFORMATION);
  76. const int PAGE_READWRITE = 0x0010;
  77. const int MEM_COMMIT = 0x00001000;
  78. #endregion consts
  79. [DllImport("kernel32.dll")]
  80. public static extern IntPtr OpenProcess(ACCESS dwDesiredAccess, bool bInheritHandle, int dwProcessId);
  81. [DllImport("kernel32.dll", SetLastError = true)]
  82. [System.Runtime.ConstrainedExecution.ReliabilityContract(System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, System.Runtime.ConstrainedExecution.Cer.Success)]
  83. [System.Security.SuppressUnmanagedCodeSecurity]
  84. [return: MarshalAs(UnmanagedType.Bool)]
  85. public static extern bool CloseHandle(IntPtr hObject);
  86. [DllImport("kernel32.dll")]
  87. public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead);
  88. [DllImport("kernel32.dll")]
  89. static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
  90. [DllImport("kernel32.dll", SetLastError = true)]
  91. static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);
  92. [DllImport("kernel32.dll", SetLastError = true)]
  93. static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritter);
  94. #endregion Memory Imports
  95. #region ToByte convertors
  96. public static byte[] ToBytes(string str,bool bigendian=true)
  97. {
  98. byte[] r = Encoding.Unicode.GetBytes(str);
  99. if (!bigendian)
  100. {
  101. Array.Reverse(r);
  102. }
  103. return r;
  104. }
  105. public static byte[] ToBytes(Int16 i, bool bigendian = true)
  106. {
  107. byte[] r = BitConverter.GetBytes(i);
  108. if (BitConverter.IsLittleEndian)
  109. {
  110. if (bigendian)
  111. {
  112. Array.Reverse(r);
  113. }
  114. } else
  115. {
  116. if (!bigendian)
  117. {
  118. Array.Reverse(r);
  119. }
  120. }
  121. return r;
  122. }
  123. public static byte[] ToBytes(UInt16 i, bool bigendian = true)
  124. {
  125. byte[] r = BitConverter.GetBytes(i);
  126. if (BitConverter.IsLittleEndian)
  127. {
  128. if (bigendian)
  129. {
  130. Array.Reverse(r);
  131. }
  132. }
  133. else
  134. {
  135. if (!bigendian)
  136. {
  137. Array.Reverse(r);
  138. }
  139. }
  140. return r;
  141. }
  142. public static byte[] ToBytes(Int32 i, bool bigendian = true)
  143. {
  144. byte[] r = BitConverter.GetBytes(i);
  145. if (BitConverter.IsLittleEndian)
  146. {
  147. if (bigendian)
  148. {
  149. Array.Reverse(r);
  150. }
  151. }
  152. else
  153. {
  154. if (!bigendian)
  155. {
  156. Array.Reverse(r);
  157. }
  158. }
  159. return r;
  160. }
  161. public static byte[] ToBytes(UInt32 i, bool bigendian = true)
  162. {
  163. byte[] r = BitConverter.GetBytes(i);
  164. if (BitConverter.IsLittleEndian)
  165. {
  166. if (bigendian)
  167. {
  168. Array.Reverse(r);
  169. }
  170. }
  171. else
  172. {
  173. if (!bigendian)
  174. {
  175. Array.Reverse(r);
  176. }
  177. }
  178. return r;
  179. }
  180. public static byte[] ToBytes(Int64 i, bool bigendian = true)
  181. {
  182. byte[] r = BitConverter.GetBytes(i);
  183. if (BitConverter.IsLittleEndian)
  184. {
  185. if (bigendian)
  186. {
  187. Array.Reverse(r);
  188. }
  189. }
  190. else
  191. {
  192. if (!bigendian)
  193. {
  194. Array.Reverse(r);
  195. }
  196. }
  197. return r;
  198. }
  199. public static byte[] ToBytes(UInt64 i, bool bigendian = true)
  200. {
  201. byte[] r = BitConverter.GetBytes(i);
  202. if (BitConverter.IsLittleEndian)
  203. {
  204. if (bigendian)
  205. {
  206. Array.Reverse(r);
  207. }
  208. }
  209. else
  210. {
  211. if (!bigendian)
  212. {
  213. Array.Reverse(r);
  214. }
  215. }
  216. return r;
  217. }
  218. #endregion ToByte convertors
  219. //Wrapper for when we don't already have a handle, will open a handle before scanning and close it afterwards
  220. public static List<IntPtr> ScanProcessForBytes(Process target, byte[] findThis, ScanOptions options, ThreadSignal signal, out bool cancelled)
  221. {
  222. IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
  223. List<IntPtr> found = ScanProcessForBytes(handle, findThis, options, signal, out cancelled);
  224. CloseHandle(handle);
  225. return found;
  226. }
  227. public static List<IntPtr> ScanProcessForBytes(IntPtr handle, byte[] findThis, ScanOptions options, ThreadSignal signal, out bool cancelled)
  228. {
  229. bool scanRW = options.ScanForPage_RW;
  230. bool scanERW = options.ScanForPage_ERW;
  231. bool scanAlign = options.ScanForAligned;
  232. uint alignTo = options.AlignOnValue;
  233. if (alignTo==0 || alignTo==1)
  234. {
  235. scanAlign = false;
  236. }
  237. bool limitRegion = options.LimitScanRegion;
  238. IntPtr oMinAddress = options.MinAddress;
  239. IntPtr oMaxAddress = options.MaxAddress;
  240. long o_min_addr_l = (long)oMinAddress;
  241. long o_max_addr_l = (long)oMaxAddress;
  242. List<IntPtr> found = new List<IntPtr>();
  243. SYSTEM_INFO sys_info = new SYSTEM_INFO();
  244. GetSystemInfo(out sys_info);
  245. IntPtr min_addr = sys_info.minimumApplicationAddress;
  246. IntPtr max_addr = sys_info.maximumApplicationAddress;
  247. long min_addr_l = (long)min_addr;
  248. long max_addr_l = (long)max_addr;
  249. if (limitRegion)
  250. {
  251. if (o_min_addr_l > min_addr_l) {
  252. min_addr = oMinAddress;
  253. min_addr_l = o_min_addr_l;
  254. }
  255. if (o_max_addr_l < max_addr_l)
  256. {
  257. max_addr = oMaxAddress;
  258. max_addr_l = o_max_addr_l;
  259. }
  260. }
  261. MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();
  262. int bytesRead = 0;
  263. while ( (!signal.cancel) && (min_addr_l < max_addr_l) )
  264. {
  265. int bytesReturned = VirtualQueryEx(handle, min_addr, out mem_basic_info, szMemBasicInfo);
  266. if (bytesReturned == 0)
  267. {
  268. int err = Marshal.GetLastWin32Error();
  269. if (err == 0)
  270. {
  271. System.Windows.Forms.MessageBox.Show("GetLastError is zero, everything should be fine"); //SO WTF ARE WE DOING HERE THEN?
  272. }
  273. else
  274. {
  275. System.Windows.Forms.MessageBox.Show("GetLastError was non-zero after VirtualQueryEx, error was " + err);
  276. }
  277. }
  278. IntPtr PageStartPtr = mem_basic_info.BaseAddress;
  279. IntPtr PageSizePtr = mem_basic_info.RegionSize;
  280. long PageStart = PageStartPtr.ToInt64();
  281. long PageSize = PageSizePtr.ToInt64();
  282. if (ShouldScanThisPage(options,mem_basic_info))
  283. {
  284. byte[] buffer = new byte[PageSize];
  285. ReadProcessMemory(handle, PageStartPtr, buffer, PageSizePtr, ref bytesRead);
  286. for (long i = 0; (i < PageSize) && (PageStart+i < max_addr_l); i++)
  287. {
  288. if (scanAlign && (i % alignTo != 0))
  289. {
  290. continue;
  291. }
  292. if (signal.cancel)
  293. {
  294. break;
  295. }
  296. if (CompareByteArrays(findThis, buffer, i))
  297. {
  298. IntPtr memLoc = new IntPtr(PageStart + i);
  299. found.Add(memLoc);
  300. }
  301. }
  302. }
  303. min_addr_l += PageSize;
  304. min_addr = new IntPtr(min_addr_l);
  305. }
  306. cancelled = signal.cancel;
  307. return found;
  308. }
  309. //Wrapper for when we don't already have a handle, will open a handle before scanning and close it afterwards
  310. public static List<IntPtr> ScanProcessForBytesUnchanged(Process target, byte[] scanForThis, List<IntPtr> lastScan1, Action<int> progressout, ScanOptions options, ThreadSignal signal, out bool cancelled)
  311. {
  312. IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
  313. List<IntPtr> found = ScanProcessForBytesUnchanged(handle, scanForThis, lastScan1, progressout, options, signal, out cancelled);
  314. CloseHandle(handle);
  315. return found;
  316. }
  317. public static List<IntPtr> ScanProcessForBytesUnchanged(IntPtr handle, byte[] scanForThis, List<IntPtr> lastScan1, Action<int> progressout, ScanOptions options, ThreadSignal signal, out bool cancelled)
  318. {
  319. List<IntPtr> found = new List<IntPtr>();
  320. SYSTEM_INFO sys_info = new SYSTEM_INFO();
  321. GetSystemInfo(out sys_info);
  322. IntPtr min_addr = sys_info.minimumApplicationAddress;
  323. IntPtr max_addr = sys_info.maximumApplicationAddress;
  324. long min_addr_l = (long)min_addr;
  325. long max_addr_l = (long)max_addr;
  326. MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();
  327. int bytesRead = 0;
  328. byte[] buffer = new byte[scanForThis.Length];
  329. IntPtr buffSize = new IntPtr(scanForThis.Length);
  330. int c = 0;
  331. foreach (IntPtr memLocation in lastScan1) {
  332. if (signal.cancel)
  333. {
  334. cancelled = signal.cancel;
  335. return found;
  336. }
  337. c++;
  338. progressout?.Invoke(c); //if progressout is not null, call it
  339. int bytesReturned = VirtualQueryEx(handle, memLocation, out mem_basic_info, szMemBasicInfo);
  340. if (bytesReturned == 0)
  341. {
  342. int err = Marshal.GetLastWin32Error();
  343. System.Windows.Forms.MessageBox.Show("GetLastError was non-zero after VirtualQueryEx, error was " + err);
  344. cancelled = signal.cancel;
  345. return found;
  346. }
  347. if (ShouldScanThisPage(options,mem_basic_info))
  348. {
  349. ReadProcessMemory(handle, memLocation, buffer, buffSize, ref bytesRead);
  350. if (CompareByteArrays(scanForThis, buffer, 0))
  351. {
  352. found.Add(memLocation);
  353. }
  354. }
  355. }
  356. cancelled = signal.cancel;
  357. return found;
  358. }
  359. //Wrapper for when we don't have a handle, will open a handle before writing and close it afterwards
  360. public static void OverwriteLocation(Process target, IntPtr location, byte[] data, out bool success, out IntPtr bytesWritten)
  361. {
  362. IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
  363. success = WriteProcessMemory(handle, location, data, data.Length, out bytesWritten);
  364. CloseHandle(handle);
  365. }
  366. public static void OverwriteLocation(IntPtr handle, IntPtr location, byte[] data, out bool success, out IntPtr bytesWritten)
  367. {
  368. success = WriteProcessMemory(handle, location, data, data.Length, out bytesWritten);
  369. }
  370. #region Helpers
  371. public static bool CompareByteArrays(byte[] findThis, byte[] searchThis, long startFrom)
  372. {
  373. long l1 = findThis.Length;
  374. long l2 = searchThis.Length;
  375. for (long i = 0; i < l1; i++)
  376. {
  377. if (startFrom + i >= l2) { return false; } //went past the end of our search space, return false]
  378. if (findThis[i] != searchThis[startFrom + i]) { return false; } //find[i] does not equal search[start+i], return false
  379. }
  380. return true; //if we reach here we must match
  381. }
  382. public static string PrintByteArray(byte[] bytes)
  383. {
  384. var sb = new StringBuilder("new byte[] { ");
  385. foreach (var b in bytes)
  386. {
  387. sb.Append(b + ", ");
  388. }
  389. sb.Append("}");
  390. return sb.ToString();
  391. }
  392. public static string PrintByteArrayHex(byte[] bytes)
  393. {
  394. return BitConverter.ToString(bytes);
  395. }
  396. private static bool ShouldScanThisPage(ScanOptions options, MEMORY_BASIC_INFORMATION mem_basic_info)
  397. {
  398. if (!(mem_basic_info.State == MEM_COMMIT))
  399. {
  400. return false;
  401. }
  402. if (options.ScanForPage_RW && mem_basic_info.Protect.HasFlag(PROTECT.READWRITE))
  403. {
  404. return true;
  405. }
  406. if (options.ScanForPage_ERW && mem_basic_info.Protect.HasFlag(PROTECT.EXECUTE_READWRITE))
  407. {
  408. return true;
  409. }
  410. return false;
  411. }
  412. #endregion Helpers
  413. #region ScanOptions struct
  414. public struct ScanOptions {
  415. public bool ScanForPage_RW;
  416. public bool ScanForPage_ERW;
  417. public bool ScanForAligned;
  418. public uint AlignOnValue;
  419. public bool LimitScanRegion;
  420. public IntPtr MinAddress;
  421. public IntPtr MaxAddress;
  422. public ScanOptions(bool scanRW=true, bool scanERW=false, bool scanAlign=false, uint alignTo=0, bool limitRegion=false, IntPtr minAddress=default(IntPtr), IntPtr maxAddress=default(IntPtr))
  423. {
  424. ScanForPage_RW = scanRW;
  425. ScanForPage_ERW = scanERW;
  426. ScanForAligned = scanAlign;
  427. AlignOnValue = alignTo;
  428. LimitScanRegion = limitRegion;
  429. MinAddress = minAddress;
  430. MaxAddress = maxAddress;
  431. }
  432. }
  433. #endregion
  434. #region ThreadSignal canceller
  435. public class ThreadSignal
  436. {
  437. public volatile bool cancel = false;
  438. }
  439. #endregion
  440. }
  441. }