/// <summary> /// Get SECURITY_LOGON_SESSION_DATA for a process or thread via a handle to its token and populate an InjectedThread object's Logon Session values /// </summary> /// <param name="hToken"></param> /// <param name="injectedThread"></param> static void GetLogonSessionData(IntPtr hToken, InjectedThread injectedThread) { int tokenInformationLength = 0; bool result = GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenOrigin, IntPtr.Zero, tokenInformationLength, out tokenInformationLength); IntPtr tokenInformation = Marshal.AllocHGlobal(tokenInformationLength); result = GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenOrigin, tokenInformation, tokenInformationLength, out tokenInformationLength); if (result) { // GetTokenInformation to retreive LUID struct TOKEN_ORIGIN tokenOrigin = (TOKEN_ORIGIN)Marshal.PtrToStructure(tokenInformation, typeof(TOKEN_ORIGIN)); IntPtr pLUID = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LUID))); // Get pointer to LUID struct for LsaGetLogonSessionData Marshal.StructureToPtr(tokenOrigin.OriginatingLogonSession, pLUID, false); IntPtr pLogonSessionData = IntPtr.Zero; LsaGetLogonSessionData(pLUID, out pLogonSessionData); SECURITY_LOGON_SESSION_DATA logonSessionData = (SECURITY_LOGON_SESSION_DATA)Marshal.PtrToStructure(pLogonSessionData, typeof(SECURITY_LOGON_SESSION_DATA)); // Check for a valid logon if (logonSessionData.PSiD != IntPtr.Zero) { if (injectedThread.Username.Equals("NO OWNER")) { string domain = Marshal.PtrToStringUni(logonSessionData.LoginDomain.buffer).Trim(); string username = Marshal.PtrToStringUni(logonSessionData.Username.buffer).Trim(); injectedThread.Username = $"{domain}\\{username}"; } // Add logon session information to InjectedThread object injectedThread.LogonSessionStartTime = DateTime.FromFileTime(logonSessionData.LoginTime); injectedThread.LogonType = Enum.GetName(typeof(SECURITY_LOGON_TYPES), logonSessionData.LogonType); injectedThread.AuthenticationPackage = Marshal.PtrToStringAuto(logonSessionData.AuthenticationPackage.buffer); } LsaFreeReturnBuffer(pLogonSessionData); } }
public static List <InjectedThread> InjectedThreads() { // Check if running as administrator first? Or at least check if SeDebugPrivilege enabled? if (IsUserAnAdmin() == false) { Console.WriteLine("Program is not running as Administrator. Exiting..."); System.Environment.Exit(1); } List <InjectedThread> injectedThreads = new List <InjectedThread>(); // Create array of Process objects for each running process Process[] runningProcesses = Process.GetProcesses(); // Iterate over each process and get all threads by ID foreach (Process process in runningProcesses) { // PID 0 and PID 4 aren't valid targets for injection if (process.Id != 0 && process.Id != 4) { IntPtr hProcess; try { // Get handle to the process hProcess = OpenProcess(ProcessAccessFlags.All, false, process.Id); } catch (System.ComponentModel.Win32Exception) { Console.WriteLine($"Couldn't get handle to process: {process.Id} - System.ComponentModel.Win32Exception - Access Is Denied"); continue; } catch (System.InvalidOperationException) { Console.WriteLine($"Couldn't get handle to process {process.Id} - System.InvalidOperationException - Process has Exited"); continue; } // Get all threads under running process ProcessThreadCollection threadCollection = process.Threads; // Iterate over each thread under the process foreach (ProcessThread thread in threadCollection) { // Get handle to the thread IntPtr hThread = OpenThread(ThreadAccess.AllAccess, false, thread.Id); // Create buffer to store pointer to the thread's base address - NTQueryInformationThread writes to this buffer IntPtr buf = Marshal.AllocHGlobal(IntPtr.Size); // Retrieve thread's Win32StartAddress - Different to thread.StartAddress Int32 result = NtQueryInformationThread(hThread, ThreadInfoClass.ThreadQuerySetWin32StartAddress, buf, IntPtr.Size, IntPtr.Zero); if (result == 0) { // Need to Marshal Win32 type pointer from CLR type IntPtr to access the thread's base address via pointer IntPtr threadBaseAddress = Marshal.ReadIntPtr(buf); // Retrieve MEMORY_BASIC_INFORMATION struct for each thread - assumes 64bit processes, otherwise need to use MEMORY_BASIC_INFORMATION32 MEMORY_BASIC_INFORMATION64 memBasicInfo = new MEMORY_BASIC_INFORMATION64(); VirtualQueryEx(hProcess, threadBaseAddress, out memBasicInfo, (uint)Marshal.SizeOf(typeof(MEMORY_BASIC_INFORMATION64))); // Check the State and Type fields for the thread's MEMORY_BASIC_INFORMATION // Resolve to false suggests code running from this thread does not have a corresponding image file on disk, likely code injection if (memBasicInfo.State == MemoryBasicInformationState.MEM_COMMIT && memBasicInfo.Type != MemoryBasicInformationType.MEM_IMAGE) { // Create new InjectedThread object and set initial variables InjectedThread injectedThread = new InjectedThread() { ProcessName = process.ProcessName, ProcessID = process.Id, ThreadId = thread.Id, BaseAddress = threadBaseAddress, Path = process.MainModule.FileName, Size = (int)memBasicInfo.RegionSize, CommandLine = GetProcessCommandLine(process), MemoryState = Enum.GetName(typeof(MemoryBasicInformationState), memBasicInfo.State), MemoryType = Enum.GetName(typeof(MemoryBasicInformationType), memBasicInfo.Type), MemoryProtection = Enum.GetName(typeof(MemoryBasicInformationProtection), memBasicInfo.Protect), AllocatedMemoryProtection = Enum.GetName(typeof(MemoryBasicInformationProtection), memBasicInfo.AllocationProtect), BasePriority = thread.BasePriority, ThreadStartTime = thread.StartTime }; // Get handle to thread token. If Impersonation is not being used, thread will use Process access token // Try OpenThreadToken() - if it fails, use OpenProcessToken() if (OpenThreadToken(hThread, TokenAccessFlags.TOKEN_QUERY, false, out IntPtr hToken) == false) { // Thread doesn't have a unique token injectedThread.IsUniqueThreadToken = false; // Open process token instead if (OpenProcessToken(hProcess, TokenAccessFlags.TOKEN_QUERY, out hToken) == false) { Console.WriteLine($"Error opening thread and process token: {Marshal.GetLastWin32Error()}\nProcess ID {process.Id}"); } } else { injectedThread.IsUniqueThreadToken = true; } // Query process or thread token information injectedThread.SecurityIdentifier = QueryToken(hToken, TOKEN_INFORMATION_CLASS.TokenUser); injectedThread.Privileges = QueryToken(hToken, TOKEN_INFORMATION_CLASS.TokenPrivileges); injectedThread.Integrity = QueryToken(hToken, TOKEN_INFORMATION_CLASS.TokenIntegrityLevel); injectedThread.LogonId = QueryToken(hToken, TOKEN_INFORMATION_CLASS.TokenOrigin); injectedThread.Username = GetProcessOwner(process.Id); // Get logon session information and add it to the InjectedThread object if (!string.IsNullOrEmpty(injectedThread.LogonId)) { GetLogonSessionData(hToken, injectedThread); } // Get thread's allocated memory via ReadProcessMemory injectedThread.ThreadBytes = GetThreadMemoryBytes(hProcess, threadBaseAddress, injectedThread.Size); // Read the full process memory ; injectedThread.ProcessBytes = GetProcessMemoryBytes(hProcess); // Read full name of executable image for the process int capacity = 1024; StringBuilder stringBuilder = new StringBuilder(capacity); QueryFullProcessImageName(hProcess, 0, stringBuilder, ref capacity); injectedThread.KernelPath = stringBuilder.ToString(0, capacity); // Check whether the kernel image path matches Process.MainModule.Filename if (injectedThread.Path.ToLower() != injectedThread.KernelPath.ToLower()) { injectedThread.PathMismatch = true; } injectedThreads.Add(injectedThread); CloseHandle(hToken); } CloseHandle(hThread); } } CloseHandle(hProcess); } } return(injectedThreads); }