打开被独占的文件方法(二) -- 修改句柄访问权限
2012-04-27 15:49
495 查看
打开被独占的文件方法(二) -- 修改句柄访问权限
2010年06月08日 星期二 11:40
来自:http://hi.baidu.com/wsh_chb/blog/item/1dfafadd4da473d48d1029a8.html
2010年06月08日 星期二 11:40
来自:http://hi.baidu.com/wsh_chb/blog/item/1dfafadd4da473d48d1029a8.html
修改句柄访问权限 所有被占用的文件通常都可以用读属性(FILE_READ_ATTRIBUTES)打开,这样就可以读取文件的属性,取得它的大小,枚举NTSF stream,但遗憾的是,ReadFile就不能成功调用了。打开文件时各种访问属性的区别在哪里呢?显然,打开文件时,系统会记录访问属性,之后会用这个属性与请求的访问作比较。如果找到了系统保存这个属性的位置并修该掉它,那就不只可以读取,甚至可以写入任何已打开的文件。 在用户这一级别上我们并不是直接与文件打交道,而是通过它的句柄(这个句柄指向FileObject),而函数ReadFile/WriteFile调用ObReferenceObjectByHandle,并指明了相应的访问类型。由此我们可以得出结论,访问权限保存在描述句柄的结构体里。实际上,HANDLE_TABLE_ENTRY结构体包含有一个GrantedAccess域,这个域不是别的,就是句柄的访问权限。遗憾的是,Microsoft的程序员们没有提供修改句柄访问权的API,所以我们不得不编写驱动自己来做这项工作。 我在《隐藏进程检测》一文中讲到过Windows 2000和XP的句柄表结构体,我想补充的只有一点,就是Windows 2003中的句柄表与XP的完全一样。与那篇文章不同,我们这里不需要枚举表中的句柄,而只需要找到某个具体的(已知的)句柄,我们不用管PspCidTable,而只操作自己进程的句柄表,表的指针位于进程的EPROCESS结构体里(2000下的偏移为0x128,XP下的为0x0C4)。 为了取得句柄结构体指针需要调用未导出函数ExpLookupHandleTableEntry,但我们不会去搜索它,因为在导出函数中没有对它的直接引用,搜索结果也很不可靠,除此之外我们此时还需要ExUnlockHandleTableEntry函数。最好的办法就是编写自己的句柄表lookup函数。考虑到Windows 2000与XP下句柄表的差异,我们将编写不同的函数。 首先是Windows 2000下的: PHANDLE_TABLE_ENTRY Win2kLookupHandleTableEntry( IN PWIN2K_HANDLE_TABLE HandleTable, IN EXHANDLE Handle ) { ULONG i, j, k; i = (Handle.Index >> 16) & 255; j = (Handle.Index >> 8) & 255; k = (Handle.Index) & 255; if (HandleTable->Table[i]) { if (HandleTable->Table[i][j]) return &(HandleTable->Table[i][j][k]); } return NULL; } 这段代码简单易懂。因为句柄的值本身是个三维表的三个索引,所以我们只需其中的各个部分并查看表中相应的元素(当然如果存在的话)。因为Windows XP中的句柄表可以有一到三个级别,所以相应的lookup代码就要更为复杂一些: PHANDLE_TABLE_ENTRY XpLookupHandleTableEntry( IN PXP_HANDLE_TABLE HandleTable, IN EXHANDLE Handle ) { ULONG i, j, k; PHANDLE_TABLE_ENTRY Entry = NULL; ULONG TableCode = HandleTable->TableCode & ~TABLE_LEVEL_MASK; p; i = (Handle.Index >> 17) & 0x1FF; j = (Handle.Index >> 9) & 0x1FF; k = (Handle.Index) & 0x1FF; switch (HandleTable->TableCode & TABLE_LEVEL_MASK) { case 0 : Entry = &((PHANDLE_TABLE_ENTRY)TableCode)[k]; break; case 1 : if (((PVOID *)TableCode)[j]) { Entry = &((PHANDLE_TABLE_ENTRY *)TableCode)[j][k]; } break; case 2 : if (((PVOID *)TableCode)[i]) if (((PVOID **)TableCode)[i][j]) { Entry = &((PHANDLE_TABLE_ENTRY **)TableCode)[i][j][k]; } break; } return Entry; } 我们看到,这段代码中的句柄并不是ULONG型的值,而是EXHANDLE结构体: typedef struct _EXHANDLE { union { struct { ULONG TagBits : 02; ULONG Index : 30; }; HANDLE GenericHandleOverlay; }; } EXHANDLE, *PEXHANDLE; 我们看到,句柄不知包含了表的索引,还包含了一个2 bit的标志。您可能已经察觉到,一个句柄可以有着几种不同的意义,这一点与这样一个事实有关,那就是并非句柄中所有的位都被使用到(依赖于在表中的级别)。这是Windows XP最具个性的特点。 现在我们就可以获取句柄表中所需的元素了,该编写为句柄设置所需访问属性的函数了: BOOLEAN SetHandleAccess( IN HANDLE Handle, IN ACCESS_MASK GrantedAccess ) { PHANDLE_TABLE ObjectTable = *(PHANDLE_TABLE *)RVATOVA(PsGetCurrentProcess(), ObjectTableOffset); PHANDLE_TABLE_ENTRY Entry; EXHANDLE ExHandle; ExHandle.GenericHandleOverlay = Handle; Entry = ExLookupHandleTableEntry(ObjectTable, ExHandle); if (Entry) Entry->GrantedAccess = GrantedAccess; return Entry > 0; } 现在编写驱动,设置句柄的访问属性,通过DeviceIoControl向驱动传递句柄。代码如下: NTSTATUS DriverIoControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION pisl = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_UNSUCCESSFUL; ULONG BuffSize = pisl->Parameters.DeviceIoControl.InputBufferLength; PUCHAR pBuff = Irp->AssociatedIrp.SystemBuffer; HANDLE Handle; ACCESS_MASK GrantedAccess; Irp->IoStatus.Information = 0; switch(pisl->Parameters.DeviceIoControl.IoControlCode) { case IOCTL1: if (pBuff && BuffSize >= sizeof(HANDLE) + sizeof(ACCESS_MASK)) { Handle =*(HANDLE*)pBuff; GrantedAccess = *(ACCESS_MASK*)(pBuff + sizeof(HANDLE)); if (Handle != (HANDLE)-1 && SetHandleAccess(Handle, GrantedAccess)) status = STATUS_SUCCESS; } break; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } NTSTATUS DriverCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) { PCWSTR dDeviceName = L"\\Device\\fread"; PCWSTR dSymbolicLinkName = L"\\DosDevices\\fread"; NTSTATUS status; PDRIVER_DISPATCH *ppdd; RtlInitUnicodeString(&DeviceName, dDeviceName); RtlInitUnicodeString(&SymbolicLinkName, dSymbolicLinkName); switch (*NtBuildNumber) { case 2600: ObjectTableOffset = 0x0C4; ExLookupHandleTableEntry = XpLookupHandleTableEntry; break; case 2195: ObjectTableOffset = 0x128; ExLookupHandleTableEntry = Win2kLookupHandleTableEntry; break; default: return STATUS_UNSUCCESSFUL; } status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, TRUE, &deviceObject); if (NT_SUCCESS(status)) { status = IoCreateSymbolicLink(&SymbolicLinkName, &DeviceName); if (!NT_SUCCESS(status)) IoDeleteDevice(deviceObject); DriverObject->DriverUnload = DriverUnload; } ppdd = DriverObject->MajorFunction; ppdd [IRP_MJ_CREATE] = ppdd [IRP_MJ_CLOSE ] = DriverCreateClose; ppdd [IRP_MJ_DEVICE_CONTROL ] = DriverIoControl; return status; } 遗憾的是句柄结构体中的GrantedAccess域并没有和文件打开的属性(GENERIC_READ、GENERIC_WRITE等)对应起来,所以在设置新的属性时我们需要以下constants: #define AC_GENERIC_READ 0x120089 #define AC_GENERIC_WRITE 0x120196 #define AC_DELETE 0x110080 #define AC_READ_CONTROL 0x120080 #define AC_WRITE_DAC 0x140080 &n bsp; #define AC_WRITE_OWNER 0x180080 #define AC_GENERIC_ALL 0x1f01ff #define AC_STANDARD_RIGHTS_ALL 0x1f0080 为了使用这个驱动将SAM文件拷贝到c盘根目录,我们可以写一个最简单的程序: #include <windows.h> #include "hchange.h" BOOLEAN SetHandleAccess( HANDLE Handle, ACCESS_MASK GrantedAccess ) { HANDLE hDriver; ULONG Bytes; ULONG Buff[2]; BOOLEAN Result = FALSE; hDriver = CreateFile("\\\\.\\haccess", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); if (hDriver != INVALID_HANDLE_VALUE) { Buff[0] = (ULONG)Handle; Buff[1] = GrantedAccess; Result = DeviceIoControl(hDriver, IOCTL1, Buff, sizeof(Buff), NULL, 0, &Bytes, NULL); CloseHandle(hDriver); } } void main() { HANDLE hFile, hDest; ULONG Size, Bytes; PVOID Data; CHAR Name[MAX_PATH]; GetSystemDirectory(Name, MAX_PATH); lstrcat(Name, "\\config\\SAM"); hFile = CreateFile(Name, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { if (SetHandleAccess(hFile, AC_GENERIC_READ)) { Size = GetFileSize(hFile, NULL); Data = VirtualAlloc(NULL, Size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (Data) { ReadFile(hFile, Data, Size, &Bytes, NULL); hDest = CreateFile("c:\\SAM", GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, 0); if (hDest != INVALID_HANDLE_VALUE) { WriteFile(hDest, Data, Size, &Bytes, NULL); CloseHandle(hDest); } VirtualFree(Data, 0, MEM_RELEASE); } } CloseHandle(hFile); } } 这个方法最大的缺陷就是强烈依赖于操作系统,而且还需要加载驱动程序,而这并不总是能实现的。但是从可靠性上来看,这种方法是最好的,所以我建议将其用在backup程序中(只是要经过长期的测试和调试!)。因为这种方法有不能胜任的情形,我们转入下一种方法。 |
相关文章推荐
- 修改系统文件访问权限的方法
- 修改系统文件访问权限的方法
- 打开被独占的文件方法(一) -- 寻找打开文件的句柄
- 打开被独占的文件方法(三) -- 使用直接硬盘访问读取文件
- Linux下查看进程打开的文件句柄数和如何修改方法
- [转] 打开被独占的文件方法 -- 寻找打开文件的句柄
- 打开ftp服务器上的文件时发生错误。请检查是否有权限访问该文件夹
- 解决useradd 用户后没有添加用户Home目录的情况,Linux改变文件或目录的访问权限命令,linux修改用户密码
- 修改TrustedInstaller权限文件的方法(无法删除文件或文件夹)
- 文件目录权限修改方法
- linux的最大打开文件数限制修改方法
- 修改mysql允许主机访问的权限方法
- 修改.htaccess文件提升网站访问速度方法
- win7修改hosts和service文件提示无权限的解决方法
- linux chmod命令: 修改文件或目录的访问权限
- 命令行下修改文件访问控制权限
- WIN7中修改Jar文件打开方式的方法
- Ubuntu 操作系统修改文件访问权限
- 命令行下修改文件访问控制权限
- c# 读取其他程序正打开的文件的时“正由另一进程使用,因此该进程无法访问该文件。"的问题解决方法