Windows内核驱动 使用 C++ 代码编程
一 丶 C++在Windows内核中的使用
1.1 简介
在驱动内核中是可以使用C++来进行编程的.只不过需要你重载一下new delete等函数
你可以看使用类 使用继承等. 但是如果是内核API的时候注意需要对其进行 C函数导出.
否则就会报解析不到名字 最好使用状态就是 c with class 使用基本的类来管理自己的函数.
比直接使用C强
1.2 头文件的引用
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include <ntimage.h>
#ifdef __cplusplus
}
#endif
在引入头文件的时候使用条件宏包含以下 其中 __cplusplus 的意思是 是否使用 C++的编译方式编译.如果是后缀名为.cpp则此宏就会启作用. 那么就会加入一个 extern "C" 修饰,这样声明的时候就是使用C的方式编译了.也不会遇到解析名称错误了.
对于DriverEntry入口我们也需要进行C修饰
如一个.cpp文件中的内容如下:
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObj,
PUNICODE_STRING pReg)
{
UNREFERENCED_PARAMETER(pDriverObj);
UNREFERENCED_PARAMETER(pReg);
}
二丶使用C++ 类
2.1 介绍内核中的内存申请函数
如果想在类中使用new和delete 那么我们需要重载一下new和delete 变成内核方式的内存申请. 当然如果你想写一个 string函数 那么你也可以重载 + [] ....等运算符
在内核中申请内存可以使用以下函数
ExAllocatePool ----过时
ExAllocatePoolWithTag ----在windows2004上过时
ExAllocatePool2
ExAllocatePool3 -----2 3 都是在2004
对应释放函数分别就是
ExFreePoolxxxxxx
申请的时候需要指定类型 类型如下:
类型 | 说明以及使用 | 是否常用 |
---|---|---|
NonPagedPool | 非分页内存,申请的内存是读写执行 可以执行ShellCode | 常用 |
PagedPool | 分页内存,分页内存可能会被换出,访问的时候要注意检查 | 一般 |
NonPagedPoolMustSucceed | 指定分贝非分页内存,必须成功. | 不使用 |
DontUseThisType | 未指定 | 不使用 |
NonPagePoolCacheAligned | 分配非分页内存,而且必须内存对齐 | 不使用 |
PagedPoolCacheAligned | 分页内存,必须内存对齐 | 不使用 |
NonPagedPoolCacheAlignedMustS | 非分页内存必须对齐必须成功 | 不使用 |
new的重载就是用ExAllocatePool xxx
2.2 重载类中的 new delete函数
看一下简单的代码吧.
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#include <ntifs.h>
#include <ntddk.h>
#include <Ntstrsafe.h>
#include <ntimage.h>
#ifdef __cplusplus
}
#endif
class father
{
public:
father();
~father();
public:
void *operator new(size_t size, POOL_TYPE poolType = NonPagedPool);
void operator delete(void *pointer);
public:
virtual void testprint();
private:
char szBuffer[1024];
};
cpp实现
#include "test.h"
father::father()
{
DbgPrint("startfather\n");
}
father::~father()
{
DbgPrint("endfather\n");
}
void father::testprint()
{
DbgPrint("father\n");
}
void *father::operator new(size_t size, POOL_TYPE poolType)
{
return ExAllocatePoolWithTag(poolType, size, 'abcd');
}
void father::operator delete(void *pointer)
{
ExFreePoolWithTag(pointer, 'abcd');
}
使用
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObj,
PUNICODE_STRING pReg)
{
UNREFERENCED_PARAMETER(pReg);
NTSTATUS status = STATUS_UNSUCCESSFUL;
pDriverObj->DriverUnload = DriverUnLoad;
father *pf = new father;
pf->testprint();
delete pf;
return STATUS_SUCCESS;
}
使用结果:
可以看到先进行构造 然后调用testprintf输出. 最后析构.
2.3 使用继承以及虚函数重写父类函数
如果定义一个类继承自父类调用testprint会怎么样?
代码如下:
class child : public father
{
public:
child();
~child();
public:
void *operator new(size_t size, POOL_TYPE poolType = NonPagedPool);
void operator delete(void *pointer);
public:
void testprint();
private:
char szBuffer[1024];
};
使用:
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT pDriverObj,
PUNICODE_STRING pReg)
{
UNREFERENCED_PARAMETER(pReg);
NTSTATUS status = STATUS_UNSUCCESSFUL;
pDriverObj->DriverUnload = DriverUnLoad;
father *pf = new child;
pf->testprint();
delete pf;
return STATUS_SUCCESS;
}
结果
少了一次析构 child没有进行析构 testprintf我们使用的是虚函数,重写了父类. 所以说虚函数是可以进行使用的.
2.4 使用虚析构
不重复粘贴代码了.做一下说明
1.父类的析构函数前边加了关键字 virtual
2.子类的析构前边也加了关键字 virtual
调用方式同上
结果:
使用虚析构结果就是正确的了 会发现子类会被析构了.而不会直接析构父类了.