You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CleanFlashInstaller/CleanFlashCommon/HandleUtil.cs

527 lines
20 KiB
C#

// Taken from: https://github.com/Walkman100/FileLocks
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Linq;
namespace CleanFlashCommon {
public static class HandleUtil {
private static Dictionary<string, string> deviceMap;
private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\";
private const int MAX_PATH = 260;
private const int handleTypeTokenCount = 27;
private static readonly string[] handleTypeTokens = new string[] {
"", "", "Directory", "SymbolicLink", "Token",
"Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant",
"Unknown11", "Semaphore", "Timer", "Profile", "WindowStation",
"Desktop", "Section", "Key", "Port", "WaitablePort",
"Unknown21", "Unknown22", "Unknown23", "Unknown24",
"IoCompletion", "File"
};
internal enum NT_STATUS {
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L)
}
internal enum SYSTEM_INFORMATION_CLASS {
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
internal enum OBJECT_INFORMATION_CLASS {
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
internal enum ProcessAccessRights {
PROCESS_DUP_HANDLE = 0x00000040
}
[Flags]
internal enum DuplicateHandleOptions {
DUPLICATE_CLOSE_SOURCE = 0x1,
DUPLICATE_SAME_ACCESS = 0x2
}
private enum SystemHandleType {
OB_TYPE_UNKNOWN = 0,
OB_TYPE_TYPE = 1,
OB_TYPE_DIRECTORY,
OB_TYPE_SYMBOLIC_LINK,
OB_TYPE_TOKEN,
OB_TYPE_PROCESS,
OB_TYPE_THREAD,
OB_TYPE_UNKNOWN_7,
OB_TYPE_EVENT,
OB_TYPE_EVENT_PAIR,
OB_TYPE_MUTANT,
OB_TYPE_UNKNOWN_11,
OB_TYPE_SEMAPHORE,
OB_TYPE_TIMER,
OB_TYPE_PROFILE,
OB_TYPE_WINDOW_STATION,
OB_TYPE_DESKTOP,
OB_TYPE_SECTION,
OB_TYPE_KEY,
OB_TYPE_PORT,
OB_TYPE_WAITABLE_PORT,
OB_TYPE_UNKNOWN_21,
OB_TYPE_UNKNOWN_22,
OB_TYPE_UNKNOWN_23,
OB_TYPE_UNKNOWN_24,
OB_TYPE_IO_COMPLETION,
OB_TYPE_FILE
};
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_HANDLE_ENTRY {
public int OwnerPid;
public byte ObjectType;
public byte HandleFlags;
public short HandleValue;
public int ObjectPointer;
public int AccessMask;
}
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern SafeProcessHandle OpenProcess(
[In] ProcessAccessRights dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out SafeObjectHandle lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] DuplicateHandleOptions dwOptions);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetProcessId(
[In] IntPtr Process);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int QueryDosDevice(
[In] string lpDeviceName,
[Out] StringBuilder lpTargetPath,
[In] int ucchMax);
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid {
private SafeObjectHandle() : base(true) { }
internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) {
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle() {
return CloseHandle(base.handle);
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid {
private SafeProcessHandle()
: base(true) { }
internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle) {
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle() {
return CloseHandle(base.handle);
}
}
private sealed class OpenFiles : IEnumerable<string> {
private readonly int processId;
internal OpenFiles(int processId) {
this.processId = processId;
}
public IEnumerator<string> GetEnumerator() {
NT_STATUS ret;
int length = 0x10000;
// Loop, probing for required memory.
do {
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try {
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
// CER guarantees that the address of the allocated
// memory is actually assigned to ptr if an
// asynchronous exception occurs.
ptr = Marshal.AllocHGlobal(length);
}
ret = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out int returnLength);
if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) {
// Round required memory up to the nearest 64KB boundary.
length = (returnLength + 0xffff) & ~0xffff;
} else if (ret == NT_STATUS.STATUS_SUCCESS) {
int handleCount = Marshal.ReadInt32(ptr);
int offset = sizeof(int);
int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY));
for (int i = 0; i < handleCount; i++) {
SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY) Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY));
if (handleEntry.OwnerPid == processId) {
IntPtr handle = (IntPtr) handleEntry.HandleValue;
SystemHandleType handleType;
if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE) {
if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out string devicePath)) {
if (ConvertDevicePathToDosPath(devicePath, out string dosPath)) {
if (File.Exists(dosPath)) {
yield return dosPath;
} else if (Directory.Exists(dosPath)) {
yield return dosPath;
}
}
}
}
}
offset += size;
}
}
} finally {
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
}
} while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
private class FileNameFromHandleState : IDisposable {
private readonly ManualResetEvent _mr;
public IntPtr Handle { get; }
public string FileName { get; set; }
public bool RetValue { get; set; }
public FileNameFromHandleState(IntPtr handle) {
_mr = new ManualResetEvent(false);
this.Handle = handle;
}
public bool WaitOne(int wait) {
return _mr.WaitOne(wait, false);
}
public void Set() {
try {
_mr.Set();
} catch { }
}
public void Dispose() {
if (_mr != null) {
_mr.Close();
}
}
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName) {
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try {
int length = 0x200; // 512 bytes
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
// CER guarantees the assignment of the allocated
// memory address to ptr, if an ansynchronous exception
// occurs.
ptr = Marshal.AllocHGlobal(length);
}
NT_STATUS ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW) {
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
// CER guarantees that the previous allocation is freed,
// and that the newly allocated memory address is
// assigned to ptr if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
ptr = Marshal.AllocHGlobal(length);
}
ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
}
if (ret == NT_STATUS.STATUS_SUCCESS) {
fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2);
return fileName.Length != 0;
}
} finally {
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
}
fileName = string.Empty;
return false;
}
private static void GetFileNameFromHandle(object state) {
FileNameFromHandleState s = (FileNameFromHandleState)state;
s.RetValue = GetFileNameFromHandle(s.Handle, out string fileName);
s.FileName = fileName;
s.Set();
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait) {
using (FileNameFromHandleState f = new FileNameFromHandleState(handle)) {
ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f);
if (f.WaitOne(wait)) {
fileName = f.FileName;
return f.RetValue;
} else {
fileName = string.Empty;
return false;
}
}
}
private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName) {
IntPtr currentProcess = GetCurrentProcess();
bool remote = processId != GetProcessId(currentProcess);
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try {
if (remote) {
processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) {
handle = objectHandle.DangerousGetHandle();
}
}
return GetFileNameFromHandle(handle, out fileName, 200);
} finally {
if (remote) {
if (processHandle != null) {
processHandle.Close();
}
if (objectHandle != null) {
objectHandle.Close();
}
}
}
}
private static string GetHandleTypeToken(IntPtr handle) {
NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out int length);
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try {
RuntimeHelpers.PrepareConstrainedRegions();
try { } finally {
if (length >= 0) {
ptr = Marshal.AllocHGlobal(length);
}
}
if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS) {
return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60));
}
} finally {
Marshal.FreeHGlobal(ptr);
}
return string.Empty;
}
private static string GetHandleTypeToken(IntPtr handle, int processId) {
IntPtr currentProcess = GetCurrentProcess();
bool remote = processId != GetProcessId(currentProcess);
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try {
if (remote) {
processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) {
handle = objectHandle.DangerousGetHandle();
}
}
return GetHandleTypeToken(handle);
} finally {
if (remote) {
if (processHandle != null) {
processHandle.Close();
}
if (objectHandle != null) {
objectHandle.Close();
}
}
}
}
private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType) {
for (int i = 1; i < handleTypeTokenCount; i++) {
if (handleTypeTokens[i] == token) {
handleType = (SystemHandleType) i;
return true;
}
}
handleType = SystemHandleType.OB_TYPE_UNKNOWN;
return false;
}
private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType) {
string token = GetHandleTypeToken(handle, processId);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath) {
EnsureDeviceMap();
int i = devicePath.Length;
while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1) {
if (deviceMap.TryGetValue(devicePath.Substring(0, i), out string drive)) {
dosPath = string.Concat(drive, devicePath.Substring(i));
return dosPath.Length != 0;
}
}
dosPath = string.Empty;
return false;
}
private static void EnsureDeviceMap() {
if (deviceMap == null) {
Dictionary<string, string> localDeviceMap = BuildDeviceMap();
Interlocked.CompareExchange(ref deviceMap, localDeviceMap, null);
}
}
private static Dictionary<string, string> BuildDeviceMap() {
string[] logicalDrives = Environment.GetLogicalDrives();
Dictionary<string, string> localDeviceMap = new Dictionary<string, string>(logicalDrives.Length);
StringBuilder lpTargetPath = new StringBuilder(MAX_PATH);
foreach (string drive in logicalDrives) {
string lpDeviceName = drive.Substring(0, 2);
QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH);
localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName);
}
localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\");
return localDeviceMap;
}
private static string NormalizeDeviceName(string deviceName) {
if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0) {
string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1);
return string.Concat(networkDevicePrefix, shareName);
}
return deviceName;
}
/// <summary>
/// Gets the open files enumerator.
/// </summary>
/// <param name="processId">The process id.</param>
/// <returns></returns>
public static IEnumerable<string> GetOpenFilesEnumerator(int processId) {
return new OpenFiles(processId);
}
public static List<Process> GetProcessesUsingFile(string fName) {
List<Process> result = new List<Process>();
foreach (Process p in Process.GetProcesses()) {
try {
if (GetOpenFilesEnumerator(p.Id).Contains(fName)) {
result.Add(p);
}
} catch { } // Some processes will fail.
}
return result;
}
public static void KillProcessesUsingFile(string fName) {
foreach (Process process in GetProcessesUsingFile(fName).OrderBy(o => o.StartTime)) {
try {
process.Kill();
process.WaitForExit();
} catch {
// Oh well...
}
}
}
}
}