
It starts the same way every time. A file shows up on a host, in a quarantine folder, or attached to a ticket, and someone asks the only question that matters: is this bad? You do not need to fully reverse it to answer. You need to know what it is, what it can do, and what it actually did. An hour of disciplined triage usually gets you there, and it keeps you from burning three days on something a hash lookup would have closed in thirty seconds.
Hash First, Always
Before you execute the sample, or even open it in a disassembler, compute a SHA-256 and write down where it came from. On Linux that is sha256sum; in PowerShell, Get-FileHash. Record the original filename, the acquisition time, and the host it came from. That context matters later when you correlate against logs.
Then check the hash, not the file, against VirusTotal or whatever intel store you trust. This distinction is not pedantry. Uploading a confidential internal binary to a public service can leak proprietary code and, in a targeted intrusion, tells the operator their sample has been found. If the file is sensitive, use a private multi-engine scanner or keep it off the internet entirely.
Identify the File Type Before Anything Else
file is the cheapest high-value command in the toolbox. Magic bytes outweigh the extension every time. A file named invoice.pdf.exe is a Windows PE. A document that reports back "ELF 64-bit LSB executable, x86-64" is not a document. On Windows, Detect It Easy or PE-bear gives you the same information plus a lot more; on ELF, readelf -h; on macOS, otool -hv.
Look for the Toolchain Fingerprint
This step saves hours more often than any other. Go binaries carry a build ID and function names in the pclntab, so symbols survive even when stripped. Rust programs emit distinctive panic strings. .NET assemblies have a CLR header and decompile almost perfectly with ILSpy or dnSpy. A PyInstaller bundle announces itself in the section names and archive contents. If you are looking at .NET or frozen Python, your "reverse engineering" problem is really a reading-comprehension problem, and you should stop treating it like assembly.
Strings: Cheap, Loud, and Often Enough
Run strings -n 8 sample, and on Windows add -el for 16-bit wide strings, because most Win32 path and registry strings are UTF-16 and a default run will miss them. Then grep for the things that matter:
- URLs, IP addresses, User-Agent strings, and TLS certificate names
- Registry paths, especially anything under CurrentVersion\Run or policies keys
- Service names, scheduled task names, and file paths
- Mutex names, which frequently identify a family outright
- Error messages, PDB paths, and build machine names
Hardcoded URLs and mutexes are the two highest-yield finds, because they lead directly to infrastructure or attribution. If the output is thin, FLOSS recovers obfuscated and stack-built strings. But never read sparse strings as a clean bill of health. A well-packed binary is supposed to look empty.
Imports, Sections, and the Linker's Leftovers
On a PE, objdump -p or dumpbin /imports gives you a capability outline. You are reading for markers, not for a verdict:
- Runtime resolution: LoadLibrary plus GetProcAddress, which hides everything else from the import table
- Injection: VirtualAllocEx, WriteProcessMemory, CreateRemoteThread, NtUnmapViewOfSection
- Network and payload fetch: WinInet, WinHTTP, URLDownloadToFile
- Persistence and evasion: CreateService, registry writes, SetFileAttributes, hooking APIs
Now look at sections: names, sizes, and permissions. Treat an executable section that is also writable as a serious flag. So is a .text with almost no raw data, a four-character random-looking name, or a resource section that is several megabytes of noise. On ELF, readelf -d and nm -D do the same job. Note that statically linked Go and Rust binaries have nearly empty dynamic symbol tables. That is normal, not suspicious.
Entropy and the Packer Question
Entropy measures randomness on a 0 to 8 scale. Above roughly 7.2, a section is compressed or encrypted. That is a signal, not a diagnosis: signed installers, self-extracting archives, and legitimate resource blobs all hit that range. Detect It Easy checks signatures and will name UPX, ASPack, Themida, VMProtect, and friends when it recognizes them.
If it is packed, do not start unpacking during your first hour. Get behavior first, then come back to unpacking when you have a specific question that only unpacked code can answer.
Sandbox Execution: Assume It Will Bite
Only analyze samples you are authorized to handle, and never on a machine you care about. Then work in this order:
- Snapshot a disposable VM. No shared folders, no shared clipboard, no mapped drives, no saved credentials.
- Use host-only networking or a simulated internet such as INetSim with a fake DNS server, so you can capture requests without letting the sample reach real infrastructure.
- Start your monitors before you run anything: Procmon with a filter, Wireshark or tcpdump, Process Hacker or System Informer, and on Linux strace. A monitor started after execution misses the interesting part.
- Launch the sample from a working directory you control, then wait. Give it sixty to a hundred and twenty seconds. Persistence and second-stage downloads rarely happen in the first two.
- Record what changed: files created, registry keys, services, scheduled tasks, child processes of the sample, and every outbound connection.
If the sample does nothing, that is a data point, not an exoneration. Plenty of malware checks for VM artifacts, debuggers, or a lack of network access before acting. Note the possibility and move on.
Write the Verdict While It Is Fresh
You have an hour's worth of notes. Compress them into one paragraph: what the file is, what it can do, what it did in your environment, and how confident you are. Include the hash, the toolchain fingerprint, and the two or three strongest pieces of evidence. Then choose a path: close it as benign, hand it to deeper reverse engineering, or escalate. If you escalate, write down the specific open questions for the next person.
The One-Hour Timebox
Follow the order, keep a plain text log as you go, and stop when the hour is up. Triage exists to route work, not to finish it. The person waiting on your answer needs to know whether to rebuild a host tonight, and a careful half-answer beats a perfect one delivered next week. Record what you do not know as clearly as what you do.
Photo: Yan Krukau / Pexels

