前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >64位内核开发第十二讲,进程监视,ring3跟ring0事件同步.

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

作者头像
IBinary
发布2019-06-14 14:16:54
9760
发布2019-06-14 14:16:54
举报
文章被收录于专栏:逆向技术逆向技术

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

1.用于线程同步的 KEVENT

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

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

事件的创建函数

** IoCreateNotificationEvent() **

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

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

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

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

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

代码如下:

代码语言:javascript
复制
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 函数.众所周知.是建立设备对象. 定义如下:

代码语言:javascript
复制
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.这个就是我们设备扩展为我们申请的那块内存. 我们可以转化为我们自定义结构体大小. 代码很简单.如下:

代码语言:javascript
复制
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很简单. 如下:

代码语言:javascript
复制
#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:

代码语言:javascript
复制
#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代码.

代码语言:javascript
复制
// 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系列.这样的话可以监视到进程的名字.等等.

演示

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一丶同步与互斥详解,以及实现一个进程监视软件.
    • 1.用于线程同步的 KEVENT
      • 2.进程监视 ring0 与 ring3同步使用Event
        • 3.进程监视代码.
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档