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.

Key takeaways
  • 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.
The core insight

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.

Figure
Malware taxonomy by propagation mechanism and privilege layer of persistence
Propagation vs. Persistence Layer needs human autonomous PROPAGATION user-space kernel / firmware PERSISTENCE / PRIVILEGE LAYER VIRUS host file infect spreads via user run TROJAN social engineering no self-replication WORM exploits network service fully autonomous scan+infect ROOTKIT subverts kernel / hypervisor hides files, procs, sockets trojan often drops → worm payload installs → virus payload can be a rootkit ↓

Complexity & correctness

Virus spread as a discrete branching process. If an infected file is run with probability pp per unit time and each execution infects on average kk new files before detection, the expected number of infected files after tt generations is O(kt)O(k^t) — exponential in principle, but throttled hard by the human-execution factor p≪1p \ll 1 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 NN vulnerable hosts and models the fraction infected I(t)/NI(t)/N with the differential equation ``` dI/dt = β · I(t) · (N − I(t)) ``` where β\beta 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 dI/dtdI/dt: growth is slow while I(t)≪NI(t) \ll N (few targets found), accelerates once a critical mass of infected scanners exists, and saturates as I(t)→NI(t) \to N (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 O(log⁡N/βN)O(\log N / \beta N) time-to-saturation under this model, meaning that doubling the scan rate (β\beta) 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 LL be the privilege layer at which a detection tool DD runs, and L′L' the layer at which the rootkit RR operates. If L′≤LL' \le L (rootkit is at or below the tool's layer, e.g., kernel rootkit vs. user-space antivirus), then every system call DD issues to gather evidence can, in principle, be intercepted and falsified by RR, because RR controls the interface DD relies on. Formally: detection from inside a compromised trust layer cannot be sound unless DD possesses information RR 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 L′L', 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 O(1)O(1) 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`.

StepMechanismWhat happens
1Trojan (social engineering)Victim double-clicks `invoice_Q3.pdf.exe`, believing it is a PDF; Windows hides the `.exe` extension by default, reinforcing the disguise.
2DropperThe executable displays a fake "Adobe Reader failed to load" dialog while silently writing `svchost32.dll` to `%APPDATA%` in the background.
3PersistenceThe dropper adds a registry `Run` key `HKCU\...\Run\WinUpdate = svchost32.dll`, ensuring execution at every user login — the classic user-space persistence layer.
4Rootkit 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.
5Worm componentOnce 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.
6PayloadOn 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 N=500N = 500 hosts on the corporate subnet, I0=1I_0 = 1, and an estimated β=0.02\beta = 0.02 (per-host-pair infection rate per minute, chosen to match observed WannaCry-class spread), the S-curve predicts:

t (minutes)I(t) (infected hosts)
01
303
6011
9061
120280
150460
180497

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

PitfallWhy 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 rootkitsSignature 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-immuneWorms 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 problemA 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 analysisIt 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
FC
Fred Cohen
born 1956 · Formalized computer viruses as a mathematical object at USC in 1983, proving detection undecidability

History
1971
Creeper, an experimental self-replicating program, moves between ARPANET hosts.
1983
Fred Cohen coins "computer virus" and proves general virus detection is undecidable.
1988
The Morris Worm exploits Unix vulnerabilities and saturates roughly 10% of the early internet.
1999
The Melissa virus spreads via email macros, forcing organizations to disable macro auto-execution by default.
2010
Stuxnet demonstrates a worm crossing an air gap via USB to sabotage industrial centrifuge controllers.
2017
WannaCry combines an NSA-linked exploit (EternalBlue) with ransomware, infecting over 200,000 hosts in days.

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.

Before this

Where this leads