base on A proof of concept demonstrating the DLL-load proxying using undocumented Syscalls. # Proxy DLL Loads A repository with different scripts to demonstrate the DLL-load proxying using undocumented Syscalls or VEH. This repo is not about teaching you what DLL Load proxying is and how it works, it is greatly explained on [this blogpost](https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing/). Instead, the main focus is on explaining how the DLL be loaded using VEH and on finding undocumented callback functions by reversing the DLLs and creating your own version. Below are the two methods used to proxy DLL Load: ## 1. VEH We'll leverage the **VEH (Vectored Exception Handler)** to modify the context, especially RIP register to take us to the LoadLibraryA, and the RCX to hold the function's argument (module name) of `LoadLibraryA`. To trigger our exception, VirtualProtect is used to set the page to `PAGE_GUARD`, thus triggering the `STATUS_GUARD_PAGE_VIOLATION`. ## 2. Tp* Syscalls ### Hunting for undocumented syscalls Before getting in directly to reversing the DLLs, we need to first know what to look for. We can start by looking at the Microsoft documentation (MSDN), which provides an excellent [example](https://learn.microsoft.com/en-us/windows/win32/procthread/using-the-thread-pool-functions?source=recommendations) of a custom thread pool, which creates a work item and a thread pool timer. The code alone is also suitable for archiving the execution of `LoadLibrary` via callback functions, but as already known, the userland functions are prone to hooking. So using their respective syscalls would be a better approach. Looking at the MSDN documentation, the example code uses the following Win32API functions: ``` CreateThreadpool SetThreadpoolThreadMaximum SetThreadpoolThreadMinimum CreateThreadpoolCleanupGroup CreateThreadpoolTimer SetThreadpoolTimer CloseThreadpoolCleanupGroupMembers ``` If you use [IDA](https://hex-rays.com/ida-free/), open kernel32.dll, go to "Exports" and search for the mentioned Win32 APIs, in this case `CreateThreadpool`. Double-clicking the function redirect us to its dissassembled code: ![Screenshot from 2023-10-23 10-33-17](https://github.com/kleiton0x00/Proxy-DLL-Loads/assets/37262788/8422c046-13df-45fd-8c48-1371f52e9f43) Through the assembly instructions, we see the `TpAllocPool` syscall being executed: `call cs:__imp_TpAllocPool` If you repeat the process with the other functions, you will end up with the following syscalls: ``` Ntdll!TpAllocPool Ntdll!TpSetPoolMaxThreads Ntdll!TpSetPoolMinThreads Ntdll!TpAllocCleanupGroup Ntdll!TpAllocTimer Ntdll!TpSetTimer Ntdll!TpReleaseCleanupGroupMembers ``` Now you have everything you need to start creating your own version of proxying the DLL Loads. You can look at [this documentation](https://processhacker.sourceforge.io/doc/nttp_8h.html#adad18de6710381f08cf36a0fa72e7529) from Process Hacker to help you implement the undocumented syscalls in your code. ### Debugging Set a breakpoint before the assembly code in Callbackstub get's executed. Look at right tab of [x64dbg](https://x64dbg.com/) as the registers are being populated. https://github.com/kleiton0x00/Proxy-DLL-Loads/assets/37262788/73af5145-2b1c-486b-ae9f-583c4e865df6 ``` RAX -> pointer to LoadLibraryA RCX -> library name string ``` ### Result ![Screenshot from 2023-10-21 20-21-05](https://github.com/kleiton0x00/Proxy-DLL-Loads/assets/37262788/2db0e36d-53e9-4697-b976-b1260f5bfcdd) ## Resources https://0xdarkvortex.dev/proxying-dll-loads-for-hiding-etwti-stack-tracing/ https://github.com/hlldz/misc/tree/main/proxy_calls https://processhacker.sourceforge.io/doc/nttp_8h.html#adad18de6710381f08cf36a0fa72e7529 ## OPSEC Considerations - Custom implmentation of GetModuleHandleA/GetProcAddress - Don't use VirtualProtect to trigger the VEH ## Detections https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_library_loaded_via_a_callback_function.toml ", Assign "at most 3 tags" to the expected json: {"id":"4068","tags":[]} "only from the tags list I provide: [{"id":77,"name":"3d"},{"id":89,"name":"agent"},{"id":17,"name":"ai"},{"id":54,"name":"algorithm"},{"id":24,"name":"api"},{"id":44,"name":"authentication"},{"id":3,"name":"aws"},{"id":27,"name":"backend"},{"id":60,"name":"benchmark"},{"id":72,"name":"best-practices"},{"id":39,"name":"bitcoin"},{"id":37,"name":"blockchain"},{"id":1,"name":"blog"},{"id":45,"name":"bundler"},{"id":58,"name":"cache"},{"id":21,"name":"chat"},{"id":49,"name":"cicd"},{"id":4,"name":"cli"},{"id":64,"name":"cloud-native"},{"id":48,"name":"cms"},{"id":61,"name":"compiler"},{"id":68,"name":"containerization"},{"id":92,"name":"crm"},{"id":34,"name":"data"},{"id":47,"name":"database"},{"id":8,"name":"declarative-gui "},{"id":9,"name":"deploy-tool"},{"id":53,"name":"desktop-app"},{"id":6,"name":"dev-exp-lib"},{"id":59,"name":"dev-tool"},{"id":13,"name":"ecommerce"},{"id":26,"name":"editor"},{"id":66,"name":"emulator"},{"id":62,"name":"filesystem"},{"id":80,"name":"finance"},{"id":15,"name":"firmware"},{"id":73,"name":"for-fun"},{"id":2,"name":"framework"},{"id":11,"name":"frontend"},{"id":22,"name":"game"},{"id":81,"name":"game-engine "},{"id":23,"name":"graphql"},{"id":84,"name":"gui"},{"id":91,"name":"http"},{"id":5,"name":"http-client"},{"id":51,"name":"iac"},{"id":30,"name":"ide"},{"id":78,"name":"iot"},{"id":40,"name":"json"},{"id":83,"name":"julian"},{"id":38,"name":"k8s"},{"id":31,"name":"language"},{"id":10,"name":"learning-resource"},{"id":33,"name":"lib"},{"id":41,"name":"linter"},{"id":28,"name":"lms"},{"id":16,"name":"logging"},{"id":76,"name":"low-code"},{"id":90,"name":"message-queue"},{"id":42,"name":"mobile-app"},{"id":18,"name":"monitoring"},{"id":36,"name":"networking"},{"id":7,"name":"node-version"},{"id":55,"name":"nosql"},{"id":57,"name":"observability"},{"id":46,"name":"orm"},{"id":52,"name":"os"},{"id":14,"name":"parser"},{"id":74,"name":"react"},{"id":82,"name":"real-time"},{"id":56,"name":"robot"},{"id":65,"name":"runtime"},{"id":32,"name":"sdk"},{"id":71,"name":"search"},{"id":63,"name":"secrets"},{"id":25,"name":"security"},{"id":85,"name":"server"},{"id":86,"name":"serverless"},{"id":70,"name":"storage"},{"id":75,"name":"system-design"},{"id":79,"name":"terminal"},{"id":29,"name":"testing"},{"id":12,"name":"ui"},{"id":50,"name":"ux"},{"id":88,"name":"video"},{"id":20,"name":"web-app"},{"id":35,"name":"web-server"},{"id":43,"name":"webassembly"},{"id":69,"name":"workflow"},{"id":87,"name":"yaml"}]" returns me the "expected json"