首页
论坛
课程
招聘
[原创]通过PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine实现进程与线程监控
2021-9-12 20:32 14689

[原创]通过PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine实现进程与线程监控

2021-9-12 20:32
14689

    PsSetCreateProcessNotifyRoutineEx是Windows提供得一个可以通过设置回调函数实现进程监控的内核API。其在文档的定义如下:

NTSTATUS
  PsSetCreateProcessNotifyRoutineEx(
    IN PCREATE_PROCESS_NOTIFY_ROUTINE_EX  NotifyRoutine,   
    IN BOOLEAN  Remove       
    );

    参数NotifyRoutine指向了CREATE_PROCESS_NOTIFY_ROUTINE_EX的指针,来指定我们需要执行的回调函数。

    参数Remove来指定是把回调函数加入回调列表中,为False则把例程添加进列表中,为True则把例程从列表中删除。当函数返回值为STATUS_SUCCESS时,则函数调用成功。

    PCREATE_PROCESS_NOTIFY_EOUTINE_EX的回调函数定义如下:

VOID
  CreateProcessNotifyEx(
    __inout PEPROCESS  Process,
    __in HANDLE  ProcessId,
    __in_opt PPS_CREATE_NOTIFY_INFO  CreateInfo
    );

    参数Process是指向EPROCESS的指针。

    参数ProcessId则是进程ID。

    参数CreateInfo是一个指向PS_CREATE_NOTIFY_INFO的指针,当它为NULL时表明进程退出,不为NULL时表明进程创建,里面存储着要创建的进程信息。

    PS_CREATE_NOTIFY_INFO的具体定义如下所示:

typedef struct _PS_CREATE_NOTIFY_INFO {
  __in SIZE_T  Size;
  union {
    __in ULONG  Flags;
    struct {
      __in ULONG  FileOpenNameAvailable : 1;
      __in ULONG  Reserved : 31;
    };
  };
  __in HANDLE  ParentProcessId;
  __in CLIENT_ID  CreatingThreadId;
  __inout struct _FILE_OBJECT  *FileObject;
  __in PCUNICODE_STRING  ImageFileName;
  __in_opt PCUNICODE_STRING  CommandLine;
  __inout NTSTATUS  CreationStatus;
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

    参数ImageFileName说明了要创建的进程的名字。

    参数CreationStatus说明了进程创建操作返回的NTSTATUS值。我们可以通过把这个值修改为STATUS_UNSUCCESSFUL来阻止进程的创建。

    根据上述内容我们可以写出相应的代码如下:

status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, FALSE);
if (!NT_SUCCESS(status))
{
    DbgPrint("回调函数设置失败, status=%X", status);
}
else
{
    DbgPrint("进程监控已开启\r\n");
}

    其中的CreateProcessNotifyEx的实现如下:

// 未导出函数声明
PCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);

VOID CreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
	//获取进程的名称
	PCHAR pszImageFileName = NULL;

	if (CreateInfo != NULL)    //不为NULL代表创建进程
	{
		pszImageFileName = PsGetProcessImageFileName(Process);
		if (pszImageFileName) DbgPrint("新创建的进程是:%s.\r\n", pszImageFileName);
		if (strcmp(pszImageFileName, "demo.exe") == 0)
		{
			CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
			DbgPrint("拦截进程:%s成功.\r\n", pszImageFileName);
		}
	} 
}

    这里我将新创建的进程打印出来且拦截名为demo.exe的进程。

    在卸载驱动时,必须要将例程从列表中删除,否则新创建或者退出的进程会因为找不到回调函数而蓝屏。所以卸载驱动的代码如下:

VOID DriverUnload(IN PDEVICE_OBJECT driverObject)
{
	NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, TRUE);

	if (!NT_SUCCESS(status))
	{
		DbgPrint("回调函数删除失败\r\n status=%X", status);
	}
	else
	{
		DbgPrint("回调函数成功删除\r\n");
	}
	DbgPrint("驱动卸载完成\r\n");
}

    这里还必须说明的是,调用PsSetCreateProcessNotifyRoutineEx函数时,需要对强制完整性签名进行破解。破解的方法也很简单,只需要把驱动对象中的DriverSection中的Flags包含0x20就行,代码如下:

pLdrData = (PKLDR_DATA_TABLE_ENTRY)driverObject->DriverSection;
pLdrData->Flags = pLdrData->Flags | 0x20;

    其中的KLDR_DATA_TABLE_ENTRY的定义如下:

typedef struct _KLDR_DATA_TABLE_ENTRY
{
	LIST_ENTRY InLoadOrderModuleList;
	LIST_ENTRY InMemoryOrderModuleList;
	LIST_ENTRY InInitializationOrderModuleList;
	PVOID DllBase;
	PVOID EntryPoint;
	UINT32 SizeOfImage;
	UNICODE_STRING FullDllName;
	UNICODE_STRING BaseDllName;
	UINT32 Flags;
	USHORT LoadCount;
	USHORT TlsIndex;
	LIST_ENTRY HashLinks;
	PVOID SectionPointer;
	UINT32 CheckSum;
	UINT32 TimeDateStamp;
	PVOID LoadedImports;
	PVOID EntryPointActivationContext;
	PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;

    代码的最终效果如下所示:

    

    相比于进程监控,设置线程监控的API相对简单,定义如下:

NTSTATUS
  PsSetCreateThreadNotifyRoutine(
    IN PCREATE_THREAD_NOTIFY_ROUTINE  NotifyRoutine
    );

    参数NotifyRoutine指向了要设置的例程,而PCREATE_THREAD_NOTIFY_ROUTINE的定义如下:

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

    参数ProcessId和ThreadId分别代表了进程与线程ID。

    参数Create为True和False分别代表了线程的创建和销毁。

    根据上述内容可以写出如下代码:

status = PsSetCreateThreadNotifyRoutine(CreateThreadNotifyRoutine);
if (!NT_SUCCESS(status))
{
    DbgPrint("回调函数设置失败, status=%X", status);
}
else
{
    DbgPrint("线程监控已开启\r\n");
}

    其中的CreateThreadNotifyRoutine实现如下:

VOID CreateThreadNotifyRoutine(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create)
{
	if (Create)
	{
		DbgPrint("新创建的线程ID为:%d,所属进程ID为:%d.\r\n", ThreadId, ProcessId);
	}
	else
	{
		DbgPrint("新销毁的线程ID为:%d,所属进程ID为:%d.\r\n", ThreadId, ProcessId);
	}
}

    这里仅仅是打印出相应的进程与线程ID。

    当驱动卸载时,依然需要删除例程,否则会带来蓝屏。删除例程的API定义如下:

NTSTATUS
  PsRemoveCreateThreadNotifyRoutine(
    IN PCREATE_THREAD_NOTIFY_ROUTINE  NotifyRoutine 
    );

    参数NotifyRoutine便是我们要删除的例程。

    所以在驱动卸载中,我们删除例程的代码如下:

NTSTATUS status = PsRemoveCreateThreadNotifyRoutine(CreateThreadNotifyRoutine);

if (!NT_SUCCESS(status))
{
    DbgPrint("回调函数删除失败\r\n status=%X", status);
}
else
{
    DbgPrint("回调函数成功删除\r\n");
}

    最终的运行结果如下图所示:


恭喜ID[飞翔的猫咪]获看雪安卓应用安全能力认证高级安全工程师!!

最后于 2021-10-17 17:12 被1900编辑 ,原因:
收藏
点赞2
打赏
分享
最新回复 (6)
雪    币: 128
活跃值: 活跃值 (908)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 活跃值 2021-9-13 08:14
2
0
mark
雪    币: 2793
活跃值: 活跃值 (1987)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 活跃值 2021-9-16 10:48
3
0
 
雪    币: 658
活跃值: 活跃值 (541)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
stand ot law 活跃值 2021-9-18 07:56
4
0
mark
雪    币: 2647
活跃值: 活跃值 (3209)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
badboyl 活跃值 2 2021-9-18 08:47
5
0
这不是早就有了吗
雪    币: 18171
活跃值: 活跃值 (18581)
能力值: ( LV15,RANK:770 )
在线值:
发帖
回帖
粉丝
1900 活跃值 5 2021-9-18 09:40
6
0
blck四 这不是早就有了吗

是嘛。。没在论坛搜索到。。我以为还没人发

最后于 2021-9-27 18:51 被1900编辑 ,原因:
雪    币: 18171
活跃值: 活跃值 (18581)
能力值: ( LV15,RANK:770 )
在线值:
发帖
回帖
粉丝
1900 活跃值 5 2021-9-18 09:41
7
0
1900 是嘛。。没在论文搜索到。。我以为还没人发


最后于 2021-9-27 18:51 被1900编辑 ,原因:
游客
登录 | 注册 方可回帖
返回