A phishing attachment gets caught by the mail gateway too late, someone already opened it, and now there's an .exe sitting in a sandbox share waiting on triage. The instinct, especially under deadline pressure, is to just run it and watch what happens: fire it up in a VM, see what it drops, call it a day. That instinct is backwards, and it's backwards in a way that costs real information. Running the sample first means you find out what it does on this run, in this environment, against whatever it happened to check for before deciding to behave or not behave. It tells you nothing about what it's built to do in general, and a reasonable fraction of malware is built specifically to notice a sandbox and do nothing at all.
The workflow that actually works starts static, before the sample ever executes a single instruction, and there are three checks in that static pass that do most of the real work.
First is the PE header itself. The header carries the machine type, the compile timestamp, and the section table, and none of it requires running the file to read. A compile timestamp that's suspiciously fresh, suspiciously round, or that predates the vulnerability the sample is exploiting is a forgery worth noting on its own, malware authors routinely patch this field to blend in or throw off timeline analysis, and a mismatch is a tell precisely because faking it convincingly takes more care than most operators bother with.
Second is the import table, and this is where static analysis earns its keep. Windows executables declare which API functions they call before they run, and certain combinations are close to diagnostic on their own. VirtualAlloc plus WriteProcessMemory plus CreateRemoteThread sitting together is the textbook signature of process injection, allocating memory in another process, writing code into it, then spinning up a thread to run that code, and legitimate software has very little reason to need all three at once. A separate pattern worth flagging: heavy reliance on LoadLibrary and GetProcAddress instead of normal static imports usually means the author is resolving API calls dynamically at runtime specifically to keep them out of the import table, a deliberate move to defeat exactly this kind of static review.
Third is section entropy. Compiled code sits in a fairly predictable entropy range, not maximally random, because instructions and data have structure. A section that reads close to maximum entropy is almost always packed or encrypted, and that's the signal to stop reading disassembly and go straight to unpacking before anything else is worth analyzing statically.
Only once static analysis has actually raised something is dynamic analysis worth the risk, and even then it happens in an isolated VM with network containment, tools like INetSim or FakeNet standing in for the real internet so the sample believes its callbacks are landing while nothing actually reaches a live C2 server. That's where you watch the file drops, the registry changes, the process tree it spawns, the real behavioral evidence that confirms what the static pass already suggested instead of contradicts it.
Skipping straight to "just run it and see" isn't a shortcut, it's giving up the one pass that works against sandbox-aware malware and getting, at best, a partial picture of behavior with no idea whether you triggered the sample's full functionality or its evasion logic. Codelivly's Static and Dynamic Malware Analysis Guide walks through building the isolated lab and running this exact static-first, contained-dynamic-second workflow end to end.
Top comments (0)