/* * Process Hacker - * windows API wrapper code * * Copyright (C) 2009 Dean * Copyright (C) 2008-2009 wj32 * * This file is part of Process Hacker. * * Process Hacker is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Process Hacker is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Process Hacker. If not, see . */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Text; namespace ProcessHacker { /// /// Provides interfacing to the Win32 and Native APIs. /// public partial class Win32 { /// /// Contains code which uses pointers. /// public unsafe class Unsafe { /// /// Converts a multi-string into a managed string array. A multi-string /// consists of an array of null-terminated strings plus an extra null to /// terminate the array. /// /// The pointer to the array. /// A string array. public static string[] GetMultiString(IntPtr ptr) { List list = new List(); char* chptr = (char*)ptr.ToPointer(); StringBuilder currentString = new StringBuilder(); while (true) { while (*chptr != 0) { currentString.Append(*chptr); chptr++; } string str = currentString.ToString(); if (str == "") { break; } else { list.Add(str); currentString = new StringBuilder(); } } return list.ToArray(); } public static int SingleToInt32(float f) { return *(int*)&f; } public static long DoubleToInt64(double d) { return *(long*)&d; } public static float Int32ToSingle(int i) { return *(float*)&i; } public static double Int64ToDouble(long l) { return *(double*)&l; } } public delegate int EnumWindowsProc(int hwnd, int param); public delegate int SymEnumSymbolsProc(SYMBOL_INFO pSymInfo, int SymbolSize, int UserContext); public delegate int FunctionTableAccessProc64(int ProcessHandle, int AddrBase); public delegate int GetModuleBaseProc64(int ProcessHandle, int Address); /// /// A cache for type names; QuerySystemInformation with ALL_TYPES_INFORMATION fails for some /// reason. The dictionary relates object type numbers to their names. /// public static Dictionary ObjectTypes = new Dictionary(); #region Consts public const int ANYSIZE_ARRAY = 1; public const int DONT_RESOLVE_DLL_REFERENCES = 0x1; public const int ERROR_NO_MORE_ITEMS = 259; public const int MAXIMUM_SUPPORTED_EXTENSION = 512; public const int SEE_MASK_INVOKEIDLIST = 0xc; public const uint SERVICE_NO_CHANGE = 0xffffffff; public const uint SHGFI_ICON = 0x100; public const uint SHGFI_LARGEICON = 0x0; public const uint SHGFI_SMALLICON = 0x1; public const int SID_SIZE = 0x1000; public const int SIZE_OF_80387_REGISTERS = 72; public const uint STATUS_INFO_LENGTH_MISMATCH = 0xc0000004; public const int SW_SHOW = 5; public const int SYMBOL_NAME_MAXSIZE = 255; public const int WAIT_ABANDONED = 0x80; public const int WAIT_OBJECT_0 = 0x0; public const int WAIT_TIMEOUT = 0x102; #endregion #region Cryptography public enum VerifyResult { Trusted = 0, TrustedInstaller, NoSignature, Expired, Revoked, Distrust, SecuritySettings } public static VerifyResult VerifyFile(string filePath) { VerifyResult result = VerifyResult.NoSignature; using (MemoryAlloc strMem = new MemoryAlloc(filePath.Length * 2 + 2)) { WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(); GUID verifyGuid = new GUID() { Data1 = 0xaac56b, Data2 = 0xcd44, Data3 = 0x11d0, Data4 = new byte[] { 0x8c, 0xc2, 0x0, 0xc0, 0x4f, 0xc2, 0x95, 0xee } }; strMem.WriteUnicodeString(0, filePath); strMem.WriteByte(filePath.Length * 2, 0); strMem.WriteByte(filePath.Length * 2 + 1, 0); fileInfo.Size = Marshal.SizeOf(fileInfo); fileInfo.FilePath = strMem; WINTRUST_DATA trustData = new WINTRUST_DATA(); trustData.Size = 12 * 4; trustData.UIChoice = 2; // WTD_UI_NONE trustData.UnionChoice = 1; // WTD_CHOICE_FILE trustData.ProvFlags = 0x100; // WTD_SAFER_FLAG using (MemoryAlloc mem = new MemoryAlloc(fileInfo.Size)) { Marshal.StructureToPtr(fileInfo, mem, false); trustData.File = mem; uint winTrustResult = WinVerifyTrust(0, ref verifyGuid, ref trustData); if (winTrustResult == 0) result = VerifyResult.Trusted; else if (winTrustResult == 0x800b0100) result = VerifyResult.NoSignature; else if (winTrustResult == 0x800b0101) result = VerifyResult.Expired; else if (winTrustResult == 0x800b010c) result = VerifyResult.Revoked; else if (winTrustResult == 0x800b0111) result = VerifyResult.Distrust; else if (winTrustResult == 0x80092026) result = VerifyResult.SecuritySettings; } } if (result == VerifyResult.NoSignature) { // check the file's permissions try { System.IO.FileInfo info = new System.IO.FileInfo(filePath); bool othersCanWrite = false; // if accounts other than TrustedInstaller can write // TrustedInstaller must own the file if (info.GetAccessControl().GetOwner(typeof(System.Security.Principal.NTAccount)).Value != "NT SERVICE\\TrustedInstaller") { result = VerifyResult.NoSignature; } else { foreach (System.Security.AccessControl.FileSystemAccessRule rule in info.GetAccessControl().GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount))) { if (rule.IdentityReference.Value == "NT SERVICE\\TrustedInstaller") { result = VerifyResult.TrustedInstaller; } else { // if any accounts other than TrustedInstaller can write to the file, then it's // not a real Windows component. if (rule.AccessControlType == System.Security.AccessControl.AccessControlType.Allow && (rule.FileSystemRights & ( System.Security.AccessControl.FileSystemRights.ChangePermissions | System.Security.AccessControl.FileSystemRights.Delete | System.Security.AccessControl.FileSystemRights.DeleteSubdirectoriesAndFiles | System.Security.AccessControl.FileSystemRights.TakeOwnership | System.Security.AccessControl.FileSystemRights.Write)) != 0) { othersCanWrite = true; break; } } } if (othersCanWrite) result = VerifyResult.NoSignature; } } catch { } } return result; } #endregion #region Errors /// /// Gets the error message associated with the specified error code. /// /// The error code. /// An error message. public static string GetErrorMessage(int ErrorCode) { StringBuilder buffer = new StringBuilder(0x100); if (FormatMessage(0x3200, 0, ErrorCode, 0, buffer, buffer.Capacity, IntPtr.Zero) == 0) return "Unknown error (0x" + ErrorCode.ToString("x") + ")"; StringBuilder result = new StringBuilder(); int i = 0; while (i < buffer.Length) { if (buffer[i] == '\0') break; result.Append(buffer[i]); i++; } return result.ToString(); } /// /// Gets the error message associated with the last error that occured. /// /// An error message. public static string GetLastErrorMessage() { return GetErrorMessage(Marshal.GetLastWin32Error()); } /// /// Throws a Win32Exception with the last error that occurred. /// public static void ThrowLastWin32Error() { int error = Marshal.GetLastWin32Error(); if (error != 0) throw new Win32Exception(error); } #endregion #region Handles public struct ObjectInformation { public OBJECT_BASIC_INFORMATION Basic; public OBJECT_NAME_INFORMATION Name; public string OrigName; public string BestName; public string TypeName; } /// /// Enumerates the handles opened by every running process. /// /// An array containing information about the handles. public static SYSTEM_HANDLE_INFORMATION[] EnumHandles() { int retLength = 0; int handleCount = 0; SYSTEM_HANDLE_INFORMATION[] returnHandles; using (MemoryAlloc data = new MemoryAlloc(0x1000)) { // This is needed because ZwQuerySystemInformation with SystemHandleInformation doesn't // actually give a real return length when called with an insufficient buffer. This code // tries repeatedly to call the function, doubling the buffer size each time it fails. while (ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, data.Memory, data.Size, out retLength) == STATUS_INFO_LENGTH_MISMATCH) data.Resize(data.Size * 2); // The structure of the buffer is the handle count plus an array of SYSTEM_HANDLE_INFORMATION // structures. handleCount = data.ReadInt32(0); returnHandles = new SYSTEM_HANDLE_INFORMATION[handleCount]; for (int i = 0; i < handleCount; i++) { returnHandles[i] = data.ReadStruct(4, i); } return returnHandles; } } public static ObjectInformation GetHandleInfo(SYSTEM_HANDLE_INFORMATION handle) { using (ProcessHandle process = new ProcessHandle(handle.ProcessId, PROCESS_RIGHTS.PROCESS_DUP_HANDLE)) { return GetHandleInfo(process, handle); } } public static ObjectInformation GetHandleInfo(ProcessHandle process, SYSTEM_HANDLE_INFORMATION handle) { int object_handle; int retLength = 0; // duplicates the handle so we can query it if (ZwDuplicateObject(process.Handle, handle.Handle, Program.CurrentProcess, out object_handle, 0, 0, 0) != 0) throw new Exception("Could not duplicate object!"); try { ObjectInformation info = new ObjectInformation(); ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectBasicInformation, IntPtr.Zero, 0, out retLength); if (retLength > 0) { using (MemoryAlloc obiMem = new MemoryAlloc(retLength)) { ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectBasicInformation, obiMem.Memory, obiMem.Size, out retLength); OBJECT_BASIC_INFORMATION obi = obiMem.ReadStruct(); info.Basic = obi; } } // If the cache contains the object type's name, use it. Otherwise, query the type // for its name. if (ObjectTypes.ContainsKey(handle.ObjectTypeNumber)) { info.TypeName = ObjectTypes[handle.ObjectTypeNumber]; } else { ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out retLength); if (retLength > 0) { using (MemoryAlloc otiMem = new MemoryAlloc(retLength)) { if (ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, otiMem.Memory, otiMem.Size, out retLength) != 0) throw new Exception("ZwQueryObject failed"); OBJECT_TYPE_INFORMATION oti = otiMem.ReadStruct(); info.TypeName = ReadUnicodeString(oti.Name); ObjectTypes.Add(handle.ObjectTypeNumber, info.TypeName); } } } // This hack is needed because certain NamedPipes block ZwQueryObject forever. The hack // is guaranteed to work, but filters out useful files as well. if (info.TypeName == "File" && (int)handle.GrantedAccess == 0x0012019f) { // FIXME: get KProcessHacker working //info.OrigName = Program.KPH.GetObjectName(handle); } else { ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out retLength); if (retLength > 0) { using (MemoryAlloc oniMem = new MemoryAlloc(retLength)) { if (ZwQueryObject(object_handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, oniMem.Memory, oniMem.Size, out retLength) != 0) throw new Exception("ZwQueryObject failed"); OBJECT_NAME_INFORMATION oni = oniMem.ReadStruct(); info.OrigName = ReadUnicodeString(oni.Name); info.Name = oni; } } } // get a better name for the handle try { switch (info.TypeName) { case "Key": string hklmString = "\\registry\\machine"; string hkcrString = "\\registry\\machine\\software\\classes"; string hkcuString = "\\registry\\user\\" + System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower(); string hkcucrString = "\\registry\\user\\" + System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower() + "_classes"; string hkuString = "\\registry\\user"; if (info.OrigName.ToLower().StartsWith(hklmString)) info.BestName = "HKLM" + info.OrigName.Substring(hklmString.Length); else if (info.OrigName.ToLower().StartsWith(hkcucrString)) info.BestName = "HKCU\\Software\\Classes" + info.OrigName.Substring(hkcucrString.Length); else if (info.OrigName.ToLower().StartsWith(hkcuString)) info.BestName = "HKCU" + info.OrigName.Substring(hkcuString.Length); else if (info.OrigName.ToLower().StartsWith(hkcrString)) info.BestName = "HKCR" + info.OrigName.Substring(hkcrString.Length); else if (info.OrigName.ToLower().StartsWith(hkuString)) info.BestName = "HKU" + info.OrigName.Substring(hkuString.Length); else info.BestName = info.OrigName; break; case "Process": { int process_handle; int processId; if (ZwDuplicateObject(process.Handle, handle.Handle, Program.CurrentProcess, out process_handle, (STANDARD_RIGHTS)Program.MinProcessQueryRights, 0, 0) != 0) throw new Exception("Could not duplicate process handle!"); try { if ((processId = GetProcessId(process_handle)) == 0) ThrowLastWin32Error(); if (Program.HackerWindow.ProcessProvider.Dictionary.ContainsKey(processId)) info.BestName = Program.HackerWindow.ProcessProvider.Dictionary[processId].Name + " (" + processId.ToString() + ")"; else info.BestName = "Non-existent process (" + processId.ToString() + ")"; } finally { CloseHandle(process_handle); } } break; case "Thread": { int thread_handle; int processId; int threadId; if (ZwDuplicateObject(process.Handle, handle.Handle, Program.CurrentProcess, out thread_handle, (STANDARD_RIGHTS)Program.MinThreadQueryRights, 0, 0) != 0) throw new Exception("Could not duplicate thread handle!"); try { if ((threadId = GetThreadId(thread_handle)) == 0) ThrowLastWin32Error(); if ((processId = GetProcessIdOfThread(thread_handle)) == 0) ThrowLastWin32Error(); if (Program.HackerWindow.ProcessProvider.Dictionary.ContainsKey(processId)) info.BestName = Program.HackerWindow.ProcessProvider.Dictionary[processId].Name + " (" + processId.ToString() + "): " + threadId.ToString(); else info.BestName = "Non-existent process (" + processId.ToString() + "): " + threadId.ToString(); } finally { CloseHandle(thread_handle); } } break; case "Token": { int token_handle; if (ZwDuplicateObject(process.Handle, handle.Handle, Program.CurrentProcess, out token_handle, (STANDARD_RIGHTS)TOKEN_RIGHTS.TOKEN_QUERY, 0, 0) != 0) throw new Exception("Could not duplicate token handle!"); try { info.BestName = TokenHandle.FromHandle(token_handle).GetUser().GetName(true); } finally { CloseHandle(token_handle); } } break; default: if (info.OrigName != null && info.OrigName != "") { info.BestName = info.OrigName; } else { info.BestName = null; } break; } } catch { if (info.OrigName != null && info.OrigName != "") { info.BestName = info.OrigName; } else { info.BestName = null; } } return info; } finally { CloseHandle(object_handle); } // this means that for some reason, the return statement above didn't execute. throw new Exception("Failed"); } #endregion #region Misc. /// /// Loads an image into kernel-mode using ZwSetSystemInformation /// with SystemLoadAndCallImage. /// /// The path to the driver. public static void LoadKernelImage(string fileName) { System.IO.FileInfo info = new System.IO.FileInfo(fileName); string ntFileName = "\\??\\" + info.FullName; SYSTEM_LOAD_AND_CALL_IMAGE laci = new SYSTEM_LOAD_AND_CALL_IMAGE(); using (MemoryAlloc stringData = new MemoryAlloc(ntFileName.Length * 2 + 2)) { laci.ModuleName = new UNICODE_STRING(); stringData.WriteUnicodeString(0, ntFileName); laci.ModuleName.Buffer = stringData; laci.ModuleName.Length = (ushort)(ntFileName.Length * 2); laci.ModuleName.MaximumLength = laci.ModuleName.Length; uint ret; if ((ret = ZwSetSystemInformation(SYSTEM_INFORMATION_CLASS.SystemLoadAndCallImage, ref laci, Marshal.SizeOf(laci))) != 0) throw new Exception("Failed to load the kernel image - error " + ret.ToString()); } } /// /// Reads a Unicode string. /// /// A UNICODE_STRING structure. /// A string. /// This function is needed because some LSA strings are not /// null-terminated, so we can't use .NET's marshalling. public static string ReadUnicodeString(UNICODE_STRING str) { if (str.Length == 0) return null; return Marshal.PtrToStringUni(new IntPtr(str.Buffer), str.Length / 2); } public static void ShowProperties(string fileName) { Win32.SHELLEXECUTEINFO info = new Win32.SHELLEXECUTEINFO(); info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(Win32.SHELLEXECUTEINFO)); info.lpFile = fileName; info.nShow = Win32.SW_SHOW; info.fMask = Win32.SEE_MASK_INVOKEIDLIST; info.lpVerb = "properties"; Win32.ShellExecuteEx(ref info); } #endregion #region Processes public struct SystemProcess { public string Name; public SYSTEM_PROCESS_INFORMATION Process; public Dictionary Threads; } /// /// Specifies the processes which should have their threads filled in. This is /// used as a performance boost. /// public static Dictionary ProcessesWithThreads = new Dictionary(); /// /// Gets a dictionary containing the currently running processes. /// /// A dictionary, indexed by process ID. public static Dictionary EnumProcesses() { int retLength = 0; Dictionary returnProcesses; ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessesAndThreadsInformation, IntPtr.Zero, 0, out retLength); using (MemoryAlloc data = new MemoryAlloc(retLength)) { ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessesAndThreadsInformation, data.Memory, data.Size, out retLength); returnProcesses = new Dictionary(); int i = 0; SystemProcess currentProcess = new SystemProcess(); while (true) { currentProcess.Process = data.ReadStruct(i, 0); currentProcess.Name = ReadUnicodeString(currentProcess.Process.ImageName); if (ProcessesWithThreads.ContainsKey(currentProcess.Process.ProcessId) && currentProcess.Process.ProcessId != 0) { currentProcess.Threads = new Dictionary(); for (int j = 0; j < currentProcess.Process.NumberOfThreads; j++) { Win32.SYSTEM_THREAD_INFORMATION thread = data.ReadStruct(i + Marshal.SizeOf(typeof(SYSTEM_PROCESS_INFORMATION)), j); currentProcess.Threads.Add(thread.ClientId.UniqueThread, thread); } } returnProcesses.Add(currentProcess.Process.ProcessId, currentProcess); if (currentProcess.Process.NextEntryOffset == 0) break; i += currentProcess.Process.NextEntryOffset; } return returnProcesses; } } public static Icon GetFileIcon(string fileName) { return GetFileIcon(fileName, false); } public static Icon GetFileIcon(string fileName, bool large) { Win32.SHFILEINFO shinfo = new Win32.SHFILEINFO(); if (fileName == null || fileName == "") throw new Exception("File name cannot be empty."); try { if (Win32.SHGetFileInfo(fileName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), Win32.SHGFI_ICON | (large ? Win32.SHGFI_LARGEICON : Win32.SHGFI_SMALLICON)) == 0) { return null; } else { return Icon.FromHandle(shinfo.hIcon); } } catch { return null; } } /// /// Gets the name of the process with the specified process ID. /// /// The ID of the process to search for. /// The name of the process public static string GetNameFromPID(int pid) { PROCESSENTRY32 proc = new PROCESSENTRY32(); int snapshot = 0; snapshot = CreateToolhelp32Snapshot(SnapshotFlags.Process, pid); if (snapshot == 0) return "(error)"; proc.dwSize = Marshal.SizeOf(typeof(PROCESSENTRY32)); Process32First(snapshot, ref proc); do { if (proc.th32ProcessID == pid) return proc.szExeFile; } while (Process32Next(snapshot, ref proc) != 0); return "(unknown)"; } public static int GetProcessSessionId(int ProcessId) { int sessionId = -1; try { if (!ProcessIdToSessionId(ProcessId, out sessionId)) ThrowLastWin32Error(); } catch { using (ProcessHandle phandle = new ProcessHandle(ProcessId, Program.MinProcessQueryRights)) { return phandle.GetToken(TOKEN_RIGHTS.TOKEN_QUERY).GetSessionId(); } } return sessionId; } #endregion #region Security public static string GetAccountName(int SID, bool IncludeDomain) { StringBuilder name = new StringBuilder(255); StringBuilder domain = new StringBuilder(255); int namelen = 255; int domainlen = 255; SID_NAME_USE use = SID_NAME_USE.SidTypeUser; try { if (!LookupAccountSid(null, SID, name, out namelen, domain, out domainlen, out use)) { // if the name is longer than 255 characters, increase the capacity. name.EnsureCapacity(namelen); domain.EnsureCapacity(domainlen); if (!LookupAccountSid(null, SID, name, out namelen, domain, out domainlen, out use)) { if (name.ToString() == "" && domain.ToString() == "") throw new Exception("Could not lookup account SID: " + Win32.GetLastErrorMessage()); } } } catch { // if we didn't find a name, then return the string SID version. return GetAccountStringSID(SID); } if (IncludeDomain) { return ((domain.ToString() != "") ? domain.ToString() + "\\" : "") + name.ToString(); } else { return name.ToString(); } } public static string GetAccountStringSID(int SID) { return new System.Security.Principal.SecurityIdentifier(new IntPtr(SID)).ToString(); } public static SID_NAME_USE GetAccountType(int SID) { StringBuilder name = new StringBuilder(255); StringBuilder domain = new StringBuilder(255); int namelen = 255; int domainlen = 255; SID_NAME_USE use = SID_NAME_USE.SidTypeUser; // we don't actually need to get the account name if (!LookupAccountSid(null, SID, name, out namelen, domain, out domainlen, out use)) { name.EnsureCapacity(namelen); domain.EnsureCapacity(domainlen); if (!LookupAccountSid(null, SID, name, out namelen, domain, out domainlen, out use)) { if (name.ToString() == "" && domain.ToString() == "") throw new Exception("Could not lookup account SID: " + Win32.GetLastErrorMessage()); } } return use; } public static string GetPrivilegeDisplayName(string PrivilegeName) { StringBuilder sb = null; int size = 0; int languageId = 0; LookupPrivilegeDisplayName(0, PrivilegeName, sb, out size, out languageId); sb = new StringBuilder(size); LookupPrivilegeDisplayName(0, PrivilegeName, sb, out size, out languageId); return sb.ToString(); } public static string GetPrivilegeName(LUID Luid) { StringBuilder sb = null; int size = 0; LookupPrivilegeName(0, ref Luid, sb, out size); sb = new StringBuilder(size); LookupPrivilegeName(0, ref Luid, sb, out size); return sb.ToString(); } public static TOKEN_PRIVILEGES ReadTokenPrivileges(TokenHandle TokenHandle) { int retLen = 0; GetTokenInformation(TokenHandle.Handle, TOKEN_INFORMATION_CLASS.TokenPrivileges, IntPtr.Zero, 0, out retLen); using (MemoryAlloc data = new MemoryAlloc(retLen)) { if (!GetTokenInformation(TokenHandle.Handle, TOKEN_INFORMATION_CLASS.TokenPrivileges, data.Memory, data.Size, out retLen)) ThrowLastWin32Error(); uint number = data.ReadUInt32(0); TOKEN_PRIVILEGES privileges = new TOKEN_PRIVILEGES(); privileges.PrivilegeCount = number; privileges.Privileges = new LUID_AND_ATTRIBUTES[number]; for (int i = 0; i < number; i++) { privileges.Privileges[i] = data.ReadStruct(4, i); } return privileges; } } public static void WriteTokenPrivilege(string PrivilegeName, SE_PRIVILEGE_ATTRIBUTES Attributes) { WriteTokenPrivilege( ProcessHandle.FromHandle(Program.CurrentProcess).GetToken(), PrivilegeName, Attributes); } public static void WriteTokenPrivilege(TokenHandle TokenHandle, string PrivilegeName, SE_PRIVILEGE_ATTRIBUTES Attributes) { TOKEN_PRIVILEGES tkp = new TOKEN_PRIVILEGES(); tkp.Privileges = new LUID_AND_ATTRIBUTES[1]; if (!LookupPrivilegeValue(null, PrivilegeName, ref tkp.Privileges[0].Luid)) throw new Exception("Invalid privilege name '" + PrivilegeName + "'."); tkp.PrivilegeCount = 1; tkp.Privileges[0].Attributes = Attributes; AdjustTokenPrivileges(TokenHandle.Handle, 0, ref tkp, 0, 0, 0); if (Marshal.GetLastWin32Error() != 0) ThrowLastWin32Error(); } #endregion #region Services public static Dictionary EnumServices() { using (ServiceManagerHandle manager = new ServiceManagerHandle(SC_MANAGER_RIGHTS.SC_MANAGER_ENUMERATE_SERVICE)) { int requiredSize; int servicesReturned; int resume; // get required size EnumServicesStatusEx(manager, 0, SERVICE_QUERY_TYPE.Win32 | SERVICE_QUERY_TYPE.Driver, SERVICE_QUERY_STATE.All, IntPtr.Zero, 0, out requiredSize, out servicesReturned, out resume, 0); using (MemoryAlloc data = new MemoryAlloc(requiredSize)) { Dictionary dictionary = new Dictionary(); if (!EnumServicesStatusEx(manager, 0, SERVICE_QUERY_TYPE.Win32 | SERVICE_QUERY_TYPE.Driver, SERVICE_QUERY_STATE.All, data, data.Size, out requiredSize, out servicesReturned, out resume, 0)) { ThrowLastWin32Error(); } for (int i = 0; i < servicesReturned; i++) { ENUM_SERVICE_STATUS_PROCESS service = data.ReadStruct(i); dictionary.Add(service.ServiceName, service); } return dictionary; } } } public static QUERY_SERVICE_CONFIG GetServiceConfig(string ServiceName) { using (ServiceHandle service = new ServiceHandle(ServiceName, SERVICE_RIGHTS.SERVICE_QUERY_CONFIG)) { int requiredSize = 0; QueryServiceConfig(service, IntPtr.Zero, 0, ref requiredSize); using (MemoryAlloc data = new MemoryAlloc(requiredSize)) { if (!QueryServiceConfig(service, data, data.Size, ref requiredSize)) throw new Exception("Could not get service configuration: " + GetLastErrorMessage()); return data.ReadStruct(); } } } #endregion #region Statistics public static IO_COUNTERS GetProcessIoCounters(ProcessHandle process) { IO_COUNTERS counters = new IO_COUNTERS(); if (!GetProcessIoCounters(process.Handle, out counters)) ThrowLastWin32Error(); return counters; } public static ulong[] GetProcessTimes(ProcessHandle process) { ulong[] times = new ulong[4]; if (!GetProcessTimes(process.Handle, out times[0], out times[1], out times[2], out times[3])) ThrowLastWin32Error(); return times; } public static ulong[] GetSystemTimes() { ulong[] times = new ulong[3]; if (!GetSystemTimes(out times[0], out times[1], out times[2])) ThrowLastWin32Error(); return times; } #endregion #region TCP public static MIB_TCPSTATS GetTcpStats() { MIB_TCPSTATS tcpStats = new MIB_TCPSTATS(); GetTcpStatistics(ref tcpStats); return tcpStats; } public static MIB_TCPTABLE_OWNER_PID GetExTcpConnexions() { MIB_TCPTABLE_OWNER_PID returnTcpExConnexions; int bufferSize = 100000; IntPtr lpTable = Marshal.AllocHGlobal(bufferSize); if (AllocateAndGetTcpExTableFromStack(ref lpTable, true, GetProcessHeap(), 0, 2) != 0) { throw new Exception("AllocateAndGetTcpExTableFromStack failed"); } int numEntries = (int)Marshal.ReadIntPtr(lpTable); lpTable = IntPtr.Zero; Marshal.FreeHGlobal(lpTable); int rowsize = 24; bufferSize = (numEntries * rowsize) + 4; returnTcpExConnexions = new MIB_TCPTABLE_OWNER_PID(); lpTable = Marshal.AllocHGlobal(bufferSize); if (AllocateAndGetTcpExTableFromStack(ref lpTable, true, GetProcessHeap(), 0, 2) != 0) { throw new Exception("AllocateAndGetTcpExTableFromStack failed"); } IntPtr current = lpTable; int currentIndex = 0; numEntries = (int)Marshal.ReadIntPtr(current); returnTcpExConnexions.NumEntries = numEntries; returnTcpExConnexions.Table = new MIB_TCPROW_OWNER_PID[numEntries]; currentIndex += 4; current = (IntPtr)((int)current + currentIndex); for (int i = 0; i < numEntries; i++) { returnTcpExConnexions.Table[i].State = (MIB_TCP_STATE)((int)Marshal.ReadIntPtr(current)); current = (IntPtr)((int)current + 4); uint localAddr = (uint)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); uint localPort = (uint)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); returnTcpExConnexions.Table[i].Local = new System.Net.IPEndPoint(localAddr, (int)ConvertPort(localPort)); uint remoteAddr = (uint)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); uint remotePort = 0; if (remoteAddr != 0) { remotePort = (uint)Marshal.ReadIntPtr(current); remotePort = (uint)ConvertPort(remotePort); } current = (IntPtr)((int)current + 4); returnTcpExConnexions.Table[i].Remote = new System.Net.IPEndPoint(remoteAddr, (int)remotePort); returnTcpExConnexions.Table[i].OwningProcessId = (int)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); } Marshal.FreeHGlobal(lpTable); current = IntPtr.Zero; return returnTcpExConnexions; } #endregion #region Terminal Server public static WTS_SESSION_INFO[] TSEnumSessions() { IntPtr sessions; int count; WTS_SESSION_INFO[] returnSessions; WTSEnumerateSessions(0, 0, 1, out sessions, out count); returnSessions = new WTS_SESSION_INFO[count]; WtsMemoryAlloc data = WtsMemoryAlloc.FromPointer(sessions); for (int i = 0; i < count; i++) { returnSessions[i] = data.ReadStruct(i); } data.Dispose(); return returnSessions; } /// /// Before we had the WtsMemoryAlloc class, these enumerator /// functions queried LSA about each process' username, /// regardless of whether they were going to be used. /// If they didn't do that, the memory allocated for the /// data would be freed and we would end up with invalid /// SID pointers. This structure keeps a WtsMemoryAlloc /// instance alive so that it isn't freed until told to /// do so. This then means that the enumerator functions /// don't need to query LSA so often. /// public struct WtsEnumProcessesData { public WTS_PROCESS_INFO[] Processes; public WtsMemoryAlloc Memory; } public static WtsEnumProcessesData TSEnumProcesses() { IntPtr processes; int count; WTS_PROCESS_INFO[] returnProcesses; WTSEnumerateProcesses(0, 0, 1, out processes, out count); returnProcesses = new WTS_PROCESS_INFO[count]; WtsMemoryAlloc data = WtsMemoryAlloc.FromPointer(processes); for (int i = 0; i < count; i++) { returnProcesses[i] = data.ReadStruct(i); } return new WtsEnumProcessesData() { Processes = returnProcesses, Memory = data }; } public struct WtsEnumProcessesFastData { public int[] PIDs; public int[] SIDs; public WtsMemoryAlloc Memory; } public unsafe static WtsEnumProcessesFastData TSEnumProcessesFast() { IntPtr processes; int count; int[] pids; int[] sids; WTSEnumerateProcesses(0, 0, 1, out processes, out count); pids = new int[count]; sids = new int[count]; WtsMemoryAlloc data = WtsMemoryAlloc.FromPointer(processes); int* dataP = (int*)data.Memory.ToPointer(); for (int i = 0; i < count; i++) { pids[i] = dataP[i * 4 + 1]; sids[i] = dataP[i * 4 + 3]; } return new WtsEnumProcessesFastData() { PIDs = pids, SIDs = sids, Memory = data }; } #endregion #region UDP public static MIB_UDPSTATS GetUdpStats() { MIB_UDPSTATS udpStats = new MIB_UDPSTATS(); GetUdpStatistics(ref udpStats); return udpStats; } public static MIB_UDPTABLE_OWNER_PID GetExUdpConnexions() { MIB_UDPTABLE_OWNER_PID returnUdpExConnexions; int bufferSize = 100000; IntPtr lpTable = Marshal.AllocHGlobal(bufferSize); if (AllocateAndGetUdpExTableFromStack(ref lpTable, true, GetProcessHeap(), 0, 2) != 0) { throw new Exception("AllocateAndGetUdpExTableFromStack failed"); } int numEntries = (int)Marshal.ReadIntPtr(lpTable); lpTable = IntPtr.Zero; Marshal.FreeHGlobal(lpTable); int rowSize = 12; bufferSize = (numEntries * rowSize) + 4; returnUdpExConnexions = new MIB_UDPTABLE_OWNER_PID(); lpTable = Marshal.AllocHGlobal(bufferSize); if (AllocateAndGetUdpExTableFromStack(ref lpTable, true, GetProcessHeap(), 0, 2) != 0) { throw new Exception("AllocateAndGetUdpExTableFromStack failed"); } IntPtr current = lpTable; int currentIndex = 0; numEntries = (int)Marshal.ReadIntPtr(current); returnUdpExConnexions.NumEntries = numEntries; returnUdpExConnexions.Table = new MIB_UDPROW_OWNER_PID[numEntries]; currentIndex += 4; current = (IntPtr)((int)current + currentIndex); for (int i = 0; i < numEntries; i++) { uint localAddr = (uint)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); uint localPort = (uint)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); returnUdpExConnexions.Table[i].Local = new System.Net.IPEndPoint(localAddr, (int)ConvertPort(localPort)); returnUdpExConnexions.Table[i].OwningProcessId = (int)Marshal.ReadIntPtr(current); current = (IntPtr)((int)current + 4); } Marshal.FreeHGlobal(lpTable); current = IntPtr.Zero; return returnUdpExConnexions; } private static ushort ConvertPort(uint port) { byte[] b = new Byte[2]; b[0] = byte.Parse((port >> 8).ToString()); b[1] = byte.Parse((port & 0xFF).ToString()); return BitConverter.ToUInt16(b, 0); } #endregion } }