有方法读取一个已被其他进程打开且dwSharemode = 0的文件吗?
最近有项目要打开一个已被其他进程打开,且其dwShareMode=0,即其它进程不得共享打开。该如何做呢?久思不得其解,于是去bbs.pediy.com询问[1],网友给出的方法是:
1) 干掉原句柄,比较常用的手段有杀死原进程,远程关闭句柄,创建远程线程调用CloseHandle 2) 复制原句柄,打开进程用DuplicateHandle复制一份即可使用 3) 如果使用驱动的话,直接下发IRP读文件内容即可,缺点是也许需要硬编码
|
我选用了第二种方法,果然能解决问题。但是中间也遇到了一些问题,本文来说说这中间遇到的问题。
既然是使用DuplicateHandle,那么必须知道原进程的PID,和文件在该进程中的句柄Handle。
所以程序的思路是先遍历系统中所有进程,然后或则进程中文件句柄,接着使用文件句柄获得文件名称——这样便可以得到PID和Handle了。
一. 遍历系统所有进程
可以使用多种方法遍历系统中所有进程,一般使用ZwQuerySystemInformation(SystemProcessesAndThreadsInformation)或者CreateToolhelp32Snapshot来实现。
二. 遍历句柄
句柄的遍历使用ZwQuerySystemInformation(SystemHandleInformation),该函数列出所有进程的所有句柄,然后使用TSYSTEM_HANDLE_INFORMATION结构体中的ProcessId来过滤出指定进程的句柄。
这里的句柄是各种类型。可以通过TSYSTEM_HANDLE_INFORMATION结构体中的ObjectTypeNumber来判断句柄类型。在程序中我使用File=28硬编码,因为我发现在Win7和XP下是成立的,
但是有网友[3]讨论说不是所有的系统都是这样的,于是我尝试遍历系统中所有的类型对象(type object),方法是使用ZwQueryObject(ObjectAllTypesInformation)。本想通过序号实时找出当前系统中的类型值,但是遗憾是序号值不对。
三. 根据文件句柄获取文件名
只有获得句柄对应的文件名,通过比较才能知道是否是想要的文件句柄,所以当务之急是获得文件名。但是得先通过调用OpenProcess和DuplicateHandle来复制一个句柄。然后再使用下面两类方法中的一种:
1. 使用ZwQueryInformationFile
该函数返回一个TFILE_NAME_INFORMATION结构,这个结构包括文件的名称,但是不包括驱动器名,所以需要通过其他方法(见下)获得驱动器名,将上述两者相连方可获得整个文件名了。
这个方法有个问题,在某些进程(如qq.exe)在调用ZwQueryInformationFile时会使调用程序“死掉”。难道qq这些程序有什么保护方法,不让复制的句柄来使用ZwQueryInformationFile?
2. 使用GetMappedFileName
这种方法使用CreateFileMapping、MapViewOfFile和GetMappedFileName获得文件名——同样没有驱动器名,所以也得通过其他方法(见下)获得驱动器名。
这种方法也有缺陷,不能调用CreateFileMapping打开没有访问权限目录中的文件。
3. 获得驱动器名
有两种方法获得驱动器名:
1) GetFileInformationByHandle和GetLogicalDriveStringsA比较
2) GetLogicalDriveStrings和QueryDosDevice比较
GetFileInformationByHandle也会使程序“死掉”,所以慎用。
四. 大功告成
知道了进程pid和句柄值,就可以通过复制句柄的方法访问文件了——这其实在前面已经这么做过一次了——获得文件名不正是复制句柄来实现的吗?
不过,在使用文件指针访问文件(如调用ReadFile和WriteFile)时要注意:因为原句柄和复制句柄共享一个文件指针,所以可能会造成指针位置的混乱。
五. 参考文献
1. http://bbs.pediy.com/showthread.php?t=198566
2. 有关遍历进程中句柄的方法总结http://blog.sina.com.cn/s/blog_58e7a03b01014c9e.html
3. http://bbs.pediy.com/showthread.php?t=179951
4. [Delphi]从handle获得文件名
http://cndkervip.blog.163.com/blog/static/32546863200772311416631/