• 凉风有兴,秋月无边, 亏我思娇的情绪好比度日如年。
  • 虽然我不是玉树临风,潇洒倜傥, 可是我有我广阔的胸襟,加强健的臂腕!

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

网站推广 villain 5个月前 (01-07) 111次浏览 已收录 0个评论

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

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

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

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

有两个解决的办法:

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

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

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

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

代码如下:

文件定义如下:

 1 #pragma warning(disable: 4996) // _tcsnicmp deprecated
 2 #include winternl.h 
 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)
 9 struct OBJECT_NAME_INFORMATION
11 UNICODE_STRING Name; // defined in winternl.h
12 TCHAR NameBuffer;
13 };
15 typedef NTSTATUS(NTAPI* PNtQueryObject)(HANDLE Handle, OBJECT_INFORMATION_CLASS Info, PVOID Buffer, ULONG BufferSize, PULONG ReturnLength);
17 typedef LONG NTSTATUS;
19 typedef struct _SYSTEM_HANDLE
21 DWORD dwProcessId;
22 BYTE bObjectType;
23 BYTE bFlags;
24 WORD wValue;
25 PVOID pAddress;
26 DWORD GrantedAccess;
27 }SYSTEM_HANDLE;
29 typedef struct _SYSTEM_HANDLE_INFORMATION
31 DWORD dwCount;
32 SYSTEM_HANDLE Handles[1];
33 } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION, **PPSYSTEM_HANDLE_INFORMATION;
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
// "\Device\HarddiskVolume3" (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp" (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat" (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive)
// "\Device\Serial1" (real COM port)
// "\Device\USBSER000" (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd" (internet socket)
// "\Device\Console000F" (unique name for any Console handle)
// "\Device\NamedPipe\Pipename" (named pipe)
// "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.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
// "\Device\HarddiskVolume3" - "E:"
// "\Device\HarddiskVolume3\Temp" - "E:\Temp"
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" - "E:\Temp\transparent.jpeg"
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" - "I:\foto.jpg"
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" - "P:\Data\Passwords.txt"
// "\Device\Floppy0\Autoexec.bat" - "A:\Autoexec.bat"
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" - "H:\VIDEO_TS\VTS_01_0.VOB"
// "\Device\Serial1" - "COM1"
// "\Device\USBSER000" - "COM4"
// "\Device\Mup\ComputerName\C$\Boot.ini" - "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" - "\\ComputerName\C$\Boot.ini"
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" - "\\ComputerName\Shares\Dance.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\n"), (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;
}


Villain博客 , 版权所有丨如有问题请联系客服QQ:1004619丨
转载请注明关闭其它进程占用的文件句柄
喜欢 (0)
[gqakcom@126.com]
分享 (0)

您必须 登录 才能发表评论!