题外话:
关于之前写的《内核编程学习笔记1-5》,那只是为了做某游戏的双开驱动而做的学习,所以比较肤浅(连NT式驱动和WDM式驱动都分不不清)。这次是一个系统的学习,会结合操作系统的知识来学习,是一个系统的学习过程,所以要重新写的学习笔记。加油吧~
NT驱动开发学习笔记一2011.05.04
关于头文件:
①#pragma once //表示这个头文件只会被包含一次,可以防止重复包含
②关于调用约定
#ifdef __cplusplus
extern "C"
{
#endif
#include <NTDDK.h>
#ifdef __cplusplus
}
#endif
从上面可以看出,在包含NTDDK.h这个头文件的时候用了extern "C",是因为C++和C语言的编译器的编译后的函数符号不一致,可能会导致链接错误。下面详细说明:
各种调用约定:
C语言调用约定 __cdecl 调用者清除堆栈,会在call后add esp,***
标准调用约定 __stdcall 被调用的子函数清除堆栈,最后ret ***
快速调用约定 __fastcall 参数在寄存器传递
C++ 类成员函数调用约定 __thiscall 用来方位类成员函数
C++ 托管函数调用约定 __clrcall 多用于.net技术
对于void Func(int a, int b) 这个函数,由于清除堆栈的方式不同
_cdecl 的这个函数在目标文件中产生的符号为 _Func
_stdcall 的这个函数在目标文件中产生的符号为 _Func@8
再举个例子:驱动入口DriverEntry这个函数的默认符号为 _DriverEntry@8,如果用的是_cdecl来编译,可想而知就会成了_DriverEntry,链接错误。
再说说C++为了方便重载技术的实现,会把上面说的Func函数编译成符号(导出名)_?Foo@@YGXHH@Z,就是把参数的类型都作为符号了,这样的话当然会出现链接错误了。
加上extern "C"就可以强制C++编译器按照C语言的方式来编译。
③NT驱动编译后是一个PE格式的sys文件
所以在.text Segment(代码段)中有不同的Section(节)
INIT节:初始化完成后释放
PAGE节:位于可以进行分页交换的空间
PAGELK节:位于不可进行分页交换的内存空间(默认不设置预编译)
通过下面的2种预编译可以使得代码编译出来后在sys文件的位置
第一种:
例如:#pragma alloc_text(INIT, DriverEntry)
第二种:
#define INITCODE code_seg("INIT")
#define PAGECODE code_seg("PAGE")
#define PAGELKCODE code_seg()
然后在DriverEntry的实现之前
#prama INITCODE
④关于设备扩展结构:DEVICE_EXTENSION
typedef struct _DEVICE_EXTENSION
{//设备扩展结构
PDEVICE_OBJECT pDeviceObject; //设备对象指针
UNICODE_STRING szDeviceName; //设备名称
UNICODE_STRING szSymLinkName; //符号链接名称
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
在DEVICE_OBJECT结构体里面有个成员 PVOID DeviceExtension;提供给程序员自己定义自己的结构体来生成对象传递某些信息,如变量,函数地址等,起到类似全局变量的作用,另外,在驱动程序中,应该尽量避免使用全局变量,使用全局变量可能会涉及不容易同步的问题。
⑤下面是一些函数的声明:
//驱动程序入口
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath);
//卸载驱动例程
VOID myDDKUnload(IN PDRIVER_OBJECT pDriverObject);
//驱动派遣例程
NTSTATUS myDDKDispatchRoutine(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp);
//创建设备例程
NTSTATUS CreateDevice(IN PDRIVER_OBJECT pDriverObject);
Mark:http://www.osronline.com/
这个老外的网站,有很多驱动开发工具下载。