diff --git a/README.md b/README.md index 5660453..c7442ed 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# drm-analysis \ No newline at end of file +# Analyzing modern DRMs +Material acompanying the guest lecture at Ruhr-Universität Bochum. + +## Slides +* https://docs.google.com/presentation/d/17TXl_pds6BC0Zm2gLUnIZK7BtlGc_TRt/edit + +## Recording +* https://www.youtube.com/watch?v=AEvpYgzDATA + +## Links: +* https://qiling.io +* https://github.com/momo5502/hypervisor +* https://hyperdbg.org/ diff --git a/qiling/analyze.py b/qiling/analyze.py new file mode 100644 index 0000000..9655b36 --- /dev/null +++ b/qiling/analyze.py @@ -0,0 +1,20 @@ +from qiling import Qiling +from qiling.const import QL_VERBOSE + +from unicorn.x86_const import * + +def hook_syscall(ql: Qiling): + ql.log.debug(f'!!! SYSCALL {ql.arch.regs.arch_pc:#x}: {ql.arch.regs.eax:#x}') + return (0, 0) + +def mem_read(ql: Qiling, access: int, address: int, size: int, value: int): + ql.log.debug(f'intercepted a memory read from {address:#x} at {ql.arch.regs.arch_pc:#x}') + +if __name__ == "__main__": + ql = Qiling(["C:\\Users\\mauri\\Desktop\\qiling-sample\\lul.exe"], + "C:\\Users\\mauri\\Desktop\\qiling-sample", verbose=QL_VERBOSE.DEBUG, libcache=True) + + ql.hook_mem_read(mem_read) + ql.hook_insn(hook_syscall, UC_X86_INS_SYSCALL) + + ql.run() diff --git a/sample/hook/dllmain.cpp b/sample/hook/dllmain.cpp new file mode 100644 index 0000000..80e9439 --- /dev/null +++ b/sample/hook/dllmain.cpp @@ -0,0 +1,101 @@ +#include "pch.h" +#include +#include +#include + +#include "hyperhook.h" +#include "../lul/peb.h" + +NTSTATUS custom_query_process_information_hook(HANDLE, + PROCESSINFOCLASS, + uint8_t* ProcessInformation, ULONG, + PULONG) +{ + puts("!!! Hook triggered"); + + auto* desired_string = L"C:\\Users\\mauri\\source\\repos\\lul\\x64\\Release\\lul.exe"; + + auto* res = reinterpret_cast(ProcessInformation); + res->Buffer = reinterpret_cast(res + 1); + res->Length = wcslen(desired_string) * 2; + res->MaximumLength = res->Length; + + memcpy(res->Buffer, desired_string, res->Length); + + return 0; +} + +std::vector get_jump_bytes(void* address) +{ + std::vector bytes{ + 0x48, 0xb8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rax, 0x1122334455667788 + 0xff, 0xe0, // jmp rax + }; + + memcpy(bytes.data() + 2, &address, sizeof(address)); + + return bytes; +} + +void write_bytes_regularly(void* place, const std::vector& bytes) +{ + DWORD old_protect{}; + VirtualProtect(place, bytes.size(), PAGE_EXECUTE_READWRITE, &old_protect); + + memcpy(place, bytes.data(), bytes.size()); + + VirtualProtect(place, bytes.size(), old_protect, &old_protect); +} + +void write_bytes_with_hypervisor(void* place, const std::vector& bytes) +{ + hyperhook_write(GetCurrentProcessId(), reinterpret_cast(place), bytes.data(), bytes.size()); +} + +void insert_hook(uint64_t address, void* target, const bool using_hypervisor) +{ + auto* place = reinterpret_cast(address); + const auto bytes = get_jump_bytes(target); + + if (using_hypervisor) + { + write_bytes_with_hypervisor(place, bytes); + } + else + { + write_bytes_regularly(place, bytes); + } +} + +void run() +{ + puts(""); + puts("Hook DLL loaded"); + puts("Use hypervisor for hooks? (y/n)"); + const auto use_hypervisor = _getch() == 'y'; + + if(use_hypervisor) + { + puts("Using hypervisor..."); + }else + { + puts("Using regular hooks..."); + } + + insert_hook(0x14004FAE8, &custom_query_process_information_hook, use_hypervisor); + + puts(""); +} + +BOOL APIENTRY DllMain(HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved +) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + run(); + } + + return TRUE; +} diff --git a/sample/hook/hook.vcxproj b/sample/hook/hook.vcxproj new file mode 100644 index 0000000..7098016 --- /dev/null +++ b/sample/hook/hook.vcxproj @@ -0,0 +1,159 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {1c5f4fcf-8744-450f-863d-923716431557} + hook + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;HOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + stdcpp20 + + + Windows + true + true + true + false + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/sample/hook/hook.vcxproj.filters b/sample/hook/hook.vcxproj.filters new file mode 100644 index 0000000..8446d5c --- /dev/null +++ b/sample/hook/hook.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Headerdateien + + + Headerdateien + + + + + Quelldateien + + + Quelldateien + + + \ No newline at end of file diff --git a/sample/hook/hook.vcxproj.user b/sample/hook/hook.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/sample/hook/hook.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sample/hook/hyperhook.h b/sample/hook/hyperhook.h new file mode 100644 index 0000000..5d0611e --- /dev/null +++ b/sample/hook/hyperhook.h @@ -0,0 +1,20 @@ +#ifndef EXTERN_C +#ifdef __cplusplus +#define EXTERN_C extern "C" +#else +#define EXTERN_C +#endif +#endif + +#ifndef DLL_IMPORT +#define DLL_IMPORT __declspec(dllimport) +#endif + +EXTERN_C DLL_IMPORT +int hyperhook_initialize(); + +EXTERN_C DLL_IMPORT +int hyperhook_write(unsigned int process_id, unsigned long long address, const void* data, + unsigned long long size); + +#pragma comment(lib, "hyperhook.lib") diff --git a/sample/hook/hyperhook.lib b/sample/hook/hyperhook.lib new file mode 100644 index 0000000..3fc6e60 Binary files /dev/null and b/sample/hook/hyperhook.lib differ diff --git a/sample/hook/pch.cpp b/sample/hook/pch.cpp new file mode 100644 index 0000000..1fcbac0 --- /dev/null +++ b/sample/hook/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: Quelldatei, die dem vorkompilierten Header entspricht + +#include "pch.h" + +// Bei der Verwendung vorkompilierter Header ist diese Quelldatei für eine erfolgreiche Kompilierung erforderlich. diff --git a/sample/hook/pch.h b/sample/hook/pch.h new file mode 100644 index 0000000..e89772d --- /dev/null +++ b/sample/hook/pch.h @@ -0,0 +1,13 @@ +// pch.h: Dies ist eine vorkompilierte Headerdatei. +// Die unten aufgeführten Dateien werden nur einmal kompiliert, um die Buildleistung für zukünftige Builds zu verbessern. +// Dies wirkt sich auch auf die IntelliSense-Leistung aus, Codevervollständigung und viele Features zum Durchsuchen von Code eingeschlossen. +// Die hier aufgeführten Dateien werden jedoch ALLE neu kompiliert, wenn mindestens eine davon zwischen den Builds aktualisiert wird. +// Fügen Sie hier keine Dateien hinzu, die häufig aktualisiert werden sollen, da sich so der Leistungsvorteil ins Gegenteil verkehrt. + +#ifndef PCH_H +#define PCH_H + +// Fügen Sie hier Header hinzu, die vorkompiliert werden sollen. +#include "framework.h" + +#endif //PCH_H diff --git a/sample/lul.sln b/sample/lul.sln new file mode 100644 index 0000000..4b70487 --- /dev/null +++ b/sample/lul.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34723.18 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lul", "lul\lul.vcxproj", "{6061641D-671E-4A1B-BCB9-070D57D87CD0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hook", "hook\hook.vcxproj", "{1C5F4FCF-8744-450F-863D-923716431557}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Debug|x64.ActiveCfg = Debug|x64 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Debug|x64.Build.0 = Debug|x64 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Debug|x86.ActiveCfg = Debug|Win32 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Debug|x86.Build.0 = Debug|Win32 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Release|x64.ActiveCfg = Release|x64 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Release|x64.Build.0 = Release|x64 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Release|x86.ActiveCfg = Release|Win32 + {6061641D-671E-4A1B-BCB9-070D57D87CD0}.Release|x86.Build.0 = Release|Win32 + {1C5F4FCF-8744-450F-863D-923716431557}.Debug|x64.ActiveCfg = Debug|x64 + {1C5F4FCF-8744-450F-863D-923716431557}.Debug|x64.Build.0 = Debug|x64 + {1C5F4FCF-8744-450F-863D-923716431557}.Debug|x86.ActiveCfg = Debug|Win32 + {1C5F4FCF-8744-450F-863D-923716431557}.Debug|x86.Build.0 = Debug|Win32 + {1C5F4FCF-8744-450F-863D-923716431557}.Release|x64.ActiveCfg = Release|x64 + {1C5F4FCF-8744-450F-863D-923716431557}.Release|x64.Build.0 = Release|x64 + {1C5F4FCF-8744-450F-863D-923716431557}.Release|x86.ActiveCfg = Release|Win32 + {1C5F4FCF-8744-450F-863D-923716431557}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8EFF6206-9C12-40B1-9077-FE736627B2A0} + EndGlobalSection +EndGlobal diff --git a/sample/lul/asm.asm b/sample/lul/asm.asm new file mode 100644 index 0000000..059f624 --- /dev/null +++ b/sample/lul/asm.asm @@ -0,0 +1,10 @@ +.code + +InlineNtQueryInformationProcess PROC + mov r10, rcx + mov eax, 19h + syscall + ret +InlineNtQueryInformationProcess ENDP + +end diff --git a/sample/lul/lul.cpp b/sample/lul/lul.cpp new file mode 100644 index 0000000..3e46814 --- /dev/null +++ b/sample/lul/lul.cpp @@ -0,0 +1,241 @@ +#define JM_XORSTR_DISABLE_AVX_INTRINSICS 1 + +#include + +#include "peb.h" +#include "xorstr.hpp" + + +#define LOAD_STR(x) (xorstr_(x)) + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +// Adapt the hash in the compiled binary +volatile uint32_t theHash = 0x12345678; + +// Adapt the value to the desired path +#define EXPECTED_FILENAME LOAD_STR("C:\\Users\\mauri\\source\\repos\\lul\\x64\\Release\\lul.exe") + +extern "C" NTSTATUS __stdcall InlineNtQueryInformationProcess(HANDLE ProcessHandle, + PROCESSINFOCLASS ProcessInformationClass, + PVOID ProcessInformation, ULONG ProcessInformationLength, + PULONG ReturnLength); + +namespace +{ + FORCEINLINE bool str_equal(const char* s1, const char* s2) + { + for (size_t i = 0;; ++i) + { + if (s1[i] != s2[i]) + { + return false; + } + + if (s1[i] == 0) + { + break; + } + } + + return true; + } + + FORCEINLINE uint32_t jenkins_one_at_a_time_hash(const uint8_t* key, const size_t length) + { + size_t i = 0; + uint32_t hash = 0; + while (i != length) + { + hash += key[i++]; + hash += hash << 10; + hash ^= hash >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; + } + + FORCEINLINE std::pair get_text_section() + { + auto* base = reinterpret_cast(&__ImageBase); + auto* nt_headers = reinterpret_cast(base + __ImageBase.e_lfanew); + + auto section = IMAGE_FIRST_SECTION(nt_headers); + + for (uint16_t i = 0; i < nt_headers->FileHeader.NumberOfSections; ++i, ++section) + { + if (str_equal(reinterpret_cast(section->Name), ".text")) + { + return {base + section->VirtualAddress, section->Misc.VirtualSize}; + } + } + + return {nullptr, 0}; + } + + FORCEINLINE uint32_t compute_text_hash() + { + const auto [addr, size] = get_text_section(); + return jenkins_one_at_a_time_hash(addr, size); + } + + FORCEINLINE bool is_integrity_violated() + { + const auto computed = compute_text_hash(); + + printf(LOAD_STR("Checksum: %08X\n"), computed); + printf(LOAD_STR("Expected: %08X\n"), theHash); + return computed != theHash; + } + + FORCEINLINE void fill_module_filename(char* buffer, const size_t size) + { + if (size == 0) return; + + char totalBuffer[0x1024]; + auto* str = reinterpret_cast(totalBuffer); + + ULONG retLength{0}; + const auto res = InlineNtQueryInformationProcess(reinterpret_cast(0xFFFFFFFFFFFFFFFF), + ProcessImageFileNameWin32, &totalBuffer, sizeof(totalBuffer), + &retLength); + if (res != 0) + { + buffer[0] = 0; + return; + } + + size_t i = 0; + for (; i < (str->Length / 2) && i < (size - 1); ++i) + { + buffer[i] = static_cast(str->Buffer[i]); + } + + buffer[i] = 0; + } + + template + FORCEINLINE void fill_module_filename(char (&buffer)[Size]) + { + fill_module_filename(buffer, Size); + } + + FORCEINLINE bool was_copied() + { + char filename[MAX_PATH]; + fill_module_filename(filename); + + printf(LOAD_STR("Filename: %s\n"), filename); + printf(LOAD_STR("Expected: %s\n"), EXPECTED_FILENAME); + + return !str_equal(filename, EXPECTED_FILENAME); + } + + FORCEINLINE void stuff() + { + puts(LOAD_STR("Loading hook.dll...")); + LoadLibraryA(LOAD_STR("hook.dll")); + + bool valid = true; + + puts(""); + + if (is_integrity_violated()) + { + puts(LOAD_STR(" -> Integrity violation!")); + valid = false; + } + + puts(""); + + if (was_copied()) + { + puts(LOAD_STR(" -> You copied the program")); + valid = false; + } + + puts(""); + + if (!valid) + { + puts(LOAD_STR("Something's wrong.")); + return; + + } + + puts(LOAD_STR("Yay program is running!")); + } + + // This essentially does nothing. + // Its only purpose is to look confusing in IDA to simulate obfuscation. + + template + FORCEINLINE bool decisionMaker(volatile unsigned int* num) + { + if constexpr (Count == 0) + { + return *num == 3; + } + + if constexpr (Count == 1) + { + return *num & 100; + } + + if constexpr (Count > 2) + { + if (*num == 3) + { + *num ^= Count; + return decisionMaker(num); + } + + if constexpr (Count < 5) + { + if (*num > 40) + { + *num = ~Count; + return decisionMaker(num); + } + } + + + if (Count % 4 && *num > 4) + { + constexpr auto newCount = Count >> 1; + return decisionMaker(num); + } + + if (*num % Count == 0) + { + *num = (*num & ~3) ^ ~Count; + return decisionMaker(num); + } + + ++*num; + + return decisionMaker(num) ^ decisionMaker(num); + } + + return true; + } +} + +int main(int argc) +{ + if (argc > 3 && decisionMaker<11>((volatile unsigned int*)&argc)) + { + return 1; + } + + stuff(); + + if (argc > 4 && decisionMaker<7>((volatile unsigned int*)&argc)) + { + return 1; + } + + return 0; +} diff --git a/sample/lul/lul.vcxproj b/sample/lul/lul.vcxproj new file mode 100644 index 0000000..91ecbd8 --- /dev/null +++ b/sample/lul/lul.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {6061641d-671e-4a1b-bcb9-070d57d87cd0} + lul + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + true + false + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + Speed + stdcpp20 + MultiThreadedDebugDLL + + + Console + true + true + true + true + false + + + + + + + + Document + + + + + + + \ No newline at end of file diff --git a/sample/lul/lul.vcxproj.filters b/sample/lul/lul.vcxproj.filters new file mode 100644 index 0000000..41015ba --- /dev/null +++ b/sample/lul/lul.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Quelldateien + + + + + Quelldateien + + + \ No newline at end of file diff --git a/sample/lul/lul.vcxproj.user b/sample/lul/lul.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/sample/lul/lul.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/sample/lul/peb.h b/sample/lul/peb.h new file mode 100644 index 0000000..ced99ac --- /dev/null +++ b/sample/lul/peb.h @@ -0,0 +1,449 @@ +#pragma once + +#include + +typedef _Return_type_success_(return >= 0) LONG NTSTATUS; + +typedef enum _PROCESSINFOCLASS +{ + ProcessBasicInformation = 0, + ProcessDebugPort = 7, + ProcessWow64Information = 26, + ProcessImageFileName = 27, + ProcessBreakOnTermination = 29, + ProcessImageFileNameWin32 = 43, +} PROCESSINFOCLASS; + +struct RTL_USER_PROCESS_PARAMETERS; + +typedef struct _LSA_UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING; + +typedef struct _LDR_MODULE +{ + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID BaseAddress; + PVOID EntryPoint; + ULONG SizeOfImage; + UNICODE_STRING FullDllName; + UNICODE_STRING BaseDllName; + ULONG Flags; + SHORT LoadCount; + SHORT TlsIndex; + LIST_ENTRY HashTableEntry; + ULONG TimeDateStamp; +} LDR_MODULE, *PLDR_MODULE; + +typedef struct _PEB_LDR_DATA +{ + ULONG Length; + BOOLEAN Initialized; + HANDLE SsHandle; + LIST_ENTRY InLoadOrderModuleList; + LIST_ENTRY InMemoryOrderModuleList; + LIST_ENTRY InInitializationOrderModuleList; + PVOID EntryInProgress; + BOOLEAN ShutdownInProgress; + HANDLE ShutdownThreadId; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +#define GDI_HANDLE_BUFFER_SIZE32 34 +#define GDI_HANDLE_BUFFER_SIZE GDI_HANDLE_BUFFER_SIZE32 +typedef ULONG GDI_HANDLE_BUFFER[GDI_HANDLE_BUFFER_SIZE]; + +typedef struct _PEB +{ + BOOLEAN InheritedAddressSpace; + BOOLEAN ReadImageFileExecOptions; + BOOLEAN BeingDebugged; + + union + { + BOOLEAN BitField; + + struct + { + BOOLEAN ImageUsesLargePages : 1; + BOOLEAN IsProtectedProcess : 1; + BOOLEAN IsImageDynamicallyRelocated : 1; + BOOLEAN SkipPatchingUser32Forwarders : 1; + BOOLEAN IsPackagedProcess : 1; + BOOLEAN IsAppContainer : 1; + BOOLEAN IsProtectedProcessLight : 1; + BOOLEAN IsLongPathAwareProcess : 1; + }; + }; + + HANDLE Mutant; + + PVOID ImageBaseAddress; + PPEB_LDR_DATA Ldr; + RTL_USER_PROCESS_PARAMETERS* ProcessParameters; + PVOID SubSystemData; + PVOID ProcessHeap; + PRTL_CRITICAL_SECTION FastPebLock; + PSLIST_HEADER AtlThunkSListPtr; + PVOID IFEOKey; + + union + { + ULONG CrossProcessFlags; + + struct + { + ULONG ProcessInJob : 1; + ULONG ProcessInitializing : 1; + ULONG ProcessUsingVEH : 1; + ULONG ProcessUsingVCH : 1; + ULONG ProcessUsingFTH : 1; + ULONG ProcessPreviouslyThrottled : 1; + ULONG ProcessCurrentlyThrottled : 1; + ULONG ProcessImagesHotPatched : 1; // REDSTONE5 + ULONG ReservedBits0 : 24; + }; + }; + + union + { + PVOID KernelCallbackTable; + PVOID UserSharedInfoPtr; + }; + + ULONG SystemReserved; + ULONG AtlThunkSListPtr32; + void* ApiSetMap; + ULONG TlsExpansionCounter; + PVOID TlsBitmap; + ULONG TlsBitmapBits[2]; // TLS_MINIMUM_AVAILABLE + + PVOID ReadOnlySharedMemoryBase; + void* SharedData; // HotpatchInformation + PVOID* ReadOnlyStaticServerData; + + PVOID AnsiCodePageData; // PCPTABLEINFO + PVOID OemCodePageData; // PCPTABLEINFO + PVOID UnicodeCaseTableData; // PNLSTABLEINFO + + ULONG NumberOfProcessors; + ULONG NtGlobalFlag; + + ULARGE_INTEGER CriticalSectionTimeout; + SIZE_T HeapSegmentReserve; + SIZE_T HeapSegmentCommit; + SIZE_T HeapDeCommitTotalFreeThreshold; + SIZE_T HeapDeCommitFreeBlockThreshold; + + ULONG NumberOfHeaps; + ULONG MaximumNumberOfHeaps; + PVOID* ProcessHeaps; // PHEAP + + PVOID GdiSharedHandleTable; // PGDI_SHARED_MEMORY + PVOID ProcessStarterHelper; + ULONG GdiDCAttributeList; + + PRTL_CRITICAL_SECTION LoaderLock; + + ULONG OSMajorVersion; + ULONG OSMinorVersion; + USHORT OSBuildNumber; + USHORT OSCSDVersion; + ULONG OSPlatformId; + ULONG ImageSubsystem; + ULONG ImageSubsystemMajorVersion; + ULONG ImageSubsystemMinorVersion; + KAFFINITY ActiveProcessAffinityMask; + GDI_HANDLE_BUFFER GdiHandleBuffer; + PVOID PostProcessInitRoutine; + + PVOID TlsExpansionBitmap; + ULONG TlsExpansionBitmapBits[32]; // TLS_EXPANSION_SLOTS + + ULONG SessionId; + + ULARGE_INTEGER AppCompatFlags; // KACF_* + ULARGE_INTEGER AppCompatFlagsUser; + PVOID pShimData; + PVOID AppCompatInfo; // APPCOMPAT_EXE_DATA + + UNICODE_STRING CSDVersion; + + void* ActivationContextData; + void* ProcessAssemblyStorageMap; + void* SystemDefaultActivationContextData; + void* SystemAssemblyStorageMap; + + SIZE_T MinimumStackCommit; + + PVOID SparePointers[2]; // 19H1 (previously FlsCallback to FlsHighIndex) + PVOID PatchLoaderData; + PVOID ChpeV2ProcessInfo; // _CHPEV2_PROCESS_INFO + + ULONG AppModelFeatureState; + ULONG SpareUlongs[2]; + + USHORT ActiveCodePage; + USHORT OemCodePage; + USHORT UseCaseMapping; + USHORT UnusedNlsField; + + PVOID WerRegistrationData; + PVOID WerShipAssertPtr; + + union + { + PVOID pContextData; // WIN7 + PVOID pUnused; // WIN10 + PVOID EcCodeBitMap; // WIN11 + }; + + PVOID pImageHeaderHash; + + union + { + ULONG TracingFlags; + + struct + { + ULONG HeapTracingEnabled : 1; + ULONG CritSecTracingEnabled : 1; + ULONG LibLoaderTracingEnabled : 1; + ULONG SpareTracingBits : 29; + }; + }; + + ULONGLONG CsrServerReadOnlySharedMemoryBase; + PRTL_CRITICAL_SECTION TppWorkerpListLock; + LIST_ENTRY TppWorkerpList; + PVOID WaitOnAddressHashTable[128]; + void* TelemetryCoverageHeader; // REDSTONE3 + ULONG CloudFileFlags; + ULONG CloudFileDiagFlags; // REDSTONE4 + CHAR PlaceholderCompatibilityMode; + CHAR PlaceholderCompatibilityModeReserved[7]; + void* LeapSecondData; // REDSTONE5 + union + { + ULONG LeapSecondFlags; + + struct + { + ULONG SixtySecondEnabled : 1; + ULONG Reserved : 31; + }; + }; + + ULONG NtGlobalFlag2; + ULONGLONG ExtendedFeatureDisableMask; // since WIN11 +} PEB, *PPEB; + + +typedef struct _CLIENT_ID +{ + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, * PCLIENT_ID; + +typedef struct _ACTIVATION_CONTEXT_STACK +{ + void* ActiveFrame; + LIST_ENTRY FrameListCache; + ULONG Flags; // ACTIVATION_CONTEXT_STACK_FLAG_* + ULONG NextCookieSequenceNumber; + ULONG StackId; +} ACTIVATION_CONTEXT_STACK, * PACTIVATION_CONTEXT_STACK; + +#define GDI_BATCH_BUFFER_SIZE 310 +#define WIN32_CLIENT_INFO_LENGTH 62 +#define STATIC_UNICODE_BUFFER_LENGTH 261 + +typedef struct _GDI_TEB_BATCH +{ + ULONG Offset; + ULONG_PTR HDC; + ULONG Buffer[GDI_BATCH_BUFFER_SIZE]; +} GDI_TEB_BATCH, * PGDI_TEB_BATCH; + +typedef struct _TEB +{ + NT_TIB NtTib; + + PVOID EnvironmentPointer; + CLIENT_ID ClientId; + PVOID ActiveRpcHandle; + PVOID ThreadLocalStoragePointer; + PPEB ProcessEnvironmentBlock; + + ULONG LastErrorValue; + ULONG CountOfOwnedCriticalSections; + PVOID CsrClientThread; + PVOID Win32ThreadInfo; + ULONG User32Reserved[26]; + ULONG UserReserved[5]; + PVOID WOW32Reserved; + LCID CurrentLocale; + ULONG FpSoftwareStatusRegister; + PVOID ReservedForDebuggerInstrumentation[16]; +#ifdef _WIN64 + PVOID SystemReserved1[30]; +#else + PVOID SystemReserved1[26]; +#endif + + CHAR PlaceholderCompatibilityMode; + BOOLEAN PlaceholderHydrationAlwaysExplicit; + CHAR PlaceholderReserved[10]; + + ULONG ProxiedProcessId; + ACTIVATION_CONTEXT_STACK ActivationStack; + + UCHAR WorkingOnBehalfTicket[8]; + NTSTATUS ExceptionCode; + + PACTIVATION_CONTEXT_STACK ActivationContextStackPointer; + ULONG_PTR InstrumentationCallbackSp; + ULONG_PTR InstrumentationCallbackPreviousPc; + ULONG_PTR InstrumentationCallbackPreviousSp; +#ifdef _WIN64 + ULONG TxFsContext; +#endif + + BOOLEAN InstrumentationCallbackDisabled; +#ifdef _WIN64 + BOOLEAN UnalignedLoadStoreExceptions; +#endif +#ifndef _WIN64 + UCHAR SpareBytes[23]; + ULONG TxFsContext; +#endif + GDI_TEB_BATCH GdiTebBatch; + CLIENT_ID RealClientId; + HANDLE GdiCachedProcessHandle; + ULONG GdiClientPID; + ULONG GdiClientTID; + PVOID GdiThreadLocalInfo; + ULONG_PTR Win32ClientInfo[WIN32_CLIENT_INFO_LENGTH]; + + PVOID glDispatchTable[233]; + ULONG_PTR glReserved1[29]; + PVOID glReserved2; + PVOID glSectionInfo; + PVOID glSection; + PVOID glTable; + PVOID glCurrentRC; + PVOID glContext; + + NTSTATUS LastStatusValue; + UNICODE_STRING StaticUnicodeString; + WCHAR StaticUnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH]; + + PVOID DeallocationStack; + PVOID TlsSlots[TLS_MINIMUM_AVAILABLE]; + LIST_ENTRY TlsLinks; + + PVOID Vdm; + PVOID ReservedForNtRpc; + PVOID DbgSsReserved[2]; + + ULONG HardErrorMode; +#ifdef _WIN64 + PVOID Instrumentation[11]; +#else + PVOID Instrumentation[9]; +#endif + GUID ActivityId; + + PVOID SubProcessTag; + PVOID PerflibData; + PVOID EtwTraceData; + PVOID WinSockData; + ULONG GdiBatchCount; + + union + { + PROCESSOR_NUMBER CurrentIdealProcessor; + ULONG IdealProcessorValue; + + struct + { + UCHAR ReservedPad0; + UCHAR ReservedPad1; + UCHAR ReservedPad2; + UCHAR IdealProcessor; + }; + }; + + ULONG GuaranteedStackBytes; + PVOID ReservedForPerf; + PVOID ReservedForOle; // tagSOleTlsData + ULONG WaitingOnLoaderLock; + PVOID SavedPriorityState; + ULONG_PTR ReservedForCodeCoverage; + PVOID ThreadPoolData; + PVOID* TlsExpansionSlots; +#ifdef _WIN64 + PVOID DeallocationBStore; + PVOID BStoreLimit; +#endif + ULONG MuiGeneration; + ULONG IsImpersonating; + PVOID NlsCache; + PVOID pShimData; + ULONG HeapData; + HANDLE CurrentTransactionHandle; + void* ActiveFrame; + PVOID FlsData; + + PVOID PreferredLanguages; + PVOID UserPrefLanguages; + PVOID MergedPrefLanguages; + ULONG MuiImpersonation; + + union + { + USHORT CrossTebFlags; + USHORT SpareCrossTebBits : 16; + }; + + union + { + USHORT SameTebFlags; + + struct + { + USHORT SafeThunkCall : 1; + USHORT InDebugPrint : 1; + USHORT HasFiberData : 1; + USHORT SkipThreadAttach : 1; + USHORT WerInShipAssertCode : 1; + USHORT RanProcessInit : 1; + USHORT ClonedThread : 1; + USHORT SuppressDebugMsg : 1; + USHORT DisableUserStackWalk : 1; + USHORT RtlExceptionAttached : 1; + USHORT InitialThread : 1; + USHORT SessionAware : 1; + USHORT LoadOwner : 1; + USHORT LoaderWorker : 1; + USHORT SkipLoaderInit : 1; + USHORT SkipFileAPIBrokering : 1; + }; + }; + + PVOID TxnScopeEnterCallback; + PVOID TxnScopeExitCallback; + PVOID TxnScopeContext; + ULONG LockCount; + LONG WowTebOffset; + PVOID ResourceRetValue; + PVOID ReservedForWdf; + ULONGLONG ReservedForCrt; + GUID EffectiveContainerId; + ULONGLONG LastSleepCounter; // Win11 + ULONG SpinCallCount; + ULONGLONG ExtendedFeatureDisableMask; +} TEB, * PTEB; \ No newline at end of file diff --git a/sample/lul/xorstr.hpp b/sample/lul/xorstr.hpp new file mode 100644 index 0000000..38a71f9 --- /dev/null +++ b/sample/lul/xorstr.hpp @@ -0,0 +1,242 @@ +/* + * Copyright 2017 - 2021 Justas Masiulis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JM_XORSTR_HPP +#define JM_XORSTR_HPP + +#if defined(_M_ARM64) || defined(__aarch64__) || defined(_M_ARM) || defined(__arm__) +#include +#elif defined(_M_X64) || defined(__amd64__) || defined(_M_IX86) || defined(__i386__) +#include +#else +#error Unsupported platform +#endif + +#include +#include +#include +#include + +#define xorstr(str) ::jm::xor_string([]() { return str; }, std::integral_constant{}, std::make_index_sequence<::jm::detail::_buffer_size()>{}) +#define xorstr_(str) xorstr(str).crypt_get() + +#ifdef _MSC_VER +#define XORSTR_FORCEINLINE __forceinline +#else +#define XORSTR_FORCEINLINE __attribute__((always_inline)) inline +#endif + +namespace jm { + + namespace detail { + + template + XORSTR_FORCEINLINE constexpr std::size_t _buffer_size() + { + return ((Size / 16) + (Size % 16 != 0)) * 2; + } + + template + XORSTR_FORCEINLINE constexpr std::uint32_t key4() noexcept + { + std::uint32_t value = Seed; + for(char c : __TIME__) + value = static_cast((value ^ c) * 16777619ull); + return value; + } + + template + XORSTR_FORCEINLINE constexpr std::uint64_t key8() + { + constexpr auto first_part = key4<2166136261 + S>(); + constexpr auto second_part = key4(); + return (static_cast(first_part) << 32) | second_part; + } + + // loads up to 8 characters of string into uint64 and xors it with the key + template + XORSTR_FORCEINLINE constexpr std::uint64_t + load_xored_str8(std::uint64_t key, std::size_t idx, const CharT* str) noexcept + { + using cast_type = typename std::make_unsigned::type; + constexpr auto value_size = sizeof(CharT); + constexpr auto idx_offset = 8 / value_size; + + std::uint64_t value = key; + for(std::size_t i = 0; i < idx_offset && i + idx * idx_offset < N; ++i) + value ^= + (std::uint64_t{ static_cast(str[i + idx * idx_offset]) } + << ((i % idx_offset) * 8 * value_size)); + + return value; + } + + // forces compiler to use registers instead of stuffing constants in rdata + XORSTR_FORCEINLINE std::uint64_t load_from_reg(std::uint64_t value) noexcept + { +#if defined(__clang__) || defined(__GNUC__) + asm("" : "=r"(value) : "0"(value) :); + return value; +#else + volatile std::uint64_t reg = value; + return reg; +#endif + } + + } // namespace detail + + template + class xor_string; + + template + class xor_string, std::index_sequence> { +#ifndef JM_XORSTR_DISABLE_AVX_INTRINSICS + constexpr static inline std::uint64_t alignment = ((Size > 16) ? 32 : 16); +#else + constexpr static inline std::uint64_t alignment = 16; +#endif + + alignas(alignment) std::uint64_t _storage[sizeof...(Keys)]; + + public: + using value_type = CharT; + using size_type = std::size_t; + using pointer = CharT*; + using const_pointer = const CharT*; + + template + XORSTR_FORCEINLINE xor_string(L l, std::integral_constant, std::index_sequence) noexcept + : _storage{ ::jm::detail::load_from_reg((std::integral_constant(Keys, Indices, l())>::value))... } + {} + + XORSTR_FORCEINLINE constexpr size_type size() const noexcept + { + return Size - 1; + } + + XORSTR_FORCEINLINE void crypt() noexcept + { + // everything is inlined by hand because a certain compiler with a certain linker is _very_ slow +#if defined(__clang__) + alignas(alignment) + std::uint64_t arr[]{ ::jm::detail::load_from_reg(Keys)... }; + std::uint64_t* keys = + (std::uint64_t*)::jm::detail::load_from_reg((std::uint64_t)arr); +#else + alignas(alignment) std::uint64_t keys[]{ ::jm::detail::load_from_reg(Keys)... }; +#endif + +#if defined(_M_ARM64) || defined(__aarch64__) || defined(_M_ARM) || defined(__arm__) +#if defined(__clang__) + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : __builtin_neon_vst1q_v( + reinterpret_cast(_storage) + Indices * 2, + veorq_u64(__builtin_neon_vld1q_v(reinterpret_cast(_storage) + Indices * 2, 51), + __builtin_neon_vld1q_v(reinterpret_cast(keys) + Indices * 2, 51)), + 51)), ...); +#else // GCC, MSVC + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : vst1q_u64( + reinterpret_cast(_storage) + Indices * 2, + veorq_u64(vld1q_u64(reinterpret_cast(_storage) + Indices * 2), + vld1q_u64(reinterpret_cast(keys) + Indices * 2)))), ...); +#endif +#elif !defined(JM_XORSTR_DISABLE_AVX_INTRINSICS) + ((Indices >= sizeof(_storage) / 32 ? static_cast(0) : _mm256_store_si256( + reinterpret_cast<__m256i*>(_storage) + Indices, + _mm256_xor_si256( + _mm256_load_si256(reinterpret_cast(_storage) + Indices), + _mm256_load_si256(reinterpret_cast(keys) + Indices)))), ...); + + if constexpr(sizeof(_storage) % 32 != 0) + _mm_store_si128( + reinterpret_cast<__m128i*>(_storage + sizeof...(Keys) - 2), + _mm_xor_si128(_mm_load_si128(reinterpret_cast(_storage + sizeof...(Keys) - 2)), + _mm_load_si128(reinterpret_cast(keys + sizeof...(Keys) - 2)))); +#else + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : _mm_store_si128( + reinterpret_cast<__m128i*>(_storage) + Indices, + _mm_xor_si128(_mm_load_si128(reinterpret_cast(_storage) + Indices), + _mm_load_si128(reinterpret_cast(keys) + Indices)))), ...); +#endif + } + + XORSTR_FORCEINLINE const_pointer get() const noexcept + { + return reinterpret_cast(_storage); + } + + XORSTR_FORCEINLINE pointer get() noexcept + { + return reinterpret_cast(_storage); + } + + XORSTR_FORCEINLINE pointer crypt_get() noexcept + { + // crypt() is inlined by hand because a certain compiler with a certain linker is _very_ slow +#if defined(__clang__) + alignas(alignment) + std::uint64_t arr[]{ ::jm::detail::load_from_reg(Keys)... }; + std::uint64_t* keys = + (std::uint64_t*)::jm::detail::load_from_reg((std::uint64_t)arr); +#else + alignas(alignment) std::uint64_t keys[]{ ::jm::detail::load_from_reg(Keys)... }; +#endif + +#if defined(_M_ARM64) || defined(__aarch64__) || defined(_M_ARM) || defined(__arm__) +#if defined(__clang__) + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : __builtin_neon_vst1q_v( + reinterpret_cast(_storage) + Indices * 2, + veorq_u64(__builtin_neon_vld1q_v(reinterpret_cast(_storage) + Indices * 2, 51), + __builtin_neon_vld1q_v(reinterpret_cast(keys) + Indices * 2, 51)), + 51)), ...); +#else // GCC, MSVC + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : vst1q_u64( + reinterpret_cast(_storage) + Indices * 2, + veorq_u64(vld1q_u64(reinterpret_cast(_storage) + Indices * 2), + vld1q_u64(reinterpret_cast(keys) + Indices * 2)))), ...); +#endif +#elif !defined(JM_XORSTR_DISABLE_AVX_INTRINSICS) + ((Indices >= sizeof(_storage) / 32 ? static_cast(0) : _mm256_store_si256( + reinterpret_cast<__m256i*>(_storage) + Indices, + _mm256_xor_si256( + _mm256_load_si256(reinterpret_cast(_storage) + Indices), + _mm256_load_si256(reinterpret_cast(keys) + Indices)))), ...); + + if constexpr(sizeof(_storage) % 32 != 0) + _mm_store_si128( + reinterpret_cast<__m128i*>(_storage + sizeof...(Keys) - 2), + _mm_xor_si128(_mm_load_si128(reinterpret_cast(_storage + sizeof...(Keys) - 2)), + _mm_load_si128(reinterpret_cast(keys + sizeof...(Keys) - 2)))); +#else + ((Indices >= sizeof(_storage) / 16 ? static_cast(0) : _mm_store_si128( + reinterpret_cast<__m128i*>(_storage) + Indices, + _mm_xor_si128(_mm_load_si128(reinterpret_cast(_storage) + Indices), + _mm_load_si128(reinterpret_cast(keys) + Indices)))), ...); +#endif + + return (pointer)(_storage); + } + }; + + template + xor_string(L l, std::integral_constant, std::index_sequence) -> xor_string< + std::remove_const_t>, + Size, + std::integer_sequence()...>, + std::index_sequence>; + +} // namespace jm + +#endif // include guard diff --git a/sample/x64/Release/hook.dll b/sample/x64/Release/hook.dll new file mode 100644 index 0000000..ba54de6 Binary files /dev/null and b/sample/x64/Release/hook.dll differ diff --git a/sample/x64/Release/lul.exe b/sample/x64/Release/lul.exe new file mode 100644 index 0000000..bd738ef Binary files /dev/null and b/sample/x64/Release/lul.exe differ