在某公司实习完,再次回到寝室。还是在学校好。
实习期间的给我的任务就是为项目添加一个强行删除的模块。
背景是硬盘上存储空间不够时,需要删掉老的文件,如果这时后,老的文件被打开了,没有关掉,就无法删除。所以叫我写一个这样的功能。
所谓干净,指的是释放掉这个被占用的句柄。强行删除的方法很多,用驱动直接发磁盘IRP。等等
查阅相关资料后。整理思路如下,如果有同学以后要写这样的功能,可以参考,
1.ZwQuerySystemInformation获取系统当前句柄列表.
2.遍历这个列表,跳过PID==4的 句柄(惹不起)。跳过不是文件的句柄
3. 根据PID判断,如果是自身进程打开的,直接取此句柄
dwCurProcessId==pSystemsHandleInformation->Handles[dwIndex].ProcessID
如果不是,DuplicateHandle相同权限的句柄到自身进程
4.根据句柄用NtQueryInformationFile获取文件路径。
这里必须用NtQueryInformationFile而不是ZwQueryInformationFile
MSDN上说的很清楚。
If the call to this function occurs in user mode, you should use the name "NtQueryInformationFile"
instead of "ZwQueryInformationFile".
这个获取到的路径是不带盘符的,盘符要用GetFileInformationByHandle,通过比对VolumeSerialNumber单独处理.
注意:
NtQueryInformationFile是堵塞的,可能会一直卡在那里不返回,所以要放到单独的线程里执行并设置超时。
我一开始想偷懒,直接用ZwQueryObject,这样获取到的就是完整的设备格式路径,不用处理盘符。
在实际编写时会发现,虽然NtQueryInformationFile和ZwQueryObject都是堵塞的,
在线程超时后,TerminateThread线程时,如果是ZwQueryObject就会导致内存泄露,句柄无法释放,
主线程退出后,发现任务管理器进程还在,CodeProject上有人提出过这个问题,不过他最后是用驱动解决的,
而我一开始就限制了,为了稳定必须在ring3下
如果我用NtQueryInformationFile,然后超时时TerminateThread就没事。
5.比对输入的文件路径和获取到文件路径(要自己处理带盘符的完整DOS风格路径)
获取此时的PID和handle,
6.打开指定PID的进程,创建远程线程closehandle,关闭掉指定的handle.
7.因为一个文件可能被多个句柄占用,所以第一次获取到之后不能自己返回,要继续,获取一个pid和handle就
用远程线程结束一次。一直到没有句柄占用此文件。
-----------------------------------------------------------------------------
无图无真相,我用千千静听做的测试。
千千静听在播放D:\www\Music\the mass-era.mp3文件时,这个时候,普通删除是无法删除掉这个文件的,
会提示
运行demo程序。拖放文件到路径框
点击删除后,显示当前占用此文件的进程和句柄相关信息,随即关闭这个句柄,并成功删除掉了文件
在单曲循环模式下,此时千千静听会直接卡死,只能通过结束进程来推出。
其他模式下,会自动跳到下一曲。
---------------------------------------------------------------------------
//提升自身到Debug权限
BOOL MyEnableDebugPriority(VOID)
{
HANDLE hTokenHandle=NULL;
TOKEN_PRIVILEGES TokenPrivileges;
BOOL bFlag=FALSE;
//打开自身进程令牌
bFlag=OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,&hTokenHandle);
if (!bFlag)
{
bFlag=FALSE;
}
else
{
//查询Debug权限
bFlag=LookupPrivilegeValueW(NULL,SE_DEBUG_NAME,&TokenPrivileges.Privileges[0].Luid);
if (!bFlag)
{
bFlag=FALSE;
}
else
{
TokenPrivileges.PrivilegeCount=1;
TokenPrivileges.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED; //我是想获得权限
bFlag=AdjustTokenPrivileges(hTokenHandle,FALSE,&TokenPrivileges,0,(PTOKEN_PRIVILEGES)NULL,0); //提升权限
}
}
if (hTokenHandle!=NULL)
{
CloseHandle(hTokenHandle);
}
return bFlag;
}
DWORD WINAPI MyQueryFileNameByHandleThreadFunc(LPVOID pParam)
{
FILE_INFO *pFileInfo=(FILE_INFO*)pParam;
WCHAR wcVolume[3]={0};
NTSTATUS MyNtStatus;
IO_STATUS_BLOCK IoStatus;
UCHAR szBuff[0x1000];
RtlZeroMemory(szBuff,sizeof(szBuff));
FILE_NAME_INFORMATION *pFileNameInformation=(FILE_NAME_INFORMATION*)szBuff;
MyNtStatus=NtQueryInformationFile(pFileInfo->hFileHandle,&IoStatus,pFileNameInformation,
sizeof(FILE_INFO)-sizeof(HANDLE)-sizeof(BOOL),FileNameInformationClass);
if(NT_SUCCESS(MyNtStatus))
{
if(pFileNameInformation->FileNameLength!=0)
{
pFileInfo->bFlag=TRUE;
pFileInfo->FileNameInfo.FileNameLength=pFileNameInformation->FileNameLength;
if (MyGetVolumeNameByHandle(pFileInfo->hFileHandle,wcVolume))// 得到盘符
{
RtlZeroMemory(pFileInfo->FileNameInfo.FileName,sizeof(pFileInfo->FileNameInfo.FileName));
pFileInfo->FileNameInfo.FileName[0]=wcVolume[0];
pFileInfo->FileNameInfo.FileName[1]=wcVolume[1];
wcsncpy(&pFileInfo->FileNameInfo.FileName[2],
pFileNameInformation->FileName,
pFileNameInformation->FileNameLength);
pFileInfo->FileNameInfo.FileName[2+pFileNameInformation->FileNameLength-1]=0;
}
}
}
return 0;
}
//获取当前操作系统中文件句柄 的object值
//这里必须用NtQuerySystemInformation
UCHAR MyGetOsFileHandleObject(VOID)
{
UCHAR ucResult=0;
DWORD dwSize=100;
DWORD dwNeedSize=0;
NTSTATUS MyNtStatus;
DWORD dwCurProcessId=GetCurrentProcessId();
DWORD dwIndex=0;
HANDLE hTempFile=CreateFileW(_T("C:\\boot.ini"),0,0,NULL,OPEN_EXISTING,0,0);
SYSTEM_HANDLE_INFORMATION* pSystemsHandleInformation = (SYSTEM_HANDLE_INFORMATION*)VirtualAlloc( NULL,dwSize, MEM_COMMIT, PAGE_READWRITE );
if (NULL==pSystemsHandleInformation)
{
return 0;
}
MyNtStatus=NtQuerySystemInformation(SystemHandleInformation,pSystemsHandleInformation,dwSize,&dwNeedSize);
if (STATUS_INFO_LENGTH_MISMATCH==MyNtStatus)
{
if (0 == dwNeedSize)
{
ucResult=0;
goto MyErrorExit;
}
else
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
pSystemsHandleInformation = (SYSTEM_HANDLE_INFORMATION*)VirtualAlloc( NULL,dwNeedSize, MEM_COMMIT, PAGE_READWRITE );
if(NULL==pSystemsHandleInformation)
{
ucResult=0;
goto MyErrorExit;
}
}
}
MyNtStatus=NtQuerySystemInformation(SystemHandleInformation,pSystemsHandleInformation,dwNeedSize,NULL);
if (!NT_SUCCESS(MyNtStatus))
{
goto MyErrorExit;
}
for (dwIndex=0;dwIndex<pSystemsHandleInformation->Count;dwIndex++)
{
if( (dwCurProcessId==pSystemsHandleInformation->Handles[dwIndex].ProcessID) &&
(hTempFile==(HANDLE)pSystemsHandleInformation->Handles[dwIndex].HandleNumber) )
{
ucResult=(UCHAR)pSystemsHandleInformation->Handles[dwIndex].HandleType; //得到句柄类型
OutputDebugStringW(_T("\n得到句柄类型\n"));
break;
}
}
MyErrorExit:
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hTempFile!=INVALID_HANDLE_VALUE)
{
CloseHandle(hTempFile);
}
return ucResult;
}
//遍历系统当前所有文件句柄,每得到一个,就查这个句柄对应的文件名
DWORD WINAPI MyLookupHandleThreadFunc(LPVOID pParam)
{
LOOKUP_INFO * pLockorInfo=(LOOKUP_INFO*)pParam;
DWORD dwSize=100;
DWORD dwNeedSize=0;
NTSTATUS MyNtStatus;
DWORD dwCurProcessId=GetCurrentProcessId();
DWORD dwIndex=0;
BOOL bRemoteFlag=FALSE;
HANDLE hRemoteProcess=NULL;
HANDLE hCurProcess=GetCurrentProcess();
BOOL bDupliFlag=FALSE;
HANDLE hTureHandle=NULL;
SYSTEM_HANDLE_INFORMATION* pSystemsHandleInformation = (SYSTEM_HANDLE_INFORMATION*)VirtualAlloc( NULL,dwSize, MEM_COMMIT, PAGE_READWRITE );
if (NULL==pSystemsHandleInformation)
{
pLockorInfo->bFlag=FALSE;
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hCurProcess!=NULL)
{
CloseHandle(hCurProcess);
}
return -1;
}
MyNtStatus=NtQuerySystemInformation(SystemHandleInformation,pSystemsHandleInformation,dwSize,&dwNeedSize);
if (STATUS_INFO_LENGTH_MISMATCH==MyNtStatus)
{
if (0 == dwNeedSize)
{
pLockorInfo->bFlag=FALSE;
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hCurProcess!=NULL)
{
CloseHandle(hCurProcess);
}
return -1;
}
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
pSystemsHandleInformation = (SYSTEM_HANDLE_INFORMATION*)VirtualAlloc( NULL,dwNeedSize, MEM_COMMIT, PAGE_READWRITE );
if(NULL==pSystemsHandleInformation)
{
pLockorInfo->bFlag=FALSE;
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hCurProcess!=NULL)
{
CloseHandle(hCurProcess);
}
return -1;
}
}
MyNtStatus=NtQuerySystemInformation(SystemHandleInformation,pSystemsHandleInformation,dwNeedSize,NULL);
if (!NT_SUCCESS(MyNtStatus))
{
pLockorInfo->bFlag=FALSE;
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hCurProcess!=NULL)
{
CloseHandle(hCurProcess);
}
return -1;
}
for (dwIndex=0;dwIndex<pSystemsHandleInformation->Count;dwIndex++)
{
if(4==pSystemsHandleInformation->Handles[dwIndex].ProcessID) //system惹不起
{
continue;
}
//不是文件句柄的直接54
if (pLockorInfo->ucOSFileHandleType!=pSystemsHandleInformation->Handles[dwIndex].HandleType)
{
continue;
}
if (dwCurProcessId==pSystemsHandleInformation->Handles[dwIndex].ProcessID)
{
bRemoteFlag=FALSE;
hTureHandle=(HANDLE)pSystemsHandleInformation->Handles[dwIndex].HandleNumber;
}
else
{
bRemoteFlag=TRUE;
hRemoteProcess=OpenProcess(PROCESS_DUP_HANDLE,FALSE,pSystemsHandleInformation->Handles[dwIndex].ProcessID);
if (hRemoteProcess!=NULL)
{
bDupliFlag=DuplicateHandle(hRemoteProcess,(HANDLE)pSystemsHandleInformation->Handles[dwIndex].HandleNumber,
hCurProcess,&hTureHandle,0,FALSE,DUPLICATE_SAME_ACCESS); //复制相同权限的handle
if (!bDupliFlag)
{
hTureHandle=NULL;
}
}
if (hRemoteProcess!=NULL)
{
CloseHandle(hRemoteProcess);
}
}
if (hTureHandle!=NULL)
{
//根据文件句柄获取文件路径
if (MyGetFileNameByHandle(hTureHandle,pLockorInfo->szFileName))
{
pLockorInfo->bFlag=TRUE;
pLockorInfo->dwLockProcessId=pSystemsHandleInformation->Handles[dwIndex].ProcessID;
pLockorInfo->wLockHandle=pSystemsHandleInformation->Handles[dwIndex].HandleNumber;
MyCloseRemoteHandle(pSystemsHandleInformation->Handles[dwIndex].ProcessID,
(HANDLE)pSystemsHandleInformation->Handles[dwIndex].HandleNumber);
}
//每一次使用后,清理
if (bRemoteFlag)
{
CloseHandle(hTureHandle);
}
}
}
if (pSystemsHandleInformation!=NULL)
{
VirtualFree(pSystemsHandleInformation, 0, MEM_RELEASE);
}
if (hCurProcess!=NULL)
{
CloseHandle(hCurProcess);
}
return 0;
}
//根据文件句柄获取文件路径的线程
BOOL MyGetFileNameByHandle(__in HANDLE hFileHandle,__out WCHAR *szFileName)
{
BOOL bFindFlag=FALSE;
FILE_INFO FileInfo;
RtlZeroMemory(&FileInfo,sizeof(FileInfo));
FileInfo.bFlag=FALSE;
FileInfo.hFileHandle=hFileHandle;
HANDLE hQueryThread=CreateThread(NULL,0,MyQueryFileNameByHandleThreadFunc,&FileInfo,0,NULL);
if (WAIT_TIMEOUT == WaitForSingleObject(hQueryThread,100))
{
TerminateThread(hQueryThread,2);
}
if (FileInfo.bFlag)
{
if (0 == wcsicmp(szFileName,FileInfo.FileNameInfo.FileName))
{
bFindFlag=TRUE;
}
}
if (hQueryThread!=NULL)
{
CloseHandle(hQueryThread);
}
return bFindFlag;
}
DWORD WINAPI MyQueryFileNameByHandleThreadFunc(LPVOID pParam)
{
FILE_INFO *pFileInfo=(FILE_INFO*)pParam;
WCHAR wcVolume[3]={0};
NTSTATUS MyNtStatus;
IO_STATUS_BLOCK IoStatus;
UCHAR szBuff[0x1000];
RtlZeroMemory(szBuff,sizeof(szBuff));
FILE_NAME_INFORMATION *pFileNameInformation=(FILE_NAME_INFORMATION*)szBuff;
MyNtStatus=NtQueryInformationFile(pFileInfo->hFileHandle,&IoStatus,pFileNameInformation,
sizeof(FILE_INFO)-sizeof(HANDLE)-sizeof(BOOL),FileNameInformationClass);
if(NT_SUCCESS(MyNtStatus))
{
if(pFileNameInformation->FileNameLength!=0)
{
pFileInfo->bFlag=TRUE;
pFileInfo->FileNameInfo.FileNameLength=pFileNameInformation->FileNameLength;
if (MyGetVolumeNameByHandle(pFileInfo->hFileHandle,wcVolume))// 得到盘符
{
RtlZeroMemory(pFileInfo->FileNameInfo.FileName,sizeof(pFileInfo->FileNameInfo.FileName));
pFileInfo->FileNameInfo.FileName[0]=wcVolume[0];
pFileInfo->FileNameInfo.FileName[1]=wcVolume[1];
wcsncpy(&pFileInfo->FileNameInfo.FileName[2],
pFileNameInformation->FileName,
pFileNameInformation->FileNameLength);
pFileInfo->FileNameInfo.FileName[2+pFileNameInformation->FileNameLength-1]=0;
}
}
}
return 0;
}
void GetOSVolumeSerialInfo(void)
{
RtlZeroMemory(&VolumeInfo,sizeof(VolumeInfo));
WCHAR szVolumeName[5]= {0};
WCHAR Drive='A';
DWORD dwDiskMask = GetLogicalDrives();
int nIndex=0;
for (nIndex=0; nIndex<26; nIndex++)
{
if( ( (1<<nIndex) & dwDiskMask )!=0)
{
Drive = nIndex + 'A';
wsprintfW(szVolumeName,_T("%c:\\"),Drive);
wsprintfW(VolumeInfo[nIndex].szVolumeName,_T("%c:"),Drive);
GetVolumeInformation(szVolumeName,NULL,0,&VolumeInfo[nIndex].dwVolumeSerial,0,0,0,0);
}
}
}void GetOSVolumeSerialInfo(void)
{
RtlZeroMemory(&VolumeInfo,sizeof(VolumeInfo));
WCHAR szVolumeName[5]= {0};
WCHAR Drive='A';
DWORD dwDiskMask = GetLogicalDrives();
int nIndex=0;
for (nIndex=0; nIndex<26; nIndex++)
{
if( ( (1<<nIndex) & dwDiskMask )!=0)
{
Drive = nIndex + 'A';
wsprintfW(szVolumeName,_T("%c:\\"),Drive);
wsprintfW(VolumeInfo[nIndex].szVolumeName,_T("%c:"),Drive);
GetVolumeInformation(szVolumeName,NULL,0,&VolumeInfo[nIndex].dwVolumeSerial,0,0,0,0);
}
}
}
BOOL MyGetVolumeNameByHandle(__in HANDLE hFile,__out WCHAR *szVolume)
{
DWORD dwIndex=0;
BY_HANDLE_FILE_INFORMATION stHandleFileInfo;
RtlZeroMemory(&stHandleFileInfo,sizeof(stHandleFileInfo));
GetFileInformationByHandle(hFile,&stHandleFileInfo);
for(dwIndex=0; dwIndex<26; dwIndex++)
{
if (stHandleFileInfo.dwVolumeSerialNumber!=0)
{
if (stHandleFileInfo.dwVolumeSerialNumber==VolumeInfo[dwIndex].dwVolumeSerial)
{
wcscpy(szVolume,VolumeInfo[dwIndex].szVolumeName);
return TRUE;
}
}
}
return FALSE;
}
//结束pid=dwProcessId中的hRemoteHandle句柄
BOOL MyCloseRemoteHandle(__in DWORD dwProcessId,__in HANDLE hRemoteHandle)
{
HANDLE hExecutHandle=NULL;
BOOL bFlag=FALSE;
HANDLE hProcess=NULL;
HMODULE hKernel32Module=NULL;
hProcess=OpenProcess(
PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ,
FALSE,dwProcessId);
if (NULL==hProcess)
{
bFlag=FALSE;
goto MyErrorExit;
}
hKernel32Module = LoadLibrary( _T("kernel32.dll") );
hExecutHandle = CreateRemoteThread(hProcess,0,0,
(DWORD (__stdcall *)( void *))GetProcAddress(hKernel32Module,"CloseHandle"),
hRemoteHandle,0,NULL);
if (NULL==hExecutHandle)
{
bFlag=FALSE;
goto MyErrorExit;
}
if (WaitForSingleObject(hExecutHandle,2000)==WAIT_OBJECT_0)
{
bFlag=TRUE;
goto MyErrorExit;
}
else
{
bFlag=FALSE;
goto MyErrorExit;
}
MyErrorExit:
if (hExecutHandle!=NULL)
{
CloseHandle(hExecutHandle);
}
if (hProcess !=NULL)
{
CloseHandle(hProcess);
}
if (hKernel32Module!=NULL)
{
FreeLibrary(hKernel32Module);
}
return bFlag;
}
//根据PID获取进程名
BOOL MyGetProcessNameByPID(DWORD dwProcessId,WCHAR *szProcessName)
{
BOOL bReturnFlag=FALSE;
PROCESSENTRY32* pProcessInfo=new PROCESSENTRY32;
pProcessInfo->dwSize=sizeof(PROCESSENTRY32);
HANDLE MyHandProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bFlag=Process32First(MyHandProcessSnap,pProcessInfo);
while (bFlag)
{
if (dwProcessId==pProcessInfo->th32ProcessID)
{
wcscpy(szProcessName,pProcessInfo->szExeFile);
bReturnFlag=TRUE;
break;
}
bFlag=Process32Next(MyHandProcessSnap,pProcessInfo);
}
if(pProcessInfo!=NULL)
{
delete pProcessInfo;
pProcessInfo=NULL;
}
if (MyHandProcessSnap!=NULL)
{
CloseHandle(MyHandProcessSnap);
}
return bReturnFlag;
}
//根据PID获取进程路径
BOOL MyGetProcessPathByPID(DWORD dwProcessId,WCHAR *szProcessPath)
{
HANDLE hModule;
MODULEENTRY32* pMoudleInfo=new MODULEENTRY32;
pMoudleInfo->dwSize=sizeof(MODULEENTRY32);
hModule=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwProcessId);
Module32First(hModule,pMoudleInfo);
wcscpy(szProcessPath,pMoudleInfo->szExePath);
if(pMoudleInfo!=NULL)
{
delete pMoudleInfo;
pMoudleInfo=NULL;
}
if (hModule!=NULL)
{
CloseHandle(hModule);
}
return TRUE;
}