Malware and Viruses
In 1988, a program called the Morris Worm quietly doubled its footprint on the early internet every few minutes. Within a day it had disabled roughly ten percent of all connected machines. This happened not through malice in its core logic but through a replication bug. This single incident crystallized a question that still defines systems security. How does self-replicating or attacker-controlled code spread, survive, and hide? It does so inside machines that were never designed to run it. Malware — malicious software — is the umbrella term for code written to violate the confidentiality, integrity, or availability of a system. It does so without the owner's informed consent. It matters because nearly every high-profile security incident tells a similar story. From ransomware crippling hospitals to nation-state espionage implants sitting silently in industrial control systems. Ultimately it is a story about some piece of malware achieving three things. Getting in, staying in, and doing damage. Understanding malware is not about memorizing exploit code. It is about understanding a taxonomy of behaviors. These include replication strategies, propagation vectors, and persistence mechanisms. These recur across decades of otherwise very different attacks. This article builds a structured taxonomy. It covers viruses that need a host and a human. It covers worms that self-propagate over networks. It covers trojans that hide behind a false promise. It covers rootkits that subvert the operating system itself to become invisible. These categories are not mutually exclusive engineering choices. Real malware families routinely combine them. We treat each as a distinct mechanism with its own propagation logic. Each has its own persistence strategy and detection surface. This builds on prior knowledge of Code Injection Attacks as the entry vector. It also builds on Sandboxing and Isolation as a primary defense. By the end, you will be able to classify malicious code by its replication and persistence strategy. You will be able to trace a worm's spread mathematically. You will be able to reason about why rootkits are fundamentally harder to detect than user-space trojans. You will also see how these mechanisms foreshadow a deeper truth. Most successful intrusions exploit humans as much as machines. This is the subject taken up in Security Engineering and the Human Factor.
- Malware is classified by replication mechanism (virus, worm) and behavioral role (trojan, rootkit, ransomware), not by "how bad" it is.
- A virus needs a host program and a human action to spread; a worm self-propagates autonomously across a network.
- Trojans rely on social engineering and a false pretense; they do not self-replicate.
- Rootkits subvert the OS's own trust boundary (kernel, hypervisor, firmware) to hide processes, files, and network connections.
- Worm propagation follows a logistic growth model, explaining why early detection windows are so short and so critical.
- Defenses map to layers: signature/heuristic detection, Sandboxing and Isolation, integrity measurement, and least-privilege architecture.
Malware taxonomy is a classification of replication strategy plus persistence mechanism — a virus needs a host and a human, a worm needs only a network, a trojan needs a lie, and a rootkit needs a foothold beneath the layer that is supposed to detect it.
The idea
A precise taxonomy starts from two orthogonal questions: how does the code get from one machine to the next (propagation), and how does the code survive reboot and evade removal once it is there (persistence). Historically, malware is named for its answer to the propagation question. A virus is code that inserts a copy of itself into a legitimate host program or document; it cannot execute or spread on its own — it requires that host to be run or opened by a person. A worm, by contrast, is a standalone program that actively seeks out and infects new machines over a network, with no human triggering each new infection. This distinction — dependent on a host and a human versus self-propagating — is the single most important axis in the taxonomy, and it explains why worms can achieve orders-of-magnitude faster spread than viruses. A trojan horse (or trojan) is malware that misrepresents its purpose: it presents itself as something desirable — a game, an invoice, a security update — while carrying out unauthorized actions. Trojans do not self-replicate at all; their propagation mechanism is entirely social, making them the purest expression of exploiting human trust rather than a software vulnerability. A rootkit is not defined by propagation but by persistence and concealment: it is a set of techniques for hiding the presence of malicious code, files, processes, or network connections from the operating system's own reporting mechanisms, typically by operating at or below the privilege level of the tools that would detect it (kernel-mode, hypervisor-mode, or firmware-level). Two further terms sharpen the picture. Payload is the malicious action carried out once malware executes — data exfiltration, encryption for ransom, keystroke logging, or destructive wiping — and is logically independent of propagation mechanism; the same payload (say, a ransomware encryptor) can be delivered by a trojan, a worm, or a virus. A dropper or downloader is a small first-stage program, often delivered via a trojan or a code injection exploit, whose only job is to fetch and install a larger, more capable payload — separating the moment of initial compromise from the moment the real malicious logic arrives, which frustrates both static analysis and early detection.
How it works
Virus mechanics. A file-infecting virus locates a target executable, appends or prepends its own code, and rewrites the entry point so its code runs first, then jumps to the original program so the infection appears transparent to the user. ``` PSEUDOCODE: Simple file-infector virus function INFECT(target_file): if IS_ALREADY_INFECTED(target_file): return # avoid re-infecting; conserves file size, avoids detection original_entry = READ_ENTRY_POINT(target_file) virus_body = READ_OWN_CODE() APPEND(target_file, virus_body) new_entry = OFFSET_OF(virus_body, target_file) PATCH_ENTRY_POINT(target_file, new_entry) STORE(virus_body, "saved_original_entry", original_entry) function ON_EXECUTION(infected_file): RUN(virus_body) # payload / further propagation runs first candidates = SCAN_DIRECTORY(CURRENT_DIR()) for f in candidates: INFECT(f) # spread requires this file to be executed JUMP_TO(saved_original_entry) # resume host program, user notices nothing ``` The crucial line is `ON_EXECUTION`: propagation only happens when a human runs the infected file. This is the defining bottleneck of viruses — their spread rate is bounded by human behavior (file sharing, email attachments, USB drives), not network bandwidth.
Worm mechanics. A worm removes the human bottleneck entirely by scanning for vulnerable machines and exploiting them directly, typically via a buffer overflow or injection vulnerability in a network-facing service. ``` PSEUDOCODE: Network worm propagation loop function PROPAGATE(): targets = GENERATE_CANDIDATE_IPS() # random scan, local-subnet scan, or hit-list for ip in targets: if PORT_OPEN(ip, VULNERABLE_SERVICE_PORT): success = SEND_EXPLOIT(ip) # e.g. crafted overflow payload if success: UPLOAD_SELF(ip) REMOTE_EXECUTE(ip, self_copy) # new copy begins its own PROPAGATE() loop SLEEP(scan_interval) PROPAGATE() # recursion / loop: fully autonomous ``` No human ever runs anything: the worm authenticates its own spread by exploiting a flaw in listening software. This is why worm outbreaks (Morris 1988, Code Red 2001, SQL Slammer 2003, WannaCry 2017) can saturate a network in minutes to hours rather than the weeks or months typical of virus outbreaks.
Trojan mechanics. A trojan's "algorithm" is almost entirely social engineering plus a payload; the interesting engineering is in disguise and delivery. ``` PSEUDOCODE: Trojan delivery and execution function BUILD_TROJAN(payload, disguise_asset): wrapper = PACKAGE(disguise_asset, payload)
# disguise_asset: legitimate-looking installer, PDF icon, "invoice.pdf.exe", etc. return wrapper function ON_USER_EXECUTION(wrapper): RENDER_DISGUISE(wrapper) # show fake UI: installer wizard, document viewer SILENTLY_RUN(payload) # drops rootkit, keylogger, ransomware, backdoor if NEEDS_PERSISTENCE(payload): INSTALL_PERSISTENCE(payload) # registry run key, cron job, launch agent ``` The only "vulnerability" exploited is human judgment — there is no code-execution flaw to patch, which is precisely why trojans remain effective against fully patched, sandboxed systems and foreshadow the human-factor material ahead.
Rootkit mechanics. A rootkit's core technique is interception and falsification of the queries that detection tools make to the operating system. ``` PSEUDOCODE: Kernel-level rootkit hiding a process (hooking system call table) function INSTALL_ROOTKIT(): original_getdents = SYSCALL_TABLE[SYS_GETDENTS] # directory listing syscall original_readdir_ptr = SYSCALL_TABLE[SYS_READDIR] SYSCALL_TABLE[SYS_GETDENTS] = HOOKED_GETDENTS # replace pointer in kernel memory STORE(original_getdents) # keep original to call through function HOOKED_GETDENTS(...args): real_result = CALL(original_getdents, args) filtered = [entry for entry in real_result if not MATCHES(entry.name, HIDDEN_PREFIX)] return filtered # userland tools never see hidden files function HOOKED_PROCESS_LIST(...args): real_result = CALL(original_process_enum, args) return [p for p in real_result if p.pid != HIDDEN_PID] ``` Because the hook sits in the kernel, every userland tool — `ls`, `ps`, antivirus scanners, even the shell — receives the falsified result. This is the structural reason rootkits are categorically harder to detect than trojans: detection tools trust the very layer that has been subverted, a direct violation of the isolation assumptions studied in Sandboxing and Isolation.
Complexity & correctness
Virus spread as a discrete branching process. If an infected file is run with probability per unit time and each execution infects on average new files before detection, the expected number of infected files after generations is — exponential in principle, but throttled hard by the human-execution factor and by the fact that files, unlike network hosts, are not globally reachable. This is why historical virus outbreaks (Melissa, ILOVEYOU) took days to weeks to peak, bounded by email-forwarding behavior rather than computational limits.
Worm spread as logistic growth. Classical worm epidemiology (used to model Code Red) treats the internet as vulnerable hosts and models the fraction infected with the differential equation ``` dI/dt = β · I(t) · (N − I(t)) ``` where is the pairwise infection rate (a function of scan rate and exploit success probability). This is the logistic equation; its solution is ``` I(t) = N / (1 + ((N − I₀)/I₀) · e^(−βNt)) ``` The key qualitative fact, provable by inspecting : growth is slow while (few targets found), accelerates once a critical mass of infected scanners exists, and saturates as (running out of susceptible hosts). This S-curve explains the empirically observed pattern in real worm outbreaks: an initially unnoticed "quiet phase," then a sudden explosive phase — SQL Slammer infected an estimated 75,000 hosts within ten minutes once past the inflection point — followed by saturation. The complexity of full network saturation is time-to-saturation under this model, meaning that doubling the scan rate () roughly halves the time-to-compromise, which is why network-level rate-limiting of anomalous outbound connections is a first-order defense.
Rootkit detection lower bound (informal argument). Let be the privilege layer at which a detection tool runs, and the layer at which the rootkit operates. If (rootkit is at or below the tool's layer, e.g., kernel rootkit vs. user-space antivirus), then every system call issues to gather evidence can, in principle, be intercepted and falsified by , because controls the interface relies on. Formally: detection from inside a compromised trust layer cannot be sound unless possesses information cannot tamper with — this is why robust rootkit detection requires either (a) a strictly lower layer (hypervisor introspection, a hardware root of trust, offline disk analysis from a clean system), or (b) cryptographic integrity measurement anchored below , such as a measured boot chain rooted in a TPM. This is the same trust-boundary reasoning underlying Sandboxing and Isolation: a sandbox is only meaningful if the enforcement layer sits strictly below what it constrains.
Tradeoffs. Signature-based detection is per file scan against a hash/pattern database but is blind to novel or polymorphic malware. Heuristic/behavioral detection catches novel variants but costs continuous runtime monitoring overhead and produces false positives. Full-system integrity measurement (measured boot, remote attestation) gives strong correctness guarantees against low-level persistence but cannot itself prevent a legitimate-looking trojan from being voluntarily executed by a user — no purely technical layer defeats a sufficiently convincing lie, which is exactly the gap explored in Security Engineering and the Human Factor.
Worked example
Trace a concrete (simplified, historically grounded) scenario combining several mechanisms, as real intrusions do.
Scenario: An attacker sends a phishing email with an attachment `invoice_Q3.pdf.exe`.
| Step | Mechanism | What happens |
|---|---|---|
| 1 | Trojan (social engineering) | Victim double-clicks `invoice_Q3.pdf.exe`, believing it is a PDF; Windows hides the `.exe` extension by default, reinforcing the disguise. |
| 2 | Dropper | The executable displays a fake "Adobe Reader failed to load" dialog while silently writing `svchost32.dll` to `%APPDATA%` in the background. |
| 3 | Persistence | The dropper adds a registry `Run` key `HKCU\...\Run\WinUpdate = svchost32.dll`, ensuring execution at every user login — the classic user-space persistence layer. |
| 4 | Rootkit component | `svchost32.dll` loads a signed-but-vulnerable kernel driver to escalate privileges, then hooks `NtQuerySystemInformation` so `Task Manager` and antivirus scanners never enumerate its own process. |
| 5 | Worm component | Once resident, the payload scans the local /24 subnet for hosts with SMB port 445 open and an unpatched remote-code-execution flaw, exactly as WannaCry's EternalBlue module did in 2017. |
| 6 | Payload | On each newly infected host, files matching common document extensions are encrypted; a ransom note is dropped demanding cryptocurrency payment. |
Applying the logistic model from the previous section with hosts on the corporate subnet, , and an estimated (per-host-pair infection rate per minute, chosen to match observed WannaCry-class spread), the S-curve predicts:
| t (minutes) | I(t) (infected hosts) |
|---|---|
| 0 | 1 |
| 30 | 3 |
| 60 | 11 |
| 90 | 61 |
| 120 | 280 |
| 150 | 460 |
| 180 | 497 |
Computed from `I(t) = N / (1 + ((N−I₀)/I₀)·e^(−βNt))` with `βN = 10`: the network stays under 3% infected for the first hour (the "quiet phase" that lets IDS false negatives go unnoticed), then crosses 50% infection between minute 130 and 140 (the explosive phase), and saturates by minute 180. This single trace shows why the first thirty minutes after initial compromise — while the trojan and rootkit stages are still setting up — are the highest-leverage window for detection, before the worm module's exponential-then-saturating spread makes containment a subnet-wide incident-response problem rather than a single-host cleanup.
Common pitfalls & traps
| Pitfall | Why it's wrong |
|---|---|
| Calling any malicious program a "virus" | Conflates propagation mechanism with harm; a trojan or worm is not a virus and requires different defenses. |
| Assuming antivirus signature scanning defeats rootkits | Signature scanners query the OS through the same interface the rootkit has hooked; the tool can be lied to. |
| Believing air-gapped or offline systems are worm-immune | Worms can still spread via removable media (Stuxnet crossed an air gap via USB); "network worm" describes exploit logic, not the only transport. |
| Treating persistence and propagation as the same problem | A worm's scanning code and its registry-key survival mechanism are separate subsystems; patching the network vulnerability doesn't remove an already-installed persistence hook. |
| Assuming polymorphic/metamorphic malware defeats all static analysis | It defeats exact-signature matching, but behavioral and heuristic detection (monitoring syscall sequences, entropy analysis) still applies. |
| Ignoring the logistic growth "quiet phase" | Low early infection counts are mistaken for "contained," when the model predicts imminent exponential acceleration. |
Pioneers & history
Fred Cohen, then a graduate student, formally defined the computer virus and demonstrated the first true self-replicating program under laboratory conditions in 1983, proving mathematically that no algorithm can perfectly detect all possible viruses — a direct analog of the halting problem. Robert Tappan Morris's 1988 worm, though intended as a research experiment in counting internet hosts, contained a propagation bug causing repeated reinfection, and it inadvertently produced the first large-scale demonstration of logistic-style network saturation, leading directly to the founding of the first CERT (Computer Emergency Response Team).
Origins · 1 minFred Cohen and how this was discovered
Where this matters
Enterprise endpoint detection and response (EDR) tools are built directly on this taxonomy: they combine signature databases (fast, cheap, blind to novel malware), behavioral heuristics (catch worm-like scanning and trojan-like persistence installation), and kernel-integrity monitoring (catch rootkit-style hooking) as layered defenses, because no single layer defeats every category. Cloud providers isolate customer workloads with hypervisor-level Sandboxing and Isolation precisely because it sits below where a guest-OS rootkit could operate, satisfying the lower-privilege-layer requirement for sound detection derived above. Secure boot and TPM-based measured boot chains in modern laptops and phones exist specifically to defeat firmware- and bootloader-level rootkits, anchoring trust in hardware that malware cannot rewrite. Email security gateways and browser sandboxing target the trojan vector by neutralizing the social-engineering delivery mechanism before a human ever makes the fatal click — and because that click is the one link in the chain no technical control fully removes, this taxonomy sets up the deeper question of Security Engineering and the Human Factor: how systems should be designed for the humans who actually operate them.
Ask the tutor: How does the logistic worm-spread model change if the network is segmented into isolated subnets rather than one flat address space?
Is a "computer virus" the correct general term for all malicious software?
No. "Virus" is a specific technical term for malware that infects a host file or document and requires a human to execute that host in order to spread further. Popular usage often uses "virus" loosely to mean any malware, but this obscures crucial differences in defense strategy. A worm, for instance, needs no human interaction and spreads over networks directly by exploiting vulnerable services, so patching a network service defeats a worm but does nothing against a trojan relying on a phishing email. Precise taxonomy matters operationally: incident responders choose different containment strategies (network isolation vs. user awareness training vs. kernel forensics) depending on which category, or combination, they are facing.
Can malware belong to more than one category at once?
Yes, and modern malware routinely does. A single intrusion chain, as in the worked example, can begin as a trojan (social engineering delivery), install rootkit components for stealth and persistence, and include a worm module for lateral network spread, all before executing a ransomware payload. The categories describe distinct mechanisms — propagation and persistence strategies — rather than mutually exclusive families, so real malware is best understood as a composition of these mechanisms rather than a single label. WannaCry is the canonical example: trojan-adjacent delivery in some variants, worm propagation via EternalBlue, and ransomware payload, all in one binary.
Why can't antivirus software simply detect every virus with a perfect algorithm?
Fred Cohen proved in 1983 that no algorithm can, in general, decide whether an arbitrary program is a virus, by a reduction closely related to the halting problem: constructing a program whose viral behavior depends on the very detection algorithm's own output leads to contradiction. This means perfect, general virus detection is undecidable, not merely difficult with current technology. Practical antivirus tools instead rely on incomplete but useful approximations: signature matching against known malware, heuristic analysis of suspicious behavior patterns, and sandboxed execution to observe runtime behavior, each trading soundness or completeness for practicality.
How does a worm actually find new machines to infect?
Worms use several scanning strategies with different tradeoffs. Random scanning generates IP addresses uniformly at random and probes each for the vulnerable service — simple but wasteful, since much of the address space is unused (SQL Slammer used this and still saturated in minutes due to raw scan speed). Local-subnet-preference scanning exploits the fact that machines on the same network segment often share the same vulnerability, accelerating spread within an organization. Hit-list scanning uses a pre-compiled list of known-vulnerable targets, achieving near-instant "flash worm" propagation with no wasted probes, though it requires reconnaissance before release. Each strategy trades reconnaissance cost against propagation speed.
Why do rootkits often target the kernel rather than staying in user space?
The operating system kernel is the layer that user-space tools — including antivirus software, `ps`, `ls`, and Task Manager — trust unconditionally when asking "what processes are running" or "what files exist." A user-space rootkit can hide itself using tricks like process-name spoofing, but a sufficiently careful user-space scanner can still detect inconsistencies. A kernel-mode rootkit instead hooks the system calls that generate those answers in the first place, so every tool built on top of the kernel receives falsified data, making detection require an entirely separate, lower trust layer such as hypervisor introspection or offline analysis, exactly the isolation principle covered in Sandboxing and Isolation.
Does patching software vulnerabilities eliminate the malware threat entirely?
No — patching closes the exploit-based propagation vector that worms and some droppers rely on, but it does nothing against trojans, which exploit human trust rather than a software flaw, and nothing against malware that gains entry through legitimate but compromised software supply chains or stolen credentials. Even a fully patched, sandboxed system can be compromised if a user is convinced to run a disguised executable voluntarily. This is precisely why the field moves from purely technical malware taxonomy toward Security Engineering and the Human Factor: the strongest technical defenses still leave the human decision-maker as an attack surface.
What role does persistence play, and why is it engineered separately from the initial infection code?
Persistence is the mechanism by which malware survives reboots, user logouts, and casual remediation attempts, and it is engineered as a distinct module because the initial exploit or social-engineering trick is often a one-time, fragile opportunity that should not need to be repeated. Common persistence techniques include registry `Run` keys, scheduled tasks or cron jobs, malicious kernel drivers or bootkits, and firmware implants — each corresponding to a different privilege layer and therefore a different removal difficulty. Separating "how did it get in" from "how does it stay" lets analysts reason about each independently: closing the entry vulnerability does not automatically remove an already-installed persistence hook.