这篇文章会持续更新,由于在驱动中,有许多常用的操作代码几乎不变,而我自己有时候长时间不用经常忘记,所以希望在这把一些常用的操作记录下来,当自己遗忘的时候,有个参考
创建设备对象
创建设备对象使用函数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
", 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_INSENSITIVE
IN HANDLE RootDirectory, //根目录,一般给NULL
IN 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
";
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
");
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
", 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);