{"id":9164,"date":"2025-12-13T04:04:06","date_gmt":"2025-12-13T04:04:06","guid":{"rendered":"https:\/\/serisec.com\/index.php\/2025\/12\/13\/32562\/"},"modified":"2025-12-13T04:04:06","modified_gmt":"2025-12-13T04:04:06","slug":"32562","status":"publish","type":"post","link":"https:\/\/serisec.com\/index.php\/2025\/12\/13\/32562\/","title":{"rendered":"Abusing DLLs EntryPoint for the Fun, (Fri, Dec 12th)"},"content":{"rendered":"<p>    Abusing DLLs EntryPoint for the Fun, (Fri, Dec 12th)<br \/>\n \t<BR><br \/>\n<BR><\/BR><br \/>\n    <!-- no image --><br \/>\n \t<BR><br \/>\n<BR><\/BR><\/p>\n<div>\n<p>In the Microsoft Windows ecosystem, DLLs (Dynamic Load Libraries) are PE files like regular programs. One of the main differences is that they export functions that can be called by programs that load them. By example, to call RegOpenKeyExA(), the program must first load the ADVAPI32.dll. A PE files has a lot of headers (metadata) that contain useful information used by the loader to prepare the execution in memory. One of them is the EntryPoint, it contains the (relative virtual) address where the program will start to execute.<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/isc.sans.edu\/diaryimages\/images\/isc-20251212-1.png?ssl=1\" style=\"width: 601px; height: 239px;\"><br \/>\nIn case of a DLL, there is also an entry point called logically the DLLEntryPoint.\u00a0The code located at this address will be executed when the library is (un)loaded. The function executed is called DllMain()[<a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/dlls\/dllmain\">1<\/a>] and expects three parameters:<\/p>\n<pre style=\"background: rgb(238, 238, 238); border: 1px solid rgb(204, 204, 204); padding: 5px 10px;\">\nBOOL WINAPI DllMain(\n\u00a0 _In_ HINSTANCE hinstDLL, \n\u00a0 _In_ DWORD fdwReason, \n\u00a0 _In_ LPVOID lpvReserved\n);\n<\/pre>\n<p>The second parmeter\u00a0indicates why the DLL entry-point function is being called:<\/p>\n<ul>\n<li>DLL_PROCESS_DETACH (0)<\/li>\n<li>DLL_PROCESS_ATTACH (1)<\/li>\n<li>DLL_THREAD_ATTACH (2)<\/li>\n<li>DLL_THREAD_DETACH (3)<\/li>\n<\/ul>\n<p>Note that this function is optional but it is usually implemented to prepare the environment used by the DLL like loading resources, creating variables, etc&#8230; Microsoft recommends also to avoid performing sensitive actions at that location.<\/p>\n<p>Many maware are deployed as DLLs because it&#8217;s more challenging to detect. The tool regsvr32.exe[<a href=\"https:\/\/attack.mitre.org\/techniques\/T1218\/010\/\">2<\/a>] is a classic attack vector because it helps to register a DLL in the system (such DLL will implement a DllRegisterServer() function). Another tool is rundll32.exe[<a href=\"https:\/\/attack.mitre.org\/techniques\/T1218\/011\/\">3<\/a>]\u00a0that allows to call a function provided by a DLL:<\/p>\n<pre style=\"background: rgb(238, 238, 238); border: 1px solid rgb(204, 204, 204); padding: 5px 10px;\">\nC:&gt; rundll32.exe mydll.dll,myExportedFunction<\/pre>\n<p>When a suspicious DLL is being investigated, the first reflex of many Reverse Engineers is\u00a0to\u00a0look at the exported function(s) but don&#8217;t pay attention to the entrypoint. They look at the export table:<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/isc.sans.edu\/diaryimages\/images\/isc-20251212-3.png?ssl=1\" style=\"width: 800px; height: 449px;\"><\/p>\n<p>This DllMain()\u00a0is a very nice place where threat actors could store malicious code that will probably\u00a0remains below the radar if you don\u2019t know that this EntryPoint exists. I wrote a proof-of-concept\u00a0DLL that executes some code once loaded (it will just pop up a calc.exe). Here is the simple code:<\/p>\n<p><meta charset=\"UTF-8\"><\/p>\n<pre style=\"background: rgb(238, 238, 238); border: 1px solid rgb(204, 204, 204); padding: 5px 10px;\">\n\/\/ evildll.cpp\n#include &lt;windows.h&gt;\n#pragma comment(lib, \"user32.lib\")\n\nextern \"C\" __declspec(dllexport) void SafeFunction() {\n    \/\/ Simple exported function\n    MessageBoxA(NULL, \"SafeFunction() was called!\", \"evildll\", MB_OK | MB_ICONINFORMATION);\n}\n\nBOOL APIENTRY DllMain(HMODULE hModule,\n                      DWORD  ul_reason_for_call,\n                      LPVOID lpReserved) {\n    switch (ul_reason_for_call) {\n        case DLL_PROCESS_ATTACH:\n        {\n            \/\/ Optional: disable thread notifications to reduce overhead\n            DisableThreadLibraryCalls(hModule);\n\n            STARTUPINFOA si{};\n            PROCESS_INFORMATION pi{};\n            si.cb = sizeof(si);\n            char cmdLine[] = \"calc.exe\";\n\n            BOOL ok = CreateProcessA(NULL, cmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &amp;si, &amp;pi);\n            if (ok) {\n                CloseHandle(pi.hThread);\n                CloseHandle(pi.hProcess);\n            } else {\n                \/\/ optional: GetLastError() handling\/logging\n            }\n            break;\n        }\n        case DLL_THREAD_ATTACH:\n        case DLL_THREAD_DETACH:\n        case DLL_PROCESS_DETACH:\n            break;\n    }\n    return TRUE;\n}<\/pre>\n<p>And now, a simple program used to load my\u00a0DLL:<\/p>\n<p><meta charset=\"UTF-8\"><\/p>\n<pre style=\"background: rgb(238, 238, 238); border: 1px solid rgb(204, 204, 204); padding: 5px 10px;\">\n\/\/ loader.cpp\n#include &lt;windows.h&gt;\n#include &lt;stdio.h&gt;\n\ntypedef void (*SAFEFUNC)();\n\nint main()\n{\n    \/\/ Load the DLL\n    HMODULE hDll = LoadLibraryA(\"evildll.dll\");\n    if (!hDll)\n    {\n        printf(\"LoadLibrary failed (error %lu)n\", GetLastError());\n        return 1;\n    }\n    printf(\"[+] DLL loaded successfullyn\");\n\n    \/\/ Resolve the function\n    SAFEFUNC SafeFunction = (SAFEFUNC)GetProcAddress(hDll, \"SafeFunction\");\n    if (!SafeFunction)\n    {\n        printf(\"GetProcAddress failed (error %lu)n\", GetLastError());\n        FreeLibrary(hDll);\n        return 1;\n    }\n    printf(\"[+] SafeFunction() resolvedn\");\n\n    \/\/ Call the function\n    SafeFunction();\n\n    \/\/ Unload DLL\n    FreeLibrary(hDll);\n\n    return 0;\n}<\/pre>\n<p>Let&#8217;s compile the DLL, the loader and execute it:<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" alt=\"\" src=\"https:\/\/i0.wp.com\/isc.sans.edu\/diaryimages\/images\/isc-20251212-2.png?ssl=1\" style=\"width: 800px; height: 445px;\"><\/p>\n<p>When the DLL is loaded with LoadLibraryA(), the calc.exe process is spawned automatically, even if no DLL function is invoked!<\/p>\n<p>Conclusion: Always have a quick look at the DLL entry point!<\/p>\n<p>[1]\u00a0<a href=\"https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/dlls\/dllmain\">https:\/\/learn.microsoft.com\/en-us\/windows\/win32\/dlls\/dllmain<\/a><br \/>\n[2]\u00a0<a href=\"https:\/\/attack.mitre.org\/techniques\/T1218\/010\/\">https:\/\/attack.mitre.org\/techniques\/T1218\/010\/<\/a><br \/>\n[3]\u00a0<a href=\"https:\/\/attack.mitre.org\/techniques\/T1218\/011\/\">https:\/\/attack.mitre.org\/techniques\/T1218\/011\/<\/a><\/p>\n<p>Xavier Mertens (@xme)<br \/>\nXameco<br \/>\nSenior ISC Handler &#8211; Freelance Cyber Security Consultant<br \/>\n<a href=\"https:\/\/keybase.io\/xme\/key.asc\">PGP Key<\/a><\/p>\n<p> (c) SANS Internet Storm Center. https:\/\/isc.sans.edu Creative Commons Attribution-Noncommercial 3.0 United States License.<\/p><\/div>\n<p> \t<BR><br \/>\n <BR><\/BR><\/p>\n<p> \t<BR><br \/>\n<BR><\/BR><br \/>\n<a href=\"https:\/\/isc.sans.edu\/diary\/rss\/32562\">Go to isc.sans.edu<\/a><br \/>\n \t<BR><br \/>\n <BR><\/BR><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Abusing DLLs EntryPoint for the Fun, (Fri, Dec 12th) In the Microsoft Windows ecosystem, DLLs (Dynamic Load Libraries) are PE files like regular programs. One of the main differences is that they export functions that can be called by programs that load them. By example, to call RegOpenKeyExA(), the program must first load the ADVAPI32.dll. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[56],"tags":[69],"class_list":["post-9164","post","type-post","status-publish","format-standard","hentry","category-isc-sans-edu","tag-isc-sans-edu"],"_links":{"self":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts\/9164"}],"collection":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/comments?post=9164"}],"version-history":[{"count":0,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/posts\/9164\/revisions"}],"wp:attachment":[{"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/media?parent=9164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/categories?post=9164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/serisec.com\/index.php\/wp-json\/wp\/v2\/tags?post=9164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}