基础知识

  1. 驱动程序无法在当前系统中进行调试,否则会导致系统卡死
  2. 可以采用双机调试的方式调试驱动程序

驱动调试

当我们使用 windbg 查看某一个地址的反汇编时,例如:
在这里插入图片描述
windbg 能够自动帮我们识别出该地址属于哪个函数(红框部分)

思考:windbg 是如何识别出该地址对应的函数的?

PDB(Program Debug Database)

  1. PDB文件是在我们编译工程的时候产生的,它是和对应的模块(exe或dll)一起生成出来的
  2. 每个模块编译的时候都可以生成自己的PDB文件,比如 .exe / .dll / .sys 等等
  3. 0环调试器(例如 WinDbg)正是通过解析pdb文件来找到函数与地址之间的对应关系

WinDbg 加载 PDB

symbols
在这里插入图片描述
在这里插入图片描述

实验:调试 .sys 文件

第一步:编译代码
#include "ntddk.h"

//卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
	DbgPrint("驱动程序已停止.\r\n");
}

//驱动程序入口函数,相当于控制台的main函数
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{

	__asm
	{
		int 3
		mov eax, eax
		mov eax, eax
		mov eax, eax
	}

	DbgPrint("驱动程序已运行.\r\n");

	//设置一个卸载函数  便于退出
	DriverObject->DriverUnload = DriverUnload;

	return STATUS_SUCCESS;
}
第二步:将 .sys 文件拷贝到虚拟机中
第三步:添加 pdb 文件路径

在这里插入图片描述
在这里插入图片描述

第四步:部署 .sys 文件并运行

在这里插入图片描述
WinDbg 成功截获中断即可
在这里插入图片描述

内核编程基础

内核API的使用

  1. 在应用层编程时,可以使用WINDOWS提供的各种API函数,只要导入头文件<windows.h>就可以了。但是在内核编程的时候,不能像在Ring3那样直接使用
  2. 微软为内核程序提供了专用的API,只要在程序中包含相应的头文件就可以使用了,如:#include <ntddk.h> (前提是已经正确安装了WDK)
  3. 在应用层编程的时候,我们通过MSDN来了解函数的详细信息,在内核编程的时候,要使用WDK自己的帮助文档
    在这里插入图片描述

未导出函数的使用

描述

  1. WDK说明文档中只包含了内核模块导出的函数,对于未导出的函数,则不能直接使用。
  2. 如果要使用未导出的函数,只要自己定义一个函数指针,为函数指针提供正确的函数地址就可以使用了

获取未导出的函数地址

  1. 特征码搜索
  2. 解析内核PDB文件

基本数据类型

在内核编程的时候,强烈建议大家遵守WDK的编码习惯,不要这样写:unsigned long length;

WDK类型

ULONG(unsigned long)		PULONG(unsigned long *)
UCHAR(unsigned char)		PUCHAR(unsigned char *)
UINT(unsigned int)			PUNIT(unsigned int *)	
VOID(void)					PVOID(void *)

返回值

大部分内核函数的返回值都是NTSTATUS类型,如:

NTSTATUS PsCreateSystemThread();    
NTSTATUS ZwOpenProcess();
NTSTATUS ZwOpenEvent();

这个值能说明函数执行的结果,如:

STATUS_SUCCESS				0x00000000	成功		
STATUS_INVALID_PARAMETER	0xC000000D	参数无效	
STATUS_BUFFER_OVERFLOW		0x80000005	缓冲区长度不够	

当调用的内核函数时,如果返回的结果不是STATUS_SUCCESS,就说明函数执行中遇到了问题,具体是什么问题,可以在ntstatus.h文件中查看

内核中的异常处理

描述

  1. 在内核中,一个小小的错误就可能导致蓝屏,比如:读写一个无效的内存地址
  2. 为了让自己的内核程序更加健壮,强烈建议大家在编写内核程序时,使用异常处理

Windows提供了结构化异常处理机制,一般的编译器都是支持的,如下:

__try{
	//可能出错的代码
}
__except(filter_value) {
	//出错时要执行的代码
}

filter_value:

EXCEPTION_EXECUTE_HANDLER(1)		//代码进入except块
EXCEPTION_CONTINUE_SEARCH(0)		//不处理异常,由上一层调用函数处理
EXCEPTION_CONTINUE_EXECUTION(-1)	//回去继续执行错误处的代码

常用内核函数(内存操作)

在这里插入图片描述

内核字符串种类

ANSI_STRING字符串

typedef struct _STRING
{
    USHORT Length;
    USHORT MaximumLength;
    PCHAR Buffer;
}STRING;

UNICODE_STRING字符串

typedef struct _UNICODE_STRING
{
    USHORT Length;
    USHORT MaxmumLength;
    PWSTR Buffer;
} UNICODE_STRING;

内核字符串常用函数

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐