44 min read

> Where you are: Part V, Chapter 32 of 40. Chapter 29 broke into encrypted devices, Chapter 30 detected the tools people use to hide, and Chapter 31 followed the evidence into the cloud. This chapter turns to the object at the center of most modern...

Chapter 32: Malware Forensics — Analyzing the Weapon After the Attack

Where you are: Part V, Chapter 32 of 40. Chapter 29 broke into encrypted devices, Chapter 30 detected the tools people use to hide, and Chapter 31 followed the evidence into the cloud. This chapter turns to the object at the center of most modern incidents — the malicious program itself. Chapter 15 captured a self-deleting implant from a live server's memory and handed you the binary "for a daylight job on copies"; Chapter 22 taught you to read what malware leaves in RAM. Now you put the recovered weapon on the workbench and answer the question every breach eventually forces: what does this thing actually do, and how do we find every place it has been? This is the home of the WEB-07 intrusion's deep analysis and the forensic side of anchor case #3, the ransomware recovery.

Learning paths: This is squarely a 🛡️ Incident Response chapter — malware analysis is how IR scopes a breach and produces the indicators that drive eviction. 🔍 Forensic Examiners need it for capability findings, family attribution, and turning a sample into court-defensible evidence. 📜 Legal/eDiscovery practitioners must understand authorization to handle live malware, the chain of custody for a sample, and why C2 infrastructure abroad means MLAT and not "hacking back." 💾 Data Recovery technicians get a direct payoff: identifying the exact ransomware family from the binary tells you whether a free decryptor exists — the difference between a full restore and a ruined quarter.


Analyzing the weapon after the attack

Every preceding forensic chapter asked what happened to this system? Malware forensics asks a sharper, adversarial question: what was this program built to do to it? The distinction matters because a piece of malware is not a passive artifact like a registry key or a deleted file. It is an active agent with intentions encoded in its instructions, and until you read those intentions you cannot honestly answer the questions an incident demands — what data was at risk, whether the attacker still has access, which other machines are infected, and what you must change to keep them out.

Recall the case from Chapter 15 — Live Response and Triage. A production web server, WEB-07 (10.20.3.17), was beaconing on a sixty-second heartbeat to a hosting-range address. On the live box you found a process named kworkerd masquerading as a kernel thread, owning the outbound C2 socket, and lsof +L1 revealed it had deleted its own executable from disk after launching — a common anti-forensic move. Because the process was still running, you recovered the live binary straight from the kernel: cp /proc/2317/exe, hashed it (e7d2a9c4…), and logged it. You also carved a plaintext configuration out of the memory image — {"c2":"https://185.220.101.47:443/gate","interval":60,"key":"a…"} — that existed nowhere on disk because the malware decrypts itself only in RAM. Tonight, on an isolated workbench, you turn that recovered binary into a complete account of the weapon. That is malware forensics.

It is worth stating the defensive framing up front, because this chapter sits next to skills that can be misused. This is post-incident analysis, not weaponization. You are studying a sample that has already been used against a victim, in order to understand it, detect it, and evict it. You are not writing malware, deploying it, or "hacking back" at the C2 server. The DataField series draws this line deliberately, exactly as the Assembly Language and Cybersecurity volumes do: we teach detection and defense, never a ready-to-use offensive playbook. The attacker's craft — how an operator builds an implant, stands up command-and-control, and moves through a network — is the subject of the DataField Ethical Hacking volume, taught there under authorization and scope. Here we arrive after the attack and reverse the question: given the weapon, what was the attack?

Why This Matters. A breach without malware analysis is a breach you cannot scope. If you re-image WEB-07 and walk away without understanding the implant, you do not know whether it spread to the forty other hosts the EDR lit up, whether it stole credentials that still work, whether it established persistence that survives the rebuild, or what the C2 address and mutex are that would let you hunt it everywhere else. Malware analysis converts a single infected box into a signature for the whole intrusion — the indicators that turn "we found one" into "we found and removed all of them." That is the difference between an incident that ends and one that quietly continues.

The deliverables: what a malware analysis must produce

Malware forensics is not an open-ended fascination with how clever the code is. It is a deliverable-driven discipline, and the deliverables are concrete:

WHAT A MALWARE ANALYSIS DELIVERS
┌────────────────────────────────────────────────────────────────────────┐
│ CAPABILITIES   What can it do? keylog, exfiltrate, encrypt, inject,      │
│                spread, persist, screenshot, steal creds, download more   │
│ PERSISTENCE    How does it survive reboot? run key / service / task /    │
│                WMI subscription / startup folder / cron / systemd        │
│ COMMUNICATIONS Where does it call home? C2 IPs, domains, URIs, protocol, │
│                beacon interval, user-agent, the config it carries        │
│ INDICATORS     IOCs others can detect with: hashes, C2, mutexes,         │
│   (IOCs)       registry keys, file paths, named pipes -> YARA / Sigma    │
│ ATTRIBUTION    What family / toolkit is this? (informs likely behavior   │
│                and whether a decryptor or known TTPs exist)              │
│ SCOPE & TIME   Which hosts, which data, what window — feeds the timeline │
└────────────────────────────────────────────────────────────────────────┘

Notice what is not on that list: a complete, instruction-by-instruction reconstruction of the program. Full reverse engineering — disassembling every function in Ghidra or IDA Pro, defeating a custom packer by hand, single-stepping in x64dbg — is a deep specialty that the DataField Assembly Language volume prepares you for, and on a hard sample it can consume weeks. Incident response rarely has weeks. The art of malware forensics, as opposed to full malware reverse engineering, is extracting the deliverables above as fast and as defensibly as possible, going only as deep as the case requires and stopping when you have what eviction and the report need. Knowing where that line is — theme five, know your limitations — is part of the skill.

Where this sits in the DataField series

Malware touches three sibling volumes, and keeping them straight keeps your role clear. The Ethical Hacking volume teaches the offensive perspective — how command-and-control frameworks like the open-source ones used in red-team work operate, how payloads are generated and obfuscated, how an operator pivots — and it does so to make defenders smarter, under authorization. The Cybersecurity volume teaches the defensive operations that consume what you produce here: the SOC analyst who loads your IOCs into the SIEM, the detection engineer who deploys your YARA and Sigma rules to the endpoint fleet, the EDR that isolates a host the moment your indicators fire. And the parent Forensic Science volume supplies the scientific method underneath all of it — hypothesis, controlled observation, reproducibility, documentation — which is exactly what separates malware forensics from merely "poking at a virus." This chapter is the bridge: you take the weapon recovered by the forensic process (Chapters 5, 14, 15, 22) and produce the intelligence that the defensive side acts on.


The safe lab: handling a live weapon

Before a single byte of analysis, settle the safety question, because the sample on your workbench is not inert. It is a working weapon that, given the chance, will try to encrypt your files, harvest your credentials, beacon to its operator, and spread to anything it can reach. The first principle of handling live malware is the same as handling a loaded firearm: assume it is live, assume it will go off, and never point it at anything you are not willing to destroy.

This is also where the book's second theme reasserts itself in a new costume. The original is sacred — never work on the original. You acquired the sample with a hash and a chain of custody (the recovered kworkerd binary was hashed e7d2a9c4… at the moment of recovery in Chapter 15). That hashed acquisition is now your evidentiary original. Every analysis action — copying it into the lab, unpacking it, detonating it — happens on working copies, and you can always prove the copy matches the original by hash. You never detonate the only copy you have, and you never alter the evidentiary one.

Building the analysis lab

A malware lab is built around one idea: total isolation by default, with any connectivity added back deliberately, in a controlled and reversible way. The standard build pairs two free, purpose-made platforms:

  • REMnux — a Linux distribution by Lenny Zeltser preloaded with static-analysis and network-emulation tools (strings, yara, ssdeep, pefile, readelf, oletools, INetSim, and dozens more). It is the default environment for triaging a sample without Windows in the loop.
  • FLARE-VM — a Windows analysis environment (originally from Mandiant/FireEye) that installs the dynamic-analysis toolkit onto a Windows VM: Process Hacker, Procmon, Regshot, x64dbg, PE-bear, Wireshark, FakeNet-NG, and more.

The two run as virtual machines on a hypervisor, wired to an isolated virtual network with no route to your host, your corporate LAN, or the internet:

MALWARE LAB — ISOLATED BY DEFAULT
                 ┌─────────────────────────────────────────────┐
                 │  HYPERVISOR (host)  — NO shared folders,      │
                 │                       NO shared clipboard,    │
                 │                       NO drag-and-drop        │
                 │   ┌──────────────┐        ┌──────────────┐    │
                 │   │  FLARE-VM    │        │   REMnux     │    │
                 │   │ (Windows)    │        │  (Linux)     │    │
                 │   │ detonate +   │◄──────►│ INetSim /    │    │
                 │   │ monitor      │  host- │ fake DNS,    │    │
                 │   └──────────────┘  only  │ HTTP, SMTP   │    │
                 │          ▲          net   └──────────────┘    │
                 │          │ SNAPSHOT before each detonation    │
                 └──────────┼──────────────────────────────────┘
                            ╳  NO bridge to host / LAN / internet

The single most important habit in that diagram is the snapshot. Before you detonate anything, you take a clean VM snapshot. You run the sample, observe, collect your data, then revert to the snapshot so the next analysis starts from a known-clean state and the malware cannot accumulate or persist across runs. A malware VM is disposable by design.

The second habit is the fake internet. Most malware does nothing interesting if it cannot reach its C2 — it just exits or sleeps. So instead of giving it the real internet (dangerous, and it tips off the operator), you point it at a simulated one. INetSim (on REMnux) and FakeNet-NG (on Windows) impersonate DNS, HTTP/S, SMTP, and other services: when the malware resolves api.cloudmetric-sync.net, the fake DNS answers; when it POSTs to /gate, the fake web server replies. The malware believes it is talking to its operator and reveals its full network behavior, while not a single packet escapes the lab.

Rules for handling live samples

The lab is the equipment; these are the operating procedures. Treat them as non-negotiable:

RULES FOR HANDLING LIVE MALWARE
1.  Never run a sample on a production, host, or networked machine. Isolated VM only.
2.  Snapshot before detonation; revert after. Never reuse a "dirty" VM.
3.  No shared folders, shared clipboard, or drag-and-drop between guest and host
    (malware uses these to escape the VM).
4.  Default to NO network; emulate it with INetSim / FakeNet-NG. Add real,
    filtered egress only deliberately and never via the corporate LAN.
5.  Transfer/store samples in a PASSWORD-PROTECTED zip (password "infected"
    by convention) so other tools/AV cannot auto-execute them.
6.  DEFANG the filename: rename winhost.exe -> winhost.exe.infected (or .vir/.mal)
    so a double-click cannot launch it.
7.  No real credentials, keys, or sensitive data anywhere in the lab.
8.  Hash the sample at acquisition; work on copies; keep the evidentiary original
    sealed with its chain of custody.
9.  Know it may be VM-aware and refuse to detonate. Isolation beats realism.
10. Confirm your authorization to possess and analyze the sample before you start.

Rule 3 deserves emphasis because it is how labs get burned. A shared folder between the malware VM and your host is a file path the malware can write to; ransomware that enumerates mapped and shared drives will happily encrypt your host through it. A shared clipboard has been used by sandbox-aware malware to detect analysis. Drag-and-drop has carried infections out of guests. The convenience features that make VMs pleasant to use are precisely the bridges a competent sample will cross. Turn them off.

War Story. An analyst, in a hurry, copied a sample into a VM that looked isolated but still had a bridged adapter from an earlier test. The malware was a worm; within minutes it had scanned the local subnet and begun spreading to other lab machines — and then to a developer's workstation that shared the segment. The cleanup cost more than the original incident. The lesson is unglamorous and absolute: verify the isolation every time, before every detonation. "I thought it was isolated" is the epitaph of many a malware lab. Check the network adapter setting with your own eyes; do not trust that it is still what it was yesterday.

Legal Note. Possessing and analyzing malware in the course of an authorized investigation or IR engagement is lawful and routine; the sample is evidence, handled with the same chain of custody as any other artifact (template in Appendix F). What you must not do is cross into the offensive: knowingly distributing malicious code, or accessing the attacker's C2 server without authorization to "take it down" or retrieve your data, can itself violate the Computer Fraud and Abuse Act (18 U.S.C. § 1030). Tempting as it is when you see the C2 address sitting there, you do not log into it. If the infrastructure is abroad — and hosting-range C2 usually is — reaching it lawfully runs through Mutual Legal Assistance Treaties (MLAT) and law enforcement, not your keyboard. Authority and these limits are the subject of Chapter 25 — The Legal Framework and Appendix E.


Static analysis: reading the file without running it

Static analysis is everything you can learn about a sample without executing it. You do it first for two reasons: it is safe (the weapon never goes off), and it is fast — a good static triage in ten minutes tells you whether you are looking at a known commodity tool, a packed unknown that needs detonation, or a targeted implant that warrants deep work. Static analysis ranges from trivial (compute a hash) to expert (read disassembly), and forensic triage lives at the productive middle: file typing, hashing and reputation, strings, the PE/ELF structure, the import table, and section entropy.

Triage: file type, hashes, and reputation

Start by establishing what the file is, never trusting the extension — malware lies about extensions constantly. The file command reads the magic bytes and tells you the real type:

# On REMnux, first establish identity. Work on a COPY.
$ cp /evidence/web07/kworkerd.bin ./work/sample
$ file ./work/sample
./work/sample: ELF 64-bit LSB pie executable, x86-64, ... dynamically linked,
               stripped

$ file ./work/winhost.exe.infected
winhost.exe.infected: PE32+ executable (GUI) x86-64, for MS Windows

So the WEB-07 implant is a 64-bit Linux ELF (stripped of symbols — a deliberate obstacle), and the Windows lateral-movement payload recovered from host WS-014 during the same intrusion is a 64-bit PE. Next, hash everything. The hash is the sample's fingerprint and the most basic indicator of compromise:

$ sha256sum ./work/sample ./work/winhost.exe.infected
e7d2a9c4b6f80153e9d2c7b6a4f80153e7d2a9c4b6f80153e9d2c7b6a4f80153  sample
b41c0e9a7d2f6c8351a0e4d9c2f7b6e3a18d5c0f4b9e2a7d6c3f1908e5b4a2d7c  winhost.exe.infected

A plain SHA-256 (or MD5/SHA-1, still used as identifiers despite being broken for collision resistance) identifies an exact file. But attackers change one byte and the hash changes entirely, so two more hash types matter for clustering related samples:

  • imphash — a hash of a PE's import table (the list of DLLs and functions it imports, in order). Because samples built from the same source with the same compiler tend to import the same functions in the same order, a shared imphash links variants that have different content hashes. It is one of the cheapest attribution signals there is.
  • ssdeep / fuzzy hashing — context-triggered piecewise hashing that produces a similarity score. Two files that are 90% identical get similar ssdeep hashes even though their SHA-256s differ completely. It catches the "same malware, slightly tweaked" case that exact hashes miss.
$ ssdeep ./work/winhost.exe.infected
768:Hk2pQ8vN3rT...:Hk2pQ8vN3rT...,"./work/winhost.exe.infected"

# imphash via Python's pefile (see PE section below):
$ python3 -c "import pefile; print(pefile.PE('./work/winhost.exe.infected').get_imphash())"
8a1f4c9e2b6d83f05c1ae97d44b2e810

Finally, reputation. Before deep work, check whether the world already knows this hash. Threat-intelligence services (VirusTotal being the best known) let you look up a hash and see whether dozens of AV engines have flagged it, when it was first seen, and what family names they assign. A known hash can save you hours. But here is the critical operational subtlety:

War Story. A security team analyzing a targeted intrusion uploaded the suspicious binary to VirusTotal to identify it. What they did not appreciate is that uploaded samples become visible to the platform's paying customers — including, it is widely understood, the threat actors themselves, who monitor VirusTotal for their own malware hashes. Within hours of the upload, the operators saw their implant had been discovered, burned that infrastructure, rotated to new C2, and went quiet — destroying the team's visibility just as the investigation was getting traction. The rule that fell out of cases like this: in a targeted incident, look up the hash, do not upload the sample. A hash lookup reveals nothing new to the attacker; an upload hands them your detection. Reserve uploads for clearly commodity malware where stealth is moot, and know your organization's policy before you click.

Strings: the cheapest intelligence in the building

After hashing, run strings — extract the human-readable character sequences embedded in the binary. It is almost insultingly simple and routinely the highest-value-per-minute step in the whole analysis, because programmers (and malware authors) embed exactly the things you want to find: URLs, IP addresses, file paths, registry keys, command lines, error messages, mutex names, and the names of the Windows API functions the program calls. Crucially, run it for both common encodings — ASCII and UTF-16LE (Windows wide strings), which plain strings misses by default:

$ strings -a -n 8 ./work/winhost.exe.infected | sort -u    # ASCII
$ strings -a -n 8 -e l ./work/winhost.exe.infected         # -e l = UTF-16LE

The yield from the Windows sample, abridged to the lines that matter:

STRINGS (winhost.exe) — the lines an analyst circles
  %APPDATA%\Microsoft\Windows\winhost.exe          <- where it copies itself
  C:\Users\Public\winhost.exe                       <- second drop location
  Software\Microsoft\Windows\CurrentVersion\Run     <- PERSISTENCE (run key)
  schtasks /create /tn "WindowsHostUpdate" /sc minute /mo 10   <- PERSISTENCE (task)
  185.220.101.47                                     <- C2 IP (matches Ch.15 alert!)
  /gate                                              <- C2 callback URI
  api.cloudmetric-sync.net                           <- C2 domain (matches DNS cache)
  Global\MSCTF_x7f3a2b                               <- MUTEX (single-instance marker)
  Mozilla/5.0 (Windows NT 10.0; Win64; x64) WH/1.2   <- custom-ish user-agent (IOC!)
  GetAsyncKeyState                                   <- KEYLOGGING hint
  {"c2":                                             <- config marker (matches RAM carve)

Read that and you already have a working hypothesis of the entire implant before running it once: it copies itself into the user profile and C:\Users\Public, persists via both a Run key and a scheduled task firing every ten minutes, beacons to 185.220.101.47 (and a backup domain) at /gate with a distinctive user-agent, guards against double-infection with a named mutex, and very likely logs keystrokes. Note the line {"c2": — the same JSON marker you carved from WEB-07's memory in Chapter 15. Static strings on the file and a carve from RAM are pointing at the same configuration structure; two independent methods, one finding.

There is also a powerful negative result from strings, which is pure theme three — the absence of a trace is itself a trace. A normal program of any size yields hundreds or thousands of meaningful strings. If strings returns almost nothing — a handful of cryptic fragments and the names of just a few loader functions — that emptiness is itself a finding: the file is packed or encrypted, its real content compressed into a high-entropy blob that only unfolds at runtime. An absence of strings tells you to expect a packer, which the entropy step will confirm.

The PE format: headers and the import table

To go beyond strings you need to understand the executable's structure. On Windows that is the Portable Executable (PE) format. You do not have to parse it by hand — tools do — but you must understand it, because the import table is one of the richest capability signals in static analysis, and because recognizing the PE structure lets you spot executables (and fragments of them) in unallocated space, exactly as you carve other file types (Chapter 7 — File Carving; signatures in Appendix A).

A PE begins with a tiny relic of history: the DOS header, starting with the magic bytes 4D 5A — "MZ", the initials of Mark Zbikowski, who designed the format. At offset 0x3C sits a 4-byte value, e_lfanew, pointing to the real PE header further into the file. There, the signature 50 45 00 00 ("PE\0\0") introduces the COFF file header and the optional header:

Offset    00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F   ASCII
00000000  4D 5A 90 00 03 00 00 00  04 00 00 00 FF FF 00 00   MZ..............
          └MZ┘  DOS magic                                     (DOS header)
0000003C  F8 00 00 00                                          e_lfanew = 0x000000F8
              └ points to the PE header at offset 0xF8 ─────────────────────────┐
                                                                                 │
000000F8  50 45 00 00 64 86 06 00  9B 41 5A 65 00 00 00 00   PE..d....AZe....◄──┘
          └PE\0\0┘ │     │         └ TimeDateStamp ─┘
                   │     └ NumberOfSections = 6
                   └ Machine = 0x8664  (AMD64 / x86-64)
          ... Optional Header magic = 0x020B (PE32+, i.e. 64-bit) ...

Two fields to read straight off that dump. Machine 0x8664 confirms a 64-bit (x64) binary. TimeDateStamp 0x655A419B is the compiler's claimed build time — a Unix epoch value decoding to around 20 November 2023 — but treat it with suspicion: it is trivially forged, and packed samples often carry zeroed or absurd (1970, or far-future) timestamps. After the headers come the sections — named regions like .text (executable code), .rdata (read-only data and the import directory), .data (writable data), .rsrc (resources), and .reloc (relocations) — each with a virtual address, a raw size, and characteristics flags.

The forensic prize inside the PE is the Import Address Table (IAT): the list of external functions the program calls, grouped by the DLL that provides them. Because Windows malware accomplishes its goals by calling documented Windows API functions, the imports betray the capabilities. Parse them with pefile:

#!/usr/bin/env python3
# pe_triage.py — list imports + per-section entropy. ILLUSTRATIVE; run in the lab,
# on a COPY, never on the evidentiary original. (pefile parses; it does not execute.)
import pefile, sys

pe = pefile.PE(sys.argv[1])
print("imphash:", pe.get_imphash())
print("\nIMPORTS (DLL -> functions):")
for entry in pe.DIRECTORY_ENTRY_IMPORT:
    print(f"  {entry.dll.decode(errors='replace')}")
    for imp in entry.imports:
        name = imp.name.decode(errors="replace") if imp.name else f"ordinal#{imp.ordinal}"
        print(f"      {name}")

print("\nSECTION ENTROPY (0.0-8.0; >7.0 ~ packed/encrypted):")
for s in pe.sections:
    print(f"  {s.Name.decode(errors='replace').rstrip(chr(0)):8s}  {s.get_entropy():.2f}")

Run against winhost.exe, the imports tell the story the strings only hinted at:

IMPORTS (winhost.exe) — read as a list of capabilities
KERNEL32.dll
   CreateMutexA               <- single-instance mutex  (host IOC: Global\MSCTF_x7f3a2b)
   VirtualAllocEx       ┐
   WriteProcessMemory   │     <- PROCESS INJECTION (classic trio): allocate in a
   CreateRemoteThread   ┘        remote process, write code, run it
   CreateProcessA             <- spawns cmd.exe (the schtasks/reg child processes)
ADVAPI32.dll
   RegCreateKeyExA      ┐
   RegSetValueExA       ┘     <- writes the Run-key persistence
   OpenSCManagerA       ┐
   CreateServiceA       ┘     <- installs a Windows SERVICE (second persistence)
USER32.dll
   SetWindowsHookExA    ┐
   GetAsyncKeyState     │     <- KEYLOGGING
   GetForegroundWindow  ┘        (which window had focus -> context for the keystrokes)
WININET.dll
   InternetOpenA        ┐
   InternetConnectA     │     <- HTTP(S) command-and-control
   HttpSendRequestA     ┘

That import list is, in effect, the implant's spec sheet: it injects code into other processes, persists two ways (Run key and a service), logs keystrokes with window context, and communicates over HTTP. A few import patterns are worth memorizing because they recur across families — the process-injection trio (VirtualAllocEx + WriteProcessMemory + CreateRemoteThread), the keylogging pair (SetWindowsHookEx / GetAsyncKeyState), and the anti-analysis tell (IsDebuggerPresent, CheckRemoteDebuggerPresent). Conversely, a binary that imports only LoadLibrary and GetProcAddress and almost nothing else is hiding its real imports, resolving them dynamically at runtime to defeat exactly this analysis — another absence that is itself a signal.

Section entropy and packing

The entropy column from the script above measures, for each section, how random its bytes are, on a Shannon scale of 0.0 to 8.0 bits per byte. Ordinary x86/x64 machine code is structured and repetitive, landing around 6.0–6.5; plain text and tables are lower. Compressed or encrypted data approaches 8.0, because randomness is the whole point of compression and encryption. So a section with entropy above roughly 7.0 is a flag: it is packed, encrypted, or carrying an embedded encrypted payload. Visualized:

SECTION ENTROPY (bits/byte, 0.0-8.0)        winhost.exe
  .text   ████████████████░░░░  6.42   normal x86-64 code
  .rdata  ██████████████░░░░░░  5.10   strings, imports
  .data   ████████░░░░░░░░░░░░  3.88   initialized data
  .rsrc   ███████████████████░  7.86   <-- ENCRYPTED blob in resources (2nd stage!)
  .reloc  ████████████░░░░░░░░  4.95

Here .text is a normal code section, so the file is not fully packed — but .rsrc at 7.86 screams that an encrypted second-stage payload is hidden in the resource section, to be decrypted and injected at runtime. That is why the imports include the injection trio: the loader you can see exists to unpack and inject the part you cannot. Contrast a fully packed sample, where the giveaways are even louder:

PACKED WITH UPX — the section NAMES alone give it away
  UPX0    ░░░░░░░░░░░░░░░░░░░░  0.00   raw size 0, huge virtual size (unpack target)
  UPX1    ████████████████████  7.94   the compressed original program
  .rsrc   ██████████████░░░░░░  5.20
  imports: LoadLibraryA, GetProcAddress, VirtualProtect, ExitProcess   (only 4!)

UPX is a legitimate, common packer (and the easiest to reverse — upx -d often unpacks it). Its fingerprints are unmistakable: section names UPX0/UPX1, a near-empty section with a huge virtual size (the runtime unpack destination), a high-entropy section holding the squeezed original, and an import table stripped to the four functions the stub needs to rebuild the rest. Custom packers are not so polite — they leave generic section names and you must unpack by detonation or by debugger — but high entropy plus a tiny import table is the universal signature of something hidden. Packing does not defeat you; it tells you static analysis has reached its limit and the next move is dynamic.

Tool Tip. For interactive PE inspection, PE-bear, PEStudio, and CFF Explorer give you a navigable view of headers, sections, imports, resources, and per-section entropy, and PEStudio flags suspicious imports and strings against curated blacklists automatically. For the Linux side, readelf -h (header), readelf -d (dynamic section), and nm -D (dynamic symbols) are the ELF equivalents. The recovered kworkerd ELF begins, predictably, with 7F 45 4C 46\x7fELF:

$ xxd -l 20 ./work/sample
00000000: 7f45 4c46 0201 0100 0000 0000 0000 0000  .ELF............
          └ELF┘ │  │  │
                │  │  └ EI_VERSION = 1
                │  └ EI_DATA = 1  (little-endian)
                └ EI_CLASS = 2    (64-bit)
00000010: 0300 3e00                                 ..>.
          │     └ e_machine = 0x3E (x86-64)
          └ e_type = 3 (ET_DYN — position-independent executable)

Limitation. Static analysis tells you what a program can do — its imports, its strings, its structure — not what it will do or did in this incident. An import of CreateRemoteThread proves the capability to inject; it does not prove injection occurred, into what, or with what payload. A packed sample may hide nearly everything from static methods entirely. Static analysis is a powerful, safe first pass that scopes the work and produces some IOCs; it is rarely the whole story. State findings at the level the method supports — "the binary imports the API set used for keylogging" — and let dynamic analysis and memory forensics (Chapter 22) carry claims about behavior.


YARA: turning observations into reusable detection

So far you have described one sample. The leap that makes analysis valuable across an incident is converting those descriptions into a detection you can run against thousands of files, memory images, and future samples. That is what YARA does. A YARA rule is a pattern — strings and/or byte sequences plus a boolean condition — that the YARA engine matches against any file or process memory. Analysts call YARA "the pattern-matching swiss army knife for malware researchers," and it is the lingua franca of file-based detection.

A rule has three parts: meta (documentation — author, date, hashes, references), strings (the byte/text patterns to look for), and condition (the logic that decides a match). Here is a defensible rule for the WEB-07 Windows implant, built entirely from the static observations above:

rule APT_WEB07_winhost_backdoor
{
    meta:
        author      = "IR team, case 2026-0428-IR"
        description = "WEB-07 intrusion Windows implant (winhost.exe family)"
        date        = "2026-04-29"
        tlp         = "AMBER"
        hash_sha256 = "b41c0e9a7d2f6c8351a0e4d9c2f7b6e3a18d5c0f4b9e2a7d6c3f1908e5b4a2d7c"
        imphash     = "8a1f4c9e2b6d83f05c1ae97d44b2e810"
        reference   = "internal; MITRE T1071.001, T1547.001, T1056.001"

    strings:
        $c2_ip  = "185.220.101.47" ascii
        $uri    = "/gate" ascii
        $mutex  = "Global\\MSCTF_x7f3a2b" ascii wide
        $ua     = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) WH/1." ascii
        $runkey = "Software\\Microsoft\\Windows\\CurrentVersion\\Run" ascii wide
        $cfg    = { 7B 22 63 32 22 3A }            // {"c2":  -- also present in RAM

    condition:
        uint16(0) == 0x5A4D and                    // "MZ"  (little-endian)
        uint32(uint32(0x3C)) == 0x00004550 and     // "PE\0\0" at e_lfanew
        filesize < 2MB and
        ( $cfg or 3 of ($c2_ip, $uri, $mutex, $ua, $runkey) )
}

Read the condition carefully, because it embodies good rule craft. uint16(0) == 0x5A4D checks that the file starts with MZ (the bytes 4D 5A read little-endian as 0x5A4D); uint32(uint32(0x3C)) == 0x00004550 follows e_lfanew to confirm a real PE\0\0 signature — together they ensure the rule only fires on actual PE files. The filesize bound and the 3-of-5 string requirement guard against false positives: any one string (a public IP, a Run-key path that appears in countless legitimate files) is weak alone, but three together, in a small PE, is specific. The hex pattern { 7B 22 63 32 22 3A } is {"c2": — and because that config marker exists in the malware's decrypted memory, this single rule matches both the file on disk and the running process in a memory image, which is exactly the cross-surface detection YARA is prized for. (Memory scanning with YARA is part of Chapter 22 — Memory Forensics.)

Running it is simple, and it scales from one file to a fleet:

$ yara -s APT_WEB07_winhost_backdoor.yar ./work/winhost.exe.infected
APT_WEB07_winhost_backdoor ./work/winhost.exe.infected
0x1a40:$c2_ip: 185.220.101.47
0x1a6c:$uri: /gate
0x2210:$mutex: Global\MSCTF_x7f3a2b

# Scan an entire triage collection (e.g., the KAPE/CyLR output from Ch.15):
$ yara -r APT_WEB07_winhost_backdoor.yar /evidence/fleet_triage/
$ yara WEB07_rules.yar /evidence/web07/WEB07-mem.raw    # scan the memory image too

This is the moment analysis becomes intelligence: one well-written rule, distributed to the endpoint fleet and the SIEM (where the Cybersecurity volume's detection engineers and SOC analysts take over), finds every other host carrying this implant and flags it if it ever returns. The same rule fed to a threat-intel platform's retrohunt can find samples seen months ago that nobody connected to this actor. You have turned a single recovered binary into a sensor for the whole campaign.

Why This Matters. A finding that lives only in your notebook protects no one. The discipline of malware forensics is to externalize what you learn into machine-readable detections — YARA for files and memory, Sigma for log events, IOC lists for the SIEM — so that the knowledge propagates faster than the attacker can re-infect. This is theme four in operational form: technology changes, principles don't. The packer will change, the C2 will rotate, the file hash will be new tomorrow — but a rule keyed to the mutex, the user-agent, and the config structure keeps catching the family long after the trivial indicators have moved on. Write the rule to the most durable behavior you can, not just the easiest byte.


Dynamic analysis: detonating in a sandbox

When static analysis hits a packer, or when you need to know what the malware actually does rather than what it can do, you detonate it: run it under observation in the isolated lab and watch its behavior. Done right, dynamic analysis answers the questions static methods cannot — what files it drops, what registry keys it writes, what processes it spawns and injects into, what it sends to its C2 — because the malware unpacks and reveals itself in the act of running.

What to monitor, and why order matters

Before pressing go, instrument the VM to capture four behavioral domains, because the malware will touch all of them and you only get one clean detonation per snapshot:

THE FOUR DOMAINS OF DYNAMIC MONITORING
  PROCESS    new processes, child processes, injected threads        Process Hacker,
             (the process TREE is the story)                          Procmon, Process Explorer
  FILE       files created/modified/deleted; where it copies itself  Procmon, Regshot (FS diff)
  REGISTRY   keys/values added/changed (persistence lives here)      Regshot (before/after diff)
  NETWORK    DNS lookups, connections, C2 traffic, exfil             Wireshark + FakeNet/INetSim

The practical workflow is a before/after diff. Tools like Regshot take a snapshot of the file system and registry before detonation and another after, then show you exactly what changed — cutting through the noise of normal OS activity to the handful of modifications the malware made. Procmon (Sysinternals Process Monitor) records every file, registry, process, and thread operation in real time with full detail. Process Hacker shows the live process tree, loaded modules, handles (including mutexes and named pipes), and network connections per process. And Wireshark plus the fake-internet services capture every packet the malware tries to send. Run them together; the cross-correlation is where the picture forms.

The sandbox options

You can build this by hand on a FLARE-VM (Procmon + Regshot + Process Hacker + Wireshark + FakeNet-NG) — and you should learn it that way, because understanding the manual process is what lets you trust and troubleshoot the automated ones. But several automated sandboxes do the orchestration for you and produce a structured report:

  • Cuckoo Sandbox and its actively maintained successor CAPE (Config And Payload Extraction) — open-source, self-hosted sandboxes that run the sample in an instrumented VM, hook API calls, dump dropped payloads, and — CAPE's specialty — automatically unpack and extract malware configurations (C2 addresses, keys) for many known families. Self-hosting keeps the sample in-house, which matters for targeted/sensitive cases.
  • ANY.RUN — an interactive online sandbox: you watch the detonation in a browser in real time and can click through dialogs the malware waits on (defeating some user-interaction checks). Fast and powerful, but it is a public, cloud service — uploading carries the same exposure risk as VirusTotal, so it is for commodity samples, not your targeted incident.
  • Joe Sandbox, Hybrid Analysis, and Triage (tria.ge) round out the commercial/online options, each with its own strengths in evasion-resistance and reporting.

The choice is a privacy/convenience trade-off you now recognize: self-hosted (CAPE) for sensitive cases, online sandboxes for commodity malware. For the WEB-07 incident — a targeted intrusion — you detonate on your own isolated FLARE-VM or a self-hosted CAPE, never a public service.

A detonation walk-through

You snapshot the clean FLARE-VM, start Procmon, Regshot (first shot), Process Hacker, and Wireshark, point the network at FakeNet-NG, rename the sample back to winhost.exe, and run it. Sixty seconds later you take Regshot's second shot and stop the captures. Revert is one click away; first, read what happened.

The process tree from Process Hacker tells the opening move:

PROCESS TREE (Process Hacker)
winhost.exe (PID 4120)
 ├─ cmd.exe /c reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run"
 │            /v WindowsHostSvc /d "%APPDATA%\Microsoft\Windows\winhost.exe" /f
 ├─ cmd.exe /c schtasks /create /tn "WindowsHostUpdate"
 │            /tr "C:\Users\Public\winhost.exe" /sc minute /mo 10 /ru SYSTEM
 └─ (no child) -- injects into -> explorer.exe (PID 2104)
                  [VirtualAllocEx + WriteProcessMemory + CreateRemoteThread on PID 2104]

There are the persistence mechanisms the static imports predicted, now confirmed in action: a Run key written via reg add, and a scheduled task created via schtasks to relaunch every ten minutes as SYSTEM. And the injection trio fired against explorer.exe — the malware copied code into a trusted process and started a thread there, so the real malicious activity now runs under explorer.exe's identity, not winhost.exe's. Regshot's diff isolates the registry changes from the OS noise:

REGSHOT — values ADDED during detonation (excerpt)
Values added: 3
HKU\<SID>\Software\Microsoft\Windows\CurrentVersion\Run\WindowsHostSvc:
    "C:\Users\<user>\AppData\Roaming\Microsoft\Windows\winhost.exe"
HKLM\SYSTEM\CurrentControlSet\Services\WinHostSvc\ImagePath:
    "C:\Users\Public\winhost.exe"      <- a SERVICE too (3rd persistence path)
HKLM\...\Schedule\TaskCache\Tree\WindowsHostUpdate\   (scheduled-task registration)
Files added: 2
    C:\Users\<user>\AppData\Roaming\Microsoft\Windows\winhost.exe   (self-copy)
    C:\Users\<user>\AppData\Local\Temp\~wh3a2b.tmp                  (keystroke buffer)

The ~wh3a2b.tmp file is the keylogger's on-disk buffer; opening it in the lab shows captured keystrokes accumulating — direct confirmation of the keylogging that GetAsyncKeyState only implied. And the network capture shows the C2 behavior that brought you here in the first place:

NETWORK (FakeNet-NG; nothing leaves the lab)
DNS  A?  api.cloudmetric-sync.net           FakeNet replies 192.0.2.123 (simulated)
TCP  ...:49812 -> 192.0.2.123:443  TLS ClientHello  SNI=api.cloudmetric-sync.net
HTTP POST /gate  Host: 185.220.101.47   (IP fallback when domain fails)
     User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) WH/1.2
     body: base64( AES( {"host":"WS-014","user":"...","os":"...","kl":"..."} ) )
     >>> repeats every ~60 s  (the beacon interval seen in the Ch.15 alert) <<<

Every piece corroborates: the domain matches WEB-07's DNS cache, the IP and /gate URI and 60-second interval match the carved RAM config and the original EDR alert, and the POST body confirms the implant exfiltrates host identity, user, and keylogger output over the C2 channel. Without the fake internet, the malware would have failed its DNS lookup and gone dormant, showing you nothing. Faking the internet is what makes the network behavior observable.

Recovery vs. Forensics. The same detonation serves both disciplines with different acceptance bars. The 🛡️/💾 recovery-and-remediation reader detonates to learn what to clean: the exact Run key, service, scheduled task, and dropped files to remove so the host is genuinely restored, not just superficially scrubbed (miss the scheduled task and the implant returns in ten minutes). The 🔍 forensic examiner detonates to prove and attribute: a reproducible behavioral record, captured in a documented environment with timestamps and hashes, that supports the report's capability findings and the IOC package. One controlled run, two purposes — eviction for the responder, evidence for the examiner. Document it well enough and it satisfies both.

Try This. In an isolated VM, take a legitimate small program — notepad.exe — and pack a copy with UPX (upx -o notepad_packed.exe notepad.exe). Compute the entropy of both with the pe_triage.py script: watch the packed copy's main section jump toward 7.9 while the original sits near 6.4, and watch the import table shrink to UPX's handful. Then run Regshot around launching a benign installer and observe the Run-key and file-system changes it makes. You are practicing the exact mechanics of malware triage on safe software — entropy, imports, before/after diffs — so the real thing holds no surprises.


Identifying capabilities

The point of static and dynamic analysis is to enumerate what the malware does, in categories the report and the defenders can act on. Map each capability to its evidence and, where useful, to its MITRE ATT&CK technique ID — the standard taxonomy of adversary behavior that the Cybersecurity and Ethical Hacking volumes also use, so your findings speak the common language of detection and red-teaming alike.

Persistence: surviving the reboot

Persistence is how malware comes back after a restart, and it is usually the first capability to nail down because you cannot evict what you cannot find. Windows offers dozens of auto-start extensibility points; the ones malware reaches for most, and where to look:

PERSISTENCE MECHANISMS (Windows)                              ATT&CK
  Registry Run keys                                           T1547.001
    HKCU\...\CurrentVersion\Run , HKLM\...\Run , RunOnce
  Windows Service                                             T1543.003
    HKLM\SYSTEM\CurrentControlSet\Services\<name>\ImagePath ; Event 7045
  Scheduled Task                                              T1053.005
    C:\Windows\System32\Tasks\<name> ; schtasks ; TaskCache registry
  WMI event subscription  (FILELESS)                          T1546.003
    root\subscription: __EventFilter + CommandLineEventConsumer
                       + __FilterToConsumerBinding
  Startup folder                                              T1547.001
    %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup

The WEB-07 Windows implant used three of these at once (Run key, service, scheduled task) — redundancy so that removing one leaves the others. The most insidious is the WMI event subscription, which stores its trigger and payload inside the WMI repository (%SystemRoot%\System32\wbem\Repository) rather than as a file — fileless persistence that survives reboot and leaves no executable on disk for a file scan to catch. But it is not invisible: it lives in the root\subscription namespace and can be enumerated, which is theme three again — even "fileless" leaves a trace, just in a place naive cleanup ignores. (Linux equivalents — cron, systemd units, ~/.bashrc, /etc/rc.local, LD_PRELOAD — are covered in Chapter 17; the WEB-07 ELF persisted via a systemd unit re-dropping the binary the live copy had deleted.) A responder hunts the predicted persistence directly:

# Hunt the persistence the analysis predicted (run on suspect hosts or triage output).
reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Run"
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
schtasks /query /fo LIST /v | Select-String "WindowsHostUpdate"
Get-CimInstance -Namespace root\subscription -Class __FilterToConsumerBinding  # WMI
# The full ASEP sweep, hashed, with reputation, in one CSV:
autorunsc.exe -accepteula -a * -h -s -c > autoruns.csv   # Sysinternals Autoruns

Recovery vs. Forensics. Persistence is a dual-use artifact in the most operational sense. The 💾 recovery/remediation reader wants to delete every persistence entry to truly restore the host — and must find all of them, because one missed scheduled task re-infects the machine. The 🔍 forensic examiner wants to preserve those same entries as evidence of how the attacker maintained access, with their creation timestamps feeding the intrusion timeline (Chapter 21). The tension is real: remediate too eagerly and you destroy evidence; preserve too long and the host stays compromised. The professional answer is to document and image first, then remediate — capture the persistence as evidence (hash the service binary, export the task XML and run-key value) before you remove it. Same registry value; one job erases it, the other exhibits it.

Keylogging, exfiltration, lateral movement, and C2

The remaining capability categories follow the same evidence-to-technique pattern:

  • Keylogging (T1056.001). Tells: imports of SetWindowsHookEx, GetAsyncKeyState, GetForegroundWindow; a growing buffer file (the ~wh3a2b.tmp you found); strings naming window titles. The implant captures keystrokes with the focused-window context so the operator knows which application the typing went into — credentials typed into a login form are far more valuable when you know it was the VPN portal.
  • Data exfiltration (T1041 — exfil over the C2 channel). Tells: the implant stages data (compresses it, often into a password-protected archive in a temp or Public directory) then uploads it. In the WEB-07 case, exfil rode the same /gate POST as the beacon. This is the technical mirror of anchor case #2 — the insider who staged an archive and uploaded it to personal cloud storage (threaded through Chapter 16 and Chapter 30). The behavior is nearly identical; only the actor (external malware vs. trusted insider) and the destination (criminal C2 vs. a consumer cloud account) differ — and so does the legal frame.
  • Lateral movement (T1021 / T1570). Tells: imports/strings for SMB and admin shares (\\HOST\C$`, `\\HOST\ADMIN$), remote-execution tooling (PsExec-style service creation on a remote host, or WMI Win32_Process.Create), and use of stolen credentials. This is how the one infection became forty — the EDR alert that lit up the fleet in Chapter 15. The malware on WS-014 carried the SMB-copy-and-remote-service routine that planted winhost.exe on its neighbors.
  • Command and control (T1071.001 / T1573). Tells: the C2 IP/domain, the /gate URI, the 60-second beacon with jitter, the distinctive user-agent, TLS to disguise the channel, and the embedded config that holds it all. The config you carved from WEB-07's memory in Chapter 15 — {"c2":"https://185.220.101.47:443/gate","interval":60,"key":"a…"}is the C2 specification, and CAPE-class sandboxes specialize in extracting exactly this structure automatically.

Assembled into the standard taxonomy, the WEB-07 implant's capability profile reads:

WEB-07 IMPLANT -> MITRE ATT&CK CAPABILITY MAP
  Persistence:        T1547.001 Run key | T1543.003 Service | T1053.005 Sched task
  Defense evasion:    T1055.002 process injection (into explorer.exe)
                      T1027 packed/encrypted payload in .rsrc
                      T1070.004 self-deletion of the dropper (the ELF; Ch.15)
  Collection:         T1056.001 keylogging (with window context)
  C2:                 T1071.001 HTTPS | T1573 encrypted channel | 60s beacon
  Exfiltration:       T1041 over the C2 channel
  Lateral movement:   T1021.002 SMB/admin shares | T1570 tool transfer

That single table is most of the report's technical heart and the spine of the eviction plan. It tells the responder what to remove, the SOC what to detect, and counsel what was at risk — which is the entire point of doing the analysis.


Indicators of compromise: from analysis to detection

The final deliverable is the indicators of compromise (IOCs) — the concrete, machine-matchable facts that let anyone detect this threat without repeating your analysis. You have collected them all along; now you organize them and, critically, weight them, because not all indicators are equal.

The pyramid of pain

Security researcher David Bianco's Pyramid of Pain ranks indicators by how much it hurts the attacker when you detect and block them — which is the same as how durable they are as detections:

THE PYRAMID OF PAIN  (Bianco) — durability of an indicator
                ▲   TTPs                  "tough!"   behaviors; attacker must
               ╱ ╲                                   relearn how to operate
              ╱   ╲  Tools                "challenging"
             ╱     ╲ Host/Network Artifacts "annoying"  mutex, named pipe,
            ╱       ╲                                    registry key, user-agent
           ╱         ╲ Domain Names        "simple"   re-register / rotate
          ╱           ╲ IP Addresses       "easy"     change hosting
         ╱_____________╲ Hash Values       "trivial"  recompile -> brand-new hash

Read it bottom to top and a strategy falls out. Hashes sit at the bottom because the attacker beats them with a one-byte change and a recompile — useful for confirming a specific file, useless for catching the next variant. IPs and domains are slightly tougher but rotate cheaply. The valuable middle is host and network artifacts — the mutex Global\MSCTF_x7f3a2b, the /gate URI, the WH/1.2 user-agent, the WindowsHostUpdate task name — which the attacker would have to modify their tooling to change, and which therefore catch variants that defeat the hashes. At the top, TTPs (the behaviors: "spawns cmd.exe to run schtasks then beacons on a fixed interval") are hardest to evade because changing them means the operator must change how they work. The lesson for IOC production: collect the easy indicators, but invest your best detections (YARA on durable strings, Sigma on behaviors) higher up the pyramid.

Producing and sharing the IOC package

Bundle everything into a single, defanged package — defanged meaning C2 indicators are written with brackets (185[.]220[.]101[.]47, hxxps://) so they cannot be accidentally clicked or auto-resolved, an industry safety convention:

IOC PACKAGE — case 2026-0428-IR   (TLP:AMBER; defanged)
FILE (SHA-256)
  b41c0e9a7d2f6c83...  winhost.exe   (Windows implant)   imphash 8a1f4c9e2b6d83f0...
  e7d2a9c4b6f80153...  kworkerd      (Linux ELF implant)
NETWORK
  185[.]220[.]101[.]47        C2 (HTTPS :443, URI /gate, ~60s beacon)
  api[.]cloudmetric-sync[.]net  C2 domain (DNS)
  User-Agent:  Mozilla/5.0 (Windows NT 10.0; Win64; x64) WH/1.2
HOST
  Mutex:    Global\MSCTF_x7f3a2b
  Run val:  HKCU\...\CurrentVersion\Run\WindowsHostSvc
  Service:  WinHostSvc        Task:  \WindowsHostUpdate (every 10 min)
  Paths:    %APPDATA%\Microsoft\Windows\winhost.exe ; C:\Users\Public\winhost.exe
            %TEMP%\~wh3a2b.tmp  (keylog buffer)
DETECTIONS
  YARA:  APT_WEB07_winhost_backdoor  (file + memory)
  Sigma: schtasks WindowsHostUpdate creation ; HTTP beacon to /gate w/ WH/ UA

Standardized formats let this travel: STIX/TAXII for structured threat-intel exchange, MISP for collaborative sharing platforms, Sigma for SIEM-portable log detections, OpenIOC for host-based indicators. You hand the YARA and Sigma to the Cybersecurity volume's detection engineers, the hashes and C2 to the SIEM and firewall teams, and — looping back to where this began — you feed the host indicators into the very triage tooling from Chapter 15 to sweep the fleet for every other infected machine. The analysis that started with one binary recovered from one server's memory now drives detection everywhere.

Why This Matters. IOCs close the loop between forensics and defense. Detection that the SOC built before your analysis caught one host (WEB-07); the indicators you produced catch all of them, evict the attacker comprehensively, and harden against return. This is the through-line of incident response: analyze the weapon, distill it into indicators, hunt with the indicators, evict, and detect on re-entry. An analysis that does not end in shareable detections has not finished its job.


Worked example: the WEB-07 implant, start to finish

Pull every thread together on the WEB-07 intrusion. You arrived (Chapter 15) at a beaconing server, captured memory, recovered a self-deleted ELF from /proc, and carved a plaintext C2 config from RAM. The fleet triage flagged WS-014, from which you recovered the Windows payload winhost.exe. Now, on the isolated lab, you produce the full picture — and note how each finding is corroborated by an independent method, which is what makes it defensible.

Static. file confirmed an x86-64 ELF (stripped) and a PE32+ Windows binary. Hashes recorded (e7d2a9c4…, b41c0e9a…); the PE's imphash (8a1f4c9e…) is your clustering key. Strings surfaced the C2 IP, domain, /gate, the mutex, the user-agent, the persistence paths, and the {"c2": config marker. Imports exposed the capability set — injection trio, keylogging pair, Run-key and service APIs, WinINet C2. Section entropy showed .text normal but .rsrc at 7.86: an encrypted second stage, explaining the injection imports. Hypothesis formed without a single execution.

Detection authored. From the static observations you wrote APT_WEB07_winhost_backdoor, keyed to durable middle-pyramid indicators (mutex, user-agent, config bytes) and constructed to match both the file and process memory.

Dynamic. Detonated on FLARE-VM with FakeNet-NG. The process tree showed winhost.exe spawning cmd.exe to add the Run key and create the WindowsHostUpdate task, then injecting into explorer.exe. Regshot's diff captured the Run key, the WinHostSvc service, the self-copy, and the keylog buffer ~wh3a2b.tmp. The packet capture showed DNS for api.cloudmetric-sync.net, fallback to 185.220.101.47, and a base64/AES-wrapped POST to /gate every ~60 seconds carrying host identity and keystrokes. Every static hypothesis confirmed in behavior; the carved RAM config, the live EDR alert cadence, and the detonated beacon all agree on the same C2 and interval — three independent confirmations.

Capabilities and IOCs. Mapped to ATT&CK (persistence ×3, injection, keylogging, HTTPS C2, exfil over C2, SMB lateral movement) and bundled into the defanged IOC package above, with YARA and Sigma attached. Fed back to the fleet triage, the host indicators identified six additional infected machines that the original EDR alert had not conclusively flagged. The intrusion is now scoped, and eviction can be comprehensive rather than whack-a-mole.

Recovery vs. Forensics. Now the ransomware angle that this chapter owes anchor case #3 (Chapter 12 — Ransomware Recovery). When the weapon is ransomware, malware forensics produces a finding with direct recovery value: family attribution. Static and dynamic analysis identify the family from its ransom-note template, file-extension marker (the .eking extension from the Chapter 12 case points to the Phobos lineage), mutex, and encryption routine. That identification answers the recovery reader's first question — does a free decryptor exist? The No More Ransom project and vendor researchers publish decryptors for families with flawed key handling or seized keys; matching the sample to such a family can turn "pay or lose everything" into "download the decryptor and restore for free." The 🔍 examiner reads the same binary for attribution, IOCs, and the breach-notification facts (what the data-stealing component exfiltrated before encrypting). One analysis, and the recovery technician and the forensic examiner each get exactly what their job needs — the clearest possible statement of why this book teaches the two disciplines together.

Ethics Note. Malware samples frequently contain victim data — the keylog buffer holds someone's passwords and private messages; a staged exfil archive holds the victim's files. When you analyze the weapon you are also handling that person's stolen, sensitive information, and theme six — the human cost is real — applies fully. Treat captured victim data within scope, protect it, and do not browse it for curiosity. In the rare, hard case where exfiltrated content is itself contraband — the domain of anchor case #4 — the mandatory preservation-and-reporting duties take over, the material is never copied or described beyond what the lawful process directs, and you stop and escalate. That handling is treated clinically and in full in Chapter 28 — Ethics. The weapon you are analyzing was used against a real person; analyze it with that always in view.


Common mistakes

  • Analyzing on a connected or production machine. The cardinal sin. A live sample will spread, encrypt, or beacon the instant it runs anywhere with reach. Isolated VM, verified isolation, snapshot-and-revert — every time, no exceptions.
  • Leaving shared folders/clipboard/drag-drop enabled. These are escape routes the malware will use to reach your host. Disable them before the sample ever enters the VM.
  • Uploading a targeted sample to VirusTotal or a public sandbox. It exposes your detection to the actor, who may be watching for their own hashes. Look up the hash; do not upload the sample in a targeted case.
  • Treating the file hash as durable detection. A one-byte change defeats it. Hashes confirm a specific file; for catching variants, detect higher on the pyramid — mutexes, user-agents, behaviors, YARA on stable strings.
  • Confusing capability with behavior. An import of CreateRemoteThread proves the ability to inject, not that injection happened here. Say "the binary imports the injection API set"; let dynamic analysis and memory evidence carry "it injected into explorer.exe."
  • Detonating once and missing the second stage. Packed and staged malware reveals itself only when it unpacks and runs. If static yields high entropy and a tiny import table, you must detonate (or unpack) — a static-only verdict on a packed sample is incomplete.
  • Remediating before preserving. Deleting the run key, service, and dropped files to "clean" the host destroys the evidence of how the attacker persisted. Image and document persistence (hash the binary, export the task) before you remove it.
  • Stopping at one host. Analyzing WEB-07 and walking away ignores the forty other alerts. The deliverable is IOCs that sweep the fleet; an analysis that does not end in shareable detections has not scoped the breach.
  • Trusting the compile timestamp. The PE TimeDateStamp is trivially forged. Use it as a weak lead, corroborated by other artifacts, never as a fact.

Limitations: knowing when to stop

Malware authors actively fight analysis, and an honest report states where their countermeasures, or simple resource limits, bounded your findings.

Anti-analysis defeats the easy path. Sophisticated samples are VM-aware and sandbox-aware: they check for hypervisor artifacts (registry keys, driver names, MAC address ranges, the CPUID hypervisor bit, implausibly small RAM/disk), for analysis tools running, and for signs of a real user (mouse movement, recent documents). Detecting any of these, the malware exits or sleeps, and your detonation shows benign behavior — a false all-clear. Anti-debugging (IsDebuggerPresent, timing checks via rdtsc, PEB flags) frustrates the deeper step. Stalling code sleeps past a sandbox's monitoring window. You can counter much of this — harden the VM, patch the checks, use bare-metal or evasion-resistant sandboxes — but it is an arms race, and you may not win every round. When you suspect the sample detected the lab, say so: "the sample exhibited anti-VM behavior and did not fully detonate under observation" is a finding, not a failure.

Packing and obfuscation can outrun forensic triage. UPX unpacks trivially; a custom crypter with anti-debugging may require expert reverse engineering — Ghidra/IDA, manual unpacking, hours to weeks — which is a different discipline (lean on the Assembly Language volume) and a different budget than incident response usually has. Knowing when a sample exceeds forensic analysis and needs reverse-engineering specialists is itself a professional judgment.

Behavior is environment-dependent. Malware may behave differently by date (logic bombs), by target (only activating on a specific domain or keyboard layout), by C2 instruction (it does nothing until told), or by missing dependencies. Your detonation captures one path through the program; it does not prove that is the only path. Report what you observed under your conditions, and note that other conditions may elicit other behavior.

Attribution is hard and easily overstated. Shared imphash, code overlap, infrastructure reuse, and TTP similarity suggest a family or actor; they rarely prove one, and actors deliberately plant false flags. "This sample shares the imphash, mutex pattern, and C2 structure of the X family" is defensible; "this is the work of group X" is an intelligence assessment with a confidence level, not a forensic certainty — keep the two rigorously separate, especially in anything that reaches court (Chapter 26, Chapter 27).

And the hardest limit, theme five made concrete: sometimes a sample is so well protected, so environment-gated, or so resource-intensive that within the time and authority you have, you cannot fully determine its behavior. "The available analysis is insufficient to fully characterize the sample's capabilities; the following were confirmed, the following were indicated but not confirmed" is a valid, professional finding. Forcing a complete-sounding conclusion the analysis does not support is how examiners are impeached. Extract what eviction and the report need, document the boundaries of what you could establish, and stop there.


Progressive project: analyze the implant in your case file

Your Forensic Case File now contains an acquired image, recovered artifacts, a timeline, and — from the live-response and memory work — a suspicious executable recovered from the subject system. Add a malware-analysis annex.

  1. Intake safely. Copy the sample into your isolated lab, defang the filename, store it password-protected, and confirm the working copy's hash matches the evidentiary original you sealed at acquisition. Record the chain of custody for the sample (template in Appendix F).
  2. Static triage. Establish the true file type, compute SHA-256 / imphash / ssdeep, do a hash-only reputation check (do not upload a targeted sample), run strings (ASCII + UTF-16LE), parse the PE/ELF header and import table, and compute per-section entropy. Record your capability hypotheses and note whether the sample appears packed.
  3. Author a YARA rule keyed to durable indicators (mutex, user-agent, config bytes), with a precise condition, and verify it matches the sample and — if you have one — the memory image.
  4. Dynamic analysis. Snapshot the VM, instrument process/file/registry/network (Procmon, Regshot, Process Hacker, Wireshark + INetSim/FakeNet), detonate, observe persistence, dropped files, injection, and C2, then revert. Record the behavioral evidence with timestamps.
  5. Produce the deliverables. A capability list mapped to ATT&CK, a defanged IOC package (hashes, C2, mutex, registry keys, paths, YARA, Sigma), and a one-paragraph attribution statement with a confidence level and its basis. Note every limitation (anti-VM behavior, packing you could not unpack, behavior you could not confirm).

Save the YARA, the IOC package, the sandbox report, and your annex into the case file. The capstone (Chapter 38) folds the malware findings into the full investigation; the IOC sweep ties back to your triage tooling from Chapter 15.


Summary

This chapter put the recovered weapon on the workbench and turned it into intelligence. You learned that malware forensics is a defensive, post-incident discipline with concrete deliverables — capabilities, persistence, communications, IOCs, attribution, scope — and that it is distinct from full reverse engineering, going only as deep as eviction and the report require. You built the non-negotiable foundation first: an isolated lab (REMnux + FLARE-VM, host-only network, snapshot-and-revert, fake internet via INetSim/FakeNet) and the rules for handling live malware that keep the weapon from going off where it can do harm, with the sample treated as hashed, chain-of-custody evidence and all work done on copies. In static analysis you established file type and hashes (SHA-256 for identity, imphash and ssdeep for clustering), learned the OPSEC of reputation checks (look up the hash, do not upload a targeted sample), mined strings in both encodings, read the PE format from the MZ/PE\0\0 headers through the import table that betrays capabilities, and used section entropy to spot packed and encrypted payloads. You converted observations into reusable YARA detections that match files and memory with a single rule. In dynamic analysis you detonated under instrumentation across the four domains — process, file, registry, network — using Cuckoo/CAPE, ANY.RUN, or a hand-built FLARE-VM, and saw why faking the internet is what makes C2 behavior visible. You enumerated capabilities — persistence (run keys, services, scheduled tasks, the fileless WMI subscription), keylogging, exfiltration, lateral movement, and command-and-control — and mapped them to MITRE ATT&CK. You organized findings into indicators of compromise, weighted by the Pyramid of Pain so your best detections target durable artifacts and behaviors rather than throwaway hashes, and fed them back into the fleet triage to scope the whole breach. Throughout, the WEB-07 implant from Chapter 15 ran as the through-line — the carved RAM config, the live alert cadence, and the detonated beacon all converging on one C2 — and the ransomware angle showed family attribution doing double duty as a recovery answer. The packers and evasions will keep evolving; the method will not — isolate safely, analyze statically then dynamically, enumerate capabilities, produce detections, and state every finding with its limits.

You can now: - Build and operate a safe, isolated malware-analysis lab and apply the rules for handling live samples without endangering your host, network, or the evidence. - Perform static triage: true file typing, identity and similarity hashing, OPSEC-aware reputation checks, strings, PE/ELF header and import-table reading, and section-entropy detection of packing. - Write YARA rules that turn observations into reusable detection across files and memory. - Detonate safely in a sandbox and monitor process, file-system, registry, and network behavior — including faking the internet to reveal C2. - Identify malware capabilities (persistence via run keys/services/tasks/WMI, keylogging, exfiltration, lateral movement, C2) and map them to MITRE ATT&CK. - Produce a defanged IOC package — hashes, C2, mutexes, registry keys, YARA/Sigma — weighted by the Pyramid of Pain, and use it to scope and detect the breach across the fleet.

What's next. Chapter 33 — Cryptocurrency Investigation — follows the money the weapon was built to extract: when the WEB-07 operators (or the ransomware crew of Chapter 12) demand payment in Bitcoin, you trace the wallets, cluster the addresses, and follow the blockchain — turning the C2's profit motive into another evidentiary trail.


Practice in exercises.md, test yourself with the quiz, apply it in the case studies (one, two), review the key takeaways, and go deeper with further reading.