驱动

http://www.cnblogs.com/mydomain/archive/2010/10/12/1849160.html

http://www.cnblogs.com/mydomain/category/264964.html

内核基础

基本概念
   首先需要安装DDK (Device Driver Kit),这里我选择Microsoft Windows Server 2003 SP1 DDK。
   Windows 驱动分成两类,一类是不支持即插即用的NT式驱动,一类是支持即插即用的WDM((Windows Driver Model))驱动。NT式驱动的安装是基于服务的,可以通过修改注册表进行,也可以直接通过服务函数,如CreateService进行安装;但WDM 式驱动不同,它安装的时候需要通过编写一个inf文件进行控制。
   Driver.h头文件中包含了开发NT式驱动所需要的NTDDK.h,此外还定义了几个标志来指明函数和变量分配在分页内存还是非分页内存中。Windows驱动程序的入口函数是DriverEntry函数。
  有两种编译驱动的办法,一种是用DDK环境来编译,需要在源代码所在目录下创建两个文件makefile和Sources,功能是引入DDK的bin目录下 的makefile.def文件,然后在开始菜单中选择“Windows XP Checked Build Environment”编译环境,进入需要编译的目录,输入”build“命令就可以;第二种编译方式是使用VC++进行编译。[1]]
为了调试方便,最好安装一个虚拟机。[2]
  Windows的驱动模型概念,本来是就驱动程序的行为而言的。比如WDM驱动,必须要满足提供n种被要求的特性(如电源管理、即插即用)才被称为WDM驱动。如果不提供这些功能,那么统一称为NT式驱动。同样的,WDF驱动也有它的一系列规范。
  WDF(Windows Driver Foundation)驱动是可以调用传统型驱动所调用的内核API的,WDF可以视为传统型的升级版。[3]
WDK = DDK (Driver Development Kit) + HCT Kit (Hardware Compatibility Test) + WDF (Windows Driver Foundation) + DTM (Driver Test Manager) + WDF Driver Verification Tools + IFS Kit (Installable File Systems Kit) + Free ISO image download - Visual Studio 2005 out of the box integration[4]

基本语法[5]
字符串
  在驱动开发中四处可见的是Unicode字符串。因此可以说:Windows的内核是使用Uincode编码的。ANSI_STRING仅仅在某些碰到窄字符的场合使用。而且这种场合非常罕见。一个定义如下:
typedef struct _UNICODE_STRING {
USHORT Length; // 字符串的长度(字节数)
USHORT MaximumLength; // 字符串缓冲区的长度(字节数)
PWSTR Buffer; // 字符串缓冲区
} UNICODE_STRING, *PUNICODE_STRING;
  UNICODE_STRING并不保证Buffer中的字符串是以空结束的。因此,类似下面的做法都是错误的,可能会会导致内核崩溃:
UNICODE_STRING str;

len = wcslen(str.Buffer); // 试图求长度。
DbgPrint(“%ws”,str.Buffer); // 试图打印str.Buffer。
  如果要用以上的方法,必须在编码中保证Buffer始终是以空结束。但这又是一个麻烦的问题。所以,使用微软提供的Rtl系列函数来操作字符串,才是正确的方法。[6]
内存与链表
ExAllocatePoolWithTag
内存分配:
LIST_ENTRY中的数据成员Flink指向下一个LIST_ENTRY。
  整个链表中的最后一个LIST_ENTRY的Flink不是空。而是指向头节点。得到LIST_ENTRY之后,要用CONTAINING_RECORD来得到链表节点中的数据。
  锁一般不会定义成局部变量。可以使用静态变量、全局变量,或者分配在堆中。
文件操作
  在内核中不能调用用户层的Win32 API函数来操作文件。在这里必须改用一系列与之对应的内核函数。
VOID InitializeObjectAttributes(
OUT POBJECT_ATTRIBUTES InitializedAttributes,
IN PUNICODE_STRING ObjectName,
IN ULONG Attributes,
IN HANDLE RootDirectory,
IN PSECURITY_DESCRIPTOR SecurityDescriptor);
  Windows内核中,无论是打开文件,还是注册表,设备等,都会先调用初始化一个OUT POBJECT_ATTRIBUTES。
OBJ_KERNEL_HANDLE表明打开的文件句柄一个“内核句柄”。内核文件句柄比应用层句柄使用更方便,可以不受线程和进程的限制。在任何线程中都可以读写。同时打开内核文件句柄不需要顾及当前进程是否有权限访问该文件的问题(如果是有安全权限限制的文件系统)。
  路径并不是像应用层一样直接写“C:\\a.dat”,而是写成了“\\??\\C:\\a.dat”。这是因为ZwCreateFile使用的是对象路径。“C:”是一个符号链接对象。符号链接对象一般都在“\\??\\”路径下。
注册表[5]
  应用编程中对应的子键 驱动编程中的路径写法
HKEY_LOCAL_MACHINE \Registry\Machine
HKEY_USERS \Registry\User
HKEY_CLASSES_ROOT 没有对应的路径
HKEY_CURRENT_USER 没有简单的对应路径,但是可以求得
ZwOpenKey
ZwQueryValueKey
ZwSetValueKey
时间
void MyGetTickCount (PULONG msec)
{
LARGE_INTEGER tick_count;
ULONG myinc = KeQueryTimeIncrement();
KeQueryTickCount(&tick_count);
tick_count.QuadPart *= myinc;
tick_count.QuadPart /= 10000;
*msec = tick_count.LowPart;
}
KeSetTimer

内核的代码始终运行在某个“中断级”上。Dispatch > APC > Passive


参考
[1] http://www.cnblogs.com/phinecos/archive/2009/02/19/1393803.html
[2] http://www.cnblogs.com/qsilence/archive/2009/06/11/1501511.html
[3 http://msdn.microsoft.com/en-us/library/ff557565%28VS.85%29.aspx
[4] http://www.cnblogs.com/wanghao111/archive/2009/05/25/1489041.html
[5] Windows驱动编程基础教程.doc
[6] Windows DDK


内核线程

在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“System”。

NTSTATUS

PsCreateSystemThread(

OUT PHANDLE ThreadHandle,

IN ULONG DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

IN HANDLE ProcessHandle OPTIONAL,

OUT PCLIENT_ID ClientId OPTIONAL,

IN PKSTART_ROUTINE StartRoutine,

IN PVOID StartContext);

线程的结束应该在线程中自己调用PsTerminateSystemThread来完成。此外得到的句柄也必须要用ZwClose来关闭。关闭句柄并不结束线程。

睡眠

#define DELAY_ONE_MICROSECOND (-10)

#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

VOID MySleep(LONG msec)

{

LARGE_INTEGER my_interval;

my_interval.QuadPart = DELAY_ONE_MILLISECOND;

my_interval.QuadPart *= msec;

KeDelayExecutionThread(KernelMode,0,&my_interval);

}

内核中的事件是一个数据结构。这个结构的指针可以当作一个参数传入一个等待函数中。如果这个事件不被“设置”,则这个等待函数不会返回,这个线程被阻塞。如果这个事件被“设置”,则等待结束,可以继续下去。

事件不需要销毁。

可以发现,关于事件的操作这一部分(如事件的重设,同步等),其原理和MFC中是很类似的,只是MFC中封封装了更好处理的API函数而已。

实际上等待线程结束并不一定要用事件。线程本身也可以当作一个事件来等待。

驱动与设备和请求处理

#include <ntddk.h>

NTSTATUS

DriverEntry (

IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath

)

{

NTSTATUS status = STATUS_UNSUCCESSFUL;

return status;

}

函数DriverEntry是每个驱动程序中必须的。如同Win32应用程序里的WinMain。DriverEntry的第一个参数就是一个 DRIVER_OBJECT的指针。这个DRIVER_OBJECT结构就对应当前编写的驱动程序。其内存是Windows系统已经分配的。

第二个参数RegistryPath是一个字符串。代表一个注册表子键。这个子键是专门分配给这个驱动程序使用的。用于保存驱动配置信息到注册表中。

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;

}

}

NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device,PIRP irp)

{

……

}

VOID MyDriverUnload(PDRIVER_OBJECT driver)

{

……

}

这个函数的地址设置到DriverObject->DriverUnload即可。

如果驱动程序要和应用程序之间通信,则应该生成设备。此外还必须为设备生成应用程序可以访问的符号链接。

“\\.\”意味后面是一个符号链接名。 目前生成设备,请总是生成在\Device\目录下。

应用程序为了和驱动通信,首先必须打开设备。然后发送或者接收信息。最后关闭它。这至少需要三个IRP:第一个是打开请求。第二个发送或者接收信息。第三个是关闭请求。

应用层调用的API 驱动层收到的IRP主功能号

CreateFile IRP_MJ_CREATE

CloseHandle IRP_MJ_CLOSE

DeviceIoControl IRP_MJ_DEVICE_CONTROL

ReadFile IRP_MJ_READ

WriteFile IRP_MJ_WRITE

关于上述几项内容的专题论述,请参见相关文档。[5,6]

参考
[1] http://www.cnblogs.com/phinecos/archive/2009/02/19/1393803.html
[2] http://www.cnblogs.com/qsilence/archive/2009/06/11/1501511.html
[3 http://msdn.microsoft.com/en-us/library/ff557565%28VS.85%29.aspx
[4] http://www.cnblogs.com/wanghao111/archive/2009/05/25/1489041.html
[5] Windows驱动编程基础教程.doc
[6] 天书夜读-从汇编语言到windows内核编程(改)
[7] Windows DDK

[8] 天书夜读——从汇编语言到Windows内核编程
http://download.csdn.net/source/2754275
http://msdn.microsoft.com/en-us/library/ff557573%28VS.85%29.aspx



Windows内核(一)
.sys放在Drivers目录下。运行在R0层。
WDK的相应环境中,进行相应代码目录,build.
一个内核程序被看作一个PE格式的DLL,它是被Windows整个内核调用的一个DLL,一旦加裁,就成为内核的组成部分。所有的内核内存空间是共享的。内核程序崩溃,Windows系统也就崩溃了。
DriverEntry是一个内核程序的入口,以system进程名出现。
编写内核的规则:
不能调用Windows应用层API函数,而应用内核API
关于字符串,内存处理,打印等,参见前面Rtl函数
浮点数要特殊处理
Windows中,不同类型设备的驱动有不同的安装方式,ini文件。非任何类型的驱动程序,当作服务安装。(搜服务安装驱动下载net start 服务名)
调试
Windbg ( http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx )
我们用虚拟机进行调试VMWare.
环境搭建:
1虚拟机中boot.ini
.......
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise" /noexecute=optout /fastdetect
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise" /noexecute=optout /fastdetect /debug /debugport=com1 /baudrate=9600
2设置虚拟机
Hardward > Serial Port > Output to named pipe,添入:
\\.\pipe\com_1,this end is the server,the other end is an application.
3Windbg设置
Windbg(本机中)的启动参数:
Windbg.exe -b -k com:port=\\.\pipe\com_1,baud=9600,pipe
(整成快捷方式)
说明1:在虚拟中在调试模式下启动windows后,马上以以上参数启动Windbg
说明2:我以指定波特率启动WINDBG时老是提示我参数出错,把指定波特项取消后可以成功。
4设置代码路径和符号表位置,就可以调试了。Symbol File Path中,sys的位置。多个路径以分号隔开。
用以下设置来下载符号表。
srv*c:\symbols*http://msdl.microsoft.com/download/symbols
(关于说明,可见:http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx)
其它更进一步的关于汇编代码的分析,见[6]
其它调试如同VS中。
C++写内核
需要自己写new操作符
ecx传递this指针对象的开头部分就是虚函数表地址
父类和子类各有一份虚函数表,没有共公部分。在子类的虚函数表中,没有重载的基类虚函数的地址和基类虚函数一样,而重载后有自己独特的地址。
参考
[1] http://www.cnblogs.com/phinecos/archive/2009/02/19/1393803.html
[2] http://www.cnblogs.com/qsilence/archive/2009/06/11/1501511.html
[3 http://msdn.microsoft.com/en-us/library/ff557565%28VS.85%29.aspx
[4] http://www.cnblogs.com/wanghao111/archive/2009/05/25/1489041.html
[5] Windows驱动编程基础教程.doc
[6] 天书夜读-从汇编语言到windows内核编程(http://download.csdn.net/source/2754275)
[7] Windows DDK
推荐其它书籍
Programming the micfosoft windows Driver Model:驱动开发
windows环境下的32位汇编语言程序设计:汇编学习
软件调试,张银奎:软件调试


运行模式:

实模式:CPU启动后处于实模式。只能访问1MB内存,单任务,所有任务都运行在R0层。如DOS

保护模式:支持分段、分页,可运行多任务。如Linux,Window.

系统管理模式(SMM)

虚拟8086模式:为了能在保护模式中运行原来能在实模式下运行的程序。

X86处理器的地址分段模式包括如下形式:

平坦模式:线性地址(页目录,页,页中偏移)。

多段模式:如数据段,地址段,etc

32windows中,04G线性内存空间。02G为应用程序内存空间(处于其中每个进程都有独立的内存空间),2G~4G为系统内核空间(内核进程完全共享)。R3级无法访问24G内存空间。

X86中,cr0~cr4为控制寄存器。Cr0为控制R0是否可以写只读页,cr2存放发生页中断时的线性地址,cr3中保留页中目录基地址,cr4来进行CPU的架构扩展。

内核进程和应用程序通信的话:内核程序生成一个设备对象,并为这个对象生成一个有应用层可以访问的符号连接。设备对象用 IoCreateDevice生成,符号连接用IoCreateSymbolicLink生成。应用程序可以用CreateFile打开这个文件,通过 ReadFile,WriteFile等和DeviceIoControl来进行通信。一个和驱动程序通信的进程应当被开发成为一个服务程序,因为服务程序拥有管理员权限。

其它
一个可控制的内核程序就可以称为一个Rootkit。库文件LIB实际上是多个OBJ文件的集合。
参考

[1] http://www.cnblogs.com/phinecos/archive/2009/02/19/1393803.html
[2] http://www.cnblogs.com/qsilence/archive/2009/06/11/1501511.html
[3 http://msdn.microsoft.com/en-us/library/ff557565%28VS.85%29.aspx
[4] http://www.cnblogs.com/wanghao111/archive/2009/05/25/1489041.html
[5] Windows驱动编程基础教程.doc
[6] 天书夜读-从汇编语言到windows内核编程( http://download.csdn.net/source/2754275)
[7] Windows DDK
[8] http://blog.vckbase.com/windowssky/archive/2008/06/18/30866.html
AVP主动防御之隐藏进程

推荐其它书籍

Programming the micfosoft windows Driver Model:驱动开发
windows环境下的32位汇编语言程序设计:汇编学习
软件调试,张银奎:软件调试
ROOTKITS Windows内核的安全防护,Butler,J
《网络盗窃——10个黑客入侵的故事》
http://www.pediy.com/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值