内核知识第四讲,简单的认识内核函数.以及调试驱动技巧
一丶驱动调试技巧.
我们写好的内核驱动代码要怎么调试?
1.自己内联汇编 进行调试
2.调用内核驱动调试API.
一丶内联汇编进行调试
内联汇编进行调试.
代码如下.
代码语言:javascript复制#include <Ntddk.h> //编写内核驱动需要包含NTddk头文件.
//卸载回调函数
VOID Unload(__in struct _DRIVER_OBJECT *DriverObject)
{
DbgPrint("Unload MyDriven");
}
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT *DriverObject,
__in PUNICODE_STRING RegistryPath)
{
int i = 0;
DbgPrint("HelloWorld, %prn",&i);
__asm int 3 //内联汇编,让自己的汇编代码在此处暂停.
//注册一下驱动卸载的函数
DriverObject->DriverUnload = Unload;
return STATUS_SUCCESS;
}
int 3则是产生一个断点,请注意,一定要配合WinDbg进行调试,也就是双机调试,否则这条代码则会蓝屏.
2.内核断点API进行调试
代码语言:javascript复制VOID NTAPI DbgBreakPoint( VOID );
只要在我们的驱动代码中加入这一行则可以进行断点调试的
在WinDbg中,本质还是调用int 3指令.
如果我们要发布Release版本,但是又担心我们的断点没有去掉怎么办?
加上条件宏.
微软也意思到了这个问题,所以提供了一个API. 这个API的本质还是调用 DbgBreakePoint.
只不过加了条件宏.
代码语言:javascript复制VOID NTAPI KdBreakPoint( VOID );
有兴趣的跟进去可以看,本质还是我们上面的API,只不过用条件宏包了一下.
二丶内核中的内核函数简单介绍.
如果我们编写内核驱动程序.那么内核函数是我们常用的接口.那么我们要熟悉一下内核函数的意义.
图示:
在内核帮助文档中,我们可以看到操作系统提供了很多API,且它们都有一致性.
ob开头的API: 一般是对象使用,操作系统是C和汇编写的,但是用的却是面向对象设计,比如进程.还有引用计数等等.所以需要调用这种API.
Mm开头的API: Mm开头的API,都是内存管理的API,比如锁定一块内存,不让其与磁盘交换数据等等.
Ps开头的API: ps开头的API,一般都是管理进程和线程的API,比如psGetCurrentProcessId,获得当前进程ID.
Io开头的API: Io开头的API一般就是负责IO操作的.如果写硬件驱动则不用管这些.直接使用in 或者out进行操作.
Po开头的API: Po开头的API一般就是管理电源的API.对于写内核驱动的我们,不关心这些.写硬件驱动的则要关心,比如通电了.硬件驱动需要做些初始化什么的.
Cm开头的API: Cm开头的API一般是注册表常用的API.
Zw开头的API: Zw开头的API,一般是内核版本的API,比如三环有CreateFile,那么在内核API中则是ZwCreateFile.
Ke开头API: ke开头的API,一般是内核层的API.在内核中,分为内核层还有执行层.
Ex开头的API: Ex开头的API,则是执行层的API.
Rtl开头的API : Rtl开头的Api和C库函数很像,在驱动中可以使用C库函数,但是微软不建议使用.所以提供了Rtl开头的API,甚至比C库函数还多.
DMA开头的API: 一般是写硬件驱动用的,我们不用.
HAL开头的API: 操作硬件抽象层的API.
ZwXXX系列的API: 这个意思就是参考ring3下的API,一般ring3下是什么,那么对应的加上个Zw开头即可.注意,Zw开头的参数不一定和Ring3下的API参数相同.