学习windows驱动(缓冲区溢出)

在WDF里,IO请求对象使用了WDFMEMORY对象来表示输入/输出缓冲区。
WDFMEMORY内部维护了内存指针、内存区长度、有效长度(字符串长度)。
维护内存区生命期,框架负责申请释放自己的内存,驱动自己创建内存,由驱动自己负责释放。
可以使用偏移量offset来实现内存区任意部分的使用。实际可用的内存区长度是总长度减去偏移值。
学习windows驱动(缓冲区溢出)_第1张图片

内存对象怎么使用?

要使用内存对象,首先要创建内存对象。
可以由框架申请内存空间。

_Must_inspect_result_
_When_(PoolType == 1 || PoolType == 257, _IRQL_requires_max_(APC_LEVEL))
_When_(PoolType == 0 || PoolType == 256, _IRQL_requires_max_(DISPATCH_LEVEL))
NTSTATUS
FORCEINLINE
WdfMemoryCreate(
    _In_opt_
    PWDF_OBJECT_ATTRIBUTES Attributes,
    _In_
    _Strict_type_match_
    POOL_TYPE PoolType,
    _In_opt_
    ULONG PoolTag,
    _In_
    _When_(BufferSize == 0, __drv_reportError(BufferSize cannot be zero))
    size_t BufferSize,
    _Out_
    WDFMEMORY* Memory,
    _Outptr_opt_result_bytebuffer_(BufferSize)
    PVOID* Buffer
    )
{
    return ((PFN_WDFMEMORYCREATE) WdfFunctions[WdfMemoryCreateTableIndex])(WdfDriverGlobals, Attributes, PoolType, PoolTag, BufferSize, Memory, Buffer);
}

函数WdfMemoryCreate 创建一个框架内存对象(framework memory object ),分配特定大小的memory buffer 。
参数PoolType、PoolTag、BufferSize用作内部申请内存参数传人,参数Buffer返回内存区指针。

休息一下,来点杂谈:
当我们为了解决一个问题而搜肠刮肚,冥思苦想,这都是在不断深化曾经学过的知识不断提炼和加工,从而得到有意义的组合,可能才解决一个有挑战的问题。
我们在简单的工作中学习运用知识是不够深入的,经过独立专业的项目研发后的检验后才是真正自己的东西。
可是对于曾经学习的知识深度思考应用的过程并不是轻松的,更何况自己不是特别天资聪明过人,因此,需要我们改变对于知识的认识的认识层次,改变知识的排列顺序。这种与平常不同的做法往往让我们不习惯,不舒服,时间久了就是痛苦。但是不经过重排和整理,进行深度加工的知识是没有价值的。
痛苦意味着知识领悟上的提高。我们学过很多知识,表面上看知道了很多,但是在工作中发现,我们理解不到位或者不对,我们曾经有自己的理解方法,但在具体工作中,那些理解很浅。在挑战性的工作面前,我们只有不断改变对既有知识的看法,从不同角度进行理解和认识,才可能有更好的理解和领悟。大多数时候,知识只是存放在大脑里,真正理解后,才感觉原来如此。这种原来如此非常重要,因为这种领悟让我们的知识活起来,为自己的工作或者生活服务。
这种收货时终生的意义。但是强迫自己离开已有的理解位置,换一个位置思考和理解,有时甚至怀疑,在心里感受上是不适应,不适应时间长了就是痛苦。但这种痛苦是必须的。
痛苦意味着黑夜中摸索之后光明的到来。
做研发没有一条康庄大道,也没有完全可以复制的成功之路,每个研发成功者都曾在自己成长的道路上遇到无数黑暗,在黑暗中不断摸索,穷尽各种智慧,最终等到光明的到来。这种黑暗中摸索的状态是人所不适应的,因为人喜欢舒适的光明。没有方向感,没有道路,每一步都有不确定性,这种面对种种不确定的心里状态毫无疑问是难受的。痛苦意味着自我承认的开始。在学生阶段,我们习惯于被老师和家长认可,这种认可长时间成为每个学生的心里支柱,但是在研发的道路上,尤其是有挑战性的研发面前,所有人都是平等的。在研发的工作中,每一个都必须独立自主,挑战自我能力极限,最终承认自我。没有自我承认,就没有信心,没有信心,就没有思路和突破。从被认可到自我认可不是个自然过程,是一个艰难的去除拐杖的过程,少了心里支撑,人是很难受的,很失落的,甚至是恐惧的。
经过研发工作,不断深化对知识理解,深化知识运用能力,学会全力以赴,学会在黑暗中摸索,最终会独立,尤其是精神独立。
从事研发工作,就要做好吃苦的准备,有了这些心理准备,也许就能少一点抱怨,加快一点速度,更快一点成长,最终得到自己想要的一切。
总而言之,痛苦是因为现实跟自己想要的有差距,可能现在自己的能力不能实现或满足自己想要的结果。
要解决这种烦恼,就必须提高自己的能力,
现在是一年级的水平,想上初中,谈何容易,那得必须有其它几个年级的水平。
道理偶尔提提神可以,但路还是要沉下心来一步一个脚印的走的。
人是不能闲的,闲下来就胡思乱想,所谓空虚寂寞,就是因为闲的慌了。

言归正传

参数应该遵循内存申请的基本原则,如果要在高中断级上使用,则让PoolType值是NonPagePool;参数Memory是新创建对象句柄。使用WdfMemoryCreate创建的对象,内存缓冲区在WdfObjectDelet被执行时和对象一起销毁。

也可以从旁视列表里申请内存,使用函数WdfMemoryCreateFromLookaside。

_Must_inspect_result_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FORCEINLINE
WdfMemoryCreateFromLookaside(
    _In_
    WDFLOOKASIDE Lookaside,
    _Out_
    WDFMEMORY* Memory
    )
{
    return ((PFN_WDFMEMORYCREATEFROMLOOKASIDE) WdfFunctions[WdfMemoryCreateFromLookasideTableIndex])(WdfDriverGlobals, Lookaside, Memory);
}

参数Lookaside是框架旁视列表对象句柄,通过WdfLookasideListCreate函数创建。旁视列表是一块预先分配好的内存区域,从旁视列表里申请内存,就像钱包里的钱一样;如果旁视列表里内存用光,还是去内存池中申请,就好比钱包里的钱花光了,想要花钱,就要去银行取一样。使用旁视列表的好处就是快速。

创建内存对象的另一种方法是先使用函数WdfMemoryCreatePreallocated申请好内存,在交给框架封装。

_Must_inspect_result_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FORCEINLINE
WdfMemoryCreatePreallocated(
    _In_opt_
    PWDF_OBJECT_ATTRIBUTES Attributes,
    _In_ __drv_aliasesMem
    PVOID Buffer,
    _In_
    _When_(BufferSize == 0, __drv_reportError(BufferSize cannot be zero))
    size_t BufferSize,
    _Out_
    WDFMEMORY* Memory
    )
{
    return ((PFN_WDFMEMORYCREATEPREALLOCATED) WdfFunctions[WdfMemoryCreatePreallocatedTableIndex])(WdfDriverGlobals, Attributes, Buffer, BufferSize, Memory);
}

参数Buffer指向预先申请好的一块内存,参数BufferSize是这块内存区的字节长度。

使用WdfMemoryCreatePreallocated创建的内存对象,需要驱动调用WdfObjectDelete将内存对象删除,紧删除对象本身,内存区并不被释放依旧有效。可以使用函数WdfMemoryAssignBuffer随时替换对象内存缓冲区。

_Must_inspect_result_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FORCEINLINE
WdfMemoryAssignBuffer(
    _In_
    WDFMEMORY Memory,
    _Pre_notnull_ _Pre_writable_byte_size_(BufferSize)
    PVOID Buffer,
    _In_
    _When_(BufferSize == 0, __drv_reportError(BufferSize cannot be zero))
    size_t BufferSize
    )
{
    return ((PFN_WDFMEMORYASSIGNBUFFER) WdfFunctions[WdfMemoryAssignBufferTableIndex])(WdfDriverGlobals, Memory, Buffer, BufferSize);
}

参数Buffer和BufferSize 代表了新的内存缓冲区。执行此函数,原来的内存区将被替换掉(但没有被释放,依旧有效、可用)。

内存对象与内存缓冲区是一一对应的,使用函数WdfMemoryGetBuffer获取内存对象的内存区指针。

_IRQL_requires_max_(DISPATCH_LEVEL)
PVOID
FORCEINLINE
WdfMemoryGetBuffer(
    _In_
    WDFMEMORY Memory,
    _Out_opt_
    size_t* BufferSize
    )
{
    return ((PFN_WDFMEMORYGETBUFFER) WdfFunctions[WdfMemoryGetBufferTableIndex])(WdfDriverGlobals, Memory, BufferSize);
}

可选参数BufferSize返回缓冲区长度,缓冲区指针通过返回值返回给调用者。

上面是创建内存对象的方法,下面来说使用内存对象的函数,分读出(WdfMemoryCopyToBuffer)和写入(WdfMemoryCopyFromBuffer)两个:

读出

_Must_inspect_result_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FORCEINLINE
WdfMemoryCopyToBuffer(
    _In_
    WDFMEMORY SourceMemory,
    _In_
    size_t SourceOffset,
    _Out_writes_bytes_( NumBytesToCopyTo )
    PVOID Buffer,
    _In_
    _When_(NumBytesToCopyTo == 0, __drv_reportError(NumBytesToCopyTo cannot be zero))
    size_t NumBytesToCopyTo
    )
{
    return ((PFN_WDFMEMORYCOPYTOBUFFER) WdfFunctions[WdfMemoryCopyToBufferTableIndex])(WdfDriverGlobals, SourceMemory, SourceOffset, Buffer, NumBytesToCopyTo);
}

读出函数用于将内存对象里的内容写入指定外部内存区。参数SourceMemory和SourceOffset表示内存对象好内部偏移;参数Buffer表示外部缓冲区;参数NumBytesToCopyTo 表示将多少内容拷贝到外部缓冲区,它的值不能大于外部缓冲区的的大小,不然会溢出。

写入

_Must_inspect_result_
_IRQL_requires_max_(DISPATCH_LEVEL)
NTSTATUS
FORCEINLINE
WdfMemoryCopyFromBuffer(
    _In_
    WDFMEMORY DestinationMemory,
    _In_
    size_t DestinationOffset,
    _In_
    PVOID Buffer,
    _In_
    _When_(NumBytesToCopyFrom == 0, __drv_reportError(NumBytesToCopyFrom cannot be zero))
    size_t NumBytesToCopyFrom
    )
{
    return ((PFN_WDFMEMORYCOPYFROMBUFFER) WdfFunctions[WdfMemoryCopyFromBufferTableIndex])(WdfDriverGlobals, DestinationMemory, DestinationOffset, Buffer, NumBytesToCopyFrom);
}

写入函数用来将指定内存区的内容写入内存对象里。参数DestinationMemory和DestinationOffset代表内存对象和内部偏移;参数Buffer代表了外部缓冲区;参数NumBytesToCopyFrom表示将多少外部缓冲区内容写入内存对象里。

你可能感兴趣的:(windows驱动开发)