驱动基础2

02驱动基础

基础类型

基础数据类型

在驱动中用基础数据类型需要用大写

R3 R0
int INT
short SHORT
char CHAR
long LONG
unsighted char UCHAR

指针是在前面加P

NTSTATUS

NTSTATUS 本质上是LONG

  • NTSTATUS >= 0 成功
  • NTSTATUS <0 失败

判断NTSTATUS是否成功使用NT_SUCESS()

本质就是判断NTSTATUS是否>=0

字符串

内核中使用Unicode字符串频率较高,Ansi字符串主要是接收外部传进来的数据转换的时候作为中间件使用

UNICODE 初始化

UNICODE_STRING uStr = { 0 };
RtlInitUnicodeString(&uStr, L"A");//L是宽字符的意思

RtlInitUnicodeString不会拷贝内存,是直接把指定字符串的指针放到前面UNICODE_STRING定义的变量里面,也就是浅拷贝

这里可以论证一下上面那句话

#include <ntifs.h>
VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	UNICODE_STRING uStr = { 0 };
	PWCH xx = L"ABCD";
	RtlInitUnicodeString(&uStr,xx);
	DbgPrintEx(77, 0, "%x-%x\r\n", xx, uStr.Buffer);//会发现这两个地址是一致的
	return STATUS_SUCCESS;
}

DbgPrintEx函数前两个参数默认填770,代表最高权限

通过DbgPrintEx函数将我们定义的字符串xxuStr.Buffer的地址打印出来,可以看到是一致的

ANSI 初始化

STRING XX = {0};
CHAR* X = "ASSIC";
RtlInitString(&XX, X);

ANSI转UNICODE

UNICODE_STRING uStr = { 0 };
	STRING aStr = {0};
	CHAR* X = "ASSIC";
	RtlInitString(&aStr, X);
	NTSTATUS status = RtlAnsiStringToUnicodeString(&uStr, &aStr,TRUE);//ANSI转UNICODE,申请新内存
	DbgPrint("%x\r\n", status);
	if (NT_SUCCESS(status))
	{
		DbgPrint("%x-%x\r\n",uStr.Buffer,aStr.Buffer);
        RtlFreeUnicodeString(&uStr);//因为前一步申请了uStr的内存,所以这里需要释放
	}

RtlAnsiStringToUnicodeString函数的最后一个参数是_In_ BOOLEAN AllocateDestinationString指的是是否为转换后的UnicodeString重现申请内存空间,正常情况下填TRUE为了避免宽字符和窄字符之间转换的时候占用的内存空间大小不同的问题。

因此我们需要在使用完uStr后使用RtlFreeUnicodeStringuStr申请的内存释放掉

UNICODE转ANSI

UNICODE转ANSI同理使用RtlUnicodeStringToAnsiString()函数完成,然后使用RtlFreeAnsiString()函数完成内存释放

字符串比较

//返回值是LONG,如果数字不为0,则是不相等,如果是0则相等
RtlCompareString();//ANSI
RtlCompareUnicodeString();//UNICODE
//返回值是BOOLEAN,TRUE是相等,FALSE是不相等
RtlEqualString();//ANSI
RtlEqualUnicodeString();//UNICODE

第三个参数_In_ BOOLEAN CaseInSensitive如果是TRUE忽略大小写,FALSE不忽略大小写

前两个参数就是需要比较的字符串

例子

UNICODE_STRING uStr1 = { 0 };
UNICODE_STRING uStr2 = { 0 };
RtlInitUnicodeString(&uStr1, L"A1aaa");
RtlInitUnicodeString(&uStr2, L"aaa");
LONG status = RtlCompareUnicodeString(&uStr1, &uStr2, TRUE);
DbgPrint("%x\r\n", status);
BOOLEAN status2 = RtlEqualUnicodeString(&uStr1, &uStr2, TRUE);
if (status2 == TRUE)
{
	DbgPrint("EQUAL\r\n");
}
else
{
	DbgPrint("not eQUAL\r\n");
}

内存申请/释放

内存申请

ExAllocatePool 分配指定类型的池内存,并返回指向已分配块的指针

函数原型

PVOID
ExAllocatePool (
    __drv_strictTypeMatch(__drv_typeExpr) _In_ POOL_TYPE PoolType,//申请的内存的类型
    _In_ SIZE_T NumberOfBytes//申请的内存大小
    );

POOL_TYPE:重要的有两个

  • NonPagedPool 非分页类型 可以执行
  • PagedPool 分页类型 不可以执行

可执行

PVOID mem = ExAllocatePool(NonPagedPool, 0x200);//可执行
DbgPrint("%x", mem);

不可执行

PVOID mem = ExAllocatePool(PagedPool, 0x200);//不可执行
DbgPrint("%x", mem);

这个可执行与不可执行在32位下没有用!

内存释放

ExFreePool 例程解除分配池内存块。

ExFreePool本质上是个宏

#define ExFreePool(a) ExFreePoolWithTag (a,0)

参数只有一个就是申请内存时得到的PVOID类型的内存指针

ExFreePool(mem);

线程

创建线程

PsCreateSystemThread 例程创建在内核模式下执行的系统线程,并返回线程的句柄

函数原型

NTSTATUS PsCreateSystemThread(
  [out]           PHANDLE            ThreadHandle,//返回的线程句柄
  [in]            ULONG              DesiredAccess,//线程权限
  [in, optional]  POBJECT_ATTRIBUTES ObjectAttributes,//NULL
  [in, optional]  HANDLE             ProcessHandle,//给那个句柄创建先线程,不写就是给Sysytem创建
  [out, optional] PCLIENT_ID         ClientId,//NULL
  [in]            PKSTART_ROUTINE    StartRoutine,//一个函数指针,新创建的系统线程的入口点
  [in, optional]  PVOID              StartContext//开始执行时传递给线程的单个参数
);

demo

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
VOID
vStart(
	_In_ PVOID StartContext
)
{	
    //这里可以写创建新线程后,新线程要做的事
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	//DbgBreakPoint();
	HANDLE hThread = NULL;
	NTSTATUS status = PsCreateSystemThread(&hThread, THREAD_ALL_ACCESS, NULL, NULL, NULL, vStart, NULL);//创建线程
	if (NT_SUCCESS(status))
	{
		ZwClose(hThread);//释放线程句柄
		DbgPrint("创建成功\r\n");
	}
	return STATUS_SUCCESS;
}

链表

驱动中最常见的是双向链表

kd> dt _EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x098 ProcessLock      : _EX_PUSH_LOCK
   +0x0a0 CreateTime       : _LARGE_INTEGER
   +0x0a8 ExitTime         : _LARGE_INTEGER
   +0x0b0 RundownProtect   : _EX_RUNDOWN_REF
   +0x0b4 UniqueProcessId  : Ptr32 Void
   +0x0b8 ActiveProcessLinks : _LIST_ENTRY
   +0x0c0 ProcessQuotaUsage : [2] Uint4B
   +0x0c8 ProcessQuotaPeak : [2] Uint4B
   +0x0d0 CommitCharge     : Uint4B
   +0x0d4 QuotaBlock       : Ptr32 _EPROCESS_QUOTA_BLOCK
   +0x0d8 CpuQuotaBlock    : Ptr32 _PS_CPU_QUOTA_BLOCK
   +0x0dc PeakVirtualSize  : Uint4B
   +0x0e0 VirtualSize      : Uint4B
   +0x0e4 SessionProcessLinks : _LIST_ENTRY
   +0x0ec DebugPort        : Ptr32 Void
   +0x0f0 ExceptionPortData : Ptr32 Void
   +0x0f0 ExceptionPortValue : Uint4B
   +0x0f0 ExceptionPortState : Pos 0, 3 Bits
   +0x0f4 ObjectTable      : Ptr32 _HANDLE_TABLE
   +0x0f8 Token            : _EX_FAST_REF
   +0x0fc WorkingSetPage   : Uint4B
   +0x100 AddressCreationLock : _EX_PUSH_LOCK
   +0x104 RotateInProgress : Ptr32 _ETHREAD
   +0x108 ForkInProgress   : Ptr32 _ETHREAD
   +0x10c HardwareTrigger  : Uint4B
   +0x110 PhysicalVadRoot  : Ptr32 _MM_AVL_TABLE
   +0x114 CloneRoot        : Ptr32 Void
   +0x118 NumberOfPrivatePages : Uint4B
   +0x11c NumberOfLockedPages : Uint4B
   +0x120 Win32Process     : Ptr32 Void
   +0x124 Job              : Ptr32 _EJOB
   +0x128 SectionObject    : Ptr32 Void
   +0x12c SectionBaseAddress : Ptr32 Void
   +0x130 Cookie           : Uint4B
   +0x134 Spare8           : Uint4B
   +0x138 WorkingSetWatch  : Ptr32 _PAGEFAULT_HISTORY
   +0x13c Win32WindowStation : Ptr32 Void
   +0x140 InheritedFromUniqueProcessId : Ptr32 Void
   +0x144 LdtInformation   : Ptr32 Void
   +0x148 VdmObjects       : Ptr32 Void
   +0x14c ConsoleHostProcess : Uint4B
   +0x150 DeviceMap        : Ptr32 Void
   +0x154 EtwDataSource    : Ptr32 Void
   +0x158 FreeTebHint      : Ptr32 Void
   +0x160 PageDirectoryPte : _HARDWARE_PTE_X86
   +0x160 Filler           : Uint8B
   +0x168 Session          : Ptr32 Void
   +0x16c ImageFileName    : [15] UChar
   +0x17b PriorityClass    : UChar
   +0x17c JobLinks         : _LIST_ENTRY
   +0x184 LockedPagesList  : Ptr32 Void
   +0x188 ThreadListHead   : _LIST_ENTRY
   +0x190 SecurityPort     : Ptr32 Void
   +0x194 PaeTop           : Ptr32 Void
   +0x198 ActiveThreads    : Uint4B
   +0x19c ImagePathHash    : Uint4B
   +0x1a0 DefaultHardErrorProcessing : Uint4B
   +0x1a4 LastThreadExitStatus : Int4B
   +0x1a8 Peb              : Ptr32 _PEB
   +0x1ac PrefetchTrace    : _EX_FAST_REF
   +0x1b0 ReadOperationCount : _LARGE_INTEGER
   +0x1b8 WriteOperationCount : _LARGE_INTEGER
   +0x1c0 OtherOperationCount : _LARGE_INTEGER
   +0x1c8 ReadTransferCount : _LARGE_INTEGER
   +0x1d0 WriteTransferCount : _LARGE_INTEGER
   +0x1d8 OtherTransferCount : _LARGE_INTEGER
   +0x1e0 CommitChargeLimit : Uint4B
   +0x1e4 CommitChargePeak : Uint4B
   +0x1e8 AweInfo          : Ptr32 Void
   +0x1ec SeAuditProcessCreationInfo : _SE_AUDIT_PROCESS_CREATION_INFO
   +0x1f0 Vm               : _MMSUPPORT
   +0x25c MmProcessLinks   : _LIST_ENTRY
   +0x264 HighestUserAddress : Ptr32 Void
   +0x268 ModifiedPageCount : Uint4B
   +0x26c Flags2           : Uint4B
   +0x26c JobNotReallyActive : Pos 0, 1 Bit
   +0x26c AccountingFolded : Pos 1, 1 Bit
   +0x26c NewProcessReported : Pos 2, 1 Bit
   +0x26c ExitProcessReported : Pos 3, 1 Bit
   +0x26c ReportCommitChanges : Pos 4, 1 Bit
   +0x26c LastReportMemory : Pos 5, 1 Bit
   +0x26c ReportPhysicalPageChanges : Pos 6, 1 Bit
   +0x26c HandleTableRundown : Pos 7, 1 Bit
   +0x26c NeedsHandleRundown : Pos 8, 1 Bit
   +0x26c RefTraceEnabled  : Pos 9, 1 Bit
   +0x26c NumaAware        : Pos 10, 1 Bit
   +0x26c ProtectedProcess : Pos 11, 1 Bit
   +0x26c DefaultPagePriority : Pos 12, 3 Bits
   +0x26c PrimaryTokenFrozen : Pos 15, 1 Bit
   +0x26c ProcessVerifierTarget : Pos 16, 1 Bit
   +0x26c StackRandomizationDisabled : Pos 17, 1 Bit
   +0x26c AffinityPermanent : Pos 18, 1 Bit
   +0x26c AffinityUpdateEnable : Pos 19, 1 Bit
   +0x26c PropagateNode    : Pos 20, 1 Bit
   +0x26c ExplicitAffinity : Pos 21, 1 Bit
   +0x26c Spare1           : Pos 22, 1 Bit
   +0x26c ForceRelocateImages : Pos 23, 1 Bit
   +0x26c DisallowStrippedImages : Pos 24, 1 Bit
   +0x26c LowVaAccessible  : Pos 25, 1 Bit
   +0x26c RestrictIndirectBranchPrediction : Pos 26, 1 Bit
   +0x26c AddressPolicyFrozen : Pos 27, 1 Bit
   +0x270 Flags            : Uint4B
   +0x270 CreateReported   : Pos 0, 1 Bit
   +0x270 NoDebugInherit   : Pos 1, 1 Bit
   +0x270 ProcessExiting   : Pos 2, 1 Bit
   +0x270 ProcessDelete    : Pos 3, 1 Bit
   +0x270 Wow64SplitPages  : Pos 4, 1 Bit
   +0x270 VmDeleted        : Pos 5, 1 Bit
   +0x270 OutswapEnabled   : Pos 6, 1 Bit
   +0x270 Outswapped       : Pos 7, 1 Bit
   +0x270 ForkFailed       : Pos 8, 1 Bit
   +0x270 Wow64VaSpace4Gb  : Pos 9, 1 Bit
   +0x270 AddressSpaceInitialized : Pos 10, 2 Bits
   +0x270 SetTimerResolution : Pos 12, 1 Bit
   +0x270 BreakOnTermination : Pos 13, 1 Bit
   +0x270 DeprioritizeViews : Pos 14, 1 Bit
   +0x270 WriteWatch       : Pos 15, 1 Bit
   +0x270 ProcessInSession : Pos 16, 1 Bit
   +0x270 OverrideAddressSpace : Pos 17, 1 Bit
   +0x270 HasAddressSpace  : Pos 18, 1 Bit
   +0x270 LaunchPrefetched : Pos 19, 1 Bit
   +0x270 InjectInpageErrors : Pos 20, 1 Bit
   +0x270 VmTopDown        : Pos 21, 1 Bit
   +0x270 ImageNotifyDone  : Pos 22, 1 Bit
   +0x270 PdeUpdateNeeded  : Pos 23, 1 Bit
   +0x270 VdmAllowed       : Pos 24, 1 Bit
   +0x270 CrossSessionCreate : Pos 25, 1 Bit
   +0x270 ProcessInserted  : Pos 26, 1 Bit
   +0x270 DefaultIoPriority : Pos 27, 3 Bits
   +0x270 ProcessSelfDelete : Pos 30, 1 Bit
   +0x270 SetTimerResolutionLink : Pos 31, 1 Bit
   +0x274 ExitStatus       : Int4B
   +0x278 VadRoot          : _MM_AVL_TABLE
   +0x298 AlpcContext      : _ALPC_PROCESS_CONTEXT
   +0x2a8 TimerResolutionLink : _LIST_ENTRY
   +0x2b0 RequestedTimerResolution : Uint4B
   +0x2b4 ActiveThreadsHighWatermark : Uint4B
   +0x2b8 SmallestTimerResolution : Uint4B
   +0x2bc TimerResolutionStackRecord : Ptr32 _PO_DIAG_STACK_RECORD
   +0x2c0 SequenceNumber   : Uint8B
   +0x2c8 CreateInterruptTime : Uint8B
   +0x2d0 CreateUnbiasedInterruptTime : Uint8B
   +0x2d8 SecurityDomain   : Uint8B

其中的

   +0x2a8 TimerResolutionLink : _LIST_ENTRY

_LIST_ENTRY就是双向链表

kd> dt _LIST_ENTRY
ntdll!_LIST_ENTRY
   +0x000 Flink            : Ptr32 _LIST_ENTRY//前驱
   +0x004 Blink            : Ptr32 _LIST_ENTRY//后驱

单向链表

kd> DT _KPROCESS
ntdll!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead  : _LIST_ENTRY
   +0x018 DirectoryTableBase : Uint4B
   +0x01c LdtDescriptor    : _KGDTENTRY
   +0x024 Int21Descriptor  : _KIDTENTRY
   +0x02c ThreadListHead   : _LIST_ENTRY
   +0x034 ProcessLock      : Uint4B
   +0x038 Affinity         : _KAFFINITY_EX
   +0x044 ReadyListHead    : _LIST_ENTRY
   +0x04c SwapListEntry    : _SINGLE_LIST_ENTRY
   +0x050 ActiveProcessors : _KAFFINITY_EX
   +0x05c AutoAlignment    : Pos 0, 1 Bit
   +0x05c DisableBoost     : Pos 1, 1 Bit
   +0x05c DisableQuantum   : Pos 2, 1 Bit
   +0x05c ActiveGroupsMask : Pos 3, 1 Bit
   +0x05c ReservedFlags    : Pos 4, 28 Bits
   +0x05c ProcessFlags     : Int4B
   +0x060 BasePriority     : Char
   +0x061 QuantumReset     : Char
   +0x062 Visited          : UChar
   +0x063 Unused3          : UChar
   +0x064 ThreadSeed       : [1] Uint4B
   +0x068 IdealNode        : [1] Uint2B
   +0x06a IdealGlobalNode  : Uint2B
   +0x06c Flags            : _KEXECUTE_OPTIONS
   +0x06d AddressPolicy    : UChar
   +0x06e IopmOffset       : Uint2B
   +0x070 Unused4          : Uint4B
   +0x074 StackCount       : _KSTACK_COUNT
   +0x078 ProcessListEntry : _LIST_ENTRY
   +0x080 CycleTime        : Uint8B
   +0x088 KernelTime       : Uint4B
   +0x08c UserTime         : Uint4B
   +0x090 VdmTrapcHandler  : Ptr32 Void

其中的

   +0x04c SwapListEntry    : _SINGLE_LIST_ENTRY

_SINGLE_LIST_ENTRY就是单向链表

kd> dt _SINGLE_LIST_ENTRY
ntdll!_SINGLE_LIST_ENTRY
   +0x000 Next             : Ptr32 _SINGLE_LIST_ENTRY

链表图示

链表相关函数

InitializeListHead();//链表初始化
IsListEmpty();//判断链表是否为空
InsertHeadList();//在链表头部插入
InsertTailList();//在链表尾部插入
RemoveHeadList();//移除链表头
RemoveTailList();//移除尾部节点
RemoveEntryList();//移除当前链表节点

Table(伸展树/泛型表)

Windows 驱动中使用伸展树(Splay Tree)或者平衡树来存储数据,加快数据的查询速度的方法总结。

伸展树

伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。它的优势在于不需要记录用于平衡树的冗余信息。

在伸展树上的一般操作都基于伸展操作。

假设想要对一个二叉查找树执行一系列的查找操作。为了使整个查找时间更小,被查频率高的那些条目就应当经常处于靠近树根的位置。于是想到设计一个简 单方法, 在每次查找之后对树进行重构,把被查找的条目搬移到离树根近一些的地方。splay tree应运而生。splay tree是一种自调整形式的二叉查找树,它会沿着从某个节点到树根之间的路径,通过一系列的旋转把这个节点搬移到树根去。

PS:MSDN上称他为泛型表

RtlInitializeGenericTable 初始化

RtlInitializeGenericTable 初始化泛型表。

函数原型

NTSYSAPI VOID RtlInitializeGenericTable(
  [out]          PRTL_GENERIC_TABLE            Table,//指向调用方分配的缓冲区的指针
  [in]           PRTL_GENERIC_COMPARE_ROUTINE  CompareRoutine,//比较回调例程的入口点(需提前声明)
  [in]           PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine,//分配回调例程的入口点(需提前声明)
  [in]           PRTL_GENERIC_FREE_ROUTINE     FreeRoutine,//解除分配回调例程的入口点(需提前声明)
  [in, optional] PVOID                         TableContext//NULL
);

接下来是声明

CompareRoutine

其中函数的FirstStructSecondStruct两个参数传进来的是用来比较的两个结构体(在添加的时候会需要比较)

RTL_GENERIC_COMPARE_RESULTS NTAPI GenicCmp(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ PVOID FirstStruct,//传进来比较的节点A
	_In_ PVOID SecondStruct//传进来比较的节点B
)
{
	PAAA a1 = (PAAA)FirstStruct;//定义结构体
	PAAA a2 = (PAAA)SecondStruct;//定义结构体
	if (a1->id == a2->id) return GenericEqual;//将id作为比较的参数,如果等于返回等于
	if (a1->id > a2->id) return GenericGreaterThan;//将id作为比较的参数,如果大于返回大于
	return GenericLessThan;//将id作为比较的参数,其他情况就是小于,所以返回小于
}

AllocateRoutine

固定写法

PVOID NTAPI GenicAlloc(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ CLONG ByteSize
)
{
	return ExAllocatePool(NonPagedPool, ByteSize);//申请内存
}

FreeRoutine

固定写法

VOID NTAPI GenicFree(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
)
{
	ExFreePool(Buffer);//释放内存
}

Demo

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}
/* 下面是三个函数的声明 */
RTL_GENERIC_COMPARE_RESULTS NTAPI GenicCmp(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ PVOID FirstStruct,//传进来比较的节点A
	_In_ PVOID SecondStruct//传进来比较的节点B
)
{
	PAAA a1 = (PAAA)FirstStruct;//定义结构体
	PAAA a2 = (PAAA)SecondStruct;//定义结构体
	if (a1->id == a2->id) return GenericEqual;//将id作为比较的参数,如果等于返回等于
	if (a1->id > a2->id) return GenericGreaterThan;//将id作为比较的参数,如果大于返回大于
	return GenericLessThan;//将id作为比较的参数,其他情况就是小于,所以返回小于
}

PVOID NTAPI GenicAlloc(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ CLONG ByteSize
)
{
	return ExAllocatePool(NonPagedPool, ByteSize);//申请内存
}

VOID NTAPI GenicFree(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
)
{
	ExFreePool(Buffer);//释放内存
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	RTL_GENERIC_TABLE table = {0};
	RtlInitializeGenericTable(&table,GenicCmp,GenicAlloc,GenicFree,NULL);//Table初始化
	return STATUS_SUCCESS;
}

RtlInsertElementGenericTable 添加

RtlInsertElementGenericTable 向泛型表添加新元素。

在插入的时候会调用对比函数(GenicCmp),如果这个元素被插入过,则不再插入。

函数原型

NTSYSAPI PVOID RtlInsertElementGenericTable(
  [in]            PRTL_GENERIC_TABLE Table,//table,指向调用方分配的缓冲区的指针
  [in]            PVOID              Buffer,//要插入的节点
  [in]            CLONG              BufferSize,//插入的节点的大小
  [out, optional] PBOOLEAN           NewElement//[返回值]如果有新元素插入表中则为TRUE,反之是FALSE
);

例如要插入下面这个节点

typedef struct _AAA
{
	//等待被插入泛型表中的元素
	int a;
	int b;
	int c;
}AAA,*AAA;

代码如下

AAA a = { 0 };//结构体初始化
BOOLEAN bNewE = FALSE;
RtlInsertElementGenericTable(&table,&a,sizeof(AAA),&bNewE);//插入节点

RtlLookupElementGenericTable 查找

RtlLookupElementGenericTable 搜索与指定数据匹配的元素的泛型表

第一个参数是泛型表,第二个参数是查找条件

这里的搜索也是基于上面定义的比较函数进行的

//查找id=1的元素
AAA searchT = { 1,0,0 };//后面的两个参数无所谓,因为比较函数是根据id进行查找的
AAA * aRet = RtlLookupElementGenericTable(&table,&searchT);//查找,返回找到的元素

用windbag可以看到已经找到了满足条件(id=1)的节点

RtlDeleteElementGenericTable 删除指定节点

RtlDeleteElementGenericTable 从泛型表中删除指定元素。

第一个参数是泛型表,第二个参数是删除的条件

AAA searchT = { 1,0,0 };//后面的两个参数无所谓,因为比较函数是根据id进行查找的
RtlDeleteElementGenericTable(&table, &searchT);//删除指定节点

删除后再通过Windbg进行查找可以看到已经没有了

RtlEnumerateGenericTableWithoutSplaying 枚举

RtlEnumerateGenericTableWithoutSplaying 用于枚举泛型表中的元素。

函数原型

NTSYSAPI PVOID RtlEnumerateGenericTableWithoutSplaying(
  [in]      PRTL_GENERIC_TABLE Table,//Table地址
  [in, out] PVOID              *RestartKey//[输入输出]上一次调用时返回的元素的地址
);

[in, out] RestartKey

上一次调用 RtlEnumerateGenericTableWithoutSplaying 返回的元素的地址。 如果枚举要从表中的第一个元素开始,则应设置为 NULL

MSDN的解释比较绕口,其实这个函数的作用有点类似于遍历节点时候的获取当前节点的下一个节点

也是就说如果要让他从头开始,就要填入NULL

示例

AAA *RestartKey = NULL;
AAA* ptr = 0;
for (ptr = RtlEnumerateGenericTableWithoutSplaying(&table, &RestartKey);
	ptr != NULL;
	ptr = RtlEnumerateGenericTableWithoutSplaying(&table, &RestartKey)) 
{
	DbgPrint("%x\r\n", ptr->id);//打印出当前遍历到的节点的id
	DbgPrint("%x\r\n", ptr);
}

泛型表完整demo

#include <ntifs.h>

VOID Unload(PDRIVER_OBJECT pDriver)
{
	DbgPrint("unload\r\n");
}

typedef struct _AAA
{
	//等待被插入泛型表中的元素
	int id;
	int x;
	int y;
}AAA,*PAAA;

RTL_GENERIC_COMPARE_RESULTS NTAPI GenicCmp(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ PVOID FirstStruct,//传进来比较的节点
	_In_ PVOID SecondStruct//传进来比较的节点
)
{
	PAAA a1 = (PAAA)FirstStruct;
	PAAA a2 = (PAAA)SecondStruct;
	//将id作为比较的参数
	if (a1->id == a2->id) return GenericEqual;
	if (a1->id > a2->id) return GenericGreaterThan;
	return GenericLessThan;
}

PVOID NTAPI GenicAlloc(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ CLONG ByteSize
)
{
	return ExAllocatePool(NonPagedPool, ByteSize);
}

VOID NTAPI GenicFree(
	_In_ struct _RTL_GENERIC_TABLE* Table,
	_In_ __drv_freesMem(Mem) _Post_invalid_ PVOID Buffer
)
{
	ExFreePool(Buffer);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
	pDriver->DriverUnload = Unload;
	RTL_GENERIC_TABLE table = {0};
	RtlInitializeGenericTable(&table,GenicCmp,GenicAlloc,GenicFree,NULL);//Table初始化
	AAA a1 = {1,2,3};
	AAA a2 = { 2,4,5 };
	AAA a3 = { 3,6,7 };
	BOOLEAN bNewE = FALSE;
	RtlInsertElementGenericTable(&table,&a1,sizeof(AAA),&bNewE);//插入
	RtlInsertElementGenericTable(&table, &a2, sizeof(AAA), &bNewE);//插入
	RtlInsertElementGenericTable(&table, &a3, sizeof(AAA), &bNewE);//插入
	//遍历
	AAA *RestartKey = NULL;
	AAA* ptr = 0;
	for (ptr = RtlEnumerateGenericTableWithoutSplaying(&table, &RestartKey);
		ptr != NULL;
		ptr = RtlEnumerateGenericTableWithoutSplaying(&table, &RestartKey)) 
	{
		DbgPrint("%x\r\n", ptr->id);//打印出指定节点的id
		DbgPrint("%x\r\n", ptr);//打印出指定节点的地址
	}
	DbgBreakPoint();
	//查找id=1的元素
	AAA searchT = { 1,0,0 };//后面的两个参数无所谓,因为比较函数是根据id进行查找的
	AAA * aRet = RtlLookupElementGenericTable(&table,&searchT);//查找,返回找到的元素
	RtlDeleteElementGenericTable(&table, &searchT);//删除指定节点
	//在删除了id为3的节点后再次进行查找
	DbgBreakPoint();
	aRet = RtlLookupElementGenericTable(&table, &searchT);//查找,返回找到的元素
	return STATUS_SUCCESS;
}

删除前

节点数是3

删除后

节点数是2

posted @ 2024-05-18 17:16  MuRKuo  阅读(50)  评论(0编辑  收藏  举报