• 64位内核开发第十二讲,进程监视,ring3跟ring0事件同步.


    一丶同步与互斥详解,以及实现一个进程监视软件.

    1.用于线程同步的 KEVENT

    事件很简单分别分为 事件状态. 以及事件类别.

    事件状态:
    有信号 Signaled
    无信号 Non-signaled
    事件类别
    自动恢复 Synchronization 自动设置
    不自动恢复. Notification 手动设置

    事件的创建函数

    ** IoCreateNotificationEvent() **

    ** KeClearEvent() ** 设置为无信号状态

    ** KeResetEvent() ** 设置为无信号状态并返回之前的状态
    ** KeSetEvent()** 设置事件为有信号.

    其实理解有信号跟无信号就可以了. 有信号了.我的等待函数才可以等待.
    无信号就会阻塞在哪里.

    事件的类别就是等待之后 根据你的设置.是自动设置为无信号. 还是不设置.
    如果自动设置为无信号.那么下个线程来就会阻塞.直到我们设置为事件为有信号.才可以.

    在内核中一般使用事件是使用匿名内核事件. 而不是使用IoCreateNotificationEvent

    代码如下:

    KEVENT myKevent;
    KeInitializeEvent(&myKevent, NotificationEvent,FALSE);
    
    //设置事件为有信号
    
    KeSetEvent(&myKevent,IO_NO_INCREMENT,FALSE);
    
    //等待事件
    KeWaitForSingleObject(&myEvent,Executive,KernerMode,False,NULL);
    
    //因为设置的类别是手动设置类别.所以我们自己要把事件信号设置为无信号
    
    //调用两个都可以
    KeResetEvent(&myEvent);
    KeClearEvent(&myEvent);
    

    2.进程监视 ring0 与 ring3同步使用Event

    如果ring0跟ring3通讯.就要使用我们上面说的
    ring0 -> ring3通讯的命名函数了.

    IoCreateNotificationEvent
    在使用Ring0 -> ring3通讯的时候.我们要了解下这个函数以及其它相关的知识

    1.ring0 创建命名事件 - > ring3使用这个事件. 那么就需要建立一个名字了.名字如下;
    ** L"\BaseNamedObjects\你自定义的名字 **
    2.再探 IoCreateDevice函数的作用.

    IoCreateDevice 函数.众所周知.是建立设备对象.
    定义如下:

    NTSTATUS IoCreateDevice(
      PDRIVER_OBJECT  DriverObject,
      ULONG           DeviceExtensionSize,   //设备扩展大小
      PUNICODE_STRING DeviceName,
      DEVICE_TYPE     DeviceType,
      ULONG           DeviceCharacteristics,
      BOOLEAN         Exclusive,
      PDEVICE_OBJECT  *DeviceObject
    );
    
    

    我们先前所说.创建设备对象的时候.第二个参数是设备扩展大小.
    我们一直给0.但是现在因为 ring0 - ring3通信. 需要我们自定义数据结构.进行存储ring0的数据. 那么可以使用这个设备扩展了.
    如:
    我们创建一个结构体. 将这个结构体的大小传入到第二个参数中.
    使用的时候在我们创建的设备对象中.有一个成员.是s DeviceExtension.这个就是我们设备扩展为我们申请的那块内存.
    我们可以转化为我们自定义结构体大小.
    代码很简单.如下:

    typedef struct _MYDEVICE_EXTENSION
    {
      //自定义数据
    }MYDEVICE_EXTENSION,*PMYDEVICE_EXTENSION;
    
    IoCreateDevice(DriverObject,sizeof(MYDEVICE_EXTENSION),....);
    主要就是第二个参数.
    
    使用:
    PMYDEVICE_EXTENSION pMyDevice = (PMYDEVICE_EXTENSION)Device->DeviceExtension; 这个成员就指向我们扩展的内存.
    强转为我们的指针即可.
    
    pMyDevice->xxx = xxx;
    pMyDevice->xxx = xxx;
    
    

    最后使用内核创建事件 进行创建即可. IoCreateNotificationEvent

    ring3想使用ring0下定义的Event很简单.
    如下:

    #define EVENT_NAME L"\Global\xxx"
    
    HANDLE hEvent = OpenEventW(SYNCHRONIZE,FASE,EVENT_NAME);
    
    while(WaitForSingleObject(hEvent,INFINITE))
    {
      发送 DeviceIoControl读取内核层的数据即可.(上面说的设备扩展数据)
    
    }
    

    ring3等待ring0的事件就很简单了. 直接打开事件.等待即可.

    3.进程监视代码.

    进程监视.首先会用到上面所说内容.然后分为下面几个步骤

    1.创建设备对象.设备对象中扩展属性我们自定义结构体.传入结构体大小即可.
    2.创建全局设备对象变量指针.保存创建的设备对象

    3.创建符号链接,ring3 跟 ring 0进行通讯

    4.创建控制派遣函数.接受ring3下发的控制吗.

    5.使用IoCreateNotificationEvent创建事件对象.用于Ring3跟Ring0的事件同步.

    6.注册进程控制回调函数.当进程创建.或者销毁会调用回调

    7.回调函数,全局设备对象指针的子成员.指向我们自定义结构体.
    转换一下. 赋值参数.并且设置事件对象

    8.ring3读取数据的时候.控制函数将回调函数中赋值出来的数据拷贝给
    ring3即可.

    9.ring3进行打开事件.等待事件.发送DeviceIoControl控制吗.读取数据.显示 数据.

    代码如下:

    ring0:

    
    #include <ntddk.h>
    #include <ntstrsafe.h>
    
    
    
    
    
    /*
    符号连接名
    设备对象名
    事件等待名
    */
    
    
    #define  IBINARY_LINKNAME L"\DosDevices\IBinarySymboliclnk"
    #define  IBINARY_DEVICENAME  L"\Device\IBinaryProcLook"
    #define  IBINARY_EVENTNAME       L"\BaseNamedObjects\ProcLook"
    
    //定义 ring0 ring3控制码
    #define CTRLCODE_BASE 0x8000
    #define MYCTRL_CODE(i) 
    CTL_CODE(FILE_DEVICE_UNKNOWN,CTRLCODE_BASE +i,METHOD_BUFFERED,FILE_ANY_ACCESS)
    #define IOCTL_PROCESS_LOCK_READ MYCTRL_CODE(1)
    UNICODE_STRING g_uSymbolicLinkName = { 0 };
    //控制派遣函数.以及卸载函数.
    
    void DriverUnLoad(PDRIVER_OBJECT pDriverObject);
    NTSTATUS InitDeviceAnSymbolicLink(
    	PDRIVER_OBJECT pDriverObj,
    	UNICODE_STRING uDeviceName, 
    	UNICODE_STRING uSymbolicLinkName,
    	UNICODE_STRING uEventName);
    
    NTSTATUS DisPatchComd(PDEVICE_OBJECT pDeviceObject, PIRP pIrp);
    NTSTATUS DisPatchIoControl(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
    VOID pMyCreateRoutine (IN HANDLE pParentId,HANDLE hProcessId,BOOLEAN bisCreate);
    
    //自定义设备扩展.以及全局变量指针.进行保存的.
    typedef struct _Device_Exten
    {
    	/*
    	自定义数据.比如保存
    	进程PID 父PID
    	进程事件对象
    	全局事件对象
    	*/
    	HANDLE  hProcess;         //进程句柄
    	PKEVENT pkProcessEvent;   //全局事件对象,ring3使用
    	HANDLE  hProcessId;       //进程的PID
    	HANDLE  hpParProcessId;   //父进程ID.当前你也可以有进程名字
    	BOOLEAN bIsCreateMark;    //表示是创建进程还是销毁.创建进程回调可以看到
    
    }DEVICE_EXTEN,* PDEVICE_EXTEN;
    PDEVICE_OBJECT g_PDeviceObject;
    
    //定义ring3->读取ring0的数据
    typedef struct _PROCESS_LONNK_READDATA
    {
    	HANDLE hProcessId;
    	HANDLE hpParProcessId;
    	BOOLEAN bIsCreateMark;
    }PROCESS_LONNK_READDATA,*PPROCESS_LONNK_READDATA;
    
    NTSTATUS
    DriverEntry(
        _In_ PDRIVER_OBJECT  pDriverObject,
        _In_ PUNICODE_STRING RegistryPath
        )
    {
    	NTSTATUS ntStatus;
    	UNICODE_STRING uDeviceName = { 0 };
    	
    	UNICODE_STRING uEventName = { 0 };
    
    
    	//setp 1注册卸载函数,以及设置通讯方式
    	pDriverObject->DriverUnload = DriverUnLoad;
    	
    
    	//setp 2 初始化符号链接名.设备名. 以及事件对象名字,并且检验一下
    	ntStatus = RtlUnicodeStringInit(&uDeviceName, IBINARY_DEVICENAME);
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("初始化设备名称失败"));
    		return ntStatus;
    	}
    	KdPrint(("初始化设备名称成功"));
    
    	ntStatus = RtlUnicodeStringInit(&g_uSymbolicLinkName, IBINARY_LINKNAME);
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("初始化符号链接名字失败"));
    		return ntStatus;
    	}
    	KdPrint(("初始化符号链接名字成功"));
    	ntStatus = RtlUnicodeStringInit(&uEventName, IBINARY_EVENTNAME);
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("初始化全局事件对象失败"));
    		return ntStatus;
    	}
    	KdPrint(("初始化全局事件对象成功"));
    
    	//setp 3建立一个函数.函数内部进行初始化设备对象.初始化符号链接.初始化全局事件对象.
    	ntStatus = InitDeviceAnSymbolicLink(
    		pDriverObject,
    		uDeviceName,
    		g_uSymbolicLinkName,
    		uEventName);
        return ntStatus;
    }
    
    //卸载驱动.关闭符号链接
    void DriverUnLoad(PDRIVER_OBJECT pDriverObject)
    {
    	NTSTATUS ntStatus;
    	UNICODE_STRING SymboLicLinkStr = { 0 };
    	ntStatus = RtlUnicodeStringInit(&SymboLicLinkStr, IBINARY_LINKNAME);
    	if (NT_SUCCESS(ntStatus))
    	{
    		ntStatus = IoDeleteSymbolicLink(&SymboLicLinkStr);
    		if (!NT_SUCCESS(ntStatus))
    		{
    			KdPrint(("删除符号链接失败"));
    		}
    	}
    	
    	IoDeleteDevice(pDriverObject->DeviceObject);
    	PsSetCreateProcessNotifyRoutine(pMyCreateRoutine, TRUE);
    	KdPrint(("驱动已卸载"));
    }
    
    NTSTATUS InitDeviceAnSymbolicLink(
    	PDRIVER_OBJECT pDriverObj,
    	UNICODE_STRING uDeviceName,
    	UNICODE_STRING uSymbolicLinkName,
    	UNICODE_STRING uEventName)
    {
    	NTSTATUS ntStatus;
    	PDEVICE_OBJECT pDeviceObject = NULL;
    	//使用自定义结构
    	ULONG i = 0;
    
    	
    	ntStatus = IoCreateDevice(
    		pDriverObj,
    		sizeof(DEVICE_EXTEN),//使用设备扩展.指定大小.那么设备对象中成员就会指向这块内存
    		&uDeviceName,
    		FILE_DEVICE_UNKNOWN,
    		FILE_DEVICE_SECURE_OPEN,
    		FALSE,                //独占设备
    		&pDeviceObject);
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("创建设备对象失败"));
    		IoDeleteDevice(pDeviceObject);
    		return ntStatus;
    	}
    	pDriverObj->Flags |= DO_BUFFERED_IO;
    	//成功之后保存到全局变量中
    	KdPrint(("创建设备对象成功"));
    	g_PDeviceObject = pDeviceObject;
    
    
    	//创建事件.ring3->ring0的事件
    	PDEVICE_EXTEN pDeviceExten = (PDEVICE_EXTEN)pDeviceObject->DeviceExtension;
    	pDeviceExten->pkProcessEvent = IoCreateNotificationEvent(&uEventName, &pDeviceExten->hProcess);
    	KeClearEvent(pDeviceExten->pkProcessEvent);
    
    	//创建符号链接
    
    	ntStatus = IoCreateSymbolicLink(&g_uSymbolicLinkName, &uDeviceName);
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("创建符号链接失败"));
    		IoDeleteDevice(pDeviceObject);
    		return ntStatus;
    	}
    	KdPrint(("创建符号链接成功"));
    
    	
    	
    	
    	/*
    	因为设备对象扩展我们传入了DEVICE_EXTEN大小.所以在调用IoCreateDevice的时候.返回的
    	设备对象中.设备对象会根据我们传入的大小创建一块内存.这块内存就保存在DeviceExtension
    	这个字段中.
    	下面调用IoCreateNotificationEvent是创建了一个命名事件.我们将事件放到我们结构体中.
    	这个函数创建的事件必须手工设置事件状态.所以我们首先初始化为无信号状态.
    	总的来说.IoCreateNotificationEvent创建的时候需要一个HANDLE以及一个PKEVENT.
    	*/
    
    
    	//注册回调控制函数.当进程来了会通知.
    	//	PsSetCreateProcessNotifyRoutine
    	ntStatus = PsSetCreateProcessNotifyRoutine(pMyCreateRoutine,FALSE); //FASLE为注册
    	if (!NT_SUCCESS(ntStatus))
    	{
    		KdPrint(("注册系统回调失败"));
    		IoDeleteDevice(pDeviceObject);
    		return ntStatus;
    	}
    	KdPrint(("注册系统回调成功"));
    
    
    	//初始化派遣函数
    	for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
    	{
    		pDriverObj->MajorFunction[i] = DisPatchComd;
    	}
    	pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DisPatchIoControl;
    
    	return STATUS_SUCCESS;
    }
    
    //当进程来的时候.会通知你.
    VOID pMyCreateRoutine(
    	IN HANDLE pParentId,
    	HANDLE hProcessId,
    	BOOLEAN bisCreate)
    {
    	/*
    	进程来的时候会通知我们.所以我们给设备扩展进行赋值.赋值进程ID以及是否创建
    	*/
    	PDEVICE_EXTEN pDeviceExten =(PDEVICE_EXTEN)g_PDeviceObject->DeviceExtension;
    	pDeviceExten->hProcessId = hProcessId;
    	pDeviceExten->hpParProcessId = pParentId;
    	pDeviceExten->bIsCreateMark = bisCreate;
    	//赋值完毕之后.设置信号状态为有信号. 这样Ring3就会等待到事件了.
    	KeSetEvent(pDeviceExten->pkProcessEvent,0,FALSE);
    	//通知ring3可以读取了.那么还要设置为ring0的事件为无信号.用来保持同步
    	//KeClearEvent(pDeviceExten->pkProcessEvent);
    	KeResetEvent(pDeviceExten->pkProcessEvent); //跟ClearEvent一样.上面的快.这个会返回上一个设置的信号状态.都用一次
    
    }
    
    NTSTATUS DisPatchComd(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
    {
    	pIrp->IoStatus.Information = 0;
    	pIrp->IoStatus.Status = STATUS_SUCCESS;
    	IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    
    	return pIrp->IoStatus.Status;
    }
    NTSTATUS DisPatchIoControl(PDEVICE_OBJECT pDeviceObject, PIRP pIrp)
    {
    	/*
    	Ring3 -> ring0通讯的控制派遣函数.自定义控制码.获取Irp堆栈.获取缓冲区.
    
    	*/
    	NTSTATUS ntStatus;
    	PIO_STACK_LOCATION pIrpStack;
    	PVOID pUserOutPutBuffer;
    	PPROCESS_LONNK_READDATA pReadData;
    	ULONG uIoControl = 0;
    	ULONG uReadLength;
    	ULONG uWriteLeng;
    	PDEVICE_EXTEN pDeviceExten;
    	/*
    	开始解析用户操作
    	*/
    	KdPrint(("解析用户控制码"));
    	pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    	//从堆栈中获取用户控制数据
    	pUserOutPutBuffer = pIrp->AssociatedIrp.SystemBuffer; //如果控制码是缓冲区方式.就使用这个.
    
    	//定义读取的数据
    	pReadData = (PPROCESS_LONNK_READDATA)pUserOutPutBuffer;
    	//获取控制码.长度.进行读取
    	uIoControl = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
    	uReadLength = pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
    	uWriteLeng = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
    	
    	switch (uIoControl)
    	{
    	case IOCTL_PROCESS_LOCK_READ:
    		//拷贝数据即可.
    		pDeviceExten = (PDEVICE_EXTEN)g_PDeviceObject->DeviceExtension;
    		pReadData->hProcessId = pDeviceExten->hProcessId;
    		pReadData->hpParProcessId = pDeviceExten->hpParProcessId;
    		pReadData->bIsCreateMark = pDeviceExten->bIsCreateMark;
    		KdPrint(("内核读取 父ID = %d,子Id = %d,是否创建 = %d", (ULONG)pDeviceExten->hpParProcessId, (ULONG)pDeviceExten->hProcessId, (ULONG)pDeviceExten->bIsCreateMark));
    
    		break;
    	default:
    		KdPrint(("其它控制码"));
    		ntStatus = STATUS_INVALID_PARAMETER;
    		uWriteLeng = 0;
    		break;
    	}
    
    	pIrp->IoStatus.Information = uWriteLeng; //读取的字节数
    	pIrp->IoStatus.Status = ntStatus;
    	IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    	return ntStatus;
    }
    
    

    ring3下打开事件对象即可. 注意我自己写的是打开 全局事件对象\Global
    然后发送控制码.ring0进行赋值即可.

    ring3代码.

    // ProcWatchClientConsole.cpp : Defines the entry point for the console application.
    //
    
    
    
    #include "windows.h"
    #include "winioctl.h"
    #include "stdio.h"
    
    
    
    
    #define EVENT_NAME    L"Global\ProcLook"
    
    
    
    
    #define CTRLCODE_BASE 0x8000
    #define MYCTRL_CODE(i) 
    CTL_CODE(FILE_DEVICE_UNKNOWN,CTRLCODE_BASE +i,METHOD_BUFFERED,FILE_ANY_ACCESS)
    #define IOCTL_PROCESS_LOCK_READ MYCTRL_CODE(1)
    
    #define		IOCTL_PROCESS_LOCK_READ		MYCTRL_CODE(1)
    
    typedef struct _PROCESS_LONNK_READDATA
    {
    	HANDLE hProcessId;
    	HANDLE hpParProcessId;
    	BOOLEAN bIsCreateMark;
    }PROCESS_LONNK_READDATA, *PPROCESS_LONNK_READDATA;
    
    
    
    int main(int argc, char* argv[])
    {
    
    	PROCESS_LONNK_READDATA pmdInfoNow = { 0 };
    	PROCESS_LONNK_READDATA pmdInfoBefore = { 0 };
    
    	
    	// 打开驱动设备对象
    	HANDLE hDriver = ::CreateFile(
    		"\\.\IBinarySymboliclnk",
    		GENERIC_READ | GENERIC_WRITE,
    		0,
    		NULL,
    		OPEN_EXISTING,
    		FILE_ATTRIBUTE_NORMAL,
    		NULL);
    	if (hDriver == INVALID_HANDLE_VALUE)
    	{
    		printf("Open device failed:%x
    ", GetLastError());
    		return -1;
    	}
    	// 打开内核事件对象
    	HANDLE hProcessEvent = ::OpenEventW(SYNCHRONIZE, FALSE, EVENT_NAME);
    
    	if (NULL == hProcessEvent)
    	{
    		OutputDebugString("打开事件对象失败");
    		system("pause");
    		return 0;
    	}
    	OutputDebugString("打开事件对象成功");
    	
    	while (TRUE)
    	{
    		::WaitForSingleObject(hProcessEvent, INFINITE); //等待事件
    		DWORD    dwRet = 0;
    		BOOL     bRet = FALSE;
    
    		bRet = ::DeviceIoControl(
    			hDriver,
    			IOCTL_PROCESS_LOCK_READ,
    			NULL,
    			0,
    			&pmdInfoNow,
    			sizeof(pmdInfoNow),
    			&dwRet,
    			NULL);
    		if (!bRet)
    		{
    			OutputDebugString("发送控制码失败");
    			system("pause");
    			return 0;
    		}
    
    		OutputDebugString("Ring3发送控制码成功");
    
    		if (bRet)
    		{
    			if (pmdInfoNow.hpParProcessId != pmdInfoBefore.hpParProcessId || 
    				pmdInfoNow.hProcessId != pmdInfoBefore.hProcessId || 
    				pmdInfoNow.bIsCreateMark != pmdInfoBefore.bIsCreateMark)
    			{
    				if (pmdInfoNow.bIsCreateMark)
    				{
    					printf("进程创建 PID = %d
    ", pmdInfoNow.hProcessId);
    				}
    				else
    				{
    					printf("进程退出,PID = %d
    ", pmdInfoNow.hProcessId);
    				}
    				pmdInfoBefore = pmdInfoNow;
    			}
    		}
    		else
    		{
    			printf("Get Proc Info Failed!
    ");
    			break;
    		}
    	}
    
    	::CloseHandle(hDriver);
    	system("pause");
    
    	return 0;
    }
    
    

    注意,ring0下设置的进程系统回调是用的 PsSetCreateProcessNotifyRoutine 这个内核函数只能监视
    进程ID 父进程ID以及一个创建或者结束标记. 我们可以使用Ex系列.这样的话可以监视到进程的名字.等等.

    演示

  • 相关阅读:
    Activity
    python 执行DOS/CMD命令
    MyISAM存储引擎的表级锁定优化
    vbs隐藏cmd窗口
    文件操作一
    Java基础知识
    Linux下利用.htaccess建立多站点
    python使用WMI监视系统-CPU使用率
    python操作mysql数据库注意
    类集框架(二)
  • 原文地址:https://www.cnblogs.com/iBinary/p/10993899.html
Copyright © 2020-2023  润新知