深入Windows NT/2000模块的组织(http://webcrazy.yeah.net)

news/2024/7/5 6:15:49
深入Windows NT/2000模块的组织 
                  WebCrazy( http://webcrazy.yeah.net/)

   在《 小议Windows NT/2000分页机制 》中我对x86平台Windows NT/2000的非分页内存内部机制有了较为详细的说明,从中也可以看出地址空间可以分为进程空间与系统空间,其中每个进程有各自的进程空间,而所有的进程则共享同一个系统空间。所以Windows NT/2000在牵涉到模块管理时也涉及到进程私有的模块管理与系统共享模块管理两部分,下面我将从这两方面分别进行介绍。
   正因为所有的进程共享同一个系统空间,所以系统模块主要是些操作系统代码模块或是些设备驱动程序代码等(它们一般进程都需要使用到),其位于系统4G内存的高端。Windows NT/2000内部由一系统变量PsLoadedModuleList指出,其具体结构为一双向链表。熟悉Windows NT/2000的人,都知道系统在发生蓝屏死机时,默认情况下都会将系统此时转储到MEMORY.DMP文件中,系统内核调试器i366kd或windbg在调试跟踪这个转储文件时也会根据这个系统变量重新装入系统崩溃前的已装载模块。我下面列出根据这个变量枚举系统模块的代码:

    //-----------------------------------------------
    //
    // EnumKernelModules
    // Only test on Windows 2000 Server Chinese Edition 
    // Build 2195(Free)!Programmed By WebCrazy
    // ( tsu00@263.net ) on 10-27-2000!
    //
    //-----------------------------------------------

    ULONG PsLoadedModuleList=0x8046a4c0;  //fetch from symbol file
    #define KERNELMOD_IMAGEBASE_OFFSET   0x18
    #define KERNELMOD_IMAGENAME_OFFSET   0x24

    void EnumKernelModules()
    {

        PLIST_ENTRY pKernelModuleListHead, pKernelModuleListPtr;
        PUNICODE_STRING pImageName;

        if(((USHORT)NtBuildNumber)!=2195){
           DbgPrint("Only test on Windows 2000 Server Build 2195!/n");
           return;
        }

        DbgPrint("/n    Base Addr/tModule Name");
        DbgPrint("/n    ---------/t-----------/n");

       pKernelModuleListHead=pKernelModuleListPtr=(PLIST_ENTRY)(ULONG *)PsLoadedModuleList;
         
        do{
            pKernelModuleListPtr=pKernelModuleListPtr->Flink;
            DbgPrint("    %08X",
                     *(ULONG *)((char *)   pKernelModuleListPtr+KERNELMOD_IMAGEBASE_OFFSET));
            pImageName = (PUNICODE_STRING)(ULONG *)((char *)
                        pKernelModuleListPtr+KERNELMOD_IMAGENAME_OFFSET);
            DbgPrint("/t%S/n",pImageName->Buffer);
        }while(pKernelModuleListPtr->Flink!=pKernelModuleListHead);
    }

    上面PsLoadedModuleList的值我直接从Symbol文件中获得,你可根据实际情况予以调整。好,先看看EnumKernelModules输出结果:

    Base Addr   Module Name
    ---------   -----------
    80400000    /WINNT/System32/ntoskrnl.exe
    80062000    /WINNT/System32/hal.dll
        .             .
        .             .
    FD0F8000    /SystemRoot/System32/Drivers/Cdfs.SYS
    FCDB1000    /SystemRoot/System32/DRIVERS/ipsec.sys
        .             .
        .             .

    与Softice的mod命令基本相同,但值得注意的是Softice的mod命令不仅输出进程内核模块,而且也列出特定进程的用户态模块列表,那么系统又是如何管理进程特定模块的呢?

    由于每个进程都拥有各自的模块,而所有这些模块都要求从用户态可以访问到。因此进程模块组织的数据结构应该位于用户态地址空间中,实际上Windows NT/2000进程模块列表是由PEB(Process Environment Block)结构中的成员指定,Windows NT/2000均将每个拥有用户态代码的进程的PEB置于0x7FFDF000处(2G空间以下,用户态代码可直接访问)。不过,Windows NT/2000一般通过TEB间接得到PEB的地址,即通过以下代码取得:
    mov eax,fs:[18]
    mov eax,[eax+30]
    第一条语句获得当前线程的TEB地址,关于TEB及TEB地址的获得,请参阅《 Windows NT/2000内部数据结构探究》,第二条语句获得位于TEB偏移30h处获得PEB地址。我想Windows NT/2000使用这种方法可能是考虑兼容性吧,我以下提供的代码直接使用了常量地址。

    看看Windbg的分析吧:

    > !ntsdexts.version
    Version 5.0 (Build 2195) Uniprocessor Free

    > !ntsdexts.peb
    PEB at 7FFDF000
        InheritedAddressSpace:    No
        ReadImageFileExecOptions: No
        BeingDebugged:            Yes
        ImageBaseAddress:         01000000
        Ldr.Initialized: Yes
        Ldr.InInitializationOrderModuleList: 71f80 . 72808
        Ldr.InLoadOrderModuleList: 71ee0 . 727f8
        Ldr.InMemoryOrderModuleList: 71ee8 . 72800
            01000000 D:/winnt/system32/calc.exe
            77F80000 D:/WINNT/System32/ntdll.dll
            77560000 D:/WINNT/system32/SHELL32.dll
            77F40000 D:/WINNT/system32/GDI32.DLL
            77E60000 D:/WINNT/system32/KERNEL32.DLL
            77DF0000 D:/WINNT/system32/USER32.DLL
            77D90000 D:/WINNT/system32/ADVAPI32.DLL
            77D20000 D:/WINNT/system32/RPCRT4.DLL
            77C50000 D:/WINNT/system32/SHLWAPI.DLL
            77B30000 D:/WINNT/system32/COMCTL32.DLL
            78000000 D:/WINNT/system32/MSVCRT.dll
        SubSystemData:     0
        ProcessHeap:       70000
        ProcessParameters: 20000
            WindowTitle:  'D:/winnt/system32/calc.exe'
            ImageFile:    'D:/winnt/system32/calc.exe'
              .                .
              .                .
              .                .

    Windbg的以上输出详细的显示了PEB各字段值,对其中数据跟踪分析后,我写了以下程序段直接读取系统结构获取进程模块列表:

    //-----------------------------------------------
    //
    // EnumUserModules-information from PEB
    // Only test on Windows 2000 Server Chinese Edition 
    // Build 2195(Free)!Programmed By WebCrazy
    // ( tsu00@263.net ) on 10-27-2000!
    //
    //-----------------------------------------------

    #define PEBADDRESS                 0x7ffdf000
    #define PEB_LDR_DATA_OFFSET        0x0c
    #define LDRDATA_IMAGEBASE_OFFSET   0x10
    #define LDRDATA_IMAGENAME_OFFSET   0x1c

    #pragma pack(4)
    typedef struct _PEB_LDR_DATA
    {
       ULONG Length;
       BOOLEAN Initialized;
       PVOID SsHandle;
       LIST_ENTRY InLoadOrderModuleList;
       LIST_ENTRY InMemoryOrderModuleList;
       LIST_ENTRY InInitializationOrderModuleList;
    } PEB_LDR_DATA, *PPEB_LDR_DATA;
    #pragma pack()

    void EnumUserModules(void *kpeb)
    {
        PLIST_ENTRY pUserModuleListHead, pUserModuleListPtr;
        PPEB_LDR_DATA pLdrData;
        PUNICODE_STRING pImageName;

        if(((USHORT)NtBuildNumber)!=2195){
           DbgPrint("Only test on Windows 2000 Server Build 2195!/n");
           return;
        }

        KeAttachProcess(kpeb);

        pLdrData=(PPEB_LDR_DATA)(ULONG *)(*(ULONG *)(PEBADDRESS+PEB_LDR_DATA_OFFSET));
        if(!pLdrData->Initialized){
           DbgPrint("Process:%08X Not Initialized!/n",(ULONG)kpeb);
           KeDetachProcess();
           return;
        } 

        DbgPrint("/n    Base Addr/tModule Name");
        DbgPrint("/n    ---------/t-----------/n");

        pUserModuleListHead=pUserModuleListPtr=
                (PLIST_ENTRY)&(pLdrData->InMemoryOrderModuleList);
         
        do{
             pUserModuleListPtr=pUserModuleListPtr->Flink;
             DbgPrint("    %08X",*(ULONG *)((char *)
                      pUserModuleListPtr+LDRDATA_IMAGEBASE_OFFSET));
             pImageName = (PUNICODE_STRING)(ULONG *)((char *)
                      pUserModuleListPtr+LDRDATA_IMAGENAME_OFFSET);
             DbgPrint("/t%S/n",pImageName->Buffer);
       }while(pUserModuleListPtr->Flink!=pUserModuleListHead);

       KeDetachProcess();
    }

    EnumUserModules程序段实现对特定进程(由参数kpeb所指定)模块的枚举,函数段并未实现对PEB合法性的检查,如Idle与system进程是纯内核态进程,他们并不存在用户态的PEB,解决办法可以通过检查TEB的合法性,这些进程TEB一般情况下都为0。EnumUserModules也没有对kpeb的合法性检查,其假设所有kpeb当前在系统中都存在,否则将出现意想不到的结果。虽然只涉及到用户态数据的读取,但程序段中使用了KeAttachProcess/KeDetachProcess内核例程,所以程序段只能在驱动代码中实现。EnumUserModules使用InMemoryOrderModuleList成员枚举模块列表的(见上Windbg输出结果,EnumUserModules的输出结果与其一致),当然你也可以使用InInitializationOrderModuleList或InLoadOrderModuleList成员。

  上面已经说明了SoftICE中的mod命令将系统模块与进程模块列出,也就是说其实现了我提供的这两个程序段(SoftICE还输出PE模块的PE Header段,可以根据Base Addr按照PE规范取出PE Header的位置,另外我不确信SoftICE是不是使用同样的方法实现的)。

    不论用户态的可执行Win32模块(.exe)或是核心态的驱动程序(.sys),还是系统动态链接库(.dll)在Windows NT/2000中均是以PE格式存在的,但也未必所有模块均为此格式,实际上所有文件均可以作为模块出现,如常见的nls文件等等。关于PE文件的装载,Windows NT/2000提供了以ldr开头的函数族,至于其结构已众所周知,我将不予介绍。

    Windows 2000中实现了枚举系统模块的PSAPI与ToolHelp API(ToolHelp API在Win9X中早已实现,但Windows NT却仅使用PSAPI函数),我在跟踪分析这部分代码时原以为多少可以参阅一下这些函数的头文件中的一些譬如MODULEENTRY32或MODULEINFO等的定义,但系统内部却使用完全不同的格式.可以这样说系统内部的结构大而全且仅使用UNICODE格式,而这些API仅呈现API使用的部分定义,向用户隐藏了好多的内部特征。不过ToolHelp等使用Section对象(Win32 API称为FileMapping对象)映射整个模块实现模块的枚举,不过其最终都使用到EnumUserModules所引用的PEB数据。关于PEB还包含着许多系统数据,如ProcessHeap、ProcessParameters等等,感兴趣的话可以使用windbg/Softice好好挖掘挖掘!

    参考资料:
      1.David Solomom《Inside Windows NT,2nd Edition》

http://www.niftyadmin.cn/n/3725569.html

相关文章

自行车运动水壶及水壶架的作用

自行车运动水壶的特点在于可以单手开启(徒步及登山用水壶则需要双手开启),这对于喜欢边骑边喝水的朋友比较实用,不过这时要记得放慢速度并注意前后来车哦。水壶架通常安装在车架上,水壶置于其上可以减轻背负重量。 【…

mysql导出数据1049_mysql怎么导入数据库 1049 42000 unknown database

前很多年, 使用mysqldump / mysql 进行数据库的备份和恢复, 都非常成功,但是, 这周换了windows2012 r2 和 mysql 5.6.15后, 并加一个discuz论坛是可以访问的 备份数据库是没有问题的, 但数据库恢复有两个错误:1 测试1命令行 .\mysql -uroot -p 数据库名错误提示内容…

剖析Windows NT/2000内核对象组织(http://webcrazy.yeah.net)

剖析Windows NT/2000内核对象组织 WebCrazy( http://webcrazy.yeah.net/)对象管理器在Windows NT/2000内核中占了极其重要的位置,其一个最主要职能是组织管理系统内核对象。在Windows NT/2000中,内核对象管理器大量引入了C面向对象的思想,即…

mysql 事务处理 存储过程_MySQL存储过程事务处理

Dev tdxDBTreeView可以快速的用tree展示层次结构,无需任何编码;对tree的操作会自动post到数据集:对数据集的操作会 在tree上表现 一.关键 设置 datasource displayField:节点的 ...《JavaScript DOM 编程艺术(第2版)》读书笔记阅读了本书第五章关于使用JavaScript的最佳实践,…

分析Windows NT/2000堆内存与虚拟内存组织(http://webcrazy.yeah.net)

分析Windows NT/2000堆内存与虚拟内存组织 WebCrazy( http://webcrazy.yeah.net/)在讨论今天这个主题时,我觉得是应该给大家重复推介以下两本书:Matt Pietrek<<Windows 95 System Programming Secrets>>Jeffrey Richter<<Programming Microsoft Windows,f…

mysql 三个关联_MySQL中三种关联查询方式的简单比较

看看下面三个关联查询的 SQL 语句有何区别&#xff1f;SELECT * FROM film JOIN film_actor ON (film.film_id film_actor.film_id)SELECT * FROM film JOIN film_actor USING (film_id)SELECT * FROM film, film_actor WHERE film.film_id film_actor.film_id最大的不同更多…

idea 里面的maven依赖下载不了,以及mabtis依赖包错误

目录前言一&#xff0c;下载maven依赖下载不了&#xff08;数据源问题&#xff09;二、之前用的MapperScan不能注入了&#xff08;mybatis包未注入&#xff09;三、mybatis版本问题&#xff08;mybatis&#xff09;后言前言 在进行了一天的奋战中&#xff0c;因为maven依赖而导…

redhat6 dns服务器安装 正向 反向解析

几个dns解析的笔记&#xff0c;以后用得着&#xff1a;redhat6 dns服务器安装 正向 反向解析1.安装bind包&#xff0c;由于6之后的caching-nameserver 已经整合到bind包里面了&#xff0c;所以不需要安装caching-nameserver了 但是6之前需要yum install bind bind-utils bind-c…