{"id":265,"date":"2024-03-18T22:54:46","date_gmt":"2024-03-18T22:54:46","guid":{"rendered":"https:\/\/fin3ss3g0d.net\/?p=265"},"modified":"2024-03-26T16:20:52","modified_gmt":"2024-03-26T16:20:52","slug":"weaponizing-windows-thread-pool-apis-proxying-dll-loads","status":"publish","type":"post","link":"https:\/\/fin3ss3g0d.net\/index.php\/2024\/03\/18\/weaponizing-windows-thread-pool-apis-proxying-dll-loads\/","title":{"rendered":"Weaponizing Windows Thread Pool APIs: Proxying DLL Loads Using I\/O Completion Callbacks"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-post\" data-elementor-id=\"265\" class=\"elementor elementor-265\">\n\t\t\t\t<div class=\"elementor-element elementor-element-17b01fb e-flex e-con-boxed e-con e-parent\" data-id=\"17b01fb\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-152ec99 elementor-widget elementor-widget-text-editor\" data-id=\"152ec99\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In today&#8217;s blog, we are going to be covering the topic of proxying DLL loads using the Windows thread pool API with C++\/assembly. This specific example is going to use an I\/O completion callback and is a complementary article for my GitHub repository <a href=\"https:\/\/github.com\/fin3ss3g0d\/IoDllProxyLoad\" target=\"_blank\" rel=\"noopener\">here<\/a>. Before we jump in, let me introduce some background information.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-cb239b9 e-flex e-con-boxed e-con e-parent\" data-id=\"cb239b9\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-c8d0ae2 elementor-widget elementor-widget-heading\" data-id=\"c8d0ae2\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Background<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-66a58ab e-flex e-con-boxed e-con e-parent\" data-id=\"66a58ab\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-be8f173 elementor-widget elementor-widget-text-editor\" data-id=\"be8f173\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><a href=\"https:\/\/twitter.com\/ninjaparanoid\" target=\"_blank\" rel=\"noopener\">Chetan Nayak<\/a> has covered this topic in depth&nbsp;<a href=\"https:\/\/0xdarkvortex.dev\/proxying-dll-loads-for-hiding-etwti-stack-tracing\/\" target=\"_blank\" rel=\"noopener\">here<\/a> and will serve as the inspiration behind this research. Getting to the core of what this research is really about, we have to imagine the situation where we have a payload in a position independent format that will ultimately load DLLs. When we are loading a shellcode, we must allocate a region of memory that is RX\/RWX. Better OPSEC would be to use a RX region. Nonetheless, if we were to load these DLLs using a standard function call such as&nbsp;<em>LoadLibraryA&nbsp;<\/em>there will be a&nbsp;<em>call<\/em> assembly instruction executed to invoke&nbsp;<em>LoadLibraryA<\/em>. This would place the return address of the caller on the stack and would originate from a RX region of memory (our shellcode). Some of you may be wondering, so what? What is the problem here?<\/p>\n<p>The problem is an EDR can hook DLL loading functions such as&nbsp;<em>LoadLibraryA<\/em> then examine the stack to determine the return address of the caller, which would point back to your RX shellcode region. It is also going to scan this memory region for anything malicious and against known payload signatures which can result in you getting caught &#8211; simply from loading specific DLLs in this way. So, you have unhooked a given DLL containing the DLL loading function, you should be fine right? This is not the only thing you have to be worried about, remember those very annoying things called kernel callbacks? There is a callback named <em>PsSetLoadImageNotifyRoutine(Ex)<\/em><em>&nbsp;<\/em>allowing EDRs to register a callback function that gets called every time a DLL gets loaded in a process. Once the EDR examines the stack after the callback gets triggered, it can get the return address of the caller who loaded the DLL and examine this memory region the same as I mentioned before. In comes proxy DLL loading to address these OPSEC issues.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8a25038 elementor-widget elementor-widget-heading\" data-id=\"8a25038\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">The Windows Thread Pool API<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9f6c3c4 elementor-widget elementor-widget-text-editor\" data-id=\"9f6c3c4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>The Windows Thread Pool API provides a high-level abstraction for managing a set of worker threads that can be used to perform various asynchronous tasks or work items. This API simplifies the management of thread resources within applications, allowing developers to focus on application logic rather than the complexities of thread management, synchronization, and concurrency control. The API is part of the Windows operating system, and it enables efficient execution of callbacks on worker threads drawn from a pool managed by the system.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0e92a32 elementor-widget elementor-widget-heading\" data-id=\"0e92a32\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h3 class=\"elementor-heading-title elementor-size-default\">Why Certain Callback Functions Exist<\/h3>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-ec47f39 elementor-widget elementor-widget-text-editor\" data-id=\"ec47f39\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<ol><li><p><strong>Work Item Callbacks<\/strong>: These functions are executed when a work item is processed by a worker thread. They encapsulate the task or computation that needs to be performed asynchronously, allowing the application to offload work from the main thread and improve responsiveness or throughput.<\/p><\/li><li><p><strong>Timer Callbacks<\/strong>: Timer callbacks are executed when a timer expires. This is useful for periodic updates, maintenance tasks, or delayed execution of code without blocking a thread by sleeping.<\/p><\/li><li><p><strong>I\/O Completion Callbacks<\/strong>: These functions are executed upon the completion of asynchronous I\/O operations. They allow applications to initiate I\/O operations without blocking and to process the results asynchronously, which is critical for maintaining high performance in I\/O-intensive applications.<\/p><\/li><li><p><strong>Wait Callbacks<\/strong>: Wait callbacks are executed when a wait object (such as an event or mutex) becomes signaled. This mechanism is used to asynchronously wait for events or conditions without blocking a worker thread, facilitating synchronization among threads or reacting to external events.<\/p><\/li><\/ol>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9b6c5e4 elementor-widget elementor-widget-heading\" data-id=\"9b6c5e4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">You Complete Me<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6e84a4c elementor-widget elementor-widget-text-editor\" data-id=\"6e84a4c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>As I previously mentioned, there are multiple callbacks that exist. The example I am sharing with you is going to be an I\/O completion callback example. To utilize an I\/O completion callback with the Windows Thread Pool API effectively, you essentially need to create a thread pool I\/O object via <em>CreateThreadpoolIo<\/em>, associating it with a file handle that supports overlapped I\/O operations. This setup allows for the execution of asynchronous I\/O operations, such as file reads or writes, in a manner that does not block the executing thread. The key to this process is the <em>OVERLAPPED<\/em> structure, which the system uses to track the progress of these operations. When initiating any asynchronous I\/O, you must first call <em>StartThreadpoolIo<\/em>\u00a0to prepare the thread pool for the incoming operation, ensuring the system is ready to handle the completion callback properly.<\/p><p>Your callback function, defined to match the thread pool API\u2019s expectations, will be invoked upon the completion of the I\/O operation. This function will receive details about the operation, including the outcome and the number of bytes transferred, allowing for any necessary post-operation processing. After the operations and their associated callbacks have been completed, cleaning up resources by closing the thread pool I\/O object and any open file handles is essential for resource management and to prevent leaks.<\/p><p>In summary, leveraging the Windows Thread Pool API for I\/O completion callbacks involves preparing for asynchronous operations with a properly configured file handle and <em>OVERLAPPED<\/em> structure, managing the lifecycle of the operation with <em>StartThreadpoolIo<\/em> and <em>CloseThreadpoolIo<\/em>, and handling the results in a predefined callback function. This approach facilitates efficient, non-blocking I\/O operations within Windows applications.<\/p><p>Now that we know what is required, let&#8217;s talk about the implementation. As I previously mentioned, the code for this example is in a GitHub repository of mine <a href=\"https:\/\/github.com\/fin3ss3g0d\/IoDllProxyLoad\" target=\"_blank\" rel=\"noopener\">here<\/a>, but the C++ code is as follows:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-43ce5ca e-flex e-con-boxed e-con e-parent\" data-id=\"43ce5ca\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-329b79f elementor-widget elementor-widget-html\" data-id=\"329b79f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/styles\/atom-one-dark.min.css\">\r\n<style>\r\n    .copy-button {\r\n        position: absolute;\r\n        top: 15px; \/* Adjusted to fine-tune the position *\/\r\n        right: 10px; \/* Adjusted to fine-tune the position *\/\r\n        background: #ffffff66; \/* Semi-transparent background for an integrated look *\/\r\n        color: #011627; \/* Dark text for contrast *\/\r\n        border: 1px solid #ffffff66; \/* Subtle border to fit the theme *\/\r\n        border-radius: 4px;\r\n        cursor: pointer;\r\n        font-family: 'Fira Code', monospace;\r\n        padding: 5px 10px; \/* Slightly larger padding for a rounded look *\/\r\n        font-size: 0.8em;\r\n        outline: none; \/* Remove the outline for a cleaner appearance *\/\r\n        transition: background-color 0.2s, color 0.2s, transform 0.2s; \/* Smooth transition for interaction *\/\r\n    }\r\n\r\n    .copy-button:hover {\r\n        background: #ffffff; \/* Full white background on hover for visibility *\/\r\n        color: #011627; \/* Dark text on hover for contrast *\/\r\n        transform: scale(1.05); \/* Slight increase in size on hover for effect *\/\r\n    }\r\n\r\n    pre {\r\n        position: relative; \/* Establish relative positioning context *\/\r\n        background: #011627; \/* Dark background color for the code block *\/\r\n        border-left: 4px solid #00ccff; \/* Accent border color *\/\r\n        overflow: auto; \/* Scroll bars as needed *\/\r\n        margin: 1em 0; \/* Space around the code block *\/\r\n        padding: 0em; \/* Space inside the code block *\/\r\n        border-radius: 4px; \/* Rounded corners for the code block *\/\r\n    }\r\n\r\n    code {\r\n        color: #ffffff; \/* Text color for the code *\/\r\n        font-family: 'Fira Code', monospace; \/* Font for the code *\/\r\n        line-height: 1.5; \/* Spacing between lines of code *\/\r\n    }\r\n<\/style>\r\n\t\r\n<pre>\r\n    <button class=\"copy-button\" onclick=\"copyToClipboard(this)\">Copy<\/button>\r\n    <code class=\"hljs language-cpp\">#include &ltwindows.h&gt\r\n#include &ltstdio.h&gt\r\n\r\nextern \"C\" void CALLBACK IoCompletionCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PVOID Overlapped, ULONG IoResult, ULONG_PTR NumberOfBytesTransferred, PTP_IO Io);\r\nvoid StartRead(HANDLE pipe, PTP_IO tpIo, OVERLAPPED* overlapped, char* buffer);\r\nvoid CALLBACK ClientWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work);\r\n\r\nPVOID pLoadLibraryA;\r\nHANDLE g_WriteCompleteEvent; \/\/ Global event to signal completion of write operation\r\n\r\ntypedef struct LOAD_CONTEXT {\r\n    char* DllName;\r\n    PVOID pLoadLibraryA;\r\n};\r\n\r\nint main()\r\n{\r\n    HANDLE pipe;\r\n    PTP_IO tpIo = NULL;\r\n    OVERLAPPED overlapped = { 0 };\r\n    char buffer[128] = { 0 };\r\n\r\n    \/\/ Get the address of LoadLibraryA\r\n    pLoadLibraryA = GetProcAddress(GetModuleHandleA(\"kernel32\"), \"LoadLibraryA\");\r\n\r\n    \/\/ Prepare the LOAD_CONTEXT structure\r\n    LOAD_CONTEXT loadContext;\r\n    loadContext.DllName = (char*)\"wininet.dll\";\r\n    loadContext.pLoadLibraryA = pLoadLibraryA;\r\n\r\n    \/\/ Create a global event to signal when the write operation is complete\r\n    g_WriteCompleteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r\n    if (g_WriteCompleteEvent == NULL) {\r\n        printf(\"Failed to create write complete event\\n\");\r\n        return 1;\r\n    }\r\n\r\n    \/\/ Create a named pipe with FILE_FLAG_OVERLAPPED flag\r\n    pipe = CreateNamedPipe(\r\n        TEXT(\"\\\\\\\\.\\\\pipe\\\\MyPipe\"),\r\n        PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,\r\n        PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,\r\n        1,  \/\/ Number of instances\r\n        4096,  \/\/ Out buffer size\r\n        4096,  \/\/ In buffer size\r\n        0,  \/\/ Timeout in milliseconds\r\n        NULL); \/\/ Default security attributes\r\n\r\n    if (pipe == INVALID_HANDLE_VALUE) {\r\n        printf(\"Failed to create named pipe\\n\");\r\n        CloseHandle(g_WriteCompleteEvent);\r\n        return 1;\r\n    }\r\n\r\n    \/\/ Create an event for the OVERLAPPED structure\r\n    overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);\r\n    if (overlapped.hEvent == NULL) {\r\n        printf(\"Failed to create event\\n\");\r\n        CloseHandle(pipe);\r\n        CloseHandle(g_WriteCompleteEvent);\r\n        return 1;\r\n    }\r\n\r\n    \/\/ Associate the pipe with the thread pool\r\n    tpIo = CreateThreadpoolIo(pipe, IoCompletionCallback, &loadContext, NULL);\r\n    if (tpIo == NULL) {\r\n        printf(\"Failed to associate pipe with thread pool\\n\");\r\n        CloseHandle(overlapped.hEvent);\r\n        CloseHandle(pipe);\r\n        CloseHandle(g_WriteCompleteEvent);\r\n        return 1;\r\n    }\r\n\r\n    \/\/ Create threadpool work item for the client code\r\n    PTP_WORK clientWork = CreateThreadpoolWork(ClientWorkCallback, NULL, NULL);\r\n    if (clientWork == NULL) {\r\n        printf(\"Failed to create threadpool work item\\n\");\r\n        CloseThreadpoolIo(tpIo);\r\n        CloseHandle(overlapped.hEvent);\r\n        CloseHandle(pipe);\r\n        CloseHandle(g_WriteCompleteEvent);\r\n        return 1;\r\n    }\r\n\r\n    \/\/ Submit the client work item to the thread pool\r\n    SubmitThreadpoolWork(clientWork);\r\n\r\n    \/\/ Wait for the client work item to signal that the write operation is complete\r\n    WaitForSingleObject(g_WriteCompleteEvent, INFINITE);\r\n\r\n    \/\/ Start an asynchronous read operation\r\n    StartRead(pipe, tpIo, &overlapped, buffer);\r\n    printf(\"Pipe buffer: %s\\n\", buffer);\r\n\r\n    \/\/ Wait for the read operation to complete\r\n    WaitForSingleObject(overlapped.hEvent, INFINITE);\r\n\r\n    \/\/ Wait for client work to complete\r\n    WaitForThreadpoolWorkCallbacks(clientWork, FALSE);\r\n    CloseThreadpoolWork(clientWork);\r\n\r\n    \/\/ Cleanup\r\n    CloseThreadpoolIo(tpIo);\r\n    CloseHandle(overlapped.hEvent);\r\n    CloseHandle(pipe);\r\n    CloseHandle(g_WriteCompleteEvent);\r\n\r\n    printf(\"wininet.dll should be loaded! Input any key to exit...\\n\");\r\n    getchar();\r\n\r\n    return 0;\r\n}\r\n\r\nvoid StartRead(HANDLE pipe, PTP_IO tpIo, OVERLAPPED* overlapped, char* buffer)\r\n{\r\n    DWORD bytesRead = 0;\r\n    StartThreadpoolIo(tpIo);\r\n    if (!ReadFile(pipe, buffer, 128, &bytesRead, overlapped) && GetLastError() != ERROR_IO_PENDING) {\r\n        printf(\"ReadFile failed, error %lu\\n\", GetLastError());\r\n        CancelThreadpoolIo(tpIo);\r\n    }\r\n}\r\n\r\nvoid CALLBACK ClientWorkCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WORK Work)\r\n{\r\n    \/\/ Open the named pipe\r\n    HANDLE pipe = CreateFile(\r\n        TEXT(\"\\\\\\\\.\\\\pipe\\\\MyPipe\"),\r\n        GENERIC_WRITE,\r\n        0,\r\n        NULL,\r\n        OPEN_EXISTING,\r\n        FILE_ATTRIBUTE_NORMAL,\r\n        NULL);\r\n\r\n    if (pipe == INVALID_HANDLE_VALUE) {\r\n        printf(\"Client failed to connect to pipe\\n\");\r\n        return;\r\n    }\r\n\r\n    const char message[] = \"Hello from the pipe!\";\r\n    DWORD bytesWritten;\r\n    if (!WriteFile(pipe, message, sizeof(message), &bytesWritten, NULL)) {\r\n        printf(\"Client WriteFile failed, error: %lu\\n\", GetLastError());\r\n    }\r\n    else {\r\n        printf(\"Client wrote to pipe\\n\");\r\n    }\r\n\r\n    \/\/ Signal that the write operation is complete\r\n    SetEvent(g_WriteCompleteEvent);\r\n\r\n    CloseHandle(pipe);\r\n}\r\n<\/code>\r\n<\/pre>\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/highlight.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/languages\/cpp.min.js\"><\/script>\r\n\r\n<script>\r\n    hljs.highlightAll();\r\n\r\n    function copyToClipboard(button) {\r\n        \/\/ Get the text from the sibling code element\r\n        var codeBlock = button.nextElementSibling;\r\n        var range = document.createRange();\r\n        range.selectNode(codeBlock);\r\n        window.getSelection().removeAllRanges();\r\n        window.getSelection().addRange(range);\r\n        document.execCommand('copy');\r\n        window.getSelection().removeAllRanges();\r\n\r\n        \/\/ Update the button text to show it was copied\r\n        button.textContent = 'Copied!';\r\n        setTimeout(function() {\r\n            button.textContent = 'Copy';\r\n        }, 2000);\r\n    }\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9465e1f elementor-widget elementor-widget-text-editor\" data-id=\"9465e1f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>Rather than going through the code line by line, I am going to focus on the key parts for this article. The proof of concept also includes some assembly code, which can be seen below:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-e08862c e-flex e-con-boxed e-con e-parent\" data-id=\"e08862c\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-9e704ed elementor-widget elementor-widget-html\" data-id=\"9e704ed\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/styles\/atom-one-dark.min.css\">\r\n<style>\r\n    .copy-button {\r\n        position: absolute;\r\n        top: 15px; \/* Adjusted to fine-tune the position *\/\r\n        right: 10px; \/* Adjusted to fine-tune the position *\/\r\n        background: #ffffff66; \/* Semi-transparent background for an integrated look *\/\r\n        color: #011627; \/* Dark text for contrast *\/\r\n        border: 1px solid #ffffff66; \/* Subtle border to fit the theme *\/\r\n        border-radius: 4px;\r\n        cursor: pointer;\r\n        font-family: 'Fira Code', monospace;\r\n        padding: 5px 10px; \/* Slightly larger padding for a rounded look *\/\r\n        font-size: 0.8em;\r\n        outline: none; \/* Remove the outline for a cleaner appearance *\/\r\n        transition: background-color 0.2s, color 0.2s, transform 0.2s; \/* Smooth transition for interaction *\/\r\n    }\r\n\r\n    .copy-button:hover {\r\n        background: #ffffff; \/* Full white background on hover for visibility *\/\r\n        color: #011627; \/* Dark text on hover for contrast *\/\r\n        transform: scale(1.05); \/* Slight increase in size on hover for effect *\/\r\n    }\r\n\r\n    pre {\r\n        position: relative; \/* Establish relative positioning context *\/\r\n        background: #011627; \/* Dark background color for the code block *\/\r\n        border-left: 4px solid #00ccff; \/* Accent border color *\/\r\n        overflow: auto; \/* Scroll bars as needed *\/\r\n        margin: 1em 0; \/* Space around the code block *\/\r\n        padding: 0em; \/* Space inside the code block *\/\r\n        border-radius: 4px; \/* Rounded corners for the code block *\/\r\n    }\r\n\r\n    code {\r\n        color: #ffffff; \/* Text color for the code *\/\r\n        font-family: 'Fira Code', monospace; \/* Font for the code *\/\r\n        line-height: 1.5; \/* Spacing between lines of code *\/\r\n    }\r\n<\/style>\r\n\t\r\n<pre>\r\n    <button class=\"copy-button\" onclick=\"copyToClipboard(this)\">Copy<\/button>\r\n    <code class=\"hljs language-x86asm\">.CODE\r\n\r\n; LOAD_CONTEXT* is passed in RDX\r\nIoCompletionCallback PROC\r\n    ; Extract the 'DllName' member (first member of the structure) to RCX\r\n    mov rcx, [rdx]       ; Moves the address pointed to by DllName into RCX\r\n\r\n    ; Extract the 'pLoadLibraryA' member (second member of the structure) into RAX\r\n    mov rax, [rdx + 8]   ; Assumes 64-bit pointers, so offset is 8 bytes\r\n\r\n    ; Now RCX contains the address of the dll string,\r\n    ; and RAX contains the address to jump to (pLoadLibraryA)\r\n\r\n    xor rdx, rdx        ; Clear RDX\r\n\r\n    ; Jump to LoadLibraryA address, avoiding call instruction and return address placement on stack\r\n    jmp rax\r\nIoCompletionCallback ENDP\r\n\r\nEND<\/code>\r\n<\/pre>\r\n\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/highlight.min.js\"><\/script>\r\n<!-- Include the assembly language syntax file (replace 'x86asm' with the specific assembly variant if needed) -->\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/highlight.js\/11.4.0\/languages\/x86asm.min.js\"><\/script>\r\n\r\n<script>\r\n    hljs.highlightAll();\r\n\r\n    function copyToClipboard(button) {\r\n        \/\/ Get the text from the sibling code element\r\n        var codeBlock = button.nextElementSibling;\r\n        var range = document.createRange();\r\n        range.selectNode(codeBlock);\r\n        window.getSelection().removeAllRanges();\r\n        window.getSelection().addRange(range);\r\n        document.execCommand('copy');\r\n        window.getSelection().removeAllRanges();\r\n\r\n        \/\/ Update the button text to show it was copied\r\n        button.textContent = 'Copied!';\r\n        setTimeout(function() {\r\n            button.textContent = 'Copy';\r\n        }, 2000);\r\n    }\r\n<\/script>\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-328a21b elementor-widget elementor-widget-text-editor\" data-id=\"328a21b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>A lot of the code is boilerplate code that is necessary to perform an I\/O completion callback with the Windows thread pool API using named pipes. This can also be done with any I\/O objects relevant to the Windows API including files and sockets. The parts we want to focus on for this example would be the <em>LOAD_CONTEXT<\/em> structure and how it is passed to\u00a0<em>CreateThreadPoolIo\u00a0<\/em>and which argument it will be when it is passed to the callback function. Also notice that our callback function\u00a0<em>IoCompletionCallback<\/em> is marked as external and is defined within the assembly code. The structure will be passed to this assembly function when the callback is triggered.<\/p><p>When we call\u00a0<em>CreateThreadpoolIo<\/em>, we pass it the pipe handle, the callback function, and the\u00a0<em>LOAD_CONTEXT\u00a0<\/em>structure. This instructs the thread pool that a pointer to the <em>loadContext<\/em> variable will be passed as the <em>Context\u00a0<\/em>argument to the I\/O completion callback function, or the second argument. This is important information as we are about to find out.<\/p><p>If you look at the\u00a0<em>LOAD_CONTEXT\u00a0<\/em>structure it contains two members:\u00a0<em>char* DllName<\/em>\u00a0and<em>\u00a0<\/em><em>PVOID pLoadLibraryA. <\/em>We have populated\u00a0<em>DllName<\/em> with a pointer to the string &#8220;wininet.dll&#8221; and we have populated\u00a0<em>pLoadLibraryA\u00a0<\/em>with the memory address of\u00a0<em>LoadLibraryA<\/em>.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-32a2aad elementor-widget elementor-widget-heading\" data-id=\"32a2aad\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Avoiding the Call Instruction<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5d6bb60 elementor-widget elementor-widget-text-editor\" data-id=\"5d6bb60\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>As I mentioned previously, what ultimately ends up leading to our detection by loading a DLL from a shellcode region on the stack is a <em>call<\/em> assembly instruction.<\/p><p>The <em>call<\/em> instruction in assembly language is used to invoke a subroutine (a procedure or function within a program). The primary purpose of this instruction is to transfer control from the calling function to the subroutine, allowing for code reuse, modular programming, and organized control flow within a program. Subroutines can perform tasks and return results without the need to replicate code across various parts of a program.<\/p><p>When a <em>call<\/em> instruction is executed, the processor does two main things:<\/p><p>1. <strong>Pushes the return address onto the stack<\/strong>: The return address is the address of the instruction immediately following the <em>call<\/em> instruction in the calling function. This address is saved on the stack so that, once the subroutine has completed its execution, the program knows where to return to continue executing the calling function. The stack is used for this purpose because it supports the nested calling of subroutines (functions calling other functions) in a Last In, First Out (LIFO) manner. This is essential for supporting recursive function calls and for managing the return addresses of multiple nested subroutines.<\/p><p>2. <strong>Transfers control to the subroutine<\/strong>: The program counter (PC) or instruction pointer (IP) is set to the address of the subroutine being called, causing execution to jump to that location.<\/p><p>After the subroutine has finished executing, a <em>ret<\/em> (return) instruction is typically used to pop the return address off the stack and jump back to that address, resuming execution of the calling function just after the point where it called the subroutine.<\/p><p>With this information in mind, we want to avoid a\u00a0<em>call<\/em>\u00a0instruction for our objective. We do not want our return address on the stack when we call <em>LoadLibraryA<\/em> so that anything looking at the stack is not led to our shellcode memory region for analysis. This is where the assembly function and the\u00a0<em>LOAD_CONTEXT\u00a0<\/em>structure come into play.<\/p><p>At the time the callback function (<em>IoCompletionCallback<\/em>) gets called, the\u00a0<em>LOAD_CONTEXT<\/em> structure is passed as the second argument to the callback. When we look at the 64-bit Windows calling convention in terms of the stack, this means that structure is going to be contained in the\u00a0<em>rdx <\/em>register.<\/p><p>The assembly function first extracts the <em>DllName<\/em> member of the structure and places it into\u00a0<em>rcx<\/em>. This is going to simulate placing the DLL string as the first argument to<em> LoadLibraryA\u00a0<\/em>when we later perform a\u00a0<em>jmp<\/em>. The next thing the assembly function does is it extracts the second member of the <em>LOAD_CONTEXT\u00a0<\/em>structure, which is the memory address of <em>LoadLibraryA<\/em> into\u00a0<em>rax<\/em>. Before performing the\u00a0<em>jmp<\/em>, we clear the\u00a0<em>rdx\u00a0<\/em>register as\u00a0<em>LoadLibraryA<\/em> only takes one argument and without doing this it would cause the function to throw an error or fail. The last thing the assembly function does is it performs a <em>jmp rax <\/em>instruction with our specially crafted stack. With the DLL name string inside\u00a0<em>rcx<\/em>, we simulate a\u00a0<em>LoadLibraryA\u00a0<\/em>call with the given DLL name as the first argument and we load the DLL.<\/p><p>By loading the DLL in this way, we have essentially &#8220;proxied&#8221; the load through the callback function. Normally, what you would see in this case would be a stack frame for the I\/O completion callback function when examining the stack after the DLL load because the return address would have been placed on the stack. But if we examine the stack in this case, we see no stack frame for the callback function and we have achieved a clean stack with nothing pointing to our shellcode memory region:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3b5043a e-flex e-con-boxed e-con e-parent\" data-id=\"3b5043a\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2e43bcb elementor-widget elementor-widget-image\" data-id=\"2e43bcb\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t<figure class=\"wp-caption\">\n\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"900\" src=\"https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-clean-stack-1024x900.png\" class=\"attachment-large size-large wp-image-299\" alt=\"\" srcset=\"https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-clean-stack-1024x900.png 1024w, https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-clean-stack-300x264.png 300w, https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-clean-stack-768x675.png 768w, https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-clean-stack.png 1265w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/>\t\t\t\t\t\t\t\t\t\t\t<figcaption class=\"widget-image-caption wp-caption-text\">Clean stack shown in Process Hacker<\/figcaption>\n\t\t\t\t\t\t\t\t\t\t<\/figure>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-96356b8 elementor-widget elementor-widget-text-editor\" data-id=\"96356b8\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>If we check the list of loaded modules inside of Process Hacker, we can also see that wininet.dll was properly loaded:<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-cec0e52 elementor-widget elementor-widget-image\" data-id=\"cec0e52\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t<figure class=\"wp-caption\">\n\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"991\" height=\"783\" src=\"https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-modules-list-cropped.png\" class=\"attachment-large size-large wp-image-327\" alt=\"\" srcset=\"https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-modules-list-cropped.png 991w, https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-modules-list-cropped-300x237.png 300w, https:\/\/fin3ss3g0d.net\/wp-content\/uploads\/2024\/03\/process-hacker-modules-list-cropped-768x607.png 768w\" sizes=\"(max-width: 991px) 100vw, 991px\" \/>\t\t\t\t\t\t\t\t\t\t\t<figcaption class=\"widget-image-caption wp-caption-text\">Process Hacker Modules tab showing loaded wininet.dll<\/figcaption>\n\t\t\t\t\t\t\t\t\t\t<\/figure>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6018597 elementor-widget elementor-widget-heading\" data-id=\"6018597\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Conclusion<\/h2>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3405b82 elementor-widget elementor-widget-text-editor\" data-id=\"3405b82\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p>In conclusion, in this article we covered a method for proxy DLL loading using an I\/O completion callback function with the Windows thread pool API and C++\/assembly. There are numerous Windows callbacks that can be used in a similar way. We do this for our OPSEC to prevent EDRs from detecting our payloads by removing the return address of the calling function that loads DLLs from the stack. I hope you enjoyed reading, cheers!<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>In today&#8217;s blog, we are going to be covering the topic of proxying DLL loads using the Windows thread pool [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-265","post","type-post","status-publish","format-standard","hentry","category-blog"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/posts\/265","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/comments?post=265"}],"version-history":[{"count":71,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/posts\/265\/revisions"}],"predecessor-version":[{"id":354,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/posts\/265\/revisions\/354"}],"wp:attachment":[{"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/media?parent=265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/categories?post=265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fin3ss3g0d.net\/index.php\/wp-json\/wp\/v2\/tags?post=265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}