内核编程基础 一、未导出函数的使用 二、返回值 三、内核中的异常处理 四、常用的内核内存函数 五、内核字符串种类 六、内核字符串常用函数、  七、课后作业

WDK说明文档中只包含了内核模块导出的函数,对于未导出的函数,则不能直接使用。

如果要使用未导出的函数,只要自己定义一个函数指针,并且为函数指针提供正确的函数地址就可以使用了。有两种办法都可以获取为导出的函数地址:

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

二、返回值

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

NTSTATUS PsCreateSystemThread();

NTSTATUS ZwOpenProcess();

NTSTATUS ZwOpenEvent();

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

  1. STATUS_SUCCESS 0x00000000  成功
  2. STATUS_INVALID_PARAMETER 0xC000000D 参数无效
  3. STATUS_BUFFER_OVERFLOW 0X80000005 缓冲区长度不够

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

三、内核中的异常处理

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

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

出现异常时,可根据filter_value的值来决定程序该如何执行,当filter_value的值为:

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

四、常用的内核内存函数

内核编程基础
一、未导出函数的使用
二、返回值
三、内核中的异常处理
四、常用的内核内存函数
五、内核字符串种类
六、内核字符串常用函数、
 七、课后作业

五、内核字符串种类

CHAR(char)/WCHAR(wchar_t)/ANSI_STRING/UNICODE_STRING

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;

六、内核字符串常用函数、

字符串常用的功能无非就是:

创建、复制、比较以及转换等等

内核编程基础
一、未导出函数的使用
二、返回值
三、内核中的异常处理
四、常用的内核内存函数
五、内核字符串种类
六、内核字符串常用函数、
 七、课后作业

 七、课后作业

 申请一块内存,并在内存中存储GDT、IDT的所有数据。然后在DebugView中显示出来,最后释放内存

#include<ntddk.h>
#include<ntstatus.h>

VOID DriverUnload(PDRIVER_OBJECT driver)
{
DbgPrint("驱动停止运行. ");
}

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING reg_path)
{
UCHAR GDT[6];
UCHAR IDT[6];
ULONG GdtAddr, GdtLen, IdtAddr, IdtLen;
PUCHAR pBuffer = NULL;
ULONG i;
// 设置一个卸载函数,便于退出
pDriver->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;
pDriver->DriverUnload = DriverUnload;

//申请内存
//参数pagepool代表是系统可用内存
PUCHAR SMemory = (PUCHAR)ExAllocatePool(PagedPool,IdtLen+GdtLen);

if (SMemory == NULL)
{
DbgPrint("申请内存失败. ");
return STATUS_SUCCESS;
}
//将gdt与idt的数据赋值到自己申请的内存中
RtlMoveMemory(SMemory, (PUCHAR)GdtAddr, GdtLen);
RtlMoveMemory(SMemory + GdtLen, (PUCHAR)IdtAddr, IdtLen);
//打印表
DbgPrint("Print GDT Table: ");
for (ULONG i = 0; i < GdtLen; i+=16)
{
DbgPrint("%08x %08x`%08x %08x`%08x ", (ULONG)(SMemory + i), ((PULONG)(SMemory + i))[0], ((PULONG)(SMemory + i))[1], ((PULONG)(SMemory + i))[2], ((PULONG)(SMemory + i)[3]));
}
DbgPrint("Print IDT TABLE: ");
for (ULONG i = 0; i < IdtLen; i += 16)
{
DbgPrint("%08x %08x`%08x %08x`%08x ", (ULONG)(SMemory + GdtLen+ i), ((PULONG)(SMemory + GdtLen + i))[0], ((PULONG)(SMemory + GdtLen + i))[1], ((PULONG)(SMemory + GdtLen + i))[2], ((PULONG)(SMemory + GdtLen + i)[3]));
}
//释放内存
ExFreePool(SMemory);

return STATUS_SUCCESS;
}

运行结果截图

内核编程基础
一、未导出函数的使用
二、返回值
三、内核中的异常处理
四、常用的内核内存函数
五、内核字符串种类
六、内核字符串常用函数、
 七、课后作业

 内核编程基础
一、未导出函数的使用
二、返回值
三、内核中的异常处理
四、常用的内核内存函数
五、内核字符串种类
六、内核字符串常用函数、
 七、课后作业