内核级进程遍历

Posted bcbobo21cn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内核级进程遍历相关的知识,希望对你有一定的参考价值。

原理

windows中,每个进程都有一个属于自己的EPROCESS结构,这个结构中包含了本程序的基本信息,并且数据中存在进程链表,通过该进程链表(双向链表)可以找到其他进程的EPROCESS 结构,所以可以借此遍历系统中的进程。

使用windbg 可以看到eprocess 的结构体,

lkd> dt _eprocess -r1
nt!_EPROCESS
   ...
   +0x084 UniqueProcessId  : Ptr32 Void  PID
   +0x088 ActiveProcessLinks : _LIST_ENTRY 活动进程链表(双向链表)
      +0x000 Flink            : Ptr32 _LIST_ENTRY
      +0x004 Blink            : Ptr32 _LIST_ENTRY
   ...
   +0x174 ImageFileName    : [16] UChar   //进程名
   ...
   ...
   ...
   +0x258 Cookie           : Uint4B

demo

用的vs2008sp1 on xp sp3,visual ddk。
其中工程选项中使用了visual studio compiler,而没有用ddk compiler,即使这样,用默认的结构去生成解决方案,生成的sys还是无法加载。于是全删掉框架代码,自己写,好在有ddk的sdk可以用vax智能提示。注意生成debug(chunk)版,否则debugview 无法显示输出。

#include "stdafx.h"

VOID OnUnload(IN PDRIVER_OBJECT pDriverObject)

    KdPrint(("OnUnload called"));


NTSTATUS enum_services()

    PEPROCESS pEprocess = NULL;
    PEPROCESS pFirstEprocess = NULL;
    ULONG ulProcessName = 0;
    ULONG ulProcessId = 0;

    pEprocess = PsGetCurrentProcess(); //get system process _eprocess_address
    if (pEprocess == 0)
    
        KdPrint(("PsGetcurrentProcess Error! \\r\\n"));
        return STATUS_SUCCESS;
    
    KdPrint(("pEprocess addr is:%08x\\r\\n",pEprocess));
    pFirstEprocess = pEprocess;

    while (pEprocess )
    
        ulProcessName = (ULONG)pEprocess+0x174;
        ulProcessId   =  *(ULONG *) ((ULONG)pEprocess+0x84);
        KdPrint(("PID=%d,process_name=%s",ulProcessId,ulProcessName));

        pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess+0x88)-0x88);  //pointer to the list ,not the eprocess_start,SO -0x88
        //双向链表,到最末尾向指向头结点,表示结束。要么就是PID不大于0,这个不知道从哪里得来的
        if (pEprocess == pFirstEprocess || (  *(LONG*)( (LONG)pEprocess+0x84   ) <= 0 )) 
        
            KdPrint(("enum over !\\r\\n"));
            break;
        
    
    return STATUS_SUCCESS;


NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING registeryPath)

    KdPrint(("Driver loaded"));
    KdPrint(("just a test."));
    pDriverObject->DriverUnload = OnUnload;
    enum_services();
    return STATUS_SUCCESS;

注意:
pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess+0x88)-0x88);  
//pointer to the list ,not the eprocess_start,SO -0x88

不能看到两个0x88相减,就以为是0.因为一个前0x88要当指针取值,取完之后才减去0x88.(即取地址,得到结果,结果-0x88)。
和PEB中的—ldr_data_table_entry 一样,链表指向的是下一个相同链表结构的地址,而不是下一个进程eprocess节点的头地址。所以需要-0x88。


DEBUG_VIEW输出:

00000001    0.00000000  OnUnload called 
00000002    2.50107551  Driver loaded   
00000003    2.50109267  just a test.    
00000004    2.50110865  pEprocess addr is:865b7660  
00000005    2.50112438  PID=4,process_name=System   
00000006    2.50113988  PID=548,process_name=smss.exe   
......
......  
00000034    2.50157094  PID=1812,process_name=cmd.exe   
00000035    2.50158644  PID=1588,process_name=notepad.exe   
00000036    2.50160122  enum over !     

demo是一个DDK驱动,DDK应该是最多到winxp能用;

PsGetCurrentProcess

一个关于PsGetCurrentProcess函数的描述如下,

通过PsGetCurrentProcess函数获取函数名
    通过PsGetCurrentProcess函数来获取当前调用驱动的进程的EPROCESS结构的地址.EPROCESS结构的0x174偏移处存放着进程名.
思路如下:
驱动程序的加载函数DriverEntry是运行在System进程中的.
(1) 通过PsGetCurrentProcess可以获取System进程的内核EPROCESS结构的地址,
(2) 从该地址开始寻找"System"字符串.
(3) 找到了便是EPROCESS的进程名存放的偏移处,得到进程名在EPROCESS结构的偏移后,
(4) 进程调用驱动的时候,就可以直接在该偏移处获取当前进程名.
代码如下:

DWORD GetProcessNameOffset()


    PEPROCESS curproc;
    DWORD procNameOffset;
    //获取EPROCESS结构的地址
    curproc = PsGetCurrentProcess();
    for(int i=0; i< 4096; i++)
    
        if( !strncmp( "System", (PCHAR) curproc + i, strlen("System") ))
        
            procNameOffset = i;
            return procNameOffset;
        
    
    return 0;

见此,https://www.cnblogs.com/xiaojinma/archive/2012/12/07/2806543.html

以上是关于内核级进程遍历的主要内容,如果未能解决你的问题,请参考以下文章

线程的实现方式之内核支持线程和用户级线程

线程的3种实现方式--内核级线程, 用户级线程和混合型线程

在LINUX内核中,进程标识符PID为1,2 ,3,4,5的进程的名称是啥?基本功能是啥?

性能工具:特定进程CPU

性能工具:特定进程CPU

20165223 《信息安全系统设计基础》第八周学习总结