驱动开发中的常用操作
这篇文章会持续更新,由于在驱动中,有许多常用的操作代码几乎不变,而我自己有时候长时间不用经常忘记,所以希望在这把一些常用的操作记录下来,当自己遗忘的时候,有个参考
创建设备对象
创建设备对象使用函数IoCreateDevice,它的参数如下:
NTSTATUS IoCreateDevice(IN PDRIVER_OBJECT DriverObject,IN ULONG DeviceExtensionSize,IN PUNICODE_STRING DeviceName OPTIONAL,IN DEVICE_TYPE DeviceType,IN ULONG DeviceCharacteristics,IN BOOLEAN Exclusive,OUT PDEVICE_OBJECT *DeviceObject);
第一个参数是驱动对象
第二个参数是设备对象扩展的大小,它会自动根据大小生成一个内存空间,与对应设备绑定
第三个参数是驱动名称
第四个参数是驱动的类型,一般用作过滤设备的驱动类型为FILE_DEVICE_UNKNOWN
第五个参数一般给FILE_DEVICE_SECURE_OPEN
第六个参数表示设备是否为独占模式,一般给FALSE
第七个参数是设备驱动的二级指针,用来返回生成的设备驱动的指针
创建一个过滤设备的代码如下:
//创建设备对象
status = IoCreateDevice(pDriverObject, sizeof(LIST_ENTRY), &uDeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pDeviceObject);
//为设备对象设置相关标识
pDeviceObject->Flags |= DO_BUFFERED_IO;
IRP的完成
在某些我们不需要进行特殊处理,但是又得需要对这个IRP进行处理的时候,一般采用完成处理的方式,这种方式主要使用函数IoCompleteRequest,使用例子如下:
Irp->IoStatus.Information = 0; //设置返回给应用层的缓冲区的大小
Irp->IoStatus.Status = STATUS_SUCCESS;//给应用层当前IO操作返回成功
IoCompleteRequest(Irp, IO_NO_INCREMENT);//结束IRP
在派遣函数中拿IRP的主功能号
IRP中保存了它的主功能号和副功能号,他们都被存储在IRP的栈中,下面是基本的代码
pStack = IoGetCurrentIrpStackLocation(Irp); //获取IRP栈
IrpCode = pStack->MajorFunction;
在MJ_DEVICE_CONTROL类型的IRP中得到对应的控制码
CtrlCode = pStack->Parameters.DeviceIoControl.IoControlCode;
获取驱动所在的进程
这个方法目前只在XP上实验过,win7或者更高版本可能有些不同。
获取当前进程主要在EPROCESS结构找到名为ProcessName的项,由于这个结构微软并没有公开,所以可能以后会根据系统版本的不同它的偏移可能也有些许不同。下面是具体的代码
pCurrProcess = IoGetCurrentProcess();RtlInitUnicodeString(&uProcessName, (PTSTR)((ULONG)pCurrProcess + 0x174));//这个偏移量是在xp上有效,是通过WinDbg获取到的,如果有变化,也可以通过windbg重新得到
数据 代码所处内存的划分
在驱动程序中,一定要非常小心的为每个函数,数据划分内存块,否则会出现蓝屏现象,比如处在DISPATCH_LEVEL的代码,只能位于非分页内存,因为这个IRQL下的代码不能被打断,如果发生缺页中断,那么只会造成蓝屏现象。而PASSIVE_LEVLE的代码则没有这个限制。下面是定义函数和数据在不同内存页的写法
#define PAGEDCODE code_seg("PAGE") //分页内存
#define LOCKEDCODE code_seg()//非分页内存
#define INITCODE code_seg("INIT")//处在这种类型的代码,当函数执行完成后,系统会立马回收它所在的内存页#define PAGEDDATA data_seg("PAGE")
#define LOCKEDDATA data_seg()
#define INITDATA data_seg("INIT")
//下面是使用这些宏的例子,使用时只需要在函数或者数据前加上对应的宏
LOCKEDCODE
void test()
{
}
给编译器提示,函数某些参数在函数中不使用
一般在编译驱动时,如果函数参数或者在函数内部定义了某些变量在函数中没有使用的话,编译器会报错,但是有的函数原型是系统规定,但是有些参数又确实用不到,这个时候有两种方式,一种是关掉相关的编译选项,另一种是使用宏告知编译器,这个变量在函数中不使用.
UNREFERENCED_PARAMETER(RegistryPath);
获取系统时间
这里的获取系统时间一般是指获取时间并将它转化我们熟悉的年月日、时分秒的格式,一般的步骤如下:
1. 利用函数KeGetSystemTime()获取系统时间,这个时间是格林尼治时间从1601年起至今经历的时间,单位为100ns
2. 利用ExSystemTimeToLocalTime()将上述的格林尼治时间转化为本时区的时间,这个值得含义和单位与上述的相同
3. 利用函数RtlTimeToTimeFields()将本地时间转化为带有年月日格式的时间函数的第二个参数是TIME_FIELDS结构,他的定义如下:
typedef struct TIME_FIELDS {CSHORT Year;CSHORT Month;CSHORT Day;CSHORT Hour;CSHORT Minute;CSHORT Second;CSHORT Milliseconds;CSHORT Weekday;
} TIME_FIELDS;
下面是一个时间转化的例子
LARGE_INTEGER current_system_time;TIME_FIELDS time_fields;LARGE_INTEGER current_local_time;KeQuerySystemTime(¤t_system_time);ExSystemTimeToLocalTime(¤t_system_time, ¤t_local_time);RtlTimeToTimeFields(¤t_local_time, &time_fields);DbgPrint("Current Time: %d/%d/%d %d:%d:%d\n", time_fields.Year, time_fields.Month, time_fields.Day, time_fields.Hour, time_fields.Minute, time_fields.Second);
他们三个可以互相转化,下面是它们之间转化的一个示意图:
文件读写
文件读写一般需要进行这样几步
1. 使用InitializeObjectAttributes初始化一个OBJECT_ATTRIBUTES对象
2. 使用ZwCreateFile创建一个文件句柄
3. 调用ZwReadFile或者ZwWriteFile读写文件
这里面复杂的是InitializeObjectAttributes和ZwCreateFile传参的问题,好在这两个函数在调用时,一般传参都是固定的。
VOID InitializeObjectAttributes(OUT POBJECT_ATTRIBUTES InitializedAttributes,IN PUNICODE_STRING ObjectName, //传希望打开的文件名称或者设备对象名称IN ULONG Attributes, //权限一般给OBJ_CASE_INSENSITIVEIN HANDLE RootDirectory, //根目录,一般给NULLIN PSECURITY_DESCRIPTOR SecurityDescriptor//安全属性,一般给NULL);
NTSTATUS ZwCreateFile(__out PHANDLE FileHandle, //返回的文件句柄__in ACCESS_MASK DesiredAccess, //权限,如果希望对文件进行同步操作,需要额外加上SYNCHRONIZE__in POBJECT_ATTRIBUTES ObjectAttributes,__out PIO_STATUS_BLOCK IoStatusBlock, //一般不怎么用这个输出参数,但是的给值__in_opt PLARGE_INTEGER AllocationSize,//一般给NULL__in ULONG FileAttributes,//文件属性,一般给FILE_ATTRIBUTE_NORMAL__in ULONG ShareAccess,//共享属性一般给0__in ULONG CreateDisposition,//创建的描述信息,根据MSDN很容易决定__in ULONG CreateOptions, //如果是同步操作,一般加上FILE_SYNCHRONOUS_IO_NONALERT,如果是异步操作一般给0__in_opt PVOID EaBuffer, //一般给NULL__in ULONG EaLength//一般给0);
下面是读写不同设备的相关代码
//同步读取驱动的设备对象NTSTATUS status;HANDLE hDeviceA;OBJECT_ATTRIBUTES ObjAtrribute;UNICODE_STRING uDeviceName;IO_STATUS_BLOCK status_block;RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);InitializeObjectAttributes(&ObjAtrribute, &uDeviceName, OBJ_CASE_INSENSITIVE, NULL, NULL);status = ZwCreateFile(&hDeviceA, SYNCHRONIZE | FILE_READ_ATTRIBUTES, &ObjAtrribute,&status_block, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);if(!NT_SUCCESS(status)){return status;}ZwReadFile(hDeviceA, NULL, NULL, NULL, &status_block, NULL, 0, NULL, NULL);ZwClose(hDeviceA);return STATUS_SUCCESS;
//同步读取文件
HANDLE hFile = NULL;
OBJECT_ATTRIBUTES ObjAttribute;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING uFileName;
WCHAR wFname[] = L"\\??\\C:\\log.txt";
CHAR buf[] = "Hello World\r\n";
NTSTATUS status = STATUS_SUCCESS;
FILE_STANDARD_INFORMATION fsi = {0};
PCHAR pBuffer = NULL;
RtlInitUnicodeString(&uFileName, wFname);
InitializeObjectAttributes(&ObjAttribute, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
//打开文件或者创建文件status = ZwCreateFile(&hFile, GENERIC_WRITE | GENERIC_READ, &ObjAttribute, &IoStatusBlock, NULL, 0, 0, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL);
if(!NT_SUCCESS(status))
{
DbgPrint("Create File Error\n");
return;
}//写文件
status = ZwWriteFile(hFile, NULL, NULL, NULL, &IoStatusBlock, buf, sizeof(buf), NULL, NULL);
if(NT_SUCCESS(status))
{
DbgPrint("Write File Success%u", IoStatusBlock.Information);
}//读取文件长度
status = ZwQueryInformationFile(hFile, &IoStatusBlock, &fsi, sizeof(fsi), FileStandardInformation);
if(NT_SUCCESS(status))
{
DbgPrint("file length:%u\n", fsi.EndOfFile.QuadPart);
}//读文件
pBuffer = (PCHAR)ExAllocatePoolWithTag(PagedPool, fsi.EndOfFile.QuadPart * sizeof(CHAR), 'eliF');
if(NULL != pBuffer)
{
status = ZwReadFile(hFile, NULL, NULL, NULL, &IoStatusBlock, pBuffer, fsi.EndOfFile.QuadPart * sizeof(CHAR), NULL, NULL);
if(NT_SUCCESS(status))
{
DbgPrint("Read File %s lenth: %u", pBuffer, fsi.EndOfFile.QuadPart * sizeof(CHAR));
}
}
//关闭文件句柄
ZwClose(hFile);
ExFreePool(pBuffer);
转载于:https://www.cnblogs.com/lanuage/p/7725709.html
驱动开发中的常用操作相关推荐
- GitHub 优秀的 Android 开源项目 淘宝技术牛p博客整理开发中最常用的GitHub上 优秀的 Android 开源项目整理(精品)...
原文地址为http://www.trinea.cn/android/android-open-source-projects-view/,作者Trinea 主要介绍那些不错个性化的View,包括Lis ...
- 开发中,常用到的Eclipse快捷键
开发中,常用到的Eclipse快捷键 注1: 本文内容中的快捷键在我平时的工作大部分都会用到,不需要一次学会,可以慢慢的回顾积累,用的次数多了自然而然就熟悉了,相对于频繁的鼠标操作,这些快捷键可以提升 ...
- 开发中,常用到的Eclipse的快捷键
转自:https://blog.csdn.net/greensure/article/details/77113045 开发中,常用到的Eclipse快捷键 注1: 本文内容中的快捷键在我平时的工作大 ...
- java ee有哪些工具_JavaEE开发中最常用到的技术和工具汇总
原标题:JavaEE开发中最常用到的技术和工具汇总 今天千锋广州小编给大家来介绍一下关于目前JavaEE开发中最常用到的技术和工具的介绍,下面我们一起来看一下吧. 项目管理:Ant,项目管理事实上的标 ...
- 开发中Docker常用容器记录
开发中Docker常用容器记录 概览 分享工作学习中常用的Docker容器使用: 比如常用数据库的使用 消息队列类的使用 用于服务发现的容器使用 还有其他工作学习中使用到的 持续更新:https:// ...
- 关于objectArx /CAD二次开发中“属性块”操作
关于objectArx /CAD二次开发中"属性块"操作 属性块就是在图块上附加一些文字属性(Attribute),这些文字可以非常方便地修改.属性块被广泛应用在工程设计和机械设计 ...
- 开发中避免延时操作技巧详解
这篇文章主要为大家介绍了开发中避免延时操作技巧详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪 前言 开发中我们或多或少会涉及到一些场景需要使用延时操作,而延时操作其实 ...
- 开发中list常用转换
开发中list常用转换: 1.将list转换为逗号分隔的字符串 org.apache.commons.lang3.StringUtils.join(applyNameList, ",&quo ...
- 驱动开发中常用的操作和小知识
1.使用cat /proc/devices,查看内核中已经注册过的字符设备驱动和块设备驱动: 2.注册字符设备驱动完成后,添加设备类的操作,以让内核帮我们发信息!见高级篇3的代码 3.驱动开发 (1) ...
最新文章
- 6G应用场景有哪些?首份6G报告给你揭晓
- Html5之基础-7 HTML列表
- (iOS-基本知识)int long NSInteger 入门与兼容问题讲解
- 金九银十,做一个百度喜欢的淘宝客网站
- Linux中W与Who命令的使用
- pdf在html中加载不出来,在网页中打开显示PDF
- ECCV 2020 论文大盘点-自动驾驶篇
- happy 2016, happy 11111100000
- Codesys提示【CmContainer/Wibukey runtime system is not installed】的解决方法
- html框架自动居中,Pandas DataFrame.to_html方法,让自动生成的html中的表格整体居中...
- JAVA中rpm什么意思,RPM常用命令介绍
- 小程序员的不完整大学回忆3
- 网页上传文件获取地址的问题
- IJCAI 2022|边界引导的伪装目标检测模型BGNet
- ical4j 实现ICS文件的生成和解析
- C语言经典题目(一)
- 搭建go语言开发环境
- 硬盘修复真经 误区、缺陷、参数与低格
- SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法
- 【Python】Python3如何将汉字转化成反斜线u(\u)开头的字符串