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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM