Created
June 16, 2021 17:44
-
-
Save AndnixSH/5f36b81dae9f8fceaf4d7687433e9a2c to your computer and use it in GitHub Desktop.
OrbScanner (AOBScan class for c# with half bytes + module finder)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace Rascal | |
{ | |
/// <summary> | |
/// Created by gigajew | |
/// Pattern scanning code by: https://github.com/mrexodia/PatternFinder | |
/// Region dumping code by: https://www.cheatengine.org/forum/viewtopic.php?t=585770&sid=5a2eca9471e50087b027863472570cac | |
/// Module base address code by: https://social.msdn.microsoft.com/Forums/vstudio/en-US/9b633be5-d7c4-4e08-8ce0-939d2e992896/can-everyone-help-me-to-convert-this-code-c-to-c-getremotemodulehandle?forum=csharpgeneral | |
/// </summary> | |
public class OrbScanner : IDisposable | |
{ | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, uint size, ref uint lpNumberOfBytesRead); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.I4)] | |
private static extern uint VirtualQueryEx(IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, uint size, uint newProtect, ref uint oldProtect); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
private static extern IntPtr CreateToolhelp32Snapshot([MarshalAs(UnmanagedType.U4)]uint dwFlags, [MarshalAs(UnmanagedType.U4)] uint th32ProcessID); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool Module32First(IntPtr hSnapshot, ref MODULEENTRY32 lpme); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool Module32Next(IntPtr hSnapshot, ref MODULEENTRY32 lpme); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
private static extern IntPtr OpenProcess([MarshalAs(UnmanagedType.U4)] uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, [MarshalAs(UnmanagedType.U4)] uint dwProcessId); | |
[DllImport("Kernel32.dll", BestFitMapping = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi, SetLastError = true)] | |
[return: MarshalAs(UnmanagedType.Bool)] | |
private static extern bool CloseHandle(IntPtr dwHandle); | |
public OrbScanner(Process process) : base() | |
{ | |
_wId = (uint)process.Id; | |
_wHandle = OpenProcess(0xFFFFFF, true, _wId); | |
//_wHandle = process.Handle; // todo: fix | |
//_wProcess = process; | |
_regions = DumpRegions(); | |
} | |
public OrbScanner(uint processId) | |
{ | |
_wId = processId; | |
_wHandle = OpenProcess(0xFFFFFF, true, _wId); | |
_regions = DumpRegions(); | |
} | |
/// <summary> | |
/// Get all regions | |
/// </summary> | |
private IList<MEMORY_BASIC_INFORMATION> DumpRegions() | |
{ | |
List<MEMORY_BASIC_INFORMATION> regions = new List<MEMORY_BASIC_INFORMATION>(); | |
IntPtr currentAddress = IntPtr.Zero; | |
while (true) | |
{ | |
MEMORY_BASIC_INFORMATION info = default(MEMORY_BASIC_INFORMATION); | |
uint dump = VirtualQueryEx(_wHandle, currentAddress, out info, (uint)Marshal.SizeOf(info)); | |
if (dump == 0) | |
break; | |
if ((info.State & 0x1000u) != 0u && (info.Protect & 0x100u) == 0u) | |
regions.Add(info); | |
currentAddress = new IntPtr(info.BaseAddress.ToInt32() + (int)info.RegionSize); | |
} | |
return regions; | |
} | |
/// <summary> | |
/// Dump a region | |
/// </summary> | |
private byte[] DumpRegion(IntPtr address, uint length) | |
{ | |
byte[] buffer = new byte[length]; | |
uint read = 0u; | |
uint fOld = 0u; | |
VirtualProtectEx(_wHandle, address, length, 0x40, ref fOld); | |
if (!ReadProcessMemory(_wHandle, address, buffer, length, ref read)) | |
return null; | |
VirtualProtectEx(_wHandle, address, length, fOld, ref fOld); | |
return buffer; | |
} | |
/// <summary> | |
/// Get a module's base address | |
/// </summary> | |
private IntPtr GetRemoteModuleHandle(uint pId, ref string module) | |
{ | |
var modEntry = new MODULEENTRY32(); | |
var tlh = CreateToolhelp32Snapshot(0x00000008u | 0x00000010u, pId); | |
modEntry.dwSize = (uint)Marshal.SizeOf(modEntry); | |
Module32First(tlh, ref modEntry); | |
do | |
{ | |
if (string.Equals(modEntry.szModule, module, StringComparison.InvariantCultureIgnoreCase)) | |
{ | |
return modEntry.hModule; | |
} | |
modEntry.dwSize = (uint)Marshal.SizeOf(modEntry); | |
} | |
while (Module32Next(tlh, ref modEntry)); | |
return IntPtr.Zero; | |
} | |
/// <summary> | |
/// Scan for a pattern in a module | |
/// </summary> | |
public IntPtr Scan(string module, string pattern) | |
{ | |
IntPtr moduleBaseAddr = GetRemoteModuleHandle(_wId, ref module); | |
if (moduleBaseAddr == IntPtr.Zero) | |
throw new Exception("Couldn't find module base address: " + module); | |
OrbPattern.Byte[] compiledPattern = OrbPattern.Transform(pattern); | |
foreach (MEMORY_BASIC_INFORMATION region in DumpRegions()) | |
{ | |
if (region.BaseAddress.ToInt64() + region.RegionSize >= moduleBaseAddr.ToInt64()) | |
{ | |
byte[] regionDump = DumpRegion(region.BaseAddress, region.RegionSize); | |
if (regionDump == null) | |
throw new Exception("Couldn't find pattern!"); | |
long offset; | |
if (OrbPattern.Find(regionDump, compiledPattern, out offset)) | |
{ | |
return IntPtr.Add(region.BaseAddress, (int)offset); | |
} | |
} | |
} | |
return IntPtr.Zero; | |
} | |
/// <summary> | |
/// Scan for a pattern in all of the process | |
/// </summary> | |
public IntPtr Scan(string pattern) | |
{ | |
OrbPattern.Byte[] compiledPattern = OrbPattern.Transform(pattern); | |
foreach (MEMORY_BASIC_INFORMATION region in _regions) | |
{ | |
byte[] regionDump = DumpRegion(region.BaseAddress, region.RegionSize); | |
if (regionDump == null) | |
throw new Exception("Couldn't find pattern!"); | |
long offset; | |
if (OrbPattern.Find(regionDump, compiledPattern, out offset)) | |
{ | |
return IntPtr.Add(region.BaseAddress, (int)offset); | |
} | |
} | |
return IntPtr.Zero; | |
} | |
public void Dispose() | |
{ | |
if (_wHandle != IntPtr.Zero) | |
{ | |
CloseHandle(_wHandle); | |
} | |
if (_wProcess != null) | |
{ | |
_wProcess.Close(); | |
_wProcess.Dispose(); | |
} | |
} | |
private IList<MEMORY_BASIC_INFORMATION> _regions; | |
private uint _wId; | |
private IntPtr _wHandle; | |
private Process _wProcess; | |
} | |
public static class OrbPattern | |
{ | |
public struct Byte | |
{ | |
public struct Nibble | |
{ | |
public bool Wildcard; | |
public byte Data; | |
} | |
public Nibble N1; | |
public Nibble N2; | |
} | |
public static string Format(string pattern) | |
{ | |
var length = pattern.Length; | |
var result = new StringBuilder(length); | |
for (var i = 0; i < length; i++) | |
{ | |
var ch = pattern[i]; | |
if (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f' || ch == '?') | |
result.Append(ch); | |
} | |
return result.ToString(); | |
} | |
private static int hexChToInt(char ch) | |
{ | |
if (ch >= '0' && ch <= '9') | |
return ch - '0'; | |
if (ch >= 'A' && ch <= 'F') | |
return ch - 'A' + 10; | |
if (ch >= 'a' && ch <= 'f') | |
return ch - 'a' + 10; | |
return -1; | |
} | |
public static Byte[] Transform(string pattern) | |
{ | |
pattern = Format(pattern); | |
var length = pattern.Length; | |
if (length == 0) | |
return null; | |
var result = new List<Byte>((length + 1) / 2); | |
if (length % 2 != 0) | |
{ | |
pattern += "?"; | |
length++; | |
} | |
var newbyte = new Byte(); | |
for (int i = 0, j = 0; i < length; i++) | |
{ | |
var ch = pattern[i]; | |
if (ch == '?') //wildcard | |
{ | |
if (j == 0) | |
newbyte.N1.Wildcard = true; | |
else | |
newbyte.N2.Wildcard = true; | |
} | |
else //hex | |
{ | |
if (j == 0) | |
{ | |
newbyte.N1.Wildcard = false; | |
newbyte.N1.Data = (byte)(hexChToInt(ch) & 0xF); | |
} | |
else | |
{ | |
newbyte.N2.Wildcard = false; | |
newbyte.N2.Data = (byte)(hexChToInt(ch) & 0xF); | |
} | |
} | |
j++; | |
if (j == 2) | |
{ | |
j = 0; | |
result.Add(newbyte); | |
} | |
} | |
return result.ToArray(); | |
} | |
public static bool Find(byte[] data, Byte[] pattern) | |
{ | |
long temp; | |
return Find(data, pattern, out temp); | |
} | |
private static bool matchByte(byte b, ref Byte p) | |
{ | |
if (!p.N1.Wildcard) //if not a wildcard we need to compare the data. | |
{ | |
var n1 = b >> 4; | |
if (n1 != p.N1.Data) //if the data is not equal b doesn't match p. | |
return false; | |
} | |
if (!p.N2.Wildcard) //if not a wildcard we need to compare the data. | |
{ | |
var n2 = b & 0xF; | |
if (n2 != p.N2.Data) //if the data is not equal b doesn't match p. | |
return false; | |
} | |
return true; | |
} | |
public static bool Find(byte[] data, Byte[] pattern, out long offsetFound, long offset = 0) | |
{ | |
offsetFound = -1; | |
if (data == null || pattern == null) | |
return false; | |
var patternSize = pattern.LongLength; | |
if (data.LongLength == 0 || patternSize == 0) | |
return false; | |
for (long i = offset, pos = 0; i < data.LongLength; i++) | |
{ | |
if (matchByte(data[i], ref pattern[pos])) //check if the current data byte matches the current pattern byte | |
{ | |
pos++; | |
if (pos == patternSize) //everything matched | |
{ | |
offsetFound = i - patternSize + 1; | |
return true; | |
} | |
} | |
else //fix by Computer_Angel | |
{ | |
i -= pos; | |
pos = 0; //reset current pattern position | |
} | |
} | |
return false; | |
} | |
public static bool FindAll(byte[] data, Byte[] pattern, out List<long> offsetsFound) | |
{ | |
offsetsFound = new List<long>(); | |
long size = data.Length, pos = 0; | |
while (size > pos) | |
{ | |
if (Find(data, pattern, out long offsetFound, pos)) | |
{ | |
offsetsFound.Add(offsetFound); | |
pos = offsetFound + pattern.Length; | |
} | |
else | |
break; | |
} | |
if (offsetsFound.Count > 0) | |
return true; | |
else | |
return false; | |
} | |
public static OrbSignature[] Scan(byte[] data, OrbSignature[] signatures) | |
{ | |
var found = new ConcurrentBag<OrbSignature>(); | |
Parallel.ForEach(signatures, signature => | |
{ | |
if (Find(data, signature.Pattern, out signature.FoundOffset)) | |
found.Add(signature); | |
}); | |
return found.ToArray(); | |
} | |
} | |
public class OrbSignature | |
{ | |
public OrbSignature(string name, OrbPattern.Byte[] pattern) | |
{ | |
Name = name; | |
Pattern = pattern; | |
FoundOffset = -1; | |
} | |
public OrbSignature(string name, string pattern) | |
{ | |
Name = name; | |
Pattern = OrbPattern.Transform(pattern); | |
FoundOffset = -1; | |
} | |
public string Name { get; private set; } | |
public OrbPattern.Byte[] Pattern { get; private set; } | |
public long FoundOffset; | |
public override string ToString() | |
{ | |
return Name; | |
} | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct MODULEENTRY32 | |
{ | |
public uint dwSize; | |
public uint th32ModuleID; | |
public uint th32ProcessID; | |
public uint GlblcntUsage; | |
public uint ProccntUsage; | |
IntPtr modBaseAddr; | |
public uint modBaseSize; | |
public IntPtr hModule; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] | |
public string szModule; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] | |
public string szExePath; | |
}; | |
[StructLayout(LayoutKind.Sequential)] | |
public struct MEMORY_BASIC_INFORMATION | |
{ | |
public IntPtr BaseAddress; | |
public IntPtr AllocationBase; | |
public uint AllocationProtect; | |
public uint RegionSize; | |
public uint State; | |
public uint Protect; | |
public uint Type; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment