(34)内核编程基础
一、未文档化函数、未导出函数
未文档化就是WDK文档里搜不到,但是在导出表里的函数,要使用这种函数可以使用GetProcAddress函数获取函数地址;
未导出函数就是不在导出表的函数,可以通过特征码搜索或者解析内核PDB的方式找到函数地址,通过函数指针调用。
二、WDK数据类型
WDK数据类型在ntdef.h中定义,下面列举部分,注意,并没有UINT
typedef unsigned char UCHAR;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
三、NTSTATUS 返回值
很多内核函数的返回值都是 NTSTATUS,这是一个4字节整型。
例如:
STATUS_SUCCESS 0x00000000 成功
STATUS_INVALID_PARAMETER 0xC000000D 参数无效
STATUS_BUFFER_OVERFLOW 0x80000005 缓冲区长度不够
四、内核异常处理
在内核中,一个小小的错误就可能导致蓝屏,比如:读写一个无效的内存地址。为了让自己的内核程序更加健壮,强烈建议大家在编写内核程序时,使用异常处。
Windows提供了结构化异常处理机制,一般的编译器都是支持的,如下:
__try{
//可能出错的代码
}
__except(filter_value) {
//出错时要执行的代码
}
出现异常时,可根据filter_value的值来决定程序该如果执行,当filter_value的值为:
EXCEPTION_EXECUTE_HANDLER(1),代码进入except块
EXCEPTION_CONTINUE_SEARCH(0),不处理异常,由上一层调用函数处理
EXCEPTION_CONTINUE_EXECUTION(-1),回去继续执行错误处的代码
用一段代码演示:
__try
{
PULONG ptr = NULL;
*ptr = 0x1234;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
DbgPrint("非法访问内存.\r\n");
}
五、常用的内核内存函数
C语言 | 内核 |
---|---|
malloc | ExAllocatePool |
memset | RtlFillMemory |
memcpy | RtlMoveMemory |
free | ExFreePool |
六、内核字符串及常用字符串函数
为了提高安全性,内核中的字符串不再是字符串首地址指针作为开始,0作为结尾,而是采用了以下两个结构体:
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;
下面的表格列出了常用的字符串函数:
功能 | ANSI_STRING字符串 | UNICODE_STRING字符串 |
---|---|---|
创建 | RtlInitAnsiString | RtlInitUnicodeString |
复制 | RtlCopyString | RtlCopyUnicodeString |
比较 | RtlCompareString | RtlCompareUnicoodeString |
转换 | RtlAnsiStringToUnicodeString | RtlUnicodeStringToAnsiString |
七、课后练习
1、申请一块内存,并在内存中存储GDT、IDT的所有数据。然后在debugview中显示出来,最后释放内存。
#include <ntddk.h>
#include <ntdef.h>
// 卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动程序停止运行了.\r\n");
}
// 入口函数,相当于main
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
UCHAR GDT[6];
UCHAR IDT[6];
ULONG GdtAddr,GdtLen,IdtAddr,IdtLen;
PUCHAR pBuffer = NULL;
ULONG i;
// 设置一个卸载函数,便于退出
driver->DriverUnload = DriverUnload;
// 读取GDT, IDT
__asm
{
sgdt fword ptr GDT
sidt fword ptr IDT
}
GdtAddr = *(PULONG)(GDT+2);
GdtLen = *(PUSHORT)GDT;
IdtAddr = *(PULONG)(IDT+2);
IdtLen = *(PUSHORT)IDT;
// DbgPrint("GDT: %08X size: %04X\r\n", GdtAddr, GdtLen);
// DbgPrint("IDT: %08X size: %04X\r\n", IdtAddr, IdtLen);
// 申请内存
pBuffer = (PUCHAR)ExAllocatePool(PagedPool, GdtLen + IdtLen);
// 检查申请是否成功
if (NULL == pBuffer)
{
DbgPrint("申请内存失败.\r\n");
return STATUS_UNSUCCESSFUL;
}
// 拷贝GDT, IDT数据
RtlMoveMemory(pBuffer, (PUCHAR)GdtAddr, GdtLen);
RtlMoveMemory(pBuffer + GdtLen, (PUCHAR)IdtAddr, IdtLen);
// 打印数据
DbgPrint("打印GDT\r\n");
for (i = 0; i < GdtLen; i += 16)
{
DbgPrint("%08X %08X %08X %08X %08X\r\n", GdtAddr + i, ((PULONG)(GdtAddr + i))[0],((PULONG)(GdtAddr + i))[1],((PULONG)(GdtAddr + i))[2],((PULONG)(GdtAddr + i))[3]);
}
DbgPrint("打印IDT\r\n");
for (i = 0; i < IdtLen; i += 16)
{
DbgPrint("%08X %08X %08X %08X %08X\r\n", IdtAddr + i, ((PULONG)(IdtAddr + i))[0],((PULONG)(IdtAddr + i))[1],((PULONG)(IdtAddr + i))[2],((PULONG)(IdtAddr + i))[3]);
}
// 释放内存
ExFreePool(pBuffer);
return STATUS_SUCCESS;
}
2、编写代码,实现如下功能:
<1> 初始化一个字符串
<2> 拷贝一个字符串
<3> 比较两个字符串是否相等
<4> ANSI_STRING与UNICODE_STRING字符串相互转换
不知为何,Unicode字符串中如果有中文,dbgview和windbg打印出来是空白。
#include <ntddk.h>
#include <ntdef.h>
// 卸载函数
VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动程序停止运行了.\r\n");
}
// 入口函数,相当于main
NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING reg_path)
{
// 创建字符串
ANSI_STRING AnsiSrc;
ANSI_STRING AnsiDst;
UNICODE_STRING UnicodeString;
// 初始化字符串
RtlInitAnsiString(&AnsiSrc,"my first ANSI_STRING");
RtlInitUnicodeString(&UnicodeString,L"my first UNICODE_STRING");
// 打印字符串
DbgPrint("%s Length: %d MaximumLength: %d\r\n", AnsiSrc.Buffer, AnsiSrc.Length, AnsiSrc.MaximumLength);
DbgPrint("%ws Length: %d MaximumLength: %d\r\n", UnicodeString.Buffer, UnicodeString.Length, UnicodeString.MaximumLength);
// 拷贝字符串
RtlCopyString(&AnsiDst, &AnsiSrc);
// 比较字符串
if (RtlCompareString(&AnsiSrc, &AnsiDst, TRUE) == 0)
{
DbgPrint("字符串相等.\r\n");
}
else
{
DbgPrint("字符串不相等.\r\n");
}
// Unicode转Ansi
DbgPrint("Unicode转Ansi\r\n");
RtlUnicodeStringToAnsiString(&AnsiDst,&UnicodeString,TRUE);
DbgPrint("%s Length: %d MaximumLength: %d\r\n", AnsiDst.Buffer, AnsiDst.Length, AnsiDst.MaximumLength);
// 设置一个卸载函数,便于退出
driver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}
上一篇: 内核编程基础