删除系统中的文件会提示 有进程已经打开了这个文件会导致不能删除该文件
在网上找到了在ring3下实现文件碎甲的一篇介绍:在ring3上实现文件碎甲功能
其中首先需要实现的就是需要枚举出系统中每个进程打开的文件句柄
枚举进程 枚举句柄 这些功能都需要用到从Ntdll.dll中导出系统内核函数
比如函数 ZwQuerySystemInformation ZwQueryInformationProcess等
其中有些函数是M$未公开函数 但是大多都可以从网上查到文档
首先从NtDll.dll中导出函数ZwQuerySystemInformation和函数RtlAdjustPrivilege
ZwQuerySystemInformation原型如下:
typedef NTSTATUS (__stdcall *ZwQuerySystemInformation1)( IN ULONG SysInfoClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG RetLen );
RtlAdjustPrivilege函数原型如下:
typedef NTSTATUS (__stdcall *RtlAdjustPrivilege1) (DWORD,BOOL,DWORD,PBOOLEAN);
导出函数:
ZwQueryObject1 ZwQueryObject; ZwQueryObject = (ZwQueryObject1) GetProcAddress(hNtDll,"ZwQueryObject");
RtlAdjustPrivilege函数也是类似
RtlAdjustPrivilege函数用于提高当前进程的权限
RtlAdjustPrivilege(20, TRUE, FALSE, NULL);
20表示赋予当前进程Debug权限 在打开其他进程和复制句柄时会用到
ZwQuerySystemInformation可以用于获得很多系统信息 第一个参数用于标示需要获得何种信息
这里使用5作为第一个参数标示获取当前系统中所有进程的信息
获得的信息是第二个参数 是一块内存块 需要将其转换为结构体SYSTEM_PROCESS
typedef struct _SYSTEM_PROCESSES { ULONG NextEntryDelta; ULONG ThreadCount; ULONG Reserved1[6]; LARGE_INTEGER CreateTime; LARGE_INTEGER UserTime; LARGE_INTEGER KernelTime; UNICODE_STRING ProcessName; KPRIORITY BasePriority; ULONG ProcessId; ULONG InheritedFromProcessId; ULONG HandleCount; ULONG Reserved2[2]; VM_COUNTERS VmCounters; IO_COUNTERS IoCounters; SYSTEM_THREADS Threads[1]; } SYSTEM_PROCESSES, * PSYSTEM_PROCESSES;
如果给定的内存块不足以存下所有进程信息则函数返回0xC0000004L
被获取的进程信息以链表的形式存放在给出的内存块中
并且每个进程由一个SYSTEM_PROCESS结构体表示
在SYSTEM_PROCESS中 NextEntryDelta表示从第一个进程信息结构到下一个结构的偏移
若其为0则表示为最后一个进程
由此我们得到了系统中所有进程的信息
现在就可以遍历每个进程中的句柄了
由于在遍历时需要打开进程句柄 所以需要跳过当前进程本身
由于已经得到了进程id所以可以使用ring3下的函数OpenProcess来打开进程句柄
HANDLE hProcess = OpenProcess(PROCESS_SUSPEND_RESUME | PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, Pid);
在打开的时候标示了需要用到的一些权限 如复制句柄等
在打开进程后 需要用函数ZwSuspendProcess将其暂时挂起
但是对于系统进程System(PID:4)不能将其挂起
然后通过函数ZwQueryInformationProcess获得该进程所拥有的句柄信息
ZwQueryInformationProcess函数原型如下:
typedef NTSTATUS (__stdcall *ZwQueryInformationProcess1)( HANDLE, PROCESSINFOCLASS, LPVOID, DWORD, PDWORD);
其中PROCESSINFOCLASS是一个枚举类型
这里需要用到的值是ProcessHandleCount(20)用于获取进程所打开的句柄数
ZwQueryInformationProcess(hProcess, ProcessHandleCount, &hc, sizeof(hc), NULL)
hc就是该进程打开的句柄数
然后就可以通过DuplicateHandle来复制出句柄了
DuplicateHandle(hProcess, (HANDLE)h, GetCurrentProcess(), &ho, 0, FALSE, DUPLICATE_SAME_ACCESS)
这里h就是需要复制的句柄的值 由于并没有对系统中所有的句柄进行遍历
所以这里并不知道每个句柄的值
但是我们知道进程中所有句柄的值都是从4开始 然后每4递增的
就是说每个进程中的句柄值必定为4 8 12 16....等等
但是不一定每个4的倍数都是一个有效的句柄值
如果你把一个无效的句柄值传入Duplicatehandle 该函数会返回错误
这样就相当于系统帮助我们判断了句柄是否存在
在遍历时如果DuplicateHandle成功则表明复制了一个有效句柄
直到复制出hc个有效句柄时 该进程就遍历完成了
由此我们就遍历出了 系统中每个进程所打开的句柄
Windows系统中句柄有很多种类型 XP系统下有如下:
1 Type 2 Directory 3 SymbolicLink 4 Token 5 Process 6 Thread 7 Job 8 DebugObject 9 Event 10 EventPair 11 Mutant 12 Callback 13 Semaphore 14 Timer 15 Profile 16 KeyedEvent 17 WindowStation 18 Desktop 19 Section 20 Key 21 Port 22 WaitablePort 23 Adapter 24 Controller 25 Device 26 Driver 27 IoCompletion 28 File 29 WmiGuid 30 FilterConnectionPort 31 FilterCommunicationPort
对于复制出来的句柄 我们可以通过函数ZwQueryObject来获取其类型信息
以及名字
但是由于使用ZwQueryObject时有可能引起系统死锁
所以需要将该操作放入一个单独的线程中处理 并设置超时
(__stdcall *ZwQueryObject1)( IN HANDLE ObjectHandle, IN OBJECT_INFORMATION_CLASS ObjectInformationClass, OUT PVOID ObjectInformation, IN ULONG Length, OUT PULONG ResultLength );
如果其中ObjectInformationClass参数为ObjectTypeInformation(2)则表示获取类型信息
存于结构体
typedef struct _OBJECT_TYPE_INFORMATION{ UNICODE_STRING TypeName; ULONG TotalNumberOfHandles; ULONG TotalNumberOfObjects; WCHAR Unused1[8]; ULONG HighWaterNumberOfHandles; ULONG HighWaterNumberOfObjects; WCHAR Unused2[8]; ACCESS_MASK InvalidAttributes; GENERIC_MAPPING GenericMapping; ACCESS_MASK ValidAttributes; BOOLEAN SecurityRequired; BOOLEAN MaintainHandleCount; USHORT MaintainTypeList; POOL_TYPE PoolType; ULONG DefaultPagedPoolCharge; ULONG DefaultNonPagedPoolCharge; BYTE Unknown2[16]; } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
如果为ObjectNameInformation(1)则获取名字信息 存于结构体
typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING,*PUNICODE_STRING;
由此我们就可以获得系统中所有进程打开的句柄了 也就可以从中得到时哪些进程打开了某个文件
对于挂起的进程一定要使用ZwResumeProcess将其恢复