Skip to content

Instantly share code, notes, and snippets.

@AndnixSH
Created June 16, 2021 17:44
Show Gist options
  • Save AndnixSH/5f36b81dae9f8fceaf4d7687433e9a2c to your computer and use it in GitHub Desktop.
Save AndnixSH/5f36b81dae9f8fceaf4d7687433e9a2c to your computer and use it in GitHub Desktop.
OrbScanner (AOBScan class for c# with half bytes + module finder)
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