{"id":103,"date":"2025-08-12T09:00:00","date_gmt":"2025-08-12T09:00:00","guid":{"rendered":"https:\/\/justruss.tech\/?p=103"},"modified":"2026-05-15T10:34:55","modified_gmt":"2026-05-15T10:34:55","slug":"hiding-in-memory","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2025\/08\/12\/hiding-in-memory\/","title":{"rendered":"Finding Malicious Code Hidden in Windows Memory"},"content":{"rendered":"<p>Sophisticated attackers avoid writing malicious files to disk where possible. The payload executes entirely in memory, never touches the filesystem, and leaves no binary artifact for file-based scanning tools to find. Memory forensics is how you find them after the fact, and understanding what to look for is what separates a useful investigation from one that concludes &#8220;we found nothing&#8221; on a system that was fully compromised.<\/p>\n<h3>Why memory analysis is non-negotiable for advanced intrusions<\/h3>\n<p>A running Windows system holds a complete record of what is executing in RAM. Process memory contains the actual code being run, injected payloads before they encrypt themselves back, decrypted C2 configurations, credential material in LSASS, and encryption keys that are never stored on disk. For intrusions using fileless techniques, reflective loading, or process injection, memory is the primary source of evidence. File system forensics alone will find nothing.<\/p>\n<h3>Memory acquisition<\/h3>\n<pre>\/\/ WinPmem: recommended open source tool for memory acquisition\n\/\/ Download from: https:\/\/github.com\/Velocidex\/WinPmem\/releases\n\nwinpmem.exe -o C:\\evidence\\memory.raw\n\n\/\/ Verify the image was captured correctly\nwinpmem.exe --verify C:\\evidence\\memory.raw\n\n\/\/ Alternative: Magnet RAM Capture (free, GUI-based)\n\/\/ Alternative: Belkasoft RAM Capturer (free, command line and GUI)\n\/\/ Alternative: DumpIt (commercial, widely used in IR contexts)\n\n\/\/ Important: capture memory BEFORE rebooting or running tools\n\/\/ Memory compression on Windows 10+ means some data is in the paging file\n\/\/ For complete coverage also capture the pagefile:\ncopy C:\\pagefile.sys C:\\evidence\\pagefile.sys<\/pre>\n<h3>Working with Volatility 3<\/h3>\n<pre>\/\/ Install Volatility 3\npip install volatility3\n\n\/\/ Verify the image and get OS details\nvol -f memory.raw windows.info\n\n\/\/ Full process listing with parent relationships\nvol -f memory.raw windows.pstree\n\n\/\/ Compare with pslist to find unlinked processes (DKOM rootkit technique)\n\/\/ Processes in pslist but not pstree have had their EPROCESS list entry removed\nvol -f memory.raw windows.pslist &gt; \/tmp\/pslist.txt\nvol -f memory.raw windows.pstree &gt; \/tmp\/pstree.txt\n\n\/\/ Check for process name spoofing\n\/\/ A process named \"svchost.exe\" running from AppData or Temp is not legitimate\nvol -f memory.raw windows.pslist | grep -v \"System32\\|SysWOW64\\|Windows\\\\\\\\\" | grep \"svchost\\|lsass\\|csrss\\|winlogon\\|services\"<\/pre>\n<h3>Finding injected code with malfind<\/h3>\n<p>Malfind is the primary tool for finding injected shellcode and reflectively loaded DLLs. It scans process memory for regions with execute permissions that are not mapped to a file on disk.<\/p>\n<pre>\/\/ Scan all processes\nvol -f memory.raw windows.malfind\n\n\/\/ Scan a specific process\nvol -f memory.raw windows.malfind --pid 1234\n\n\/\/ Example output indicating reflective DLL injection:\n\/\/ Process: explorer.exe  Pid: 1234\n\/\/ Address: 0x400000\n\/\/ Vad Tag: VadS\n\/\/ Protection: PAGE_EXECUTE_READWRITE\n\/\/ Hexdump:\n\/\/ 4d 5a 90 00 03 00 00 00  MZ......   &lt;-- MZ header: this is a PE file\n\/\/ 04 00 00 00 ff ff 00 00  ........\n\/\/ b8 00 00 00 00 00 00 00  ........\n\/\/ Disassembly:\n\/\/ 0x400000:  dec ebp      ; 4d\n\/\/ 0x400001:  pop edx      ; 5a\n\/\/ This is a PE file in memory with no corresponding file on disk\n\n\/\/ Dump the suspicious region for analysis\nvol -f memory.raw windows.dumpfiles --virtaddr 0x400000 --pid 1234 -o \/tmp\/dumped\/\n\n\/\/ Analyse the dumped PE\nfile \/tmp\/dumped\/file.0x1234.0x400000.img\nstrings -a \/tmp\/dumped\/file.0x1234.0x400000.img | grep -i &quot;http\\|C2\\|beacon\\|server&quot;\nsha256sum \/tmp\/dumped\/file.0x1234.0x400000.img\n# Submit hash to VirusTotal for quick identification<\/pre>\n<h3>Checking loaded modules for injection indicators<\/h3>\n<pre>\/\/ List all DLLs loaded in a process\nvol -f memory.raw windows.dlllist --pid 1234\n\n\/\/ A legitimate svchost.exe should load a specific set of service DLLs\n\/\/ An unexpected DLL, especially one with no path or a path in AppData or Temp,\n\/\/ indicates DLL injection\n\n\/\/ Cross-reference module list with VAD (Virtual Address Descriptor) entries\nvol -f memory.raw windows.vadinfo --pid 1234 | grep EXECUTE\n\n\/\/ Modules loaded by a process that appear in memory with no disk backing:\n\/\/ VAD Protection: PAGE_EXECUTE_READWRITE with no FileObject = injected code<\/pre>\n<h3>Network connections from injected processes<\/h3>\n<pre>\/\/ Find all active and recently closed network connections\nvol -f memory.raw windows.netscan\n\n\/\/ Example suspicious output:\n\/\/ TCPv4   192.168.1.101  54321  198.51.100.5  443  ESTABLISHED  1234  explorer.exe\n\/\/ explorer.exe making an outbound HTTPS connection is suspicious\n\/\/ explorer.exe does not legitimately connect to external servers\n\n\/\/ Cross-reference with process details\nvol -f memory.raw windows.pslist | grep 1234\n\/\/ Then check malfind for PID 1234 to confirm injection\n\n\/\/ Full investigation flow:\n\/\/ 1. netscan: find suspicious outbound connection from unexpected process\n\/\/ 2. pslist: verify process details\n\/\/ 3. malfind: confirm injected code in that process\n\/\/ 4. dumpfiles: extract the injected code\n\/\/ 5. strings\/pe analysis: identify what the injected code is<\/pre>\n<h3>Extracting LSASS credentials from memory<\/h3>\n<pre>\/\/ If the investigation involves credential theft, extract credentials from the dump\nvol -f memory.raw windows.lsadump\n\n\/\/ This outputs the same credential material Mimikatz extracts from a live system\n\/\/ Use this to assess which credentials were at risk during the compromise\n\n\/\/ Alternative approach using pypykatz (Python implementation of Mimikatz logic)\npip install pypykatz\npypykatz lsa minidump memory.raw\n\n\/\/ Parse the LSASS process memory region specifically\n\/\/ First find the LSASS process\nvol -f memory.raw windows.pslist | grep lsass\n\/\/ Then dump its address space\nvol -f memory.raw windows.memmap --pid [lsass_pid] -o \/tmp\/lsass_dump\/\npypykatz lsa minidump \/tmp\/lsass_dump\/pid.[lsass_pid].dmp<\/pre>\n<h3>Building a memory forensics workflow<\/h3>\n<pre>\/\/ Automated triage script for memory images\npython3 &lt;&lt; EOF\nimport subprocess, json, sys\n\nimage = sys.argv[1]\n\nprint(&quot;[*] Process tree&quot;)\nsubprocess.run([&quot;vol&quot;, &quot;-f&quot;, image, &quot;windows.pstree&quot;])\n\nprint(&quot;\\n[*] Network connections&quot;)\nsubprocess.run([&quot;vol&quot;, &quot;-f&quot;, image, &quot;windows.netscan&quot;])\n\nprint(&quot;\\n[*] Malfind scan (all processes)&quot;)\nresult = subprocess.run([&quot;vol&quot;, &quot;-f&quot;, image, &quot;windows.malfind&quot;],\n                         capture_output=True, text=True)\nlines = result.stdout.split(&quot;\\n&quot;)\nsuspicious_pids = set()\nfor line in lines:\n    if &quot;PAGE_EXECUTE_READWRITE&quot; in line or &quot;MZ&quot; in line:\n        print(line)\n        # Extract PID from context\n        # ... parse and collect suspicious PIDs\n\nprint(f&quot;\\n[*] Suspicious PIDs: {suspicious_pids}&quot;)\nfor pid in suspicious_pids:\n    print(f&quot;\\n[*] DLL list for PID {pid}&quot;)\n    subprocess.run([&quot;vol&quot;, &quot;-f&quot;, image, &quot;windows.dlllist&quot;, &quot;--pid&quot;, str(pid)])\nEOF\npython3 triage.py memory.raw<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Fileless malware and process injection leave no file on disk for AV to find. This covers memory forensics with Volatility 3 from first principles: acquiring an image, finding injected code with malfind, recovering credentials, and tracing network connections.<\/p>\n","protected":false},"author":1,"featured_media":104,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-103","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dfir"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/103","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"}],"author":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=103"}],"version-history":[{"count":6,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/103\/revisions"}],"predecessor-version":[{"id":341,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/103\/revisions\/341"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media\/104"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=103"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=103"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}