• 枚举进程句柄


    删除系统中的文件会提示 有进程已经打开了这个文件会导致不能删除该文件

    在网上找到了在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将其恢复

  • 相关阅读:
    架构设计之NodeJS操作消息队列RabbitMQ
    如何搭建一个功能复杂的前端配置化框架(一)
    Web as a App(Web既APP)的概念可以提出吗?
    Bottle源码阅读笔记(二):路由
    Bottle源码阅读笔记(一):WSGI
    Python__slots__详解
    [译]如何在Web开发中使用Python
    C#中Internal关键字的总结
    [DataContract]引用
    分享一个与ABP配套使用的代码生成器源码
  • 原文地址:https://www.cnblogs.com/lichaoxyz/p/3245528.html
Copyright © 2020-2023  润新知