編寫程式/執行緒監視器

Just4life發表於2013-05-16
有時候我們希望能夠動態監視系統中任意程式/執行緒的建立與銷燬。為了達
到此目的我翻閱了 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);

}

相關文章