入口与对象
设计高标准驱动程式当然少不了内核和内存管理、进程管理等等,程序员编写的所有内容无碍乎离不了一个结构,那就是DRIVER_OBJECT,它对应着一个驱动结构,
最简单的驱动程式
Win32程序里有Winmain(),而windows中的DriverEntry和其中的WINDOWSmain函数一样必不可少。
#include <ntddk.h>
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
){
NTSTATUS status = STATYS_UNSUCCESSFUL;
return status;
}
DriverEntry参数及其作用:
参数 | 作用 |
---|---|
DIVER_OBJECT指针 | 对应编写的驱动程式,系统预分配内存 |
RegistryPath字符串 | 注册表子键,专用于本程序信息保存到注册表中 |
DriverEntry的返回值决定这个驱动的加载是否成功,如果返回STATUS_SUCCESS,则驱动将成功加载,否则不成功加载。
重点:当读者打算反汇编阅读一个内核程序的时候,先可寻找DriverEntry的位置。
分发函数和卸载函数
分发函数指针,就是用来处理发送到这个驱动程序中的请求的,包含于DRIVER_OBJECT中。windows总是自己调用DRIVER_OBJECT下的分发函数来处理这些被发总到驱动中的请求。所以可以理解编写驱动程序的实质是编写请求处理请求的分发函数。
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
){
ULONG i;
for(i=0;i<IRP_MJ_MAXIMUM_FUNCTION;++i)
{
DriverObject->MajorFunctions[i] = MyDispatchFunction;
}
}
然后编写MyDispatchFunction:
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device,PIRP irp){
//device是由本驱动生成的设备
//irp是一个系统请求
//这个函数要处理的是发送给设备device的请求irp
}
卸载函数不放在分发函数数组中,在卸载时,该函数动态执行,起到了该驱动的动态卸载的作用:
NTSTATUS MyDispatchFunction(PDEVICE_OBJECT driver){
//无返回值,地址设置到DriverObject->DriverUnload;
}
设备与符号链接
如果一个驱动程序只是想写写日志文件或者内核钩子来做一些Rootkit,就不发要分发函数:
#include <ntifs.h>
DriverEntry(
IN PDRIVER_OBJECT river,
IN PUNICODE_STRING Reg_path
){
NTSTATUS status;
PDEVICE_OBJECT device;
UNICODE_STRING device_name = RTL_CONSTANT_STRING("\\Device\\MyCDO");
UNICODE_STRING symb_link = RTL_CONSTANT_STRING("\\DosDevice\\MyCDOSL");
STATUS=IoCreateDevice(
driver,
0,
device_name,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&device
);
if(!NT_SUCCESS(status))
return status;
status=IoCreateSymbolicLink(
&symb_link,
&device_name
);
if(!NT_SUCCESS(status)){
IoDeleteDevice(device);
return status;
}
device->Flag &=~DO_DEVICE_INITIALIZING;
return status;
}
这个驱动加载之后,生成了一个叫\Device\MyCDO的设备,符号连接叫做\DosDevices\MySDOSL,应用层可以通过打开符号链接打开设备。