• 关闭其它进程占用的文件句柄


    当我们启动一个子进程,

    打开读写管道,同时设置子进程继承这些管道的句柄,

    我们就可以操作子进程的标准输入和标准输出了。

    这样有个弊端,子进程会继承父进程打开的所有文件句柄。

    如果子进程不退出,一直持续任务,那么,被打开的文件就无法重命名和删除了。

    有两个解决的办法:

    1. CreateFile的时候属性参数直接指定不允许继承句柄。但是这个方法有个弊端:

        c/c++的文件操作并没有提供属性设置,默认的属性是被继承的。

        如果写的是跨平台程序必须使用标准库时,就没有办法了。

    2. 使用系统API枚举所有打开的句柄内核对象,然后关闭这些内核对象。

        代码如下:

        头文件定义如下:

     1 #pragma warning(disable: 4996) // _tcsnicmp deprecated
     2 #include <winternl.h>
     3 
     4 // This makro assures that INVALID_HANDLE_VALUE (0xFFFFFFFF) returns FALSE
     5 #define IsConsoleHandle(h) (((((ULONG_PTR)h) & 0x10000003) == 0x3) ? TRUE : FALSE)
     6 #define SystemHandleInformation (SystemProcessInformation + 11)
     7 #define ObjectNameInformation (ObjectTypeInformation - 1)
     8 
     9 struct OBJECT_NAME_INFORMATION
    10 {
    11     UNICODE_STRING Name; // defined in winternl.h
    12     TCHAR NameBuffer;
    13 };
    14 
    15 typedef NTSTATUS(NTAPI* PNtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);
    16 
    17 typedef LONG NTSTATUS;
    18 
    19 typedef struct _SYSTEM_HANDLE
    20 {
    21     DWORD       dwProcessId;
    22     BYTE        bObjectType;
    23     BYTE        bFlags;
    24     WORD        wValue;
    25     PVOID       pAddress;
    26     DWORD GrantedAccess;
    27 }SYSTEM_HANDLE;
    28 
    29 typedef struct _SYSTEM_HANDLE_INFORMATION
    30 {
    31     DWORD         dwCount;
    32     SYSTEM_HANDLE Handles[1];
    33 } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION, **PPSYSTEM_HANDLE_INFORMATION;
    34 
    35 
    36 typedef NTSTATUS(WINAPI *PNtQuerySystemInformation)
    37 (IN    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    38 OUT    PVOID                     SystemInformation,
    39 IN    ULONG                     SystemInformationLength,
    40 OUT    PULONG                     ReturnLength OPTIONAL);

    实现代码:

    // returns
    // "DeviceHarddiskVolume3"                                (Harddisk Drive)
    // "DeviceHarddiskVolume3Temp"                           (Harddisk Directory)
    // "DeviceHarddiskVolume3Temp	ransparent.jpeg"          (Harddisk File)
    // "DeviceHarddisk1DP(1)0-0+6foto.jpg"                  (USB stick)
    // "DeviceTrueCryptVolumePDataPasswords.txt"            (Truecrypt Volume)
    // "DeviceFloppy0Autoexec.bat"                           (Floppy disk)
    // "DeviceCdRom1VIDEO_TSVTS_01_0.VOB"                   (DVD drive)
    // "DeviceSerial1"                                        (real COM port)
    // "DeviceUSBSER000"                                      (virtual COM port)
    // "DeviceMupComputerNameC$Boot.ini"                   (network drive share,  Windows 7)
    // "DeviceLanmanRedirectorComputerNameC$Boot.ini"      (network drive share,  Windwos XP)
    // "DeviceLanmanRedirectorComputerNameSharesDance.m3u" (network folder share, Windwos XP)
    // "DeviceAfd"                                            (internet socket)
    // "DeviceConsole000F"                                    (unique name for any Console handle)
    // "DeviceNamedPipePipename"                             (named pipe)
    // "BaseNamedObjectsObjectname"                           (named mutex, named event, named semaphore)
    // "REGISTRYMACHINESOFTWAREClasses.txt"                (HKEY_CLASSES_ROOT.txt)
    DWORD GetNtPathFromHandle(HANDLE h_File, CString* ps_NTPath)
    {
        if (h_File == 0 || h_File == INVALID_HANDLE_VALUE)
            return ERROR_INVALID_HANDLE;
    
        // NtQueryObject() returns STATUS_INVALID_HANDLE for Console handles
        if (IsConsoleHandle(h_File))
        {
            ps_NTPath->Format(_T("\Device\Console%04X"), (DWORD)(DWORD_PTR)h_File);
            return ERROR_SUCCESS;
        }
    
        BYTE  u8_Buffer[2000];
        DWORD u32_ReqLength = 0;
    
        UNICODE_STRING* pk_Info = &((OBJECT_NAME_INFORMATION*)u8_Buffer)->Name;
        pk_Info->Buffer = 0;
        pk_Info->Length = 0;
    
        HMODULE h_NtDll = GetModuleHandle(_T("Ntdll.dll")); // Ntdll is loaded into EVERY process!
        PNtQueryObject NtQueryObject = (PNtQueryObject)GetProcAddress(h_NtDll, "NtQueryObject");
        if (NtQueryObject == NULL)
        {
            return ERROR_FUNCTION_FAILED;
        }
    
        // IMPORTANT: The return value from NtQueryObject is bullshit! (driver bug?)
        // - The function may return STATUS_NOT_SUPPORTED although it has successfully written to the buffer.
        // - The function returns STATUS_SUCCESS although h_File == 0xFFFFFFFF
        NtQueryObject(h_File, OBJECT_INFORMATION_CLASS(ObjectNameInformation), u8_Buffer, sizeof(u8_Buffer), &u32_ReqLength);
    
        // On error pk_Info->Buffer is NULL
        if (!pk_Info->Buffer || !pk_Info->Length)
            return ERROR_FILE_NOT_FOUND;
    
        pk_Info->Buffer[pk_Info->Length / 2] = 0; // Length in Bytes!
    
        *ps_NTPath = pk_Info->Buffer;
        return ERROR_SUCCESS;
    }
    
    // converts
    // "DeviceHarddiskVolume3"                                -> "E:"
    // "DeviceHarddiskVolume3Temp"                           -> "E:Temp"
    // "DeviceHarddiskVolume3Temp	ransparent.jpeg"          -> "E:Temp	ransparent.jpeg"
    // "DeviceHarddisk1DP(1)0-0+6foto.jpg"                  -> "I:foto.jpg"
    // "DeviceTrueCryptVolumePDataPasswords.txt"            -> "P:DataPasswords.txt"
    // "DeviceFloppy0Autoexec.bat"                           -> "A:Autoexec.bat"
    // "DeviceCdRom1VIDEO_TSVTS_01_0.VOB"                   -> "H:VIDEO_TSVTS_01_0.VOB"
    // "DeviceSerial1"                                        -> "COM1"
    // "DeviceUSBSER000"                                      -> "COM4"
    // "DeviceMupComputerNameC$Boot.ini"                   -> "\ComputerNameC$Boot.ini"
    // "DeviceLanmanRedirectorComputerNameC$Boot.ini"      -> "\ComputerNameC$Boot.ini"
    // "DeviceLanmanRedirectorComputerNameSharesDance.m3u" -> "\ComputerNameSharesDance.m3u"
    // returns an error for any other device type
    DWORD GetDosPathFromNtPath(const TCHAR* u16_NTPath, CString* ps_DosPath)
    {
        DWORD u32_Error;
    
        if (_tcsnicmp(u16_NTPath, _T("\Device\Serial"), 14) == 0 || // e.g. "Serial1"
            _tcsnicmp(u16_NTPath, _T("\Device\UsbSer"), 14) == 0)   // e.g. "USBSER000"
        {
            HKEY h_Key;
            if (u32_Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Hardware\DeviceMap\SerialComm"), 0, KEY_QUERY_VALUE, &h_Key))
                return u32_Error;
    
            TCHAR u16_ComPort[50];
    
            DWORD u32_Type;
            DWORD u32_Size = sizeof(u16_ComPort);
            if (u32_Error = RegQueryValueEx(h_Key, u16_NTPath, 0, &u32_Type, (BYTE*)u16_ComPort, &u32_Size))
            {
                RegCloseKey(h_Key);
                return ERROR_UNKNOWN_PORT;
            }
    
            *ps_DosPath = u16_ComPort;
            RegCloseKey(h_Key);
            return ERROR_SUCCESS;
        }
    
        if (_tcsnicmp(u16_NTPath, _T("\Device\LanmanRedirector\"), 25) == 0) // Win XP
        {
            *ps_DosPath = _T("\\");
            *ps_DosPath += (u16_NTPath + 25);
            return ERROR_SUCCESS;
        }
    
        if (_tcsnicmp(u16_NTPath, _T("\Device\Mup\"), 12) == 0) // Win 7
        {
            *ps_DosPath = _T("\\");
            *ps_DosPath += (u16_NTPath + 12);
            return ERROR_SUCCESS;
        }
    
        TCHAR u16_Drives[300];
        if (!GetLogicalDriveStrings(300, u16_Drives))
            return GetLastError();
    
        TCHAR* u16_Drv = u16_Drives;
        while (u16_Drv[0])
        {
            TCHAR* u16_Next = u16_Drv + _tcslen(u16_Drv) + 1;
    
            u16_Drv[2] = 0; // the backslash is not allowed for QueryDosDevice()
    
            TCHAR u16_NtVolume[1000];
            u16_NtVolume[0] = 0;
    
            // may return multiple strings!
            // returns very weird strings for network shares
            if (!QueryDosDevice(u16_Drv, u16_NtVolume, sizeof(u16_NtVolume) / sizeof(TCHAR)))
                return GetLastError();
    
            int s32_Len = (int)_tcslen(u16_NtVolume);
            if (s32_Len > 0 && _tcsnicmp(u16_NTPath, u16_NtVolume, s32_Len) == 0)
            {
                *ps_DosPath = u16_Drv;
                *ps_DosPath += (u16_NTPath + s32_Len);
                return ERROR_SUCCESS;
            }
    
            u16_Drv = u16_Next;
        }
        return ERROR_BAD_PATHNAME;
    }
    
    //EnableTokenPrivilege( SE_DEBUG_NAME );
    BOOL EnableTokenPrivilege(LPCTSTR pszPrivilege)
    {
        // do it only once
        static bool bEnabled = false;
        if (bEnabled)
        {
            return TRUE;
        }
        bEnabled = true;
    
        HANDLE hToken = 0;
        TOKEN_PRIVILEGES tkp = { 0 };
    
        // Get a token for this process.
        if (!OpenProcessToken(GetCurrentProcess(),
            TOKEN_ADJUST_PRIVILEGES |
            TOKEN_QUERY, &hToken))
        {
            return FALSE;
        }
    
        // Get the LUID for the privilege. 
        if (LookupPrivilegeValue(NULL, pszPrivilege,
            &tkp.Privileges[0].Luid))
        {
            tkp.PrivilegeCount = 1;  // one privilege to set    
            tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
            // Set the privilege for this process. 
            AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
                (PTOKEN_PRIVILEGES)NULL, 0);
    
            if (GetLastError() != ERROR_SUCCESS)
                return FALSE;
    
            return TRUE;
        }
    
        return FALSE;
    }
    
    /*
     * Basically, with the above code, we can close the handles created by another process. 
     * After closing the handle, we can rename or delete that file or directory. 
     * But there are cases where after closing the handle, we can rename the folder but deleting is not possible.
     * using this option on a DLL used by another process is just ignored.
    */
    BOOL CloseHandleWithProcess(SYSTEM_HANDLE& sh)
    {
        HANDLE hFile = (HANDLE)sh.wValue;
        HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, sh.dwProcessId);
        if (hProcess)
        {
            HANDLE hDup = 0;
            BOOL b = DuplicateHandle(hProcess, hFile, GetCurrentProcess(),
                &hDup, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_CLOSE_SOURCE);
            if (hDup)
            {
                CloseHandle(hDup);
            }
    
            CloseHandle(hProcess);
        }    
    
        return FALSE;
    }
    
    BOOL RenameWithClose(const CString& srcpath, const CString& dstpath)
    {
        if (MoveFileEx(srcpath, dstpath, MOVEFILE_REPLACE_EXISTING))
        {
            return TRUE;
        }
        else
        {
            // The process cannot access the file because it is being used by another process
            // Try to close file handle used by another process
            if (GetLastError() == ERROR_SHARING_VIOLATION)
            {
                HMODULE hModule = GetModuleHandle(_T("ntdll.dll"));
                PNtQuerySystemInformation NtQuerySystemInformation = (PNtQuerySystemInformation)GetProcAddress(hModule, "NtQuerySystemInformation");
                if (NtQuerySystemInformation == NULL)
                {
                    SetLastError(ERROR_SHARING_VIOLATION);
                    return FALSE;
                }
                else
                {
                    // Get the list of all handles in the system
                    PSYSTEM_HANDLE_INFORMATION pSysHandleInformation = new SYSTEM_HANDLE_INFORMATION;
                    DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
                    DWORD needed = 0;
                    NTSTATUS status = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS(SystemHandleInformation), pSysHandleInformation, size, &needed);
                    if (!NT_SUCCESS(status))
                    {
                        if (0 == needed)
                        {
                            delete pSysHandleInformation;
                            SetLastError(ERROR_SHARING_VIOLATION);
                            return FALSE;// some other error
                        }
                        // The previously supplied buffer wasn't enough.
                        delete pSysHandleInformation;
                        size = needed + 1024;
                        pSysHandleInformation = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
                        status = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS(SystemHandleInformation), pSysHandleInformation, size, &needed);
                        if (!NT_SUCCESS(status))
                        {
                            // some other error so quit.
                            delete pSysHandleInformation;
                            SetLastError(ERROR_SHARING_VIOLATION);
                            return FALSE;
                        }
                    }
    
                    // iterate over every handle
                    for (DWORD i = 0; i < pSysHandleInformation->dwCount; i++)
                    {
                        HANDLE hDup = (HANDLE)pSysHandleInformation->Handles[i].wValue;
                        if (pSysHandleInformation->Handles[i].dwProcessId == GetCurrentProcessId())
                        {
                            CString strNtPath;
                            CString strDosPath;
                            GetNtPathFromHandle(hDup, &strNtPath);
                            GetDosPathFromNtPath(strNtPath, &strDosPath);
                            if (strDosPath == srcpath)
                            {
                                _tprintf(_T("%s: (process id: %d) (filetype: %d) with handle 0x%x
    "), (LPCTSTR)strDosPath,
                                    pSysHandleInformation->Handles[i].dwProcessId, pSysHandleInformation->Handles[i].bObjectType,
                                    pSysHandleInformation->Handles[i].wValue);
                                
                                //now we can close file open by another process
                                //do rename or delete file again
                                //EnableTokenPrivilege(SE_DEBUG_NAME);
                                //CloseHandleWithProcess(pSysHandleInformation->Handles[i]);
                            }
                        }
                    }
    
                    delete pSysHandleInformation;
                }
            }
            else
            {
                return FALSE;
            }
        }
    
        return TRUE;
    }
  • 相关阅读:
    天国近了(一) 揭穿OOP神话
    自然思考:流程任意定制的一个问题
    风水占卜是迷信吗?
    飘浮的鸡蛋壳真正的原理研究
    ListView
    Java与WCF交互(一):Java客户端调用WCF服务 (转)
    WPF ListView 数据绑定
    .NET中Trace类和Debug类的应用举例(转)
    Print Visual Tree
    EntityFramework之领域驱动设计实践(六)(转)
  • 原文地址:https://www.cnblogs.com/jojodru/p/5346029.html
Copyright © 2020-2023  润新知