一丶同步與互斥詳解,以及實現一個進程監視軟件.
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\n", 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\n", pmdInfoNow.hProcessId);
}
else
{
printf("進程退出,PID = %d\n", pmdInfoNow.hProcessId);
}
pmdInfoBefore = pmdInfoNow;
}
}
else
{
printf("Get Proc Info Failed!\n");
break;
}
}
::CloseHandle(hDriver);
system("pause");
return 0;
}
注意,ring0下設置的進程系統回調是用的 PsSetCreateProcessNotifyRoutine 這個內核函數只能監視
進程ID 父進程ID以及一個創建或者結束標記. 我們可以使用Ex系列.這樣的話可以監視到進程的名字.等等.
演示