[转载]深入Windows NT/2000模块的组织 - 专题安全方向{ Security Research Papers } - 邪恶八进制信息安全团队技术讨论组 努力为祖国的信息安全撑起一片蓝天

导读:

 

[转载]深入Windows NT/2000模块的组织




文章作者:WebCrazy

信息来源:WebCrazy( http://www.geocities.jp/webcrazyjp/



深入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,2n 



本文转自

https://forum.eviloctal.com/thread-17931-1-9.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值