创建时间:2003-03-21
文章属性:原创
文章来源:http://www.whitecell.org
文章提交:sinister (jiasys_at_21cn.com)

编写进程/线程监视器


Author   : sinister
Email    : sinister@whitecell.org
HomePage: http://www.whitecell.org

(首先说明一下。有不少朋友来信问一些进程/线程监视工具是如何实现的。
我写出来是为了让那些朋友有进一步的了解,也省的我一封封的回MAIL。如果您
是 NT DRIVER熟手,那么此文提到的方法您可能早已掌握,完全可以略过不看。)

   有时候我们希望能够动态监视系统中任意进程/线程的创建与销毁。为了达
到此目的我翻阅了 DDK 手册,发现其提供的 PsSetCreateProcessNotifyRoutine(),
PsSetCreateThreadNotifyRoutine(),等函数可以实现此功能。这两个函数可以
通过向系统注册一个 CALLBALCK 函数来监视进程/线程等操作。函数原形如下:

NTSTATUS
   PsSetCreateProcessNotifyRoutine(
   IN PCREATE_PROCESS_NOTIFY_ROUTINE   NotifyRoutine,
   IN BOOLEAN   Remove
   );

VOID
(*PCREATE_PROCESS_NOTIFY_ROUTINE) (
     IN HANDLE   ParentId,
     IN HANDLE   ProcessId,
     IN BOOLEAN   Create
     );


NTSTATUS
   PsSetCreateThreadNotifyRoutine(
   IN PCREATE_THREAD_NOTIFY_ROUTINE   NotifyRoutine
   );

VOID
(*PCREATE_THREAD_NOTIFY_ROUTINE) (
     IN HANDLE   ProcessId,
     IN HANDLE   ThreadId,
     IN BOOLEAN   Create
     );


通过原形可以看出,其 CALLBACK 函数只提供了进程ID/线程ID。并没有提供
进程名。那么我们要进一步通过进程ID来获取进程名。这需要用到一个未公开
的函数 PsLookupProcessByProcessId()。函数原形如下:

NTSTATUS PsLookupProcessByProcessId(
      IN ULONG ulProcId,
      OUT PEPROCESS * pEProcess
      );

函数输出的 EPROCESS 结构也是未公开的内核进程结构,很多人称其为 KPEB。
EPROCESS 结构中的偏移 0x1FC 指向当前进程名的偏移。(这个结构虽然可以在
驱动程序中直接使用。但没有公布其结构,网上有不少高手已将其结构给出。有
兴趣可以自行搜索,或去 IFS DDK 中获取,这里因为结构太长,就不贴出来了)
有了这个结构我们就可以从中得到进程名。NT系统还提供了一个函数可以动态监
视进程装载映像。此函数可以得到进程加栽时所调用的 DLL 名称与全路径,还有
一些映像信息。为我们获得更详细的进程装载信息提供了更好的帮助。

函数原形如下:

NTSTATUS
   PsSetLoadImageNotifyRoutine(
   IN PLOAD_IMAGE_NOTIFY_ROUTINE   NotifyRoutine
   );

VOID
(*PLOAD_IMAGE_NOTIFY_ROUTINE) (
     IN PUNICODE_STRING   FullImageName,
     IN HANDLE   ProcessId, // where image is mapped
     IN PIMAGE_INFO   ImageInfo
     );

typedef struct   _IMAGE_INFO {
     union {
         ULONG   Properties;
         struct {
             ULONG ImageAddressingMode   : 8; //code addressing mode
             ULONG SystemModeImage       : 1; //system mode image
             ULONG ImageMappedToAllPids : 1; //mapped in all processes
             ULONG Reserved              : 22;
         };
     };
     PVOID   ImageBase;
     ULONG   ImageSelector;
     ULONG   ImageSize;
     ULONG   ImageSectionNumber;
} IMAGE_INFO, *PIMAGE_INFO;

利用以上提供的函数与结构,我们便能实现一个进程/线程监视器。下面这段
代码演示了如何实现此功能。


/*****************************************************************
文件名         : WssProcMon.c
描述           : 进程/线程监视器
作者           : sinister
最后修改日期   : 2002-11-02

*****************************************************************/

#include "ntddk.h"
#include "string.h"

#define ProcessNameOffset   0x1fc

static NTSTATUS   MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS * pEProcess);
VOID ProcessCreateMon ( IN HANDLE   hParentId, IN HANDLE PId,IN BOOLEAN bCreate);
VOID ThreadCreateMon (IN HANDLE   PId, IN HANDLE TId, IN BOOLEAN   bCreate);
VOID ImageCreateMon (IN PUNICODE_STRING   FullImageName, IN HANDLE   ProcessId, IN PIMAGE_INFO   ImageInfo );


// 驱动入口
NTSTATUS   DriverEntry( IN PDRIVER_OBJECT DriverObject,   IN PUNICODE_STRING RegistryPath )
{
    
     UNICODE_STRING   nameString, linkString;
     PDEVICE_OBJECT   deviceObject;
     NTSTATUS         status;
     int                 i;
    

     //建立设备
     RtlInitUnicodeString( &nameString, L"\\Device\\WssProcMon" );
    
     status = IoCreateDevice( DriverObject,
                              0,
                              &nameString,
                              FILE_DEVICE_UNKNOWN,
                              0,
                              TRUE,
                              &deviceObject
                            );
                           

     if (!NT_SUCCESS( status ))
         return status;
    

     RtlInitUnicodeString( &linkString, L"\\DosDevices\\WssProcMon" );

     status = IoCreateSymbolicLink (&linkString, &nameString);

     if (!NT_SUCCESS( status ))
     {
         IoDeleteDevice (DriverObject->DeviceObject);
         return status;
     }    
    
     status = PsSetLoadImageNotifyRoutine(ImageCreateMon);
     if (!NT_SUCCESS( status ))
     {
         DbgPrint("PsSetLoadImageNotifyRoutine()\n");
         return status;
     }    

     status = PsSetCreateThreadNotifyRoutine(ThreadCreateMon);
     if (!NT_SUCCESS( status ))
     {
         DbgPrint("PsSetCreateThreadNotifyRoutine()\n");
         return status;
     }    

     status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, FALSE);
     if (!NT_SUCCESS( status ))
     {
         DbgPrint("PsSetCreateProcessNotifyRoutine()\n");
         return status;
     }    
    

     for ( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)     {

           DriverObject->MajorFunction[i] = MydrvDispatch;
     }
     
   return STATUS_SUCCESS;

}



//处理设备对象操作

static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
     Irp->IoStatus.Status = STATUS_SUCCESS;
     Irp->IoStatus.Information = 0L;
     IoCompleteRequest( Irp, 0 );
     return Irp->IoStatus.Status;
    
}


VOID ProcessCreateMon ( IN HANDLE hParentId, IN HANDLE PId,IN BOOLEAN bCreate )
{

     PEPROCESS   EProcess;
     ULONG       ulCurrentProcessId;
     LPTSTR        lpCurProc;
     NTSTATUS    status;

     status = PsLookupProcessByProcessId( (ULONG)PId, &EProcess);
     if (!NT_SUCCESS( status ))
     {
         DbgPrint("PsLookupProcessByProcessId()\n");
         return ;
     }
    

     if ( bCreate )
     {
           lpCurProc = (LPTSTR)EProcess;
         lpCurProc = lpCurProc + ProcessNameOffset;

         DbgPrint( "CREATE PROCESS = PROCESS NAME: %s , PROCESS PARENTID: %d, PROCESS ID: %d, PROCESS ADDRESS %x:\n",
                               lpCurProc,
                               hParentId,
                               PId,
                               EProcess );
     }
     
     else
     {

         DbgPrint( "TERMINATED == PROCESS ID: %d\n", PId);

     }

}

VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN   bCreate)
{

     PEPROCESS    EProcess;
     ULONG         ulCurrentProcessId;
     LPTSTR         lpCurProc;
     NTSTATUS     status;

     status = PsLookupProcessByProcessId( (ULONG)PId, &EProcess);
     if (!NT_SUCCESS( status ))
     {
         DbgPrint("PsLookupProcessByProcessId()\n");
         return ;
     }    

     if ( bCreate )
     {
           lpCurProc     = (LPTSTR)EProcess;
         lpCurProc     = lpCurProc + ProcessNameOffset;

         DbgPrint( "CREATE THREAD = PROCESS NAME: %s PROCESS ID: %d, THREAD ID: %d\n", lpCurProc, PId, TId );
                              
     }
     
     else
     {

         DbgPrint( "TERMINATED == THREAD ID: %d\n", TId);

     }

}

VOID ImageCreateMon (IN PUNICODE_STRING   FullImageName, IN HANDLE   ProcessId, IN PIMAGE_INFO   ImageInfo )
{
     DbgPrint("FullImageName: %S,Process ID: %d\n",FullImageName->Buffer,ProcessId);
     DbgPrint("ImageBase: %x,ImageSize: %d\n",ImageInfo->ImageBase,ImageInfo->ImageSize);

}