File:  [cleanflash] / cleanflash / CleanFlashCommon / HandleUtil.cs
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed Oct 13 09:04:22 2021 UTC (4 years, 7 months ago) by root
Branches: UNKNWN, MAIN
CVS tags: v3400192, HEAD
cleanflash 34.0.0.192

// 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...
                }
            }
        }
    }
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.