/NovoAnimus/MemScan.cs
C# | 491 lines | 438 code | 50 blank | 3 comment | 53 complexity | a5f36c5fd097be2ca12c475687d26940 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading.Tasks;
- namespace NovoAnimus
- {
- public class MemScan
- {
- #region Memory Imports
- #region structs
- [Flags]
- public enum ACCESS : uint
- {
- All = 0x001F0FFF,
- Terminate = 0x00000001,
- CreateThread = 0x00000002,
- VMOperation = 0x00000008,
- VMRead = 0x00000010,
- VMWrite = 0x00000020,
- DuplicateHandle = 0x00000040,
- CreateProcess = 0x000000080,
- SetQuota = 0x00000100,
- SetInformation = 0x00000200,
- QueryInformation = 0x00000400,
- QueryLimitedInformation = 0x00001000,
- Synchronize = 0x00100000
- }
- [Flags]
- public enum PROTECT : uint
- {
- EXECUTE = 0x00000010,
- EXECUTE_READ = 0x00000020,
- EXECUTE_READWRITE = 0x00000040,
- EXECUTE_WRITECOPY = 0x00000080,
- NOACCESS = 0x00000001,
- READONLY = 0x00000002,
- READWRITE = 0x00000004,
- WRITECOPY = 0x00000008,
- GUARD = 0x00000100,
- NOCACHE = 0x00000200,
- WRITECOMBINE = 0x00000400
- }
- public struct MEMORY_BASIC_INFORMATION
- {
- public IntPtr BaseAddress;
- public IntPtr AllocationBase;
- public uint AllocationProtect;
- public uint __alignment1;
- public IntPtr RegionSize;
- public uint State;
- public PROTECT Protect;
- public uint lType;
- public uint __alignment2;
- }
- public struct SYSTEM_INFO
- {
- public ushort processorArchitecture;
- ushort reserved;
- public uint pageSize;
- public IntPtr minimumApplicationAddress;
- public IntPtr maximumApplicationAddress;
- public IntPtr activeProcessorMask;
- public uint numberOfProcessors;
- public uint processorType;
- public uint allocationGranularity;
- public ushort processorLevel;
- public ushort processorRevision;
- }
- #endregion structs
- #region consts
- const uint szMemBasicInfo = 48; //28; //sizeof(MEMORY_BASIC_INFORMATION);
- const int PAGE_READWRITE = 0x0010;
- const int MEM_COMMIT = 0x00001000;
- #endregion consts
- [DllImport("kernel32.dll")]
- public static extern IntPtr OpenProcess(ACCESS dwDesiredAccess, bool bInheritHandle, int dwProcessId);
- [DllImport("kernel32.dll", SetLastError = true)]
- [System.Runtime.ConstrainedExecution.ReliabilityContract(System.Runtime.ConstrainedExecution.Consistency.WillNotCorruptState, System.Runtime.ConstrainedExecution.Cer.Success)]
- [System.Security.SuppressUnmanagedCodeSecurity]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CloseHandle(IntPtr hObject);
- [DllImport("kernel32.dll")]
- public static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr dwSize, ref int lpNumberOfBytesRead);
- [DllImport("kernel32.dll")]
- static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern int VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength);
- [DllImport("kernel32.dll", SetLastError = true)]
- static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritter);
- #endregion Memory Imports
- #region ToByte convertors
- public static byte[] ToBytes(string str,bool bigendian=true)
- {
- byte[] r = Encoding.Unicode.GetBytes(str);
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- return r;
- }
- public static byte[] ToBytes(Int16 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- } else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- public static byte[] ToBytes(UInt16 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- }
- else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- public static byte[] ToBytes(Int32 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- }
- else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- public static byte[] ToBytes(UInt32 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- }
- else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- public static byte[] ToBytes(Int64 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- }
- else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- public static byte[] ToBytes(UInt64 i, bool bigendian = true)
- {
- byte[] r = BitConverter.GetBytes(i);
- if (BitConverter.IsLittleEndian)
- {
- if (bigendian)
- {
- Array.Reverse(r);
- }
- }
- else
- {
- if (!bigendian)
- {
- Array.Reverse(r);
- }
- }
- return r;
- }
- #endregion ToByte convertors
- //Wrapper for when we don't already have a handle, will open a handle before scanning and close it afterwards
- public static List<IntPtr> ScanProcessForBytes(Process target, byte[] findThis, ScanOptions options, ThreadSignal signal, out bool cancelled)
- {
- IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
- List<IntPtr> found = ScanProcessForBytes(handle, findThis, options, signal, out cancelled);
- CloseHandle(handle);
- return found;
- }
- public static List<IntPtr> ScanProcessForBytes(IntPtr handle, byte[] findThis, ScanOptions options, ThreadSignal signal, out bool cancelled)
- {
- bool scanRW = options.ScanForPage_RW;
- bool scanERW = options.ScanForPage_ERW;
- bool scanAlign = options.ScanForAligned;
- uint alignTo = options.AlignOnValue;
- if (alignTo==0 || alignTo==1)
- {
- scanAlign = false;
- }
- bool limitRegion = options.LimitScanRegion;
- IntPtr oMinAddress = options.MinAddress;
- IntPtr oMaxAddress = options.MaxAddress;
- long o_min_addr_l = (long)oMinAddress;
- long o_max_addr_l = (long)oMaxAddress;
- List<IntPtr> found = new List<IntPtr>();
- SYSTEM_INFO sys_info = new SYSTEM_INFO();
- GetSystemInfo(out sys_info);
- IntPtr min_addr = sys_info.minimumApplicationAddress;
- IntPtr max_addr = sys_info.maximumApplicationAddress;
- long min_addr_l = (long)min_addr;
- long max_addr_l = (long)max_addr;
- if (limitRegion)
- {
- if (o_min_addr_l > min_addr_l) {
- min_addr = oMinAddress;
- min_addr_l = o_min_addr_l;
- }
- if (o_max_addr_l < max_addr_l)
- {
- max_addr = oMaxAddress;
- max_addr_l = o_max_addr_l;
- }
- }
- MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();
- int bytesRead = 0;
- while ( (!signal.cancel) && (min_addr_l < max_addr_l) )
- {
-
- int bytesReturned = VirtualQueryEx(handle, min_addr, out mem_basic_info, szMemBasicInfo);
- if (bytesReturned == 0)
- {
- int err = Marshal.GetLastWin32Error();
- if (err == 0)
- {
- System.Windows.Forms.MessageBox.Show("GetLastError is zero, everything should be fine"); //SO WTF ARE WE DOING HERE THEN?
- }
- else
- {
- System.Windows.Forms.MessageBox.Show("GetLastError was non-zero after VirtualQueryEx, error was " + err);
- }
- }
- IntPtr PageStartPtr = mem_basic_info.BaseAddress;
- IntPtr PageSizePtr = mem_basic_info.RegionSize;
- long PageStart = PageStartPtr.ToInt64();
- long PageSize = PageSizePtr.ToInt64();
- if (ShouldScanThisPage(options,mem_basic_info))
- {
- byte[] buffer = new byte[PageSize];
- ReadProcessMemory(handle, PageStartPtr, buffer, PageSizePtr, ref bytesRead);
- for (long i = 0; (i < PageSize) && (PageStart+i < max_addr_l); i++)
- {
- if (scanAlign && (i % alignTo != 0))
- {
- continue;
- }
- if (signal.cancel)
- {
- break;
- }
- if (CompareByteArrays(findThis, buffer, i))
- {
- IntPtr memLoc = new IntPtr(PageStart + i);
- found.Add(memLoc);
- }
- }
- }
- min_addr_l += PageSize;
- min_addr = new IntPtr(min_addr_l);
- }
- cancelled = signal.cancel;
- return found;
- }
- //Wrapper for when we don't already have a handle, will open a handle before scanning and close it afterwards
- public static List<IntPtr> ScanProcessForBytesUnchanged(Process target, byte[] scanForThis, List<IntPtr> lastScan1, Action<int> progressout, ScanOptions options, ThreadSignal signal, out bool cancelled)
- {
- IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
- List<IntPtr> found = ScanProcessForBytesUnchanged(handle, scanForThis, lastScan1, progressout, options, signal, out cancelled);
- CloseHandle(handle);
- return found;
- }
- public static List<IntPtr> ScanProcessForBytesUnchanged(IntPtr handle, byte[] scanForThis, List<IntPtr> lastScan1, Action<int> progressout, ScanOptions options, ThreadSignal signal, out bool cancelled)
- {
- List<IntPtr> found = new List<IntPtr>();
- SYSTEM_INFO sys_info = new SYSTEM_INFO();
- GetSystemInfo(out sys_info);
- IntPtr min_addr = sys_info.minimumApplicationAddress;
- IntPtr max_addr = sys_info.maximumApplicationAddress;
- long min_addr_l = (long)min_addr;
- long max_addr_l = (long)max_addr;
- MEMORY_BASIC_INFORMATION mem_basic_info = new MEMORY_BASIC_INFORMATION();
-
- int bytesRead = 0;
- byte[] buffer = new byte[scanForThis.Length];
- IntPtr buffSize = new IntPtr(scanForThis.Length);
- int c = 0;
- foreach (IntPtr memLocation in lastScan1) {
- if (signal.cancel)
- {
- cancelled = signal.cancel;
- return found;
- }
- c++;
- progressout?.Invoke(c); //if progressout is not null, call it
- int bytesReturned = VirtualQueryEx(handle, memLocation, out mem_basic_info, szMemBasicInfo);
- if (bytesReturned == 0)
- {
- int err = Marshal.GetLastWin32Error();
- System.Windows.Forms.MessageBox.Show("GetLastError was non-zero after VirtualQueryEx, error was " + err);
- cancelled = signal.cancel;
- return found;
- }
- if (ShouldScanThisPage(options,mem_basic_info))
- {
- ReadProcessMemory(handle, memLocation, buffer, buffSize, ref bytesRead);
- if (CompareByteArrays(scanForThis, buffer, 0))
- {
- found.Add(memLocation);
- }
- }
- }
- cancelled = signal.cancel;
- return found;
- }
- //Wrapper for when we don't have a handle, will open a handle before writing and close it afterwards
- public static void OverwriteLocation(Process target, IntPtr location, byte[] data, out bool success, out IntPtr bytesWritten)
- {
- IntPtr handle = OpenProcess(ACCESS.VMRead | ACCESS.VMWrite | ACCESS.QueryInformation | ACCESS.VMOperation, false, target.Id);
- success = WriteProcessMemory(handle, location, data, data.Length, out bytesWritten);
- CloseHandle(handle);
- }
- public static void OverwriteLocation(IntPtr handle, IntPtr location, byte[] data, out bool success, out IntPtr bytesWritten)
- {
- success = WriteProcessMemory(handle, location, data, data.Length, out bytesWritten);
- }
-
- #region Helpers
- public static bool CompareByteArrays(byte[] findThis, byte[] searchThis, long startFrom)
- {
- long l1 = findThis.Length;
- long l2 = searchThis.Length;
- for (long i = 0; i < l1; i++)
- {
- if (startFrom + i >= l2) { return false; } //went past the end of our search space, return false]
- if (findThis[i] != searchThis[startFrom + i]) { return false; } //find[i] does not equal search[start+i], return false
- }
- return true; //if we reach here we must match
- }
- public static string PrintByteArray(byte[] bytes)
- {
- var sb = new StringBuilder("new byte[] { ");
- foreach (var b in bytes)
- {
- sb.Append(b + ", ");
- }
- sb.Append("}");
- return sb.ToString();
- }
- public static string PrintByteArrayHex(byte[] bytes)
- {
- return BitConverter.ToString(bytes);
- }
- private static bool ShouldScanThisPage(ScanOptions options, MEMORY_BASIC_INFORMATION mem_basic_info)
- {
- if (!(mem_basic_info.State == MEM_COMMIT))
- {
- return false;
- }
- if (options.ScanForPage_RW && mem_basic_info.Protect.HasFlag(PROTECT.READWRITE))
- {
- return true;
- }
- if (options.ScanForPage_ERW && mem_basic_info.Protect.HasFlag(PROTECT.EXECUTE_READWRITE))
- {
- return true;
- }
- return false;
- }
- #endregion Helpers
- #region ScanOptions struct
- public struct ScanOptions {
- public bool ScanForPage_RW;
- public bool ScanForPage_ERW;
- public bool ScanForAligned;
- public uint AlignOnValue;
- public bool LimitScanRegion;
- public IntPtr MinAddress;
- public IntPtr MaxAddress;
- 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))
- {
- ScanForPage_RW = scanRW;
- ScanForPage_ERW = scanERW;
- ScanForAligned = scanAlign;
- AlignOnValue = alignTo;
- LimitScanRegion = limitRegion;
- MinAddress = minAddress;
- MaxAddress = maxAddress;
- }
- }
- #endregion
- #region ThreadSignal canceller
- public class ThreadSignal
- {
- public volatile bool cancel = false;
- }
- #endregion
- }
- }