A first 64-bit kernel development, the drive frame.

Driver Framework Introduction

1. Application Framework 3 0 ring to ring

Drive frame 3 1.1 0 ring to ring.

The first is our 3-ring API

The API -> command with encapsulated data -> kerner32 calling function or the ntdll -> encapsulated transmitted to IRP structure -> call to the driver

Here the contact of a new concept .IRP .IRP tricyclic structure is actually a command, and data. Encapsulation transmitted to 0 when the ring. In this structure stored inside. 0 NT ring in turn calls the function by reading the ring 0 performed.

As we call ReadFile. So call us directly driven to write dispatch function
DispathRead
which 0x1B (27) a distribution dispatch function. DriverUnLoad and a function.

Our data are stored in the IRP. If we want to complete the routine, then Settings. IRP in
IOstatus can be. Our drivers are layered driver. If you do not set. He also calls the other drive.

1.2 NT drive frame

Above we said, API 3 ring will call ring 0, where data and command information will be placed IRP structure.

So if we call CreateFile. So it will produce a IRP_MJ_CREATE
Our core layer will be set to call DispathCreate ().

as follows:

Nt model, function News
DriverEntry Single-threaded environment, program entry point.
DispatchCreate IRP_MJ_CREATE
DispatchRead IRP_MJ_READ
DispatchWrite IRP_MJ_WRITE
DisPatchchClose IRP_MJ_CLOSE FileObject kernel objects
DispatchClean IRP_MJ_CLEANUP HANDLE is handle
DisPatchControl irp_mj_device_control
DriverUnLoad Single-threaded environment, uninstall the program.

文件句柄为0.那么系统就会发送IRP_MJ_CLEANUP
FileOBject内核对象.如果对文件的内核对象没有在操作了(包括内核)
则会发送IRP_MJ_CLOSE. 大部分情况这两种都会同时发生的.

WDM模型
WDM是网卡等.它引入了两个新的函数
WDMAddDevice()
wdmpnp()
链接即可.

应用框架
Sfilter/Minifilter 文件过滤框架.可以使用Nt模型.
TDI/NDIS/WFP 基于NT模型加的新的框架.防火墙用的
DISPERF 磁盘基于Nt模型.产生的磁盘过滤框架
HOOK

二丶编写自己的最简单的 NT模型驱动.



#include <ntddk.h>   //很多驱动的结构体函数的声明呀.都包含在这里面

#define DEVICE_NAME L"\\device\\IBinaryFirst"  // 驱动的设备的名字       格式为 \device\你自定义的名字. \\是代表转义 在source中要一样.

//#define LINK_NAME L"\\DosDevices\IBinaryFirst" // 驱动的符号连接名 格式\dosdevices\自定义的名字  也可以\\??\\自定义的名字
#define LINK_NAME L"\\DosDevices\\IBinaryFirst"


/*
控制码,应用层,内核层通用.

*/
#define IOCTRL_BASE 0x800
#define MYIOCTRL_CODE(i)\
    CTL_CODE(FILE_DEVICE_UNKNOWN,IOCTRL_BASE+i,METHOD_BUFFERED,FILE_ANY_ACCESS)
//驱动设备类型 设备控制码数值  定义R3跟R0的通讯方式.是指定Device.  我们的权限.

/*
METHOD_BUFFERED  以缓存方式读取
METHOD_IN_DIRECT 只读,只有打开设备的时候 IoControl将会成功 METHOD_OUT_DIRECT 则会失败
METHOD_OUT_DIRECT 读写方式的时候.两种方式都会成功 都在MDL中拿数据

METHOD_NEITHER  在 type3InputBuffer拿数据 IN的数据. stack->Parameters.DeviceIoControl.Type3InputBuffer
发送给R3 在 pIrp->UserBuffer里面.
3中通讯方式.
*/

#define CTL_HELLO MYIOCTRL_CODE(0) //控制码为0则是HELLO.

NTSTATUS DispatchCommon(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
NTSTATUS DispatchRead(PDEVICE_OBJECT  pDeviceObject,PIRP pIrp);
NTSTATUS DispatchWrite(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
NTSTATUS DispatchClose(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
NTSTATUS DispatchClean(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
NTSTATUS DispatchControl(PDEVICE_OBJECT pDeviceObject,PIRP pIrp);
VOID     DriverUnLoad(PDRIVER_OBJECT pDriverObject);

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegPath)
{
  
    UNICODE_STRING uDeviceName = {0};
    UNICODE_STRING uLinkName = {0};
    NTSTATUS ntStatus = 0;
    PDEVICE_OBJECT pDeviceObject = NULL;
    ULONG i = 0;
    
    pDriverObject->DriverUnload = DriverUnLoad;
    
    DbgPrint("Load Driver Sucess");
    
    
    
    RtlInitUnicodeString(&uDeviceName,DEVICE_NAME); //初始化驱动设备名字
    RtlInitUnicodeString(&uLinkName,LINK_NAME);     //初始化3环宇0环通信的设备名字
    
    
    ntStatus = IoCreateDevice(pDriverObject,0,&uDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,&pDeviceObject);//创建设备对象
    /*
    参数1: 驱动对象 
    参数2: 设备扩展,创建完设备对象之后,申请的一段额外内存.可以保存设备对象的上下文的一些数据
    参数3: 设备名字,传入函数,需要传地址 
    参数4: 设备类型.普通的驱动设置为FILE_DEVICE_UNKNOWN
    参数5: 设备的属性
    
    参数6: 设备对象是用来传入IRP请求的.是让我们应用层打开它. R3 发送IRP -> 设备对象(我们自己创建的)
    参数6的意思就是 如果为TRUE 只能一个进程打开,独占打开.FALSE是可以多个进程打开的.
    参数7: 创建好的设备对象通过最后一个参数传出. 注意是2级指针.
    */
    
    
    
    DbgPrint("IoCreateDevice load.....\r\n");
    if (!NT_SUCCESS(ntStatus))
    {
        //判断是否设置成功
        DbgPrint(L"IoCreateDevice Failed \r\n");
        return 0;
    }
    
    //设置通讯的方式
    pDeviceObject->Flags |= DO_BUFFERED_IO; 
    /*
    R3 -> IRP ->内核. 通过IRP发送给内核层.
    三种通讯方式
    1.缓存方式:
      DO_BUFFERED_IO 最安全的一个通讯方式.(数据的交换)基于缓存
      内核中会专门会分配跟R3的 Buffer一样的缓存. 内核层从这个空间读取
      这个就是 DO_BUFFERED. 处理完毕之后.在放到分配的缓存区中.那么IO管理器
      在拷拷贝给应用层.完成数据交互.
     2.直接IO方式
       DO_DIRECT_IO
       R3 有一块数据. 会使用MDL方式. 会将R3发送的数据.映射到物理内存中.
       并且锁住. 
       就相当于 R3的数据地址 映射到内核中物理地址. R3往内核中写数据其实也是
       往内核数据读取. 这个通讯完全就是在内核中映射的物理内存中进行的.
     3.虚拟地址直接发送到R0
     第三种方式是虚拟地址 直接发送到R0. 前提条件.进程不能切换.必须处在
     同一个线程上下文.
     这样不安全所以我们要对这块内存进行检查才可以.
     ProbeFroWrite
     ProbeFroRead 
     
    */
    
    DbgPrint("IoCreateSymbolicLink load.... \r\n");
    ntStatus = IoCreateSymbolicLink(&uLinkName,&uDeviceName); //创建符号链接名字.
    if (!NT_SUCCESS(ntStatus))
    {
        //创建失败,我们就要删除
        IoDeleteDevice(pDeviceObject);
        DbgPrint("IoCreateSymbolicLink Error");
        return 0;
    }
    DbgPrint("IoCreateSymbolicLink Sucess");
    //初始化分发派遣函数.
    
    
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION +1;i++)
    {
        //分发函数有0x1b个(27)我们不注意的可以进行设置通用的分发函数.分发函数都是一样的.
        pDriverObject->MajorFunction[i] = DispatchCommon;
    }
    
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreate;
    pDriverObject->MajorFunction[IRP_MJ_READ] = DispatchRead;
    pDriverObject->MajorFunction[IRP_MJ_WRITE]= DispatchWrite;
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchControl;
    pDriverObject->MajorFunction[IRP_MJ_CLEANUP]=DispatchClean;
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchClose;
    
    
    DbgPrint("驱动安装成功IBinary \r\n");
    //设置驱动卸载
    
    
    
    return STATUS_SUCCESS;  
}


NTSTATUS DispatchCommon(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    pIrp->IoStatus.Status = STATUS_SUCCESS; //IRP记录这次操作与否的.
    pIrp->IoStatus.Information = 0;         //Information用来记录实际传输的字节数的.
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    return STATUS_SUCCESS;                  //上面的 STATUS_SUCCESS是给R3看的.现在的返回时给IO管理器系统的
}
NTSTATUS DispatchCreate(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
    
}
NTSTATUS DispatchRead(PDEVICE_OBJECT  pDeviceObject,PIRP pIrp)
{
    
    PVOID pReadBuffer = NULL;
    ULONG uReadLength = 0;
    PIO_STACK_LOCATION pStack = NULL;
    ULONG uMin = 0;
    ULONG uHelloStr = 0;
    
    uHelloStr = (wcslen(L"Hello World") + 1) * sizeof(WCHAR);
    pReadBuffer = pIrp->AssociatedIrp.SystemBuffer; //缓冲区通讯方式.则是这个值
    //获取IRP堆栈.我们说过3环调用0环.需要封装在IRP结构中.windows是分层驱动.所以IRP头部是共用的.其余的是栈传递.
    
    pStack = IoGetCurrentIrpStackLocation(pIrp);
    uReadLength = pStack->Parameters.Read.Length;
    uMin = uReadLength > uHelloStr ? uHelloStr : uReadLength;
    
    RtlCopyMemory(pReadBuffer,L"Hello World",uMin); //拷贝到缓冲区中给3环.
    
    
    
    
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = uMin;
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
    
}
NTSTATUS DispatchWrite(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    PVOID pWriteBuffer = NULL;
    ULONG uWriteLength = 0;
    PIO_STACK_LOCATION pIrpStack = NULL;
    PVOID pBuffer = NULL;
    //获取IRP堆栈
    pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    
    //获取写的长度.
    uWriteLength = pIrpStack->Parameters.Write.Length;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    
    
    //申请内存.
    pBuffer = ExAllocatePoolWithTag(PagedPool,uWriteLength,'TSET');
    /*
    PagedPool 在分页中分配内存 CPU无分页才能在分页中分配. Dispathch级别则不能使用分页内存.
    NoPagePool非分页中分配.
    优先级最低的才能使用分页内存.
    
    参数2: 长度
    参数3: 标记. 不能超过4个字节. 单引号引起来. 参数3是用来跟踪我们分配的内存的.
    注意是低位优先, 内存中看到的是 TEST.
    */
    if (NULL == pBuffer)
    {
        pIrp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
        pIrp->IoStatus.Information = 0;
        IoCompleteRequest(pIrp,IO_NO_INCREMENT);
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    //提交请求.
    
    memset(pBuffer,0,uWriteLength);
    //拷贝到0环缓冲区
    RtlCopyMemory(pBuffer,pWriteBuffer,uWriteLength);
    ExFreePool(pBuffer);
    pBuffer = NULL;
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = uWriteLength;
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}
NTSTATUS DispatchClose(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    //控制 其它交互都通过控制码传送.
    
    
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}
NTSTATUS DispatchClean(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT); 
    
    return STATUS_SUCCESS;
}
NTSTATUS DispatchControl(PDEVICE_OBJECT pDeviceObject,PIRP pIrp)
{
    
    //内核中共享 SystemBuffer 有时间差.先读在写.
    PIO_STACK_LOCATION pIrpStack;
    PVOID InPutBuffer = NULL;
    PVOID OutPutBuffer = NULL;
    ULONG uInPutLength = 0;
    ULONG uOutPutBufferLength = 0;
    ULONG IoCtrl = 0;
    
    InPutBuffer = OutPutBuffer = pIrp->AssociatedIrp.SystemBuffer;
    pIrpStack = IoGetCurrentIrpStackLocation(pIrp);
    
    //uOutPutBufferLength = pIrpStack->Parameters.DeviceIoControl.OutPutBufferLength;
    //uInPutLength = pIrpStack->Parameters.DeviceIoControl.InPutBufferLength;
    
    
    IoCtrl = pIrpStack->Parameters.DeviceIoControl.IoControlCode; //获取控制码.
    
/*
    switch(IoCtrl)
    {
    case CTL_HELLO:
        KdPrint("Hello World");
        break;
    default:
        break;  
    }
*/
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    
    //提交请求.
    IoCompleteRequest(pIrp,IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}

//驱动卸载

VOID  DriverUnLoad(PDRIVER_OBJECT pDriverObject)
{
  DbgPrint("Unload MyDrive\n");
}


根据上面代码我们可以做个解析
1.DriverEntry();这个是NT驱动的入口点.两个参数. 驱动对象.以及注册表路劲.
2.使用IoCreateDevice函数创建了一个驱动设备对象.这样当r3使用ReadFile等函数会传送给设备对象.
3.使用IoCreateSymbolicLink();创建符号链接.此时我们R3调用CreateFile则可以进行链接了.
4.最后注册派遣函数即可.
5.在派遣函数中写入你的操作.如读取操作.我们将数据返还给R3.

1.3 IRP 结构

上面我们看的IRP有头部.
可以看到 IOSTATUS .里面保存了状态.以及实际Buffer字节.
SystemBuffer.这个是缓存IO.就是我们现在用的. 内核中开辟空间保存3环.再从里面读取.最后再给这个缓冲区设置.进行输出.

MdlAddress 这个则是直接IO.我们上面代码的注释中说了.直接IO是
3环的缓冲区地址,映射到0环的物理聂村. 进而0环读取物理内存进行操作.

UserBuffer
UserBuffer是自定义的.其中UserBuffer是传出的.而内部还有一个Buffer是用来读取的.

n以后就是IRP的栈. 在我们文件驱动与磁盘驱动.那么共享IRP头部.

磁盘设备则会使用0层的.
因为驱动是分层的.

而在栈中有一个很重要的联合体.

Read Write DeviceControl...等等.不同结构体对应不同的IRP请求.

所以在Read派遣函数中.获取ReadIrp的堆栈.

二丶编译驱动.

我用的是WDK7600.可以使用XP进行测试.
编译的时候需要使用WDK的 命令行.
当你安装WDK7600之后再开始菜单中则会看到.

打开之后切换到你的编写代码的目录.直接输入build进行编译即可.
注意你的驱动代码后缀名要为.c的文件.这样不会编译错误.
cpp有名字粉碎.你需要使用 extern C 表示这个函数名不会名称粉碎.

在编译的时候我们还需要提供一个sources 文件.

内容为:

TARGETNAME= IBinaryFirst     //编译的驱动名字.

TARGETTYPE=DRIVER            //编译的类型为驱动
  
SOURCES= IBinaryFirst.c     //你驱动的代码文件

这是我的:
TARGETNAME=IBinaryFirst 
TARGETTYPE=DRIVER       
SOURCES=IBinaryFirst.c      

编译之后如下.

3.加载驱动.

加载驱动有专门的的API进行操作.我以前写过.
可以看之前的文章.

https://www.cnblogs.com/iBinary/p/8280912.html

现在我们直接用工具加载了.

可以看到加载成功.宽字符打印出错.不影响.

4.ring3操作内核.进行读取.

可以看到我们的 HelloWorld已经正常读取了.

ring3下完整代码.


// Ring3.cpp : Defines the entry point for the console application.
//


#include <windows.H>
#include <stdio.h>

int main(int argc, char* argv[])
{
    HANDLE hFile = CreateFile(TEXT("\\\\?\\IBinaryFirst"),
        GENERIC_WRITE | GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile ErrorCode:%d\n", GetLastError());
        return 0;
    }
    system("pause");
    TCHAR szBuff[0X100];
    DWORD dwBytes = 0;
    if (!ReadFile(hFile, szBuff, sizeof(szBuff)/sizeof(szBuff[0]), &dwBytes, NULL))
    {
        CloseHandle(hFile);
        printf("ReadFile ErrorCode:%d\n", GetLastError());
        return 0;
    }
    printf("bytes:%d data:%ls\n", dwBytes, szBuff);
    system("pause");
    //WriteFile();
    //DeviceIoControl
    CloseHandle(hFile);
    system("pause");
    return 0;
}

Guess you like

Origin www.cnblogs.com/iBinary/p/10990660.html