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