64位内核开发第十三讲,内核下C++编程

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
调用方式同上

结果:

使用虚析构结果就是正确的了 会发现子类会被析构了.而不会直接析构父类了.

三丶全局new delete 重构

3.1 代码

father中的重载代码进行删除 使用全局的new delete一样可以.

void *__cdecl operator new(size_t Size, POOL_TYPE PoolType)
{
    PAGED_CODE();
    Size = (Size != 0) ? Size : 1;
    void *pObject = ExAllocatePoolWithTag(PoolType, Size, POOLTAG);
#if DBG
    if (pObject != NULL)
    {
        RtlFillMemory(pObject, Size, 0xCC);
    }
#endif // DBG
    return pObject;
}
void *__cdecl operator new[](size_t Size, POOL_TYPE PoolType)
{
    PAGED_CODE();
    Size = (Size != 0) ? Size : 1;
    void *pObject = ExAllocatePoolWithTag(PoolType, Size, POOLTAG);
#if DBG
    if (pObject != NULL)
    {
        RtlFillMemory(pObject, Size, 0xCC);
    }
#endif // DBG
    return pObject;
}
void __cdecl operator delete(void *pObject)
{
    PAGED_CODE();
    if (pObject != NULL)
    {
        ExFreePoolWithTag(pObject, POOLTAG);
    }
}
void __cdecl operator delete[](void *pObject)
{
    PAGED_CODE();
    if (pObject != NULL)
    {
        ExFreePoolWithTag(pObject, POOLTAG);
    }
}

extern "C" NTSTATUS DriverEntry(
    PDRIVER_OBJECT pDriverObj,
    PUNICODE_STRING pReg)
{
    UNREFERENCED_PARAMETER(pReg);
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    pDriverObj->DriverUnload = DriverUnLoad;

    PCHAR pszBuffer = NULL;
    PWCHAR pwzBuffer = NULL;
    father *pFather = NULL;
    pszBuffer = new (NonPagedPool) CHAR[100];
    pwzBuffer = new (NonPagedPool) WCHAR[100];
    pFather = new (NonPagedPool) father;
    // check ...
    delete pFather;
    delete[] pszBuffer;
    delete[] pwzBuffer;
    return STATUS_SUCCESS;
}

四丶建议

  • 可使用 DBG宏判断是否是DBG版本下编译,如果是可以初始化内存为0XCC
posted @ 2022-01-08 21:52  iBinary  阅读(1259)  评论(0编辑  收藏  举报