首页 > 系统相关 >4.4 Windows驱动开发:内核监控进程与线程创建

4.4 Windows驱动开发:内核监控进程与线程创建

时间:2023-11-18 10:23:49浏览次数:39  
标签:4.4 函数 ProcessId Windows 线程 NOTIFY 进程 回调

当你需要在Windows操作系统中监控进程的启动和退出时,可以使用PsSetCreateProcessNotifyRoutineEx函数来创建一个MyCreateProcessNotifyEx回调函数,该回调函数将在每个进程的创建和退出时被调用。

PsSetCreateProcessNotifyRoutineEx 用于在系统启动后向内核注册一个回调函数,以监视新进程的创建和退出,其函数原型如下:

NTSTATUS PsSetCreateProcessNotifyRoutineEx(
  PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
  BOOLEAN                             Remove
);

其中,参数NotifyRoutine是一个指向回调函数的指针,该函数将在新进程创建或退出时被调用。参数Remove是一个布尔值,用于指定是否从内核中删除之前注册的回调函数。如果要删除之前注册的回调函数,则将此参数设置为TRUE。如果要注册一个新的回调函数,则将此参数设置为FALSE。

如上PCREATE_PROCESS_NOTIFY_ROUTINE_EX用于接收一个自定义回调,该回调函数的参数传递需要定义为如下所示的样子;

VOID CreateProcessNotifyRoutineEx(
  PEPROCESS           Process,
  HANDLE              ProcessId,
  PPS_CREATE_NOTIFY_INFO CreateInfo
);

其中,参数Process是一个指向新创建进程的EPROCESS结构的指针。参数ProcessId是新进程的PID(进程ID)。参数CreateInfo是一个指向一个PS_CREATE_NOTIFY_INFO结构的指针,该结构包含了有关新进程的详细信息。如果新进程是由系统启动的,CreateInfo将为空。

回调函数应该在执行完后尽快返回,以避免对系统性能的影响。同时,回调函数不应该调用任何可能导致死锁或系统崩溃的函数。

对于进程的创建与退出,则可通过MyCreateProcessNotifyEx自定义函数的PPS_CREATE_NOTIFY_INFO字段进行判断,如果PPS_CREATE_NOTIFY_INFO不等于NULL则说明该进程是被创建了,反之则说明进程是退出了,有了这些基础知识那么实现监视进程加载将变得很容易,如下案例所示;

#include <ntddk.h>

NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);

PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
    NTSTATUS st = STATUS_UNSUCCESSFUL;
    PEPROCESS ProcessObj = NULL;
    PCHAR string = NULL;
    st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
    if (NT_SUCCESS(st))
    {
        string = PsGetProcessImageFileName(ProcessObj);
        ObfDereferenceObject(ProcessObj);
    }
    return string;
}

VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
    char ProcName[16] = { 0 };
    if (CreateInfo != NULL)
    {
        strcpy(ProcName, PsGetProcessImageFileName(Process));
        DbgPrint("父进程ID: %ld  --->父进程名: %s --->进程名: %s---->进程路径:%wZ", CreateInfo->ParentProcessId,
            GetProcessNameByProcessId(CreateInfo->ParentProcessId),
            PsGetProcessImageFileName(Process),CreateInfo->ImageFileName);
    }
    else
    {
        strcpy(ProcName, PsGetProcessImageFileName(Process));
        DbgPrint("进程[ %s ] 离开了,程序被关闭了",ProcName);
    }
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

输出效果图如下所示:

那么如何进行进程监控呢?

进程监控的思路很简单,只需要在如上MyCreateProcessNotifyEx()这个自定义回调函数中进行改进即可,首先通过PsGetProcessImageFileName即将进程的PID转换为进程名,然后就可以通过_stricmp对比该进程是否为我们所需要的,如果发现是calc.exe等特定进程名,则可以将CreateInfo->CreationStatus中的参数修改为STATUS_UNSUCCESSFUL这意味着对象的创建过程未成功完成,从而实现拒绝进行执行的目的;

#include <ntddk.h>

NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);

PCHAR GetProcessNameByProcessId(HANDLE ProcessId)
{
    NTSTATUS st = STATUS_UNSUCCESSFUL;
    PEPROCESS ProcessObj = NULL;
    PCHAR string = NULL;
    st = PsLookupProcessByProcessId(ProcessId, &ProcessObj);
    if (NT_SUCCESS(st))
    {
        string = PsGetProcessImageFileName(ProcessObj);
        ObfDereferenceObject(ProcessObj);
    }
    return string;
}

VOID MyCreateProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
    char ProcName[16] = { 0 };
    if (CreateInfo != NULL)
    {
        strcpy(ProcName, PsGetProcessImageFileName(Process));
        if (!_stricmp(ProcName, "calc.exe"))
        {
            CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
        }
    }
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, TRUE);
    DbgPrint(("驱动卸载成功"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    status = PsSetCreateProcessNotifyRoutineEx((PCREATE_PROCESS_NOTIFY_ROUTINE_EX)MyCreateProcessNotifyEx, FALSE);
    Driver->DriverUnload = UnDriver;
    DbgPrint("驱动加载成功!");
    return STATUS_SUCCESS;
}

将上方代码编译,当我们加载驱动程序以后,再次打开C:\Windows\System32\calc.exe 计算器进程则提示无法打开,我们的驱动已经成功的拦截了本次的请求。

与进程检测类似,如果要检测线程创建则只需要通过PsSetCreateThreadNotifyRoutine创建线程回调即可,PsSetCreateThreadNotifyRoutine 函数的原型如下:

NTKERNELAPI
VOID
PsSetCreateThreadNotifyRoutine(
  _Inout_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine,
  _In_ BOOLEAN                           Remove
);

PsSetCreateThreadNotifyRoutine 它允许注册一个回调函数,以便在新的线程被创建时被调用。该函数有两个参数:

  • 第一个参数:是一个指向回调函数的指针,这个回调函数将在新的线程被创建时被调用。
  • 第二个参数:是一个布尔值,表示是否将此回调函数添加到一个已有的回调列表中。如果此参数为TRUE,则将该回调函数添加到列表中,如果为FALSE,则将替换掉已有的回调函数。

当一个新的线程被创建时,操作系统会调用所有已注册的回调函数,并将新线程的ThreadID和进程ID作为参数传递给回调函数。这些参数可以用来识别新线程所属的进程以及新线程本身的标识符。

对于PCREATE_THREAD_NOTIFY_ROUTINE来说,它指向一个回调函数,用于通知进程中新线程的创建。该函数指针的定义如下:

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

回调函数的参数说明如下:

  • ProcessId:新线程所属进程的进程ID。
  • ThreadId:新线程的线程ID。
  • Create:布尔值,指示新线程是创建还是销毁。如果为TRUE,则表示新线程已创建;如果为FALSE,则表示新线程已销毁。

在 PsSetCreateThreadNotifyRoutine 函数中注册的回调函数应该符合这个函数指针的定义,以便在新线程被创建或销毁时被调用。

而当调用结束后,用户需要通过PsRemoveCreateThreadNotifyRoutine来删除已注册的回调函数,目前该函数只需要一个参数,只需要传入注册时的函数指针即可;

NTKERNELAPI
VOID
PsRemoveCreateThreadNotifyRoutine(
  _Inout_ PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);

通过上述知识点的总结,相信你也可以很容易的编写处线程检测相关代码片段,具体代码如下:

#include <ntddk.h>

NTKERNELAPI PCHAR PsGetProcessImageFileName(PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS *Process);

VOID MyCreateThreadNotify(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create)
{
    PEPROCESS eprocess = NULL;
  // 通过此函数拿到程序的EPROCESS结构
    PsLookupProcessByProcessId(ProcessId, &eprocess);
    if (Create)
  {
        DbgPrint("线程TID: %1d --> 所属进程名: %s --> 进程PID: %1d \n", ThreadId, PsGetProcessImageFileName(eprocess), PsGetProcessId(eprocess));
  }
    else
  {
        DbgPrint("%s 线程已退出...", ThreadId);
  }
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    PsRemoveCreateThreadNotifyRoutine(MyCreateThreadNotify);
    DbgPrint(("驱动卸载成功"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    NTSTATUS status;
    status = PsSetCreateThreadNotifyRoutine(MyCreateThreadNotify);
    DbgPrint("PsSetCreateThreadNotifyRoutine: %x", status);
    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

输出效果图如下所示:

标签:4.4,函数,ProcessId,Windows,线程,NOTIFY,进程,回调
From: https://www.cnblogs.com/LyShark/p/17840123.html

相关文章

  • 4.5 Windows驱动开发:内核中实现进程数据转储
    多数ARK反内核工具中都存在驱动级别的内存转存功能,该功能可以将应用层中运行进程的内存镜像转存到特定目录下,内存转存功能在应对加壳程序的分析尤为重要,当进程在内存中解码后,我们可以很容易的将内存镜像导出,从而更好的对样本进行分析,当然某些加密壳可能无效但绝大多数情况下是可以......
  • C++线程
    进程以CPU为运行单位,多个CPU可以实现进程并行,单个CPU可以实现进程并发(进程调度)线程以CPU的核心为运行单位,多个CPU内核可以实现线程并行,单个内核可以实现线程并发(线程调度)1、创建和结束一个线程 #include<iostream>#include<pthread.h>///@brief创建一个线程///@param......
  • 编辑 windows 操作系统 local host 文件的注意事项
    本地主机文件(也称为hosts文件)是操作系统中的一个文本文件,用于将主机名映射到IP地址。这个文件在Windows、Linux和macOS等各种操作系统中都存在,它允许计算机用户手动指定特定主机名与其相应的IP地址之间的关系,以便在域名解析时绕过DNS服务器。本地主机文件通常用于本地开发、网络故......
  • Windows rustup update 速度慢,使用字节跳动Rust镜像加速
    不设置镜像加速rustup更新升级会非常慢RsProxy字节跳动的Rust镜像 Windows想要使用这个镜像需要按照官方提示去设置两个系统变量分别为 RUSTUP_DIST_SERVER RUSTUP_UPDATE_ROOT 之后来到当前用户文件夹下修改cargo的配置文件(没有就创建一个)C:\Users\你PC名\.c......
  • .NET处理线程安全的方法、类和解决方案
    使用锁机制:Monitor类:提供了Enter和Exit方法,可以使用Monitor.Enter(obj)锁定一个对象,使用Monitor.Exit(obj)释放锁。lock关键字:C#中的lock关键字会自动创建一个Monitor锁,确保在同一时间只有一个线程可以进入被lock包裹的代码块。使用互斥体(Mutex):Mutex类:提供了WaitOne......
  • JAVA解析Excel文件 + 多线程 + 事务回滚
    1.项目背景:客户插入Excel文件,Ececel文件中包含大量的数据行和数据列,单线程按行读取,耗时大约半小时,体验感不好。思路:先将excel文件按行读取,存入List,然后按照100均分,n=list.szie()/100+1;n就是要开启的线程总数。(实际使用的时候,数据库连接池的数量有限制,n的大小要结合数据库连......
  • JAVA 解析Excel + 多线程 + 事务回滚(2)
    该方法为网上查询,感觉可行,并未真正尝试。主线程:packagecom.swagger.demo.service;importcom.alibaba.excel.context.AnalysisContext;importcom.alibaba.excel.event.AnalysisEventListener;importcom.swagger.demo.config.SpringJobBeanFactory;importcom.swagger.demo.m......
  • 【Windows Server】利用Windows Server中的SMTP功能搭建简易的邮件传输服务
    介绍:SMTP(简单邮件传输协议)是一种服务,使电子邮件交换在互联网和本地网络。为了实现这一点,SMTP与邮件传输代理(MTA)进行交互,并确保消息到达预期的收件人。邮件服务器]和其他消息传输代理通常使用SMTP发送和接收电子邮件消息。在本文中,我们会演示如何在Windows上安装和配置SMTP服务......
  • windows停止占用指定端口的后台进程
    说明有点类似于linux下的使用lsof得到是哪个程序占用了端口,然后再使用kill结束程序#查询占用端口的程序的相关信息sudolsof-i:8080#停止程序kill11111Windows下使用#类似于lsofnetstat-ano|findstr8080#类似于kill-9taskkill/PID11111/F......
  • 安装 IIS 访问临时文件夹 C:\WINDOWS\TEMP\3C 读取/写入权限 错误: 0x80070005
    在windows中使用命令行方式安装IIS(Web服务器)WindowsServer2022安装IIS报错访问临时文件夹C:\WINDOWS\TEMP\3C读取/写入权限错误:0x80070005,可以使用命令行方式来安装和配置Web服务(IIS)。以下是使用DeploymentImageServicingandManagement(DISM)工具的步骤:1.打......