windows内核驱动开发实用函数

//https://git.back.engineering/_xeroxz/kutils/src/branch/main/kutils.hpp
/*
 * HEADER ONLY KERNEL UTIL LIBRARY - NO IMPORTS - NO STRINGS
 */

#pragma once
#include <intrin.h>
#include <ntifs.h>

using u64 = unsigned long long;
using u32 = unsigned long;
using u16 = unsigned short;
using u8 = unsigned char;

namespace hashstr {
template <unsigned... pack>
struct idx_list_t {};
template <typename idx_list_t, unsigned right>
struct append_t;
template <unsigned... left, unsigned right>
struct append_t<idx_list_t<left...>, right> {
  typedef idx_list_t<left..., right> result_t;
};

template <unsigned N>
struct constr_idx_list {
  typedef typename append_t<typename constr_idx_list<N - 1>::result_t,
                            N - 1>::result_t result_t;
};

template <>
struct constr_idx_list<0> {
  typedef idx_list_t<> result_t;
};

template <typename T>
consteval T add(T first) {
  return first;
}

template <typename T, typename... Args>
consteval T add(T first, Args... args) {
  return first + add(args...);
}

template <typename T>
consteval T mul(T first) {
  return first;
}

template <typename T, typename... Args>
consteval T mul(T first, Args... args) {
  return first * mul(args...);
}

template <typename idx_list_t>
class hashstr_t;
template <int... idx>
struct hashstr_t<idx_list_t<idx...> > {
  consteval hashstr_t(const char (&str)[sizeof...(idx) + 1])
      : _hash((mul(hashchar(str[idx], idx)...) +
               add(hashchar(str[idx], idx)...)) ^
              0xCBF29CE484222325) {}

  unsigned _hash;
  consteval unsigned hashchar(const char c, unsigned idx) {
    return (c + 1) * 0x100000001B3 * (idx + 1 + c);
  }

  unsigned hash() { return _hash; }
};

// runtime equivelent of the compile time hash...
inline unsigned hash(const char* str) {
  unsigned add_res = 0u, mul_res = 1u;
  for (auto idx = 0u; str[idx]; ++idx) {
    add_res += (str[idx] + 1) * 0x100000001B3 * (idx + 1 + str[idx]);
    mul_res *= (str[idx] + 1) * 0x100000001B3 * (idx + 1 + str[idx]);
  }
  return (add_res + mul_res) ^ 0xCBF29CE484222325;
}
}  // namespace hashstr

#define HSTRING(str)                                                        \
  (hashstr::hashstr_t<hashstr::constr_idx_list<sizeof(str) - 1>::result_t>( \
       str)                                                                 \
       .hash())

#define DYN_MOD(x) KUtils::Driver::GetDriverBaseByHash(HSTRING(#x))

#define DYN_NT_SYM(x)                                   \
  ((decltype(&x))KUtils::Driver::GetDriverExportByHash( \
      KUtils::Driver::GetKernelBase(), HSTRING(#x)))

#define DYN_MOD_SYM(mod, exp)                                         \
  ((decltype(&y))KUtils::Driver::GetDriverExportByHash(HSTRING(#mod), \
                                                       HSTRING(#exp)))

#define DBG_PRINT(...)   \
  DYN_NT_SYM(DbgPrintEx) \
  (DPFLTR_SYSTEM_ID, DPFLTR_ERROR_LEVEL, "[KUTILS]" __VA_ARGS__);

// Export Directory
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
// Import Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
// Resource Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
// Exception Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
// Security Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
// Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
// Debug Directory
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
// Description String
#define IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7
// Machine Value (MIPS GP)
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
// TLS Directory
#define IMAGE_DIRECTORY_ENTRY_TLS 9
// Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10

#define DOS_HEADER_MAGIC 0x5A4D
#define PE_HEADER_MAGIC 0x4550

namespace KUtils {
typedef struct _IMAGE_DOS_HEADER {
  USHORT e_magic;
  USHORT e_cblp;
  USHORT e_cp;
  USHORT e_crlc;
  USHORT e_cparhdr;
  USHORT e_minalloc;
  USHORT e_maxalloc;
  USHORT e_ss;
  USHORT e_sp;
  USHORT e_csum;
  USHORT e_ip;
  USHORT e_cs;
  USHORT e_lfarlc;
  USHORT e_ovno;
  USHORT e_res[4];
  USHORT e_oemid;
  USHORT e_oeminfo;
  USHORT e_res2[10];
  LONG e_lfanew;
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_FILE_HEADER {
  short Machine;
  short NumberOfSections;
  unsigned TimeDateStamp;
  unsigned PointerToSymbolTable;
  unsigned NumberOfSymbols;
  short SizeOfOptionalHeader;
  short Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
  unsigned VirtualAddress;
  unsigned Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

typedef struct _IMAGE_SECTION_HEADER {
  unsigned char Name[8];
  union {
    unsigned PhysicalAddress;
    unsigned VirtualSize;
  } Misc;
  unsigned VirtualAddress;
  unsigned SizeOfRawData;
  unsigned PointerToRawData;
  unsigned PointerToRelocations;
  unsigned PointerToLinenumbers;
  unsigned short NumberOfRelocations;
  unsigned short NumberOfLinenumbers;
  unsigned Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

typedef struct _IMAGE_OPTIONAL_HEADER64 {
  short Magic;
  unsigned char MajorLinkerVersion;
  unsigned char MinorLinkerVersion;
  unsigned SizeOfCode;
  unsigned SizeOfInitializedData;
  unsigned SizeOfUninitializedData;
  unsigned AddressOfEntryPoint;
  unsigned BaseOfCode;
  ULONGLONG ImageBase;
  unsigned SectionAlignment;
  unsigned FileAlignment;
  short MajorOperatingSystemVersion;
  short MinorOperatingSystemVersion;
  short MajorImageVersion;
  short MinorImageVersion;
  short MajorSubsystemVersion;
  short MinorSubsystemVersion;
  unsigned Win32VersionValue;
  unsigned SizeOfImage;
  unsigned SizeOfHeaders;
  unsigned CheckSum;
  short Subsystem;
  short DllCharacteristics;
  ULONGLONG SizeOfStackReserve;
  ULONGLONG SizeOfStackCommit;
  ULONGLONG SizeOfHeapReserve;
  ULONGLONG SizeOfHeapCommit;
  unsigned LoaderFlags;
  unsigned NumberOfRvaAndSizes;
  IMAGE_DATA_DIRECTORY DataDirectory[16];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

typedef struct _IMAGE_NT_HEADERS64 {
  unsigned Signature;
  IMAGE_FILE_HEADER FileHeader;
  IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
  union {
    unsigned long Characteristics;  // 0 for terminating null import descriptor
    unsigned long
        OriginalFirstThunk;  // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
  } DUMMYUNIONNAME;
  unsigned long
      TimeDateStamp;  // 0 if not bound,
                      // -1 if bound, and real date\time stamp
                      //     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
                      // O.W. date/time stamp of DLL bound to (Old BIND)

  unsigned long ForwarderChain;  // -1 if no forwarders
  unsigned long Name;
  unsigned long
      FirstThunk;  // RVA to IAT (if bound this IAT has actual addresses)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED* PIMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME {
  unsigned long Hint;
  CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

typedef struct _IMAGE_THUNK_DATA64 {
  union {
    ULONGLONG ForwarderString;  // PBYTE
    ULONGLONG Function;         // PDWORD
    ULONGLONG Ordinal;
    ULONGLONG AddressOfData;  // PIMAGE_IMPORT_BY_NAME
  } u1;
} IMAGE_THUNK_DATA64, *PIMAGE_THUNK_DATA64;
typedef PIMAGE_THUNK_DATA64 PIMAGE_THUNK_DATA;

typedef struct _LDR_DATA_TABLE_ENTRY {
  LIST_ENTRY InLoadOrderLinks;
  LIST_ENTRY InMemoryOrderLinks;
  LIST_ENTRY InInitializationOrderLinks;
  PVOID DllBase;
  PVOID EntryPoint;
  ULONG SizeOfImage;
  UNICODE_STRING FullDllName;
  UNICODE_STRING BaseDllName;
  ULONG Flags;
  unsigned short LoadCount;
  unsigned short TlsIndex;
  union {
    LIST_ENTRY HashLinks;
    struct {
      PVOID SectionPointer;
      ULONG CheckSum;
    };
  };
  union {
    ULONG TimeDateStamp;
    PVOID LoadedImports;
  };
  PVOID EntryPointActivationContext;
  PVOID PatchInformation;
  LIST_ENTRY ForwarderLinks;
  LIST_ENTRY ServiceTagLinks;
  LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

typedef struct _PEB_LDR_DATA {
  ULONG Length;
  UCHAR Initialized;
  PVOID SsHandle;
  LIST_ENTRY InLoadOrderModuleList;
  LIST_ENTRY InMemoryOrderModuleList;
  LIST_ENTRY InInitializationOrderModuleList;
  PVOID EntryInProgress;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

typedef struct _PEB {
  BOOLEAN InheritedAddressSpace;
  BOOLEAN ReadImageFileExecOptions;
  BOOLEAN BeingDebugged;
  BOOLEAN Spare;
  HANDLE Mutant;
  PVOID ImageBaseAddress;
  PPEB_LDR_DATA LoaderData;
  PVOID ProcessParameters;
  PVOID SubSystemData;
  PVOID ProcessHeap;
  PVOID FastPebLock;
  PVOID FastPebLockRoutine;
  PVOID FastPebUnlockRoutine;
  ULONG EnvironmentUpdateCount;
  PVOID KernelCallbackTable;
  PVOID EventLogSection;
  PVOID EventLog;
  PVOID FreeList;
  ULONG TlsExpansionCounter;
  PVOID TlsBitmap;
  ULONG TlsBitmapBits[0x2];
  PVOID ReadOnlySharedMemoryBase;
  PVOID ReadOnlySharedMemoryHeap;
  PVOID* ReadOnlyStaticServerData;
  PVOID AnsiCodePageData;
  PVOID OemCodePageData;
  PVOID UnicodeCaseTableData;
  ULONG NumberOfProcessors;
  ULONG NtGlobalFlag;
  unsigned char Spare2[0x4];
  LARGE_INTEGER CriticalSectionTimeout;
  ULONG HeapSegmentReserve;
  ULONG HeapSegmentCommit;
  ULONG HeapDeCommitTotalFreeThreshold;
  ULONG HeapDeCommitFreeBlockThreshold;
  ULONG NumberOfHeaps;
  ULONG MaximumNumberOfHeaps;
  PVOID** ProcessHeaps;
  PVOID GdiSharedHandleTable;
  PVOID ProcessStarterHelper;
  PVOID GdiDCAttributeList;
  PVOID LoaderLock;
  ULONG OSMajorVersion;
  ULONG OSMinorVersion;
  ULONG OSBuildNumber;
  ULONG OSPlatformId;
  ULONG ImageSubSystem;
  ULONG ImageSubSystemMajorVersion;
  ULONG ImageSubSystemMinorVersion;
  ULONG GdiHandleBuffer[0x22];
  ULONG PostProcessInitRoutine;
  ULONG TlsExpansionBitmap;
  unsigned char TlsExpansionBitmapBits[0x80];
  ULONG SessionId;
} PEB, *PPEB;

typedef struct _SYSTEM_THREAD {
  LARGE_INTEGER KernelTime;
  LARGE_INTEGER UserTime;
  LARGE_INTEGER CreateTime;
  ULONG WaitTime;
  PVOID StartAddress;
  CLIENT_ID ClientId;
  KPRIORITY Priority;
  LONG BasePriority;
  ULONG ContextSwitchCount;
  ULONG State;
  KWAIT_REASON WaitReason;
} SYSTEM_THREAD, *PSYSTEM_THREAD;

typedef struct _SYSTEM_PROCESS_INFORMATION {
  ULONG NextEntryOffset;
  ULONG NumberOfThreads;
  LARGE_INTEGER Reserved[3];
  LARGE_INTEGER CreateTime;
  LARGE_INTEGER UserTime;
  LARGE_INTEGER KernelTime;
  UNICODE_STRING ImageName;
  KPRIORITY BasePriority;
  HANDLE ProcessId;
  HANDLE InheritedFromProcessId;
  ULONG HandleCount;
  ULONG Reserved2[2];
  ULONG PrivatePageCount;
  VM_COUNTERS VirtualMemoryCounters;
  IO_COUNTERS IoCounters;
  SYSTEM_THREAD Threads[0];

} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

typedef struct _RTL_PROCESS_MODULE_INFORMATION {
  HANDLE Section;
  PVOID MappedBase;
  PVOID ImageBase;
  ULONG ImageSize;
  ULONG Flags;
  USHORT LoadOrderIndex;
  USHORT InitOrderIndex;
  USHORT LoadCount;
  USHORT OffsetToFileName;
  UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;

typedef struct _RTL_PROCESS_MODULES {
  ULONG NumberOfModules;
  RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;

typedef struct _OBJECT_DIRECTORY_ENTRY {
  struct _OBJECT_DIRECTORY_ENTRY* ChainLink;
  PVOID Object;
  ULONG HashValue;
} OBJECT_DIRECTORY_ENTRY, *POBJECT_DIRECTORY_ENTRY;

typedef struct _OBJECT_DIRECTORY {
  POBJECT_DIRECTORY_ENTRY HashBuckets[37];
  EX_PUSH_LOCK Lock;
  struct _DEVICE_MAP* DeviceMap;
  ULONG SessionId;
  PVOID NamespaceEntry;
  ULONG Flags;
} OBJECT_DIRECTORY, *POBJECT_DIRECTORY;

typedef struct _DEVICE_MAP {
  POBJECT_DIRECTORY DosDevicesDirectory;
  POBJECT_DIRECTORY GlobalDosDevicesDirectory;
  ULONG ReferenceCount;
  ULONG DriveMap;
  UCHAR DriveType[32];
} DEVICE_MAP, *PDEVICE_MAP;

typedef struct _IMAGE_EXPORT_DIRECTORY {
  ULONG Characteristics;
  ULONG TimeDateStamp;
  USHORT MajorVersion;
  USHORT MinorVersion;
  ULONG Name;
  ULONG Base;
  ULONG NumberOfFunctions;
  ULONG NumberOfNames;
  ULONG AddressOfFunctions;
  ULONG AddressOfNames;
  ULONG AddressOfNameOrdinals;
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

#pragma pack(push, 1)
typedef struct _IDTR {
  USHORT Size;
  ULONG64 Address;
} IDTR, PIDTR;
#pragma pack(pop)

typedef union _KIDTENTRY64 {
  union {
    struct {
      /* 0x0000 */ unsigned short OffsetLow;
      /* 0x0002 */ unsigned short Selector;
      struct /* bitfield */
      {
        /* 0x0004 */ unsigned short IstIndex : 3;  /* bit position: 0 */
        /* 0x0004 */ unsigned short Reserved0 : 5; /* bit position: 3 */
        /* 0x0004 */ unsigned short Type : 5;      /* bit position: 8 */
        /* 0x0004 */ unsigned short Dpl : 2;       /* bit position: 13 */
        /* 0x0004 */ unsigned short Present : 1;   /* bit position: 15 */
      };                                           /* bitfield */
      /* 0x0006 */ unsigned short OffsetMiddle;
      /* 0x0008 */ unsigned long OffsetHigh;
      /* 0x000c */ unsigned long Reserved1;
    }; /* size: 0x0010 */
    /* 0x0000 */ unsigned __int64 Alignment;
  };                          /* size: 0x0010 */
} KIDTENTRY64, *PKIDTENTRY64; /* size: 0x0010 */

typedef union _IDT_ADDR_T {
  ULONG64 Addr;
  struct {
    ULONG64 OffsetLow : 16;
    ULONG64 OffsetMiddle : 16;
    ULONG64 OffsetHigh : 32;
  };
} IDT_ADDR_T, PIDT_ADDR_T;

typedef enum _SYSTEM_INFORMATION_CLASS {
  SystemBasicInformation,
  SystemProcessorInformation,
  SystemPerformanceInformation,
  SystemTimeOfDayInformation,
  SystemPathInformation,
  SystemProcessInformation,
  SystemCallCountInformation,
  SystemDeviceInformation,
  SystemProcessorPerformanceInformation,
  SystemFlagsInformation,
  SystemCallTimeInformation,
  SystemModuleInformation,
  SystemLocksInformation,
  SystemStackTraceInformation,
  SystemPagedPoolInformation,
  SystemNonPagedPoolInformation,
  SystemHandleInformation,
  SystemObjectInformation,
  SystemPageFileInformation,
  SystemVdmInstemulInformation,
  SystemVdmBopInformation,
  SystemFileCacheInformation,
  SystemPoolTagInformation,
  SystemInterruptInformation,
  SystemDpcBehaviorInformation,
  SystemFullMemoryInformation,
  SystemLoadGdiDriverInformation,
  SystemUnloadGdiDriverInformation,
  SystemTimeAdjustmentInformation,
  SystemSummaryMemoryInformation,
  SystemNextEventIdInformation,
  SystemEventIdsInformation,
  SystemCrashDumpInformation,
  SystemExceptionInformation,
  SystemCrashDumpStateInformation,
  SystemKernelDebuggerInformation,
  SystemContextSwitchInformation,
  SystemRegistryQuotaInformation,
  SystemExtendServiceTableInformation,
  SystemPrioritySeperation,
  SystemPlugPlayBusInformation,
  SystemDockInformation
} SYSTEM_INFORMATION_CLASS,
    *PSYSTEM_INFORMATION_CLASS;

extern "C" PVOID PsGetProcessSectionBaseAddress(__in PEPROCESS Process);
extern "C" PPEB PsGetProcessPeb(PEPROCESS Process);
extern "C" NTSTATUS ZwQuerySystemInformation(
    SYSTEM_INFORMATION_CLASS SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength);

namespace Driver {
/// <summary>
/// walk export directory of a module given its base and the hash of the export
/// string.
/// </summary>
/// <param name="lpDriverBase"></param>
/// <param name="nStrHash"></param>
/// <returns></returns>
FORCEINLINE PVOID GetDriverExportByHash(_In_ PVOID ModuleBase,
                                        _In_ ULONG nStrHash) {
  PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)ModuleBase;
  PIMAGE_NT_HEADERS64 lpNtHeader =
      (PIMAGE_NT_HEADERS64)(lpDosHeader->e_lfanew + (ULONG64)ModuleBase);

  PIMAGE_EXPORT_DIRECTORY lpExportDir =
      (PIMAGE_EXPORT_DIRECTORY)((ULONG64)ModuleBase +
                                lpNtHeader->OptionalHeader
                                    .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
                                    .VirtualAddress);

  ULONG32* lpNameArr =
      (ULONG32*)(lpExportDir->AddressOfNames + (ULONG64)ModuleBase);

  ULONG32* lpFuncs =
      (ULONG32*)(lpExportDir->AddressOfFunctions + (ULONG64)ModuleBase);

  USHORT* lpOrdinals =
      (USHORT*)(lpExportDir->AddressOfNameOrdinals + (ULONG64)ModuleBase);

  for (auto nIdx = 0u; nIdx < lpExportDir->NumberOfFunctions; ++nIdx) {
    if (!lpNameArr[nIdx] || !lpOrdinals[nIdx])
      continue;

    if (hashstr::hash((PCHAR)((ULONG64)ModuleBase + lpNameArr[nIdx])) ==
        nStrHash)
      return (PVOID)((ULONG64)ModuleBase + lpFuncs[lpOrdinals[nIdx]]);
  }
  return NULL;
}

/// <summary>
/// Gets ntoskrnl.exe module base address by reading the interrupt descriptor
/// table. It reads the first IDT entry which is just div 0 handler. it then
/// loops backwards page by page looking for valid DOS/NT headers and ensures
/// that the size of the image is legit...
/// </summary>
/// <returns>returns the kernel base address...</returns>
FORCEINLINE PVOID GetKernelBase() {
  IDTR stIdtr;
  __sidt(&stIdtr);

  PKIDTENTRY64 lpDivZero = (PKIDTENTRY64)stIdtr.Address;
  IDT_ADDR_T stHndlrAddr;

  stHndlrAddr.OffsetLow = lpDivZero->OffsetLow;
  stHndlrAddr.OffsetMiddle = lpDivZero->OffsetMiddle;
  stHndlrAddr.OffsetHigh = lpDivZero->OffsetHigh;

  // loop backwards until we find the kernels base address...
  ULONG64 nStartPage = (ULONG64)PAGE_ALIGN(stHndlrAddr.Addr);
  for (ULONG64 nPage = nStartPage;; nPage -= PAGE_SIZE) {
    if (*(USHORT*)nPage == DOS_HEADER_MAGIC) {
      PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)nPage;
      PIMAGE_NT_HEADERS64 lpNtHeaders =
          (PIMAGE_NT_HEADERS64)(lpDosHeader->e_lfanew + nPage);

      if (lpNtHeaders->Signature != PE_HEADER_MAGIC)
        continue;

      if (!GetDriverExportByHash((PVOID)nPage,
                                 HSTRING("ExAllocatePoolWithTag")))
        continue;

      return (PVOID)nPage;
    }
  }
}

/// <summary>
/// Gets driver base address by the hash of the file name of the driver...
/// </summary>
/// <param name="dwHash">hash of the drivers file name...</param>
/// <returns></returns>
FORCEINLINE PVOID GetDriverBaseByHash(_In_ CONST ULONG dwHash) {
  ULONG dwAllocSize{};
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemModuleInformation, NULL, dwAllocSize, &dwAllocSize);

  PRTL_PROCESS_MODULES moduleInfo = (PRTL_PROCESS_MODULES)DYN_NT_SYM(
      ExAllocatePool)(NonPagedPool, dwAllocSize);

  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemModuleInformation, moduleInfo, dwAllocSize, &dwAllocSize);

  for (INT nIdx = 0u; nIdx < moduleInfo->NumberOfModules; ++nIdx) {
    PCHAR pszModuleName = (PCHAR)moduleInfo->Modules[nIdx].FullPathName +
                          moduleInfo->Modules[nIdx].OffsetToFileName;

    if (hashstr::hash(pszModuleName) == dwHash) {
      PVOID lpModuleBase = moduleInfo->Modules[nIdx].ImageBase;
      DYN_NT_SYM(ExFreePool)(moduleInfo);
      return lpModuleBase;
    }
  }

  DYN_NT_SYM(ExFreePool)(moduleInfo);
  return NULL;
}

/// <summary>
/// Get driver base given the module name of the driver... This function calls
/// GetDriverBaseByHash internally...
/// </summary>
/// <param name="pszDriverName">file name of the driver, this is not case
/// dependant...</param> <returns>returns a void pointer to the drivers
/// base...</returns>
FORCEINLINE PVOID GetDriverBase(_In_ CONST CHAR* pszDriverName) {
  return GetDriverBaseByHash(hashstr::hash(pszDriverName));
}

/// <summary>
/// GetDriverExport via driver name and export routine name... this function
/// calls GetDriverExportByHash internally...
/// </summary>
/// <param name="pszDriverName">null terminated c-string given the module name
/// of the driver... example: "win32k.sys"</param> <param
/// name="pszRoutineName">the c-string name of the export...</param>
/// <returns>returns a linear virtual address to the export...</returns>
FORCEINLINE PVOID GetDriverExport(_In_ CONST CHAR* pszDriverName,
                                  _In_ CONST CHAR* pszRoutineName) {
  PVOID lpDriverBase = GetDriverBase(pszDriverName);
  return lpDriverBase ? GetDriverExportByHash(lpDriverBase,
                                              hashstr::hash(pszRoutineName))
                      : NULL;
}

FORCEINLINE PDRIVER_OBJECT GetDriverObject(_In_ CONST WCHAR* pwszDriverName) {
  HANDLE handle{};
  OBJECT_ATTRIBUTES attr{};
  UNICODE_STRING dirName{};
  PVOID dir{};
  BOOLEAN success = FALSE;

  DYN_NT_SYM(RtlInitUnicodeString)(&dirName, L"\\Driver");
  InitializeObjectAttributes(&attr, &dirName, OBJ_CASE_INSENSITIVE, NULL, NULL);

  // open OBJECT_DIRECTORY for \\Driver
  NTSTATUS status =
      DYN_NT_SYM(ZwOpenDirectoryObject)(&handle, DIRECTORY_ALL_ACCESS, &attr);

  if (!NT_SUCCESS(status)) {
    DYN_NT_SYM(ZwClose)(handle);
    return NULL;
  }

  // Get OBJECT_DIRECTORY pointer from HANDLE
  status = DYN_NT_SYM(ObReferenceObjectByHandle)(
      handle, DIRECTORY_ALL_ACCESS, nullptr, KernelMode, &dir, nullptr);

  if (!NT_SUCCESS(status)) {
    DYN_NT_SYM(ZwClose)(handle);
    return NULL;
  }

  const auto directory_object = POBJECT_DIRECTORY(dir);
  DYN_NT_SYM(ExAcquirePushLockExclusiveEx)(&directory_object->Lock, 0);

  for (auto entry : directory_object->HashBuckets) {
    if (!entry)
      continue;

    while (entry && entry->Object) {
      PDRIVER_OBJECT driver = (PDRIVER_OBJECT)entry->Object;
      if (!driver)
        continue;

      if (!DYN_NT_SYM(wcscmp)(driver->DriverExtension->ServiceKeyName.Buffer,
                              pwszDriverName)) {
        DYN_NT_SYM(ExReleasePushLockExclusiveEx)(&directory_object->Lock, 0);
        DYN_NT_SYM(ObfDereferenceObject)(dir);
        DYN_NT_SYM(ZwClose)(handle);
        return driver;
      }

      entry = entry->ChainLink;
    }
  }

  DYN_NT_SYM(ExReleasePushLockExclusiveEx)(&directory_object->Lock, 0);
  DYN_NT_SYM(ObfDereferenceObject)(dir);
  DYN_NT_SYM(ZwClose)(handle);
  return NULL;
}
}  // namespace Driver

namespace Process {
FORCEINLINE HANDLE GetPid(_In_ CONST WCHAR* pwszProcessName) {
  u32 allocSize{};
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, NULL, allocSize, &allocSize);

  PSYSTEM_PROCESS_INFORMATION procInfo =
      (PSYSTEM_PROCESS_INFORMATION)DYN_NT_SYM(ExAllocatePool)(NonPagedPool,
                                                              allocSize);

  const auto origPtr = procInfo;
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, procInfo, allocSize, &allocSize);

  while (true) {
    if (procInfo->ImageName.Buffer) {
      if (!DYN_NT_SYM(_wcsicmp)(procInfo->ImageName.Buffer, pwszProcessName)) {
        auto result = procInfo->ProcessId;
        DYN_NT_SYM(ExFreePool)(origPtr);
        return result;
      }
    }

    if (!procInfo->NextEntryOffset)
      break;

    procInfo = reinterpret_cast<PSYSTEM_PROCESS_INFORMATION>(
        reinterpret_cast<u64>(procInfo) + procInfo->NextEntryOffset);
  }

  DYN_NT_SYM(ExFreePool)(origPtr);
  return NULL;
}

FORCEINLINE PVOID GetProcessBase(_In_ HANDLE hPid) {
  PEPROCESS lpProc;
  if (NT_SUCCESS(DYN_NT_SYM(PsLookupProcessByProcessId)(hPid, &lpProc))) {
    PVOID lpBaseAddr = DYN_NT_SYM(PsGetProcessSectionBaseAddress)(lpProc);
    DYN_NT_SYM(ObfDereferenceObject)(lpProc);
    return lpBaseAddr;
  }
  return NULL;
}

VOID TdCallbackExample(CONST SYSTEM_THREAD& ThreadInfo);
VOID PsCallbackExample(CONST SYSTEM_PROCESS_INFORMATION& PsInfo);

using TdCallbackPtr = decltype(&TdCallbackExample);
using PsCallbackPtr = decltype(&PsCallbackExample);

FORCEINLINE VOID ForEachProcess(_In_ PsCallbackPtr lpCallback) {
  ULONG nAllocSize{};
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, NULL, nAllocSize, &nAllocSize);

  auto procInfo = reinterpret_cast<PSYSTEM_PROCESS_INFORMATION>(
      DYN_NT_SYM(ExAllocatePool)(NonPagedPool, nAllocSize));

  const auto origPtr = procInfo;
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, procInfo, nAllocSize, &nAllocSize);

  while (true) {
    for (auto idx = 0u; idx < procInfo->NumberOfThreads; ++idx)
      lpCallback(*procInfo);

    if (!procInfo->NextEntryOffset)
      break;

    procInfo = reinterpret_cast<PSYSTEM_PROCESS_INFORMATION>(
        reinterpret_cast<u64>(procInfo) + procInfo->NextEntryOffset);
  }
  DYN_NT_SYM(ExFreePool)(origPtr);
}

FORCEINLINE VOID ForEachThread(_In_ HANDLE hPid,
                               _In_ TdCallbackPtr lpCallback) {
  ULONG nAllocSize{};
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, NULL, nAllocSize, &nAllocSize);

  PSYSTEM_PROCESS_INFORMATION lpstProcInfo =
      (PSYSTEM_PROCESS_INFORMATION)DYN_NT_SYM(ExAllocatePool)(NonPagedPool,
                                                              nAllocSize);

  CONST PSYSTEM_PROCESS_INFORMATION lpstOrigPtr = lpstProcInfo;
  DYN_NT_SYM(ZwQuerySystemInformation)
  (SystemProcessInformation, lpstProcInfo, nAllocSize, &nAllocSize);

  while (true) {
    if (lpstProcInfo->ProcessId == hPid)
      for (INT idx = 0u; idx < lpstProcInfo->NumberOfThreads; ++idx)
        lpCallback(lpstProcInfo->Threads[idx]);

    if (!lpstProcInfo->NextEntryOffset)
      break;

    lpstProcInfo = (PSYSTEM_PROCESS_INFORMATION)((ULONG64)lpstProcInfo +
                                                 lpstProcInfo->NextEntryOffset);
  }
  DYN_NT_SYM(ExFreePool)(lpstOrigPtr);
}

FORCEINLINE PVOID GetModuleBase(_In_ HANDLE hPid,
                                _In_ CONST WCHAR* pwszModuleName) {
  PEPROCESS lpProc;
  KAPC_STATE stApcState;
  if (NT_SUCCESS(DYN_NT_SYM(PsLookupProcessByProcessId)(hPid, &lpProc))) {
    DYN_NT_SYM(KeStackAttachProcess)(lpProc, &stApcState);
    {
      const auto ldrData = reinterpret_cast<PPEB_LDR_DATA>(
          DYN_NT_SYM(PsGetProcessPeb)(lpProc)->LoaderData);

      auto currentEntry = ldrData->InMemoryOrderModuleList.Flink;
      while (currentEntry != &ldrData->InMemoryOrderModuleList) {
        const auto currentEntryData = reinterpret_cast<PLDR_DATA_TABLE_ENTRY>(
            reinterpret_cast<u64>(currentEntry) - sizeof LIST_ENTRY);

        const auto entryModuleName = currentEntryData->BaseDllName.Buffer;
        if (!DYN_NT_SYM(_wcsicmp)(entryModuleName, pwszModuleName)) {
          DYN_NT_SYM(ObfDereferenceObject)(lpProc);
          auto moduleBase = currentEntryData->DllBase;

          DYN_NT_SYM(KeUnstackDetachProcess)(&stApcState);
          return moduleBase;
        }
        currentEntry = currentEntry->Flink;
      }
    }
    DYN_NT_SYM(KeUnstackDetachProcess)(&stApcState);
    DYN_NT_SYM(ObfDereferenceObject)(lpProc);
  }
  return NULL;
}
}  // namespace Process

namespace Signature {
inline auto Scan(void* base, u32 size, const char* pattern, const char* mask)
    -> void* {
  static const auto check_mask = [&](const char* base, const char* pattern,
                                     const char* mask) -> bool {
    for (; *mask; ++base, ++pattern, ++mask)
      if (*mask == 'x' && *base != *pattern)
        return false;
    return true;
  };

  static const auto _strlen = [&](const char* str) -> unsigned {
    const char* s;
    for (s = str; *s; ++s)
      ;
    return (s - str);
  };

  size -= _strlen(mask);
  for (auto i = 0; i <= size; ++i) {
    void* addr = (void*)&(((char*)base)[i]);
    if (check_mask((char*)addr, pattern, mask))
      return addr;
  }
  return nullptr;
}
}  // namespace Signature
}  // namespace KUtils