diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4096055 --- /dev/null +++ b/.clang-format @@ -0,0 +1,101 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign +AlignOperands: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: TopLevel +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: false + BeforeCatch: true + BeforeElse: true +BreakBeforeBraces: Custom +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: AfterColon +BreakStringLiterals: false +ColumnLimit: 120 +CommentPragmas: '^begin_wpp|^end_wpp|^FUNC |^USESUFFIX |^USESUFFIX ' +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: false +IndentPPDirectives: AfterHash +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp +MacroBlockBegin: '^BEGIN_MODULE$|^BEGIN_TEST_CLASS$|^BEGIN_TEST_METHOD$' +MacroBlockEnd: '^END_MODULE$|^END_TEST_CLASS$|^END_TEST_METHOD$' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +PointerAlignment: Right +ReflowComments: true +SortIncludes: false +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp11 +StatementMacros: [ + 'EXTERN_C', + 'PAGED', + 'PAGEDX', + 'NONPAGED', + 'PNPCODE', + 'INITCODE', + '_At_', + '_When_', + '_Success_', + '_Check_return_', + '_Must_inspect_result_', + '_IRQL_requires_', + '_IRQL_requires_max_', + '_IRQL_requires_min_', + '_IRQL_saves_', + '_IRQL_restores_', + '_IRQL_saves_global_', + '_IRQL_restores_global_', + '_IRQL_raises_', + '_IRQL_lowers_', + '_Acquires_lock_', + '_Releases_lock_', + '_Acquires_exclusive_lock_', + '_Releases_exclusive_lock_', + '_Acquires_shared_lock_', + '_Releases_shared_lock_', + '_Requires_lock_held_', + '_Use_decl_annotations_', + '_Guarded_by_', + '__drv_preferredFunction', + '__drv_allocatesMem', + '__drv_freesMem', + ] +TabWidth: '4' +UseTab: Never diff --git a/kli.hpp b/kli.hpp new file mode 100644 index 0000000..b7c86e7 --- /dev/null +++ b/kli.hpp @@ -0,0 +1,403 @@ +#pragma once +#include +#include + +namespace clonestd { +// STRUCT TEMPLATE remove_reference +template +struct remove_reference +{ + using type = _Ty; +}; + +template +struct remove_reference<_Ty &> +{ + using type = _Ty; +}; + +template +struct remove_reference<_Ty &&> +{ + using type = _Ty; +}; + +template +using remove_reference_t = typename remove_reference<_Ty>::type; + +// STRUCT TEMPLATE remove_const +template +struct remove_const +{ // remove top-level const qualifier + using type = _Ty; +}; + +template +struct remove_const +{ + using type = _Ty; +}; + +template +using remove_const_t = typename remove_const<_Ty>::type; +} // namespace clonestd + +namespace kli { +constexpr auto Time = __TIME__; +constexpr auto Seed = static_cast(Time[7]) + static_cast(Time[6]) * 10 + + static_cast(Time[4]) * 60 + static_cast(Time[3]) * 600 + + static_cast(Time[1]) * 3600 + + static_cast(Time[0]) * 36000; +constexpr auto Seed2 = static_cast(Time[7]) + static_cast(Time[6]) * 10 + + static_cast(Time[4]) * 60; +constexpr auto Seed3 = static_cast(Time[7]) + static_cast(Time[5]) * 10 + + static_cast(Time[2]) * 60; +constexpr auto Seed4 = static_cast(Time[7]) + static_cast(Time[3]) * 10 + + static_cast(Time[1]) * 60; + +namespace cache { +constexpr unsigned long long base_init = Seed3; +inline unsigned long long base = base_init; +} // namespace cache + +namespace hash { +namespace detail { +template +struct fnv_constants; + +template <> +struct fnv_constants +{ + constexpr static unsigned int default_offset_basis = 0x811C9DC5UL; + constexpr static unsigned int prime = 0x01000193UL; +}; + +template <> +struct fnv_constants +{ + constexpr static unsigned long long default_offset_basis = 0xCBF29CE484222325ULL; + constexpr static unsigned long long prime = 0x100000001B3ULL; +}; + +} // namespace detail + +// Dumb hack to force a constexpr value to be evaluated in compiletime +template +struct force_cx +{ + constexpr static auto value = Value; +}; + +template +__forceinline constexpr Type +hash_fnv1a(const Char *str) +{ + Type val = detail::fnv_constants::default_offset_basis; + + // random + val += Seed; + + for (; *str != static_cast(0); ++str) + { + Char c = *str; + val ^= static_cast(c); + val *= static_cast(detail::fnv_constants::prime); + } + + return val; +} + +#define KLI_HASH_RTS(str) \ + (::kli::hash:: \ + hash_fnv1a>>( \ + (str))) + +#define KLI_HASH_STR(str) \ + (::kli::hash::force_cx< \ + unsigned long long, \ + ::kli::hash:: \ + hash_fnv1a>>( \ + (str))>::value) + +} // namespace hash + +namespace crypto { +typedef struct s_rc4_state +{ + int x, y; + unsigned char m[256]; +} rc4_state; + +__forceinline void +rc4_init_key(rc4_state *s, unsigned char *key, int length) +{ + int i, j, k, a; + + unsigned char *m; + + s->x = 0; + s->y = 0; + m = s->m; + + for (i = 0; i < 256; i++) + { + m[i] = (unsigned char)i; + } + + j = k = 0; + + for (i = 0; i < 256; i++) + { + a = m[i]; + j = (unsigned char)(j + a + key[k]); + m[i] = m[j]; + m[j] = (unsigned char)a; + if (++k >= length) + k = 0; + } +} + +__forceinline void +rc4_crypt(rc4_state *s, unsigned char *data, int length) +{ + int i, x, y, a, b; + + unsigned char *m; + + x = s->x; + y = s->y; + m = s->m; + + for (i = 0; i < length; i++) + { + x = (unsigned char)(x + 1); + a = m[x]; + y = (unsigned char)(y + a); + m[x] = b = m[y]; + m[y] = (unsigned char)a; + data[i] ^= m[(unsigned char)(a + b)]; + } + + s->x = x; + s->y = y; +} + +__forceinline void +rc4_crypt_all_in_once(unsigned char *key_buff, unsigned long key_len, unsigned char *enc_buff, unsigned long enc_len) +{ + rc4_state state; + rc4_init_key(&state, key_buff, key_len); + rc4_crypt(&state, enc_buff, enc_len); +} + +template +__forceinline Type +RC4Base(Type Ptr) +{ + unsigned long long rc4_key = Seed2; + unsigned long long rc4_data = Seed4; + crypto::rc4_crypt_all_in_once( + (unsigned char *)(&rc4_key), sizeof(rc4_key), (unsigned char *)(&rc4_data), sizeof(rc4_data)); + return (Type)((unsigned long long)Ptr ^ rc4_data); +} + +template +__forceinline Type +RC4Func(Type Ptr) +{ + unsigned long long rc4_key = Seed4; + unsigned long long rc4_data = Seed2; + crypto::rc4_crypt_all_in_once( + (unsigned char *)(&rc4_key), sizeof(rc4_key), (unsigned char *)(&rc4_data), sizeof(rc4_data)); + return (Type)((unsigned long long)Ptr ^ rc4_data); +} + +} // namespace crypto + +namespace detail { +typedef struct _IMAGE_DOS_HEADER +{ // DOS .EXE header + unsigned short e_magic; // Magic number + unsigned short e_cblp; // Bytes on last page of file + unsigned short e_cp; // Pages in file + unsigned short e_crlc; // Relocations + unsigned short e_cparhdr; // GetSize of header in paragraphs + unsigned short e_minalloc; // Minimum extra paragraphs needed + unsigned short e_maxalloc; // Maximum extra paragraphs needed + unsigned short e_ss; // Initial (relative) SS value + unsigned short e_sp; // Initial SP value + unsigned short e_csum; // Checksum + unsigned short e_ip; // Initial IP value + unsigned short e_cs; // Initial (relative) CS value + unsigned short e_lfarlc; // File address of relocation table + unsigned short e_ovno; // Overlay number + unsigned short e_res[4]; // Reserved words + unsigned short e_oemid; // OEM identifier (for e_oeminfo) + unsigned short e_oeminfo; // OEM information; e_oemid specific + unsigned short e_res2[10]; // Reserved words + int e_lfanew; // File address of new exe header +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +typedef struct _IMAGE_FILE_HEADER +{ + unsigned short Machine; + unsigned short NumberOfSections; + unsigned int TimeDateStamp; + unsigned int PointerToSymbolTable; + unsigned int NumberOfSymbols; + unsigned short SizeOfOptionalHeader; + unsigned short Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + +typedef struct _IMAGE_DATA_DIRECTORY +{ + unsigned int VirtualAddress; + unsigned int Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + +typedef struct _IMAGE_OPTIONAL_HEADER64 +{ + unsigned short Magic; + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + unsigned int SizeOfCode; + unsigned int SizeOfInitializedData; + unsigned int SizeOfUninitializedData; + unsigned int AddressOfEntryPoint; + unsigned int BaseOfCode; + unsigned long long ImageBase; + unsigned int SectionAlignment; + unsigned int FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + unsigned int Win32VersionValue; + unsigned int SizeOfImage; + unsigned int SizeOfHeaders; + unsigned int CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + unsigned long long SizeOfStackReserve; + unsigned long long SizeOfStackCommit; + unsigned long long SizeOfHeapReserve; + unsigned long long SizeOfHeapCommit; + unsigned int LoaderFlags; + unsigned int NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; +} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; + +typedef struct _IMAGE_NT_HEADERS64 +{ + unsigned int Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; + +typedef struct _IMAGE_EXPORT_DIRECTORY +{ + unsigned int Characteristics; + unsigned int TimeDateStamp; + unsigned short MajorVersion; + unsigned short MinorVersion; + unsigned int Name; + unsigned int Base; + unsigned int NumberOfFunctions; + unsigned int NumberOfNames; + unsigned int AddressOfFunctions; // RVA from base of image + unsigned int AddressOfNames; // RVA from base of image + unsigned int AddressOfNameOrdinals; // RVA from base of image +} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY; + +__declspec(naked) __forceinline unsigned long long get_kernel_base() +{ + _asm { + mov rax, qword ptr gs:[18h] + mov rcx, [rax+38h] + mov rax, 0FFFFFFFFFFFFF000h + and rax, [rcx+4h] + jmp while_begin + search_begin: + add rax, 0FFFFFFFFFFFFF000h + while_begin: + xor ecx, ecx + jmp search_cmp + search_next: + add rcx, 1 + cmp rcx, 0FF9h + jz search_begin + search_cmp: + cmp byte ptr[rax+rcx], 48h + jnz search_next + cmp byte ptr[rax+rcx+1], 8Dh + jnz search_next + cmp byte ptr[rax+rcx+2], 1Dh + jnz search_next + cmp byte ptr[rax+rcx+6], 0FFh + jnz search_next + mov r8d,[rax+rcx+3] + lea edx,[rcx+r8] + add edx, eax + add edx, 7 + test edx, 0FFFh + jnz search_next + mov rdx, 0FFFFFFFF00000000h + and rdx, rax + add r8d, eax + lea eax,[rcx+r8] + add eax, 7 + or rax, rdx + ret + } +} + +} // namespace detail + +__forceinline unsigned long long +find_kernel_export(unsigned long long export_hash) +{ + if (cache::base == cache::base_init) + { + cache::base = crypto::RC4Base(detail::get_kernel_base()); + } + + const auto dos_header = (detail::PIMAGE_DOS_HEADER)crypto::RC4Base(cache::base); + const auto nt_headers = (detail::PIMAGE_NT_HEADERS64)(crypto::RC4Base(cache::base) + dos_header->e_lfanew); + const auto export_directory = (detail::PIMAGE_EXPORT_DIRECTORY)( + crypto::RC4Base(cache::base) + nt_headers->OptionalHeader.DataDirectory[0].VirtualAddress); + + const auto address_of_functions = + (unsigned int *)(crypto::RC4Base(cache::base) + export_directory->AddressOfFunctions); + const auto address_of_names = (unsigned int *)(crypto::RC4Base(cache::base) + export_directory->AddressOfNames); + const auto address_of_name_ordinals = + (unsigned short *)(crypto::RC4Base(cache::base) + export_directory->AddressOfNameOrdinals); + + for (unsigned int i = 0; i < export_directory->NumberOfNames; ++i) + { + const auto export_entry_name = (char *)(crypto::RC4Base(cache::base) + address_of_names[i]); + const auto export_entry_hash = KLI_HASH_RTS(export_entry_name); + + // address_of_functions is indexed through an ordinal + // address_of_name_ordinals gets the ordinal through our own index - i. + if (export_entry_hash == export_hash) + { + auto func = crypto::RC4Base(cache::base) + address_of_functions[address_of_name_ordinals[i]]; + return crypto::RC4Func(func); + } + } + + return 0ULL; +} + +// once +#define KLI_CALL(name, ...) \ + ((decltype(&name))(kli::crypto::RC4Func(kli::find_kernel_export(KLI_HASH_STR(#name)))))(__VA_ARGS__) + +// cached +#define KLI_CACHED_DEF(name) decltype(&name) KLI##name = nullptr +#define KLI_CACHED_SET(name) KLI##name = (decltype(&name))(kli::find_kernel_export(KLI_HASH_STR(#name))) +#define KLI_CACHED_CALL(name, ...) ((decltype(&name))(kli::crypto::RC4Func(KLI##name)))(__VA_ARGS__) + +} // namespace kli diff --git a/test-kli-ex/IAT.cached.h b/test-kli-ex/IAT.cached.h new file mode 100644 index 0000000..9b0fa69 --- /dev/null +++ b/test-kli-ex/IAT.cached.h @@ -0,0 +1,12 @@ +#pragma once +#include "../kli.hpp" + +KLI_CACHED_DEF(ExAllocatePoolWithTag); +KLI_CACHED_DEF(ExFreePoolWithTag); + +__forceinline void +IATInitCached() +{ + KLI_CACHED_SET(ExAllocatePoolWithTag); + KLI_CACHED_SET(ExFreePoolWithTag); +} diff --git a/test-kli-ex/Source.cpp b/test-kli-ex/Source.cpp new file mode 100644 index 0000000..d8867f9 --- /dev/null +++ b/test-kli-ex/Source.cpp @@ -0,0 +1,69 @@ +#include "IAT.cached.h" + +#define dprintf(...) DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, __VA_ARGS__) + +void +DriverUnload(PDRIVER_OBJECT DriverObject) +{ + UNREFERENCED_PARAMETER(DriverObject); + dprintf("exit world\n"); +} + +DECLSPEC_NOINLINE +void +test_once() +{ + auto pExAllocatePoolWithTag = KLI_CALL(ExAllocatePoolWithTag, NonPagedPool, PAGE_SIZE, 'enoN'); + if (pExAllocatePoolWithTag) + { + dprintf("test_once:ExAllocatePoolWithTag=%p\n", pExAllocatePoolWithTag); + KLI_CALL(ExFreePoolWithTag, pExAllocatePoolWithTag, 'enoN'); + } +} + +DECLSPEC_NOINLINE +void +test_cached_init() +{ + // Make cache + IATInitCached(); +} + +DECLSPEC_NOINLINE +void +test_cached() +{ + auto pAddr = KLI_CACHED_CALL(ExAllocatePoolWithTag, NonPagedPool, PAGE_SIZE, 'x1x1'); + if (pAddr) + { + dprintf("test_cached:pAddr=%p\n", pAddr); + KLI_CACHED_CALL(ExFreePoolWithTag, pAddr, 'x1x1'); + } +} + +DECLSPEC_NOINLINE +void +test_cached2() +{ + auto pAddr = KLI_CACHED_CALL(ExAllocatePoolWithTag, NonPagedPool, PAGE_SIZE, 'xxxx'); + if (pAddr) + { + dprintf("test_cached2:pAddr=%p\n", pAddr); + KLI_CACHED_CALL(ExFreePoolWithTag, pAddr, 'xxxx'); + } +} + +NTSTATUS +DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) +{ + dprintf("new world\n"); + DriverObject->DriverUnload = DriverUnload; + + test_once(); + + test_cached_init(); + test_cached(); + test_cached2(); + + return STATUS_SUCCESS; +} diff --git a/test-kli-ex/test-kli-ex.sln b/test-kli-ex/test-kli-ex.sln new file mode 100644 index 0000000..c0c3401 --- /dev/null +++ b/test-kli-ex/test-kli-ex.sln @@ -0,0 +1,35 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32929.385 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-kli-ex", "test-kli-ex.vcxproj", "{F26B367C-6F01-4E98-8887-28A594BFC49E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|ARM64.Build.0 = Debug|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|x64.ActiveCfg = Debug|x64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|x64.Build.0 = Debug|x64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Debug|x64.Deploy.0 = Debug|x64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|ARM64.ActiveCfg = Release|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|ARM64.Build.0 = Release|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|ARM64.Deploy.0 = Release|ARM64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|x64.ActiveCfg = Release|x64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|x64.Build.0 = Release|x64 + {F26B367C-6F01-4E98-8887-28A594BFC49E}.Release|x64.Deploy.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2C9BC118-6D65-4FED-8318-1DF821ABD3F6} + EndGlobalSection +EndGlobal diff --git a/test-kli-ex/test-kli-ex.vcxproj b/test-kli-ex/test-kli-ex.vcxproj new file mode 100644 index 0000000..d4942e3 --- /dev/null +++ b/test-kli-ex/test-kli-ex.vcxproj @@ -0,0 +1,143 @@ + + + + + Debug + x64 + + + Release + x64 + + + Debug + ARM64 + + + Release + ARM64 + + + + {F26B367C-6F01-4E98-8887-28A594BFC49E} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 12.0 + Debug + x64 + test_uimp + $(LatestTargetPlatformVersion) + + + + Windows10 + true + LLVM-MSVC_v143_KernelMode + Driver + KMDF + Desktop + false + + + Windows10 + false + LLVM-MSVC_v143_KernelMode + Driver + KMDF + Desktop + false + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Universal + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + sha256 + + + false + + + false + Level3 + false + stdcpp20 + + + DriverEntry + + + + + sha256 + + + false + + + false + Level3 + false + stdcpp20 + + + DriverEntry + + + + + sha256 + + + + + sha256 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test-kli-ex/test-kli-ex.vcxproj.filters b/test-kli-ex/test-kli-ex.vcxproj.filters new file mode 100644 index 0000000..a9ff02f --- /dev/null +++ b/test-kli-ex/test-kli-ex.vcxproj.filters @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file