内核级进程遍历

原理

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   //进程名
   ...
   +0x1b0 Peb              : Ptr32 _PEB   //PEB,本节用不到
      +0x000 InheritedAddressSpace : UChar
      +0x001 ReadImageFileExecOptions : UChar
      +0x002 BeingDebugged    : UChar
      +0x003 SpareBool        : UChar
      +0x004 Mutant           : Ptr32 Void
      +0x008 ImageBaseAddress : Ptr32 Void
      +0x00c Ldr              : Ptr32 _PEB_LDR_DATA  //指向模块结点表头
      ...
   ...
   +0x258 Cookie           : Uint4B

demo

用的vs2008sp1 on xp sp3,visual ddk。

其中工程选项中没有使用了visual studio,而部没有用ddk compiler,即使这样,用默认的结构去生成解决方案,还是无法加载。于是我全删掉,自己写,好在有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;
    //lkd> !process 0 0
    //lkd> dt _eprocess xxxxxxxx
    //+0x174 ImageFileName    : [16]  "smss.exe"
    //+0x084 UniqueProcessId  : 0x00000224 Void
    //lkd> db 864bb0a8-0x88+0x174
    //864bb194  73 6d 73 73 2e 65 78 65-00 00 00 00 00 00 00 00  smss.exe........ ascii 

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

//      +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x862669a8 - 0x865b76e8 ]
//      +0x000 Flink            : 0x862669a8 _LIST_ENTRY [ 0x863570a8 - 0x864bb0a8 ]
//      +0x004 Blink            : 0x865b76e8 _LIST_ENTRY [ 0x864bb0a8 - 0x8055b158 ]
        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
  1. 不能看到两个0x88相减,就以为是0.因为一个前0x88要当指针取值,取完之后才减去0x88.(即取地址,得到结果,结果-0x88)。
  2. 和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
00000007    2.50115538  PID=612,process_name=csrss.exe
00000008    2.50117064  PID=636,process_name=winlogon.exe
00000009    2.50118613  PID=688,process_name=services.exe
00000010    2.50120139  PID=700,process_name=lsass.exe
00000011    2.50121689  PID=856,process_name=vmacthlp.exe
00000012    2.50123215  PID=868,process_name=svchost.exe
00000013    2.50124717  PID=952,process_name=svchost.exe
00000014    2.50126266  PID=1044,process_name=svchost.exe
00000015    2.50127792  PID=1100,process_name=svchost.exe
00000016    2.50129294  PID=1160,process_name=svchost.exe
00000017    2.50130844  PID=1416,process_name=spoolsv.exe
00000018    2.50132370  PID=1664,process_name=vmtoolsd.exe
00000019    2.50133944  PID=1956,process_name=explorer.exe
00000020    2.50135541  PID=496,process_name=vmtoolsd.exe
00000021    2.50137067  PID=536,process_name=ctfmon.exe
00000022    2.50138617  PID=1256,process_name=alg.exe
00000023    2.50140166  PID=1772,process_name=wscntfy.exe
00000024    2.50141716  PID=448,process_name=devenv.exe
00000025    2.50143218  PID=524,process_name=conime.exe
00000026    2.50144792  PID=184,process_name=Kernel Detectiv
00000027    2.50146341  PID=1284,process_name=windbg.exe
00000028    2.50147891  PID=1700,process_name=OSRLOADER.exe
00000029    2.50149441  PID=1880,process_name=Dbgview.exe
00000030    2.50150990  PID=428,process_name=mspdbsrv.exe
00000031    2.50152516  PID=1788,process_name=procexp.exe
00000032    2.50154066  PID=1656,process_name=wmiprvse.exe
00000033    2.50155592  PID=244,process_name=calc.exe
00000034    2.50157094  PID=1812,process_name=cmd.exe
00000035    2.50158644  PID=1588,process_name=notepad.exe
00000036    2.50160122  enum over !
时间: 2024-10-12 02:40:16

内核级进程遍历的相关文章

Linux内核编程:Linux2.6内核源码解析_进程遍历 &nbsp; &nbsp; &nbsp; &nbsp;

/*     *File    : test.c   *Author  : DavidLin        *Date    : 2014-12-07pm        *Email   : [email protected] or [email protected]        *world   : the city of SZ, in China        *Ver     : 000.000.001        *history :     editor      time    

Linux内核学习-进程

先说几个术语: 一.Linux进程的五个段 下面我们来简单归纳一下进程对应的内存空间中所包含的5种不同的数据区都是干什么的.重点:代码段.数据段.堆栈段,这是一个概念堆.栈.全局区.常量区,这是另一个概念1)代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存中的镜像.代码段需要防止在运行时被非法修改,所以只准许读取操作,而不允许写入(修改)操作--它是不可写的.代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域.这部分

操作系统--用户级线程和内核级线程

在多线程操作系统中,各个系统的实现方式并不相同.在有的系统中实现了用户级线程,有的系统中实现了内核级线程 1.内核级线程: (1)线程的创建.撤销和切换等,都需要内核直接实现,即内核了解每一个作为可调度实体的线程. (2)这些线程可以在全系统内进行资源的竞争. (3)内核空间内为每一个内核支持线程设置了一个线程控制块(TCB),内核根据该控制块,感知线程的存在,并进行控制. 在一定程度上类似于进程,只是创建.调度的开销要比进程小.有的统计是1:10 2.用户级线程: (1)用户级线程仅存在于用户

SSDT HOOK实现内核级保护

SSDT Hook实现内核级的进程保护 目录 SSDT Hook效果图 SSDT简介 SSDT结构 SSDT HOOK原理 Hook前准备 如何获得SSDT中函数的地址呢 SSDT Hook流程 SSDT Hook实现进程保护 Ring3与Ring0的通信 如何安装启动停止卸载服务 参考文献 源码附件 版权 SSDT Hook效果图 加载驱动并成功Hook  NtTerminateProcess函数: 当对 指定的进程进行保护后,尝试使用“任务管理器”结束进程的时候,会弹出“拒绝访问”的窗口,说

操作系统: 用户级线程和内核级线程

1 .内核级线程:切换由内核控制,当线程进行切换的时候,由用户态转化为内核态.切换完毕要从内核态返回用户态:可以很好的利用smp,即利用多核cpu.windows线程就是这样的. 2. 用户级线程内核的切换由用户态程序自己控制内核切换,不需要内核干涉,少了进出内核态的消耗,但不能很好的利用多核Cpu,目前Linux pthread大体是这么做的. 线程的实现可以分为两类:用户级线程(User-Level Thread)和内核线线程(Kernel-Level Thread),后者又称为内核支持的线

内核级虚拟化技术

这篇可能讲的有一点点的无聊,因为基本上是概念性的东西,我也是理解了很久才慢慢的搞懂的. 一.虚拟化与虚拟化技术 1.1.虚拟化的定义 虚拟化主要指的是特殊的技术,通过隐藏特定计算平台的实际物理特性,为用户提供抽象的.统一的.模拟的计算环境(称为虚拟机)(IBM定义).虚拟化为有效利用大型机的资源提供了技术支持. 虚拟机技术也是多种多样,而可以虚拟的层次或者可虚拟的方面也是遍布从硬件到应用层整个计算机系统. 1.2.虚拟化技术 其实现在的虚拟化技术非常多的,比如说我现在在用的:VMWare.Vir

用户级线程和内核级线程的区别

转载于http://col1.blog.163.com/blog/static/1909775192012719114033352/ 1 .内核级线程:切换由内核控制,当线程进行切换的时候,由用户态转化为内核态.切换完毕要从内核态返回用户态:可以很好的利用smp,即利用多核cpu.windows线程就是这样的. 2. 用户级线程内核的切换由用户态程序自己控制内核切换,不需要内核干涉,少了进出内核态的消耗,但不能很好的利用多核Cpu,目前Linux pthread大体是这么做的. 线程的实现可以分

多线程 用户级线程和内核级线程 from C++多核高级编程

转 http://book.51cto.com/art/201006/206946.htm 6.1.1 用户级线程和内核级线程 2010-06-21 20:37 齐宁/董泽惠 译 清华大学出版社 字号:T | T <C++多核高级编程>第6章多线程,本章将介绍:什么是线程; 用于线程管理的pthread API;线程调度及优先级;线程竞争范围;扩展thread_object以封装线程属性功能.本节为大家介绍用户级线程和内核级线程. AD: 6.1.1  用户级线程和内核级线程 线程有3种实现模

(转)用户级和内核级线程

转:http://book.51cto.com/art/201006/206946.htm 6.1.1  用户级线程和内核级线程 线程有3种实现模型: 用户级或应用程序级线程 内核级线程 用户级和内核级混合线程 图6-1显示了3种线程实现模型.图6-1(a)显示了用户级线程,图6-1(b)显示了内核级线程,图6-1(c)则显示了用户线程和内核线程的混合.   (点击查看大图)(a) 用户级线程   (点击查看大图)(b) 内核级线程图6-1   (点击查看大图)(c) 混合线程图6-1(续) 这