打开被独占的文件方法Word格式.docx
《打开被独占的文件方法Word格式.docx》由会员分享,可在线阅读,更多相关《打开被独占的文件方法Word格式.docx(17页珍藏版)》请在冰豆网上搜索。
这种方法不能用于许多情形,比如要在运行的系统中拷贝注册表文件,用这种方法就不会成功。
我们先来试着实现对系统中所有已打开文件的句柄的枚举。
为枚举句柄,每个句柄都由以下结构体描述:
typedefstruct_SYSTEM_HANDLE
{
ULONG
uIdProcess;
UCHAR
ObjectType;
Flags;
USHORT
Handle;
POBJECT
pObject;
ACCESS_MASKGrantedAccess;
}SYSTEM_HANDLE,*PSYSTEM_HANDLE;
这里的ObjectType域定义了句柄所属的对象类型。
这里我们又遇到了问题――File类型的ObjectType在Windows
2000、XP和2003下的取值各不相同,所以我们不得不动态的定义这个值。
为此我们用CreateFile来打开NUL设备,找到它的句柄并记下它的类型:
UCHARGetFileHandleType()
HANDLE
hFile;
PSYSTEM_HANDLE_INFORMATIONInfo;
r;
Result=0;
hFile=CreateFile("
NUL"
GENERIC_READ,0,NULL,OPEN_EXISTING,0,0);
if(hFile!
=INVALID_HANDLE_VALUE)
Info=GetInfoTable(SystemHandleInformation);
if(Info)
for(r=0;
r<
Info->
uCount;
r++)
if(Info->
aSH[r].Handle==(USHORT)hFile&
&
aSH[r].uIdProcess==GetCurrentProcessId())
Result=Info->
aSH[r].ObjectType;
break;
}
}
HeapFree(hHeap,0,Info);
CloseHandle(hFile);
returnResult;
现在知道了句柄的类型我们就可以枚举系统中打开的文件了。
首先我们来用句柄获取打开文件的文件名:
typedefstruct_NM_INFO
FILE_NAME_INFORMATIONInfo;
WCHARName[MAX_PATH];
}NM_INFO,*PNM_INFO;
DWORDWINAPI
GetFileNameThread(PVOIDlpParameter)
PNM_INFO
NmInfo=lpParameter;
IO_STATUS_BLOCKIoStatus;
intr;
NtQueryInformationFile(NmInfo->
hFile,&
IoStatus,&
NmInfo->
Info,
sizeof(NM_INFO)-sizeof(HANDLE),
FileNameInformation);
return0;
voidGetFileName(HANDLEhFile,PCHARTheName)
hThread;
PNM_INFOInfo=HeapAlloc(hHeap,0,sizeof(NM_INFO));
hFile=hFile;
hThread=CreateThread(NULL,0,GetFileNameThread,Info,0,NULL);
if(WaitForSingleObject(hThread,INFINITE)==WAIT_TIMEOUT)
TerminateThread(hThread,0);
CloseHandle(hThread);
memset(TheName,0,MAX_PATH);
WideCharToMultiByte(CP_ACP,0,Info->
Info.FileName,
Info.FileNameLength>
>
1,TheName,MAX_PATH,NULL,NULL);
现在来枚举打开的文件:
voidmain()
CHAR
Name[MAX_PATH];
hProcess,hFile;
hHeap=GetProcessHeap();
ObFileType=GetFileHandleType();
aSH[r].ObjectType==ObFileType)
hProcess=OpenProcess(PROCESS_DUP_HANDLE,FALSE,
aSH[r].uIdProcess);
if(hProcess)
if(DuplicateHandle(hProcess,(HANDLE)Info->
aSH[r].Handle,
GetCurrentProcess(),&
hFile,0,FALSE,
DUPLICATE_SAME_ACCESS))
GetFileName(hFile,Name);
printf("
%s\n"
Name);
CloseHandle(hProcess);
}
现在对于文件的拷贝我们剩下的工作只是找到所需句柄后用ReadFile读取它。
这里一定要使用前面提到的机制,不可疏忽。
这种方法的优点是实现简单,但是其缺点更多,所以这个方法只适用于确定文件被那个进程占用。
打开被独占的文件方法
(二)--修改句柄访问权限
修改句柄访问权限
所有被占用的文件通常都可以用读属性(FILE_READ_ATTRIBUTES)打开,这样就可以读取文件的属性,取得它的大小,枚举NTSF
stream,但遗憾的是,ReadFile就不能成功调用了。
打开文件时各种访问属性的区别在哪里呢?
显然,打开文件时,系统会记录访问属性,之后会用这个属性与请求的访问作比较。
如果找到了系统保存这个属性的位置并修该掉它,那就不只可以读取,甚至可以写入任何已打开的文件。
在用户这一级别上我们并不是直接与文件打交道,而是通过它的句柄(这个句柄指向FileObject),而函数ReadFile/WriteFile调用ObReferenceObjectByHandle,并指明了相应的访问类型。
由此我们可以得出结论,访问权限保存在描述句柄的结构体里。
实际上,HANDLE_TABLE_ENTRY结构体包含有一个GrantedAccess域,这个域不是别的,就是句柄的访问权限。
遗憾的是,Microsoft的程序员们没有提供修改句柄访问权的API,所以我们不得不编写驱动自己来做这项工作。
我在《隐藏进程检测》一文中讲到过Windows2000和XP的句柄表结构体,我想补充的只有一点,就是Windows
2003中的句柄表与XP的完全一样。
与那篇文章不同,我们这里不需要枚举表中的句柄,而只需要找到某个具体的(已知的)句柄,我们不用管PspCidTable,而只操作自己进程的句柄表,表的指针位于进程的EPROCESS结构体里(2000下的偏移为0x128,XP下的为0x0C4)。
为了取得句柄结构体指针需要调用未导出函数ExpLookupHandleTableEntry,但我们不会去搜索它,因为在导出函数中没有对它的直接引用,搜索结果也很不可靠,除此之外我们此时还需要ExUnlockHandleTableEntry函数。
最好的办法就是编写自己的句柄表lookup函数。
考虑到Windows
2000与XP下句柄表的差异,我们将编写不同的函数。
首先是Windows2000下的:
PHANDLE_TABLE_ENTRY
Win2kLookupHandleTableEntry(
INPWIN2K_HANDLE_TABLEHandleTable,
INEXHANDLE
Handle)
ULONGi,j,k;
i=(Handle.Index>
16)&
255;
j=(Handle.Index>
8)
&
k=(Handle.Index)
if(HandleTable->
Table[i])
Table[i][j])
return&
(HandleTable->
Table[i][j][k]);
returnNULL;
这段代码简单易懂。
因为句柄的值本身是个三维表的三个索引,所以我们只需其中的各个部分并查看表中相应的元素(当然如果存在的话)。
因为Windows
XP中的句柄表可以有一到三个级别,所以相应的lookup代码就要更为复杂一些:
XpLookupHandleTableEntry(
INPXP_HANDLE_TABLEHandleTable,
PHANDLE_TABLE_ENTRYEntry=NULL;
ULONGTableCode=HandleTable->
TableCode&
~TABLE_LEVEL_MASK;
p;
17)&
0x1FF;
9)
switch(HandleTable->
TABLE_LEVEL_MASK)
case0:
Entry=&
((PHANDLE_TABLE_ENTRY)TableCode)[k];
case1:
if(((PVOID*)TableCode)[j])
((PHANDLE_TABLE_ENTRY*)TableCode)[j][k];
case2:
if(((PVOID*)TableCode)[i])
if(((PVOID**)TableCode)[i][j])
((PHANDLE_TABLE_ENTRY**)TableCode)[i][j][k];
returnEntry;
我们看到,这段代码中的句柄并不是ULONG型的值,而是EXHANDLE结构体:
typedefstruct_EXHANDLE
union
struct
ULONGTagBits:
02;
ULONGIndex
:
30;
};
HANDLEGenericHandleOverlay;
}EXHANDLE,*PEXHANDLE;
我们看到,句柄不知包含了表的索引,还包含了一个2bit的标志。
您可能已经察觉到,一个句柄可以有着几种不同的意义,这一点与这样一个事实有关,那就是并非句柄中所有的位都被使用到(依赖于在表中的级别)。
这是Windows
XP最具个性的特点。
现在我们就可以获取句柄表中所需的元素了,该编写为句柄设置所需访问属性的函数了:
BOOLEANSetHandleAccess(
INHANDLE
Handle,
INACCESS_MASKGrantedAccess
)
PHANDLE_TABLE
ObjectTable=*(PHANDLE_TABLE
*)RVATOVA(PsGetCurrentProcess(),ObjectTableOffset);
PHANDLE_TABLE_ENTRYEntry;
EXHANDLE
ExHandle;
ExHandle.GenericHandleOverlay=Handle;
Entry=ExLookupHandleTableEntry(ObjectTable,ExHandle);
if(Entry)Entry->
GrantedAccess=GrantedAccess;
returnEntry>
0;
现在编写驱动,设置句柄的访问属性,通过DeviceIoControl向驱动传递句柄。
代码如下:
NTSTATUSDriverIoControl(
INPDEVICE_OBJECTDeviceObject,
INPIRPIrp)
PIO_STACK_LOCATIONpisl
=IoGetCurrentIrpStackLocation(Irp);
NTSTATUS
status
=STATUS_UNSUCCESSFUL;
BuffSize=
pisl->
Parameters.DeviceIoControl.InputBufferLength;
PUCHAR
pBuff
=Irp->
AssociatedIrp.SystemBuffer;
ACCESS_MASK
GrantedAccess;
Irp->
IoStatus.Information=0;
switch(pisl->
Parameters.DeviceIoControl.IoControlCode)
caseIOCTL1:
if(pBuff&
BuffSize>
=sizeof(HANDLE)+
sizeof(ACCESS_MASK))
Handle
=*(HANDLE*)pBuff;
GrantedAccess=*(ACCESS_MASK*)(pB