内核与驱动_00_内核编程基础知识

内核编程环境

概述

  • 在Windows下,32为系统中每个进程都有其自身享有的4GB内存空间,每个进程的内存空间都是相互隔离的。其中低2GB是用户空间,高2GB是内核空间。原则上,每个进程的高2GB内核空间内的数据是共享的,即绝大部分都是一样的。
  • 而内核空间是受到硬件保护的,比如在x86架构下R0层(Ring0)的代码才可以访问内核空间,普通的应用程序编译出来后都运行在R3层,R3层程序要调用R0层功能时,只能通过系统提供的一个入口(该入口中调用sysenter指令)来实现。

无处不在的内核模块

  • 内核是由接口的,微软提供规定的格式,用于让硬件驱动的编程人员能够按照规定的格式编写“驱动程序”。这些驱动程序能加载到内核中,称为内核的一部分,这样内核就有了可扩展性,只要简单的安装驱动程序,就可以适应各种不同的硬件了。
  • 把内核模块叫做驱动程序Dirver,也可以按照Linux程序员们的叫法,称之为内核模块(Kernel module)。
  • 内核模块位于内核空间,作为R0级代码执行,可以不受任何限制,任意修改内核。
  • 内核模块位于内核空间,而内核空间又被所有的进程所共享,因此内核模块实际上位于任何一个进程空间,但是任意一段代码的一次执行,一定是位于某个具体的进程空间中的,至于这个进程是哪个,这取决于当时的运行状况。
  • 一个函数PsGetCurrentprocessId,能够得到当前进程的进程号,函数返回进程HANDLE句柄,实际上就是一个进程的PID。
  • 一个误区:认为所有内核代码都运行在系统进程内:windows中的系统进程是一个名为“System”的进程,是Windows自身生成的一个特殊进程,在XP中这个进程的PID始终为4 。DirverEntry函数被调用时,一般都位于系统进程中,这是因为Windows一般都使用系统进程来加载内核模块,并不是说内核代码始终运行在System进程中。
  • 使用微软提供的驱动开发包WDK进行开发。

数据类型

基本数据类型

  • 在进行内核编程时,应当遵守WDK的编码习惯,这样可以使得代码在不同的目标平台上编译不会产生不一致的问题。

  • WDK中已经将很多数据类型向SDK那样重新定义过,这样做的好处是,万一有了什么问题,再重新定义一下即可,不至于使得代码产生不可控的问题。

  • 以下是一些需要习惯使用的数据类型:

    • unsigned long ---->ULONG
    • unsigned char ---->UCHAR
    • unsigned int ---->UINT
    • void ---->VOID
    • unsigned long* ---->PULONG
    • unsigned char * ---->PUCHAR
    • unsigned int * ---->PUINT
    • void* ---->PVOID
  • 一般我们使用x86和x64平台进行编译,它们的区别除了指针从四个字节变为了8个字节之外,其余几种类型字节的宽度都没有什么变化。

返回状态

  • 绝大部分的内核API的返回值都是一个返回状态,就是一个错误码。类型为NTSTATUS。如下示例:
NTSTATUS MyFun()
{
   
    NTSTATUS status;
    //打开一个文件...
    status=ZwCreateFile(..);
    //宏NT_SUCESS()用来判断一个返回值是否成功
    if(!NT_SUCESS(status))
    {
   
        //出错就返回错误码
        return status;
	}
}
  • 返回的错误码完全由编写者说了算,不过错误码有一些约定成俗的含义,如下一些示例:
    一些状态码

字符串

  • 驱动字符串一般用一个结构来容纳,定义如下:
typedef struct _UNICODE_STRING{
   
    USHORT Length;			//字符串的长度,单位是字节数
    USHORT MaximumLength;	//最大字节数
    PWSTR Buffer;
}UNICODE_STRING,*PUNICODE_STRING;
//字符串的字符是宽字符,双字节的
  • UNICODE_STRING是可以直接打印的,可以使用如下的写法:
//定义和输出字符串
UNICODE_STRING buff = RTL_CONSTANT_STRING(L"使用字符串结构体的字符串");
	DbgPrint("%wZ\n", &buff);			//输出使用%wZ
  • UNICODE_STRING结构体的指针(注意是指针,结构体本身不能打印)可以用%wZ来打印。

  • UNICODE_STRING除了可以用于初始化和打印之外,其它普通字符串的操作也可以同样做到。

重要的数据结构

  • Windows内核编程中有三个重要的概念:
    • 驱动对象
    • 设备对象
    • IRP

驱动对象

  • Windows内核采用了面对对象的编程方式,但使用的却是C语言。所以Windows内核中所谓的内核对象并不是一个C++类对象,而是一种使用C语言对面对对象编程方式的一种模拟。
  • 在Windows中很多东西都是“对象”,比如一个驱动、一个设备等,“对象”在这里相当于一个基类。
  • 驱动对象代表着当前编写的驱动程序,从面对对象的角度来看就如同Windows编程时的示例句柄INSTANCE代表一个应用程序一样。
  • 当驱动一加载,对象管理器便会创建一个驱动对象,并调用DriverEntry将驱动对象传入,然后我们就可以在DriverEntry中为驱动对象结构体的各个字段赋值,为整体的驱动程序定下总的基调。
  • 驱动对象的结构如下:
typedef staruct _DRIVER_OBJECT{
   
    //结构的类型和大小
    SHORT Type;                                                             //0x0
    SHORT Size;                     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值