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
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/15779751.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能. QQ群: