jafeeri/wannacry-malware-analysis
GitHub: jafeeri/wannacry-malware-analysis
一份针对 WannaCry 勒索蠕虫的全面恶意软件分析报告,涵盖静态分析、动态分析和逆向工程。
Stars: 0 | Forks: 0
# WannaCry Ransomware — Full Malware Analysis
**Static analysis · Dynamic analysis · Reverse engineering of a self-propagating ransomware worm**





**Analyst:** Ali Mehdi Jafeeri · **Date:** July 2026
## ⚠️ Safety Notice
## Table of Contents
- [Executive Summary](#executive-summary)
- [Why This Sample](#why-this-sample)
- [Laboratory Environment](#laboratory-environment)
- [Phase 1 — Sample Identification](#phase-1--sample-identification)
- [Phase 2 — Static Analysis](#phase-2--static-analysis)
- [Phase 3 — Dynamic Analysis](#phase-3--dynamic-analysis)
- [Phase 4 — Payload Extraction & Reverse Engineering](#phase-4--payload-extraction--reverse-engineering)
- [MITRE ATT&CK Mapping](#mitre-attck-mapping)
- [Indicators of Compromise](#indicators-of-compromise)
- [YARA Detection Rule](#yara-detection-rule)
- [Defensive Recommendations](#defensive-recommendations)
- [What I Learned](#what-i-learned)
- [References & Prior Work](#references--prior-work)
## Executive Summary
WannaCry is a **hybrid threat**: a self-propagating network worm carrying a file-encrypting
ransomware payload. This analysis established its full behaviour through three coordinated phases —
static characterisation without execution, controlled detonation under instrumentation, and
source-level reverse engineering in Ghidra.
### Key findings
| # | Finding |
|---|---|
| 1 | The sample masquerades as a **Microsoft disk utility**, is **unsigned**, carries a **forged 2010 compile timestamp**, and was distributed as `invoice_greenanimals.pdf.exe` — a double-extension lure. 68/70 engines detect it. |
| 2 | **Three capabilities were proven statically, without executing the file:** mass file encryption (AES round tables + CryptoAPI in the image), autonomous SMBv1 propagation (a full downgrade-negotiation client), and privileged persistence (complete service-control API set). |
| 3 | The file is a **three-layer nested container** — a readable ~5% loader wrapping a 94.4% high-entropy resource, which is itself a PE containing a password-protected 36-file archive. |
| 4 | Behaviour is gated by a single network check (the **kill switch**), demonstrated experimentally **in both directions** and then confirmed in the decompiled code. |
| 5 | On detonation: dropped its toolkit to `C:\ProgramData`, installed auto-start service **`mssecsvc2.0`** granting **SYSTEM**, destroyed all recovery options, encrypted files at 6.65 MB/s, demanded Bitcoin, and scanned the internet for further SMB victims. |
| 6 | **Propagation failed** against a patched target — a direct demonstration that MS17-010 patching neutralises the worm. |
| 7 | Complete **C2 infrastructure (5 Tor hidden services)** was recovered **statically**, with zero network contact. |
## Why This Sample
This is the second specimen in a two-sample series. The first was **RedLine Stealer** — a covert
infostealer. WannaCry inverts every one of its assumptions, and the contrast drives several
observations throughout this analysis:
| | RedLine (sample 1) | WannaCry (sample 2) |
|---|---|---|
| **Objective** | Harvest credentials covertly | Deny access and extort |
| **Delivery** | Delivered by operator | **Self-propagating (SMBv1 / MS17-010)** |
| **Concealment** | Packed loader + **process hollowing** | **Nested container** (PE → PE → encrypted ZIP) |
| **Code visibility** | Hidden until unpacked in memory | **Loader readable; only cargo encrypted** |
| **Payload recovery** | Memory dump (pe-sieve / dnSpy) | **Static resource extraction — no execution** |
| **Persistence** | None — grab-and-go | Service `mssecsvc2.0`, auto-start, SYSTEM |
| **Privileges** | User context | **SYSTEM** |
| **Attacks** | Confidentiality | **Availability + integrity** |
**The lesson:** the concealment strategy an attacker chooses dictates the recovery strategy the
analyst must use. RedLine hid its *code* and forced a memory dump. WannaCry hides its *cargo* but
leaves its *brain* in the open — so its entire decision structure was recoverable from a file that
was never executed.
## Laboratory Environment
Because the specimen is both destructive and self-propagating, containment was designed **before**
the sample was unpacked. A worm does not need a second manual execution to spread — one run is
enough for it to begin attempting outbound connections. Containment therefore could not depend on
operator discipline during the run; it had to be **structural**, enforced by the hypervisor.
| Host | Address | Role |
|---|---|---|
| Windows 10 (FLARE-VM) | `192.168.56.20` | Victim / detonation host, static tooling |
| REMnux | `192.168.56.102` | Gateway, DNS sinkhole (dnsmasq), fake services (INetSim) |
| Network | `192.168.56.0/24` | VirtualBox **Host-Only** — no external route |
The victim's gateway and DNS both pointed at REMnux, so every packet and name resolution passed
through infrastructure under observation. Shared folders, clipboard, drag-and-drop and USB
passthrough were all disabled; the VM was reverted to a clean snapshot between every experiment.
**Toolset:** VirusTotal · MalwareBazaar · HashMyFiles · Detect It Easy · PE Detective · CFF Explorer ·
PEStudio · FLOSS · Ghidra 12.1.2 · Resource Hacker · 7-Zip · HxD · Process Monitor · Process Hacker ·
Regshot · Wireshark · dnsmasq · INetSim
## Phase 1 — Sample Identification
Analysis began by confirming the specimen obtained is the specimen intended for study. Malware
families have thousands of variants, and analysing one while reasoning from notes on another
produces conclusions that are confidently wrong.
| Property | Value |
|---|---|
| **SHA-256** | `24d004a104d4d54034dbcffc2a4b19a11f39008a575aa614ea04703480b1022c` |
| **SHA-1** | `e889544aff85ffaf8b0d0da705105dee7c97fe26` |
| **MD5** | `db349b97c37d22f5ea1d1841e3c89eb4` |
| File size | 3,723,264 bytes (3.55 MiB) |
| Distribution name | `invoice_greenanimals.pdf.exe` (double-extension lure) |
| Masquerade name | `lhdfrgui.exe` (genuine Disk Defragmenter filename) |
| imphash | `9ecee117164e0b870a53dd187cdd7174` — shared with 82 WannaCry samples |

*68 of 70 engines flag the file. Threat categories: **trojan**, **ransomware** and **worm** simultaneously.*
Three details here outweigh the detection ratio. The **triple categorisation** announces a hybrid —
a component that spreads autonomously bolted onto a component that extorts. The **behavioural tags**
(`detect-debug-environment`, `checks-network-adapters`, `long-sleeps`, `exploit`) each predict a
mechanism to confirm or refute from the binary. Most informative are the **CVE tags**:
`CVE-2017-0144` and `CVE-2017-0147` are the MS17-010 SMBv1 vulnerabilities. Their presence means the
sample does not merely *use* the network — it *attacks* it, with a pre-authentication RCE against a
service enabled by default. **This single observation justified the entire Host-Only lab design.**

*Repository metadata — already signature-tagged WannaCry, imphash shared with 82 samples.*

*Content-based typing identifies a Win32 MSVC executable independent of its deceptive filename.*

*Samples are distributed password-protected so they cannot execute accidentally or be quarantined mid-download.*

*Sample staged inside the isolated VM, still archived. Transfer used a temporary NAT adapter inside the guest, returned to Host-Only before extraction — the file never touched the host filesystem.*

*Independent verification: byte-identical to published values. Every subsequent finding is anchored to this exact artefact.*
## Phase 2 — Static Analysis
### 2.1 File type, provenance and packing

*The densest single result of the static phase.*
DIE identifies a **PE32 (32-bit) GUI executable** built with **Visual Studio 6.0**. Three results
drove everything that followed:
1. The heuristic flags `.rsrc` as **compressed/packed high-entropy data**
2. A **nested PE32** at offset `0x000320A4`, size `0x0035A000` — an entire second executable
3. Inside that, an **encrypted ZIP archive (Zip 2.0), 55.8%, 36 files**
**On the compile timestamp:** DIE reads November 2010, yet the sample carries exploits for
vulnerabilities patched in March 2017. A binary cannot contain a working exploit for a bug that will
not be publicly known for another six years. The timestamp is **forged** — and forged consistently
with the version resource, which claims to be a 2010 Windows 7 component. That is deliberate
provenance falsification, not a compiler artefact.
**On the compiler:** Visual Studio 6.0 was obsolete by 2017. It produces small binaries linking
against `MSVCRT.DLL`, present on every Windows install without redistributables. For a worm that must
run on arbitrary unknown hosts, maximum compatibility beats modern language features.

*Overall file entropy 7.96/8.0 ("packed, 99%") — but `.text` measures only 6.13.*
Entropy measures information density per byte; encrypted or compressed data approaches 8.0 because
it removes the redundancy that lowers the score. The **per-region breakdown is what makes this
diagnostic**:
| Region | Entropy | Status |
|---|---|---|
| PE header | 0.727 | not packed |
| `.text` (code) | **6.135** | **not packed** |
| Whole file | **7.964** | **packed** |
The executable code is **not** packed. This distinguishes a **container** (a readable loader shipping
an encrypted cargo) from a **packer** (compressed code reconstructed at runtime). WannaCry is a
container — an enormous analytical advantage, because the loader's logic is fully available to static
analysis, which is exactly why Phase 4 succeeded without execution.

*Independent corroboration: Microsoft Visual C++ 6.0, nine signature families.*
The low-confidence `Armadillo v1.71` match is a **known false positive** keyed on entry-point byte
patterns that plain MSVC 6.0 also produces. I record it rather than discarding it silently — reporting
only the convenient result is how analysis becomes advocacy.
### 2.2 Metadata and the masquerade

*Header parsing and version resource: "Microsoft® Disk Defragmenter", `lhdfrgui.exe`.*

*Note `certificate > n/a` — **the file is unsigned**.*

*The raw version-resource strings as stored in `.rsrc` — the free-text fields that constitute the masquerade.*
Every claimed value is false. The author copied authentic metadata from a real Microsoft binary. But
**the file is unsigned**, and that collapses the masquerade: authentic Microsoft binaries are
Authenticode-signed, whereas version strings are free text any compiler embeds on request.
### 2.3 String and capability extraction (FLOSS)
FLOSS runs conventional extraction, then *emulates* each function to recover **stack strings**
(assembled character-by-character at runtime, so they never appear contiguously) and **decoded
strings**. This matters against malware specifically, because authors know `strings` is the first
tool anyone runs.

**87 functions analysed · 15 stack strings · 6 decoded strings · 299 UTF-16 strings**
#### SMB protocol strings — the worm component

SMBu SMBr SMBs SMB2 PIPE
PC NETWORK PROGRAM 1.0 LANMAN1.0
Windows for Workgroups 3.1a LM1.2X002
LANMAN2.1 NT LM 0.12
Windows 2000 2195 /K__USERID__PLACEHOLDER__
This is the most informative static evidence of propagation. The list from `PC NETWORK PROGRAM 1.0`
through `NT LM 0.12` is the **dialect array sent in an SMB negotiation request**. A modern Windows
client negotiates SMB2 or SMB3; offering the *complete historical list* beginning with 1980s LAN
Manager dialects is code deliberately negotiating **downward to SMBv1** — because MS17-010 exists
only in SMBv1. **The exploit does not work against SMB2**, so the malware must first talk the victim's
server into speaking an obsolete protocol. `IPC$` is the named-pipe endpoint the exploit targets.

*FLOSS summary. The "decoded" output (`AWAVAUATSQRUWVPP`, `XX^_]ZY[A\A]A^A_H`) is not text — these are **x86-64 register save/restore sequences** misread as strings. Their presence matters: 64-bit instruction patterns inside a 32-bit binary indicate **embedded 64-bit shellcode**, which is what MS17-010 requires to run in the kernel-mode SMB driver on x64 targets.*
#### Target file extensions — the encryption scope

The list is notable not for its length but its **composition**, segmented by victim type:
- **Office/productivity** (`.doc`, `.docx`, `.ods`) — general users
- **Databases** (`.mdf`, `.ldf`, `.myd`, `.myi`, `.ibd`, `.accdb`) — SQL Server, MySQL, Access. *Server* artefacts
- **Source code** (`.cpp`, `.java`, `.asm`, `.sln`, `.suo`) — developer machines
- **Scripts** (`.ps1`, `.bat`, `.cmd`, `.vbs`) — administrator tooling; attacks the *recovery capability itself*
- **Engineering** (`.max`, `.3dm`, `.brd`, `.sch`) — irreplaceable professional work
Ransomware pricing depends on the victim's cost of *not* paying. A list weighted toward databases,
source code and engineering data is optimised for victims whose downtime is expensive and whose data
is unique — exactly the profile that pays.
#### Component filenames and 28 localisations

Twenty-eight localisations of the ransom note, bundled **before a single victim is infected**.
Localisation is expensive and pointless for a targeted attack, where one language suffices. Twenty-eight
languages selected at runtime from the victim's system locale is the signature of an operation designed
for **indiscriminate global distribution** — consistent with a worm, which cannot choose whom it reaches.

*Two distinct version-info blocks in one binary.*
The outer file poses as **Disk Defragmenter** (`lhdfrgui.exe`); a second complete block impersonates
**DiskPart** (`diskpart.exe`). Since a PE has exactly one version resource of its own, the second
belongs to the **embedded executable**. The masquerade is layered — and both choices are deliberate:
disk utilities are exactly the class of program a user *expects* to consume CPU and touch every file
on the volume, which is a precise description of ransomware encrypting. *(Confirmed at runtime in
Phase 3 — the running payload displays the description "DiskPart".)* The path templates
`%s\ProgramData` and `%s\Intel` in the same view predicted the install location, also later confirmed.

*Predominantly C-runtime diagnostics and calendar names — library noise distinguished from author-written content.*


*The visual signature of the encrypted archive occupying ~94% of the file — no intelligible strings.*
### 2.4 Imports — capability inference

*91 imports across 7 libraries, 30 flagged.*
The import table is a **declaration of intent** — a program cannot call an API it has not resolved.
| Library | Key functions | Capability |
|---|---|---|
| **ADVAPI32** | `OpenSCManagerA`, `CreateServiceA`, `StartServiceA`, `StartServiceCtrlDispatcherA` | **Service install + service-mode execution → persistence at SYSTEM** |
| **ADVAPI32** | `CryptAcquireContextA`, `CryptGenRandom` | Cryptographically secure key generation |
| **WS2_32** | `socket`, `connect`, `send`, `recv`, `select` *(by ordinal)* | Raw TCP client — SMB propagation |
| **WININET** | `InternetOpenA`, `InternetOpenUrlA`, `InternetCloseHandle` | **The kill-switch check** |
| **IPHLPAPI** | `GetAdaptersInfo`, `GetPerAdapterInfo` | Network enumeration — deriving the subnet to scan |
| **KERNEL32** | `FindResourceA`, `LoadResource`, `LockResource`, `SizeofResource` | Embedded payload extraction |
Four observations beyond reading the table:
- **The service APIs form a complete set** covering both *installation* and *service-mode execution* —
so the binary runs in **two modes**: once as a user-launched installer, again as the service.
- **`GetAdaptersInfo` is targeting infrastructure.** Reading the local interface config yields the
subnet to scan. Combined with `rand`/`srand`, it supports the dual local-sweep + random-internet strategy.
- **`CryptGenRandom` is the cryptographically secure choice.** Ransomware families that used a
predictable PRNG have been broken by researchers reconstructing the seed. This forecloses that route.
- **WS2_32 is imported by ordinal**, so networking function *names* never appear as strings — naive
string-based triage finds nothing.
**What's absent is also informative:** neither `WriteFile` nor any `CreateProcess` variant appears,
despite both being used at runtime, while `GetProcAddress` and `GetModuleHandle` *are* present. This
indicates **dynamic API resolution** — the noisiest, most-monitored functions are looked up at runtime
to keep them out of the static import table.
### 2.5 Cryptographic and anti-analysis signatures

| Offset | Signature | Significance |
|---|---|---|
| `0x0003AAA0` | AES Rijndael S-box | AES substitution table |
| `0x0003ACA0`–`0x0003B8A0` | Rijndael `Te0`–`Te3` | AES **encryption** round tables |
| `0x0003C0A0`–`0x0003C8A0` | Rijndael `Td1`–`Td3` | AES **decryption** round tables |
| `0x000411A4` / `0x00041174` | `CryptImportKey` / `CryptDecrypt` | CryptoAPI key handling |
| `0x0003EF10`–`0x0003F080` | `zinflate_*` | DEFLATE — the ZIP extractor |
| **`0x000197F4`** | **anti-debug: `IsDebuggerPresent`** | **Anti-analysis** |
Locating **full AES round tables statically** confirms symmetric encryption is implemented natively.
The presence of *both* forward (`Te`) and inverse (`Td`) tables shows the same code can encrypt **and**
decrypt — architecturally necessary, because the operator must be able to decrypt for payment to be
plausible. **The decryption capability ships with the malware; only the key is withheld.**
Combined with the CryptoAPI imports this is the classic **hybrid scheme**: AES for fast bulk file
encryption, RSA to wrap the AES key so only the operator's private key recovers it.
`IsDebuggerPresent` confirms the `detect-debug-environment` tag and establishes the author anticipated
analysis. A shallow check, but a real one.
### 2.6 The embedded payload — the decisive finding

| Name | Location | Size | Entropy | Signature | First bytes |
|---|---|---|---|---|---|
| `version` | `0x0038C0A4` | 944 B | 3.532 | version | `B0 03 34 00` |
| **`R` / 1831** | **`0x000320A4`** | **3,514,368 B** | **7.995** | **executable** | **`4D 5A` = `MZ`** |
Resource **`R`/1831 contains 94.4% of the entire file** at essentially maximum entropy, and PEStudio
identifies it as an **executable** beginning with the `MZ` magic. The outer program is a **delivery
wrapper**: ~5% is code that runs; 94% is cargo.
Cross-referencing the DIE scan, the complete architecture resolves to **three layers**:
wannacrymalware.exe ← Layer 1: worm / dropper (readable, ~5%)
└── .rsrc → resource "R" / 1831 ← Layer 2: tasksche.exe, a full PE (94.4%)
└── .rsrc → encrypted ZIP, 36 files ← Layer 3: the ransomware toolkit
└── password: WNcry@2ol7 ← recovered in Phase 4
Each layer serves a distinct purpose. Layer 1 decides *whether* to act (kill switch) and *how to
spread* (SMB). Layer 2 handles installation, persistence and encryption. Layer 3 holds supporting
assets. **A scanner must defeat all three layers to reach the payload**, and the innermost is
protected by a password existing only in the middle layer's code — so static signatures written
against the payload *never match the delivered file*.
## Phase 3 — Dynamic Analysis
### 3.1 The kill switch — the controlling variable
Static analysis established the binary's first action is an HTTP request to one hardcoded domain, and
that the result governs everything. It could not determine the **polarity** of that decision — so it
was derived experimentally, then confirmed in the disassembler.
hxxp://www[.]iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea[.]com
- **Request succeeds** → the sample **terminates**. Nothing is encrypted.
- **Request fails** → **full ransomware and worm routine**.
The logic is **inverted** relative to normal sandbox evasion. Rather than looking for evidence a
sandbox is *present*, it looks for evidence a domain that *should not exist* resolves anyway. On the
real internet a long random domain resolves to nothing; inside a sandbox faking all DNS, it resolves
fine. **Successful resolution is treated as proof of an artificial environment.**
#### Run A — kill switch REACHABLE (predicted: no encryption)

*The first packet the malware generates — before touching a single file, it asks whether this domain exists.*

*The complete transaction: DNS resolution (5–6), TCP handshake (7–9), `GET /` (10), `HTTP/1.1 200 OK` from INetSim (15), `RST` (16).*
**Result: the process exited.** No encryption, no wallpaper change, no ransom note, no service.
A synthetic `200 OK` from my own fake web server was enough to shut the malware down completely.
*(The ICMP traffic to 8.8.8.8 / 1.1.1.1 in the capture is Windows' own connectivity checking, not
malware activity. Distinguishing guest-OS noise from sample behaviour is a necessary discipline.)*

*Manual confirmation of the Run A DNS state.*
#### Run B — kill switch UNREACHABLE (predicted: full detonation)
Reverted to clean snapshot, then broke **only** that domain — keeping the rest of the sinkhole intact
to retain full DNS visibility. dnsmasq applies specific rules ahead of the wildcard:
sudo dnsmasq -d --conf-file=/dev/null --no-resolv \
--listen-address=192.168.56.102 --bind-interfaces \
--address=/iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea.com/0.0.0.0 \
--address=/#/192.168.56.102 --log-queries

*Verified **before** execution: the kill-switch domain now resolves to `0.0.0.0` while every other domain still sinkholes normally.*
This state was verified rather than assumed — the entire experiment depends on this one resolution
being different. **Result: full detonation.**
### 3.2 Process behaviour and privilege


Explorer.EXE
└── wannacrymalware.exe ← exits immediately after handoff
└── tasksche.exe [C:\ProgramData\rgltpxbykpoo197\] "DiskPart"
└── @WanaDecryptor@.exe "Load PerfMon Counters"
└── taskhsvc.exe [TaskData\Tor\] — bundled Tor client
└── conhost.exe
**The parent exits immediately after dropping the payload** — the same handoff pattern seen in
RedLine, achieved differently (RedLine hollowed a legitimate process; WannaCry drops a fresh
executable). In both cases the artefact the user launched is gone before anything interesting happens.
**Both process descriptions confirm static predictions:** `tasksche.exe` reports as **"DiskPart"** —
exactly the second version-info block found in §2.3.

*The entire chain runs as **NT AUTHORITY\SYSTEM**.*
This is one of the most consequential observations. The escalation route is the service mechanism
identified statically: **a service created through the SCM runs as `LocalSystem` by default**, so
registering a service is *simultaneously* the persistence mechanism **and** the privilege escalation.
SYSTEM permits encrypting all users' files, deleting Volume Shadow Copies (an administrative
operation), and sitting above user-context security tooling.

*`tasksche.exe` during active encryption: **7.92% CPU, 6.65 MB/s sustained disk I/O** — the encryption loop executing.*
The sustained I/O rate is the clearest quantitative signature of mass encryption, and the most
practical basis for behavioural detection: sustained high-volume writes from a single non-backup
process are anomalous in a way the malware cannot easily disguise.

*`VSSVC.exe` (Volume Shadow Copy Service) and `WmiPrvSE.exe` active — the observable consequence of the malware invoking them to destroy recovery options.*
### 3.3 Dropped toolkit

*`C:\ProgramData\rgltpxbykpoo197\` — the complete deployed toolkit (18 items).*
Three files are the **cryptographic heart of the extortion**, visible on disk:
per-file AES key → encrypted with victim RSA public key (00000000.pky)
victim RSA private key → encrypted with operator RSA public key (00000000.eky)
operator RSA private key → NEVER present on the victim machine
Everything needed to decrypt is physically present on the victim's disk **except one value**, which
exists only on the operator's infrastructure. That single missing item is what is being sold. The
design is sound — there is no implementation flaw to exploit from the files alone.

*All 28 localisations deployed to disk — exactly as predicted from the filenames recovered by FLOSS.*

*A complete, self-contained Tor client with its OpenSSL dependencies (`taskhsvc.exe` is `tor.exe` renamed).*
The malware carries its **own** onion router rather than relying on anything installed on the victim.
This is a deliberate OPSEC choice: it removes any dependency on victim software and routes all C2
through Tor, so the operator's real infrastructure is not exposed by traffic analysis.
### 3.4 Recovery destruction and the ransom demand
vssadmin delete shadows /all /quiet
wmic shadowcopy delete
wbadmin delete catalog -quiet
bcdedit /set {default} bootstatuspolicy ignoreallfailures
bcdedit /set {default} recoveryenabled no
**This is the step that converts an inconvenience into an extortion, and its ordering is the point.**
Encryption alone is survivable — Windows keeps Volume Shadow Copies precisely so previous versions can
be restored. By deleting shadow copies, destroying the backup catalog and disabling recovery
**before** presenting the demand, the malware removes every escape route while the victim isn't
looking. The victim is only told the game has started after they have already lost every alternative move.
This is also **why SYSTEM was a prerequisite, not a luxury** — `vssadmin` requires administrative
privilege. The chain is: service → SYSTEM → shadow deletion → irreversibility → leverage.

*"Wana Decrypt0r 2.0" — $300 in Bitcoin, two countdown timers, language selector populated from the 28 bundled localisations.*
The interface is engineered around **behavioural pressure**: a 72-hour price increase manufactures
urgency, a 7-day deletion threat forecloses waiting for a public decryptor, free-sample decryption
establishes credibility, and a *Contact Us* channel provides the reassurance of a support process.
*(No control was interacted with beyond capturing it.)*

*Regshot differential: 36 files modified, **44,728 total changes** — the aggregate footprint of a SYSTEM-level infection.*
### 3.5 Network behaviour — the worm

*4,025 packets captured — 98% of them a single behaviour.*


192.168.56.20 → 24.6.203.21 TCP 62408 → 445 [SYN]
192.168.56.20 → 99.190.59.122 TCP 62409 → 445 [SYN]
192.168.56.20 → 95.39.4.236 TCP 62413 → 445 [SYN]
192.168.56.20 → 21.153.46.222 TCP 62416 → 445 [SYN]
...
Every packet is a SYN to **port 445**, directed at **randomly generated public IPv4 addresses**
spanning unrelated allocations worldwide. Source ports increment rapidly in microsecond-spaced bursts,
consistent with the multi-threaded scanner predicted by the `_beginthreadex` import.
Every connection remained `SYN_SENT` with no response. **Containment held completely: not one packet
left the lab.**

*Reverse-DNS flood for randomly generated public addresses — corroborating the Wireshark evidence from an independent vantage point.*
## Phase 4 — Payload Extraction & Reverse Engineering
### 4.1 Why static extraction was possible
The outer loader is **not packed** (`.text` entropy 6.13). The payload is *contained*, not compressed
around the code — so it is a static object at a known file offset, sealed with a password present in
the binary. It can be lifted out with a resource editor and an archive tool **while the sample never
executes**.
| | RedLine | WannaCry |
|---|---|---|
| Concealment | Packed + process hollowing | Nested encrypted container |
| Payload location | Reconstructed in memory only | Static resource at `0x000320A4` |
| **Recovery requires execution** | **Yes** | **No** |
| Risk during recovery | High — live malware running | **None — file is only read** |
### 4.2 Extraction, and a documented failure
The first attempt **failed**, and it is documented here because the failure mode is instructive and
omitting it would give a misleadingly clean account of how analysis actually proceeds.



7-Zip created all 36 output files as **empty placeholders before authenticating**, then failed to
populate them — so the downstream symptom (a zero-byte hex view) *masqueraded as a corrupt payload*
rather than an authentication failure. The actual fault was **a single character**.
The confirmed count of 36 entries matched DIE's static prediction exactly, confirming the correct
resource had been dumped even while authentication was failing.

*Verified hashes of the recovered components. **Note the modification dates: all May 2017.***
**The timestamps are the notable detail.** Every component carries a May 2017 build date, preserved
inside the archive. This **directly contradicts the outer binary's forged 2010 timestamp**: the author
falsified the PE header a scanner reads, but did not alter archive metadata buried three layers deep.
### 4.3 Ghidra — the kill-switch decision

*Only **55 functions** identified for a 3.5 MB binary — confirming the container architecture: very little actual code, mostly inert data.*




*Of **857 strings**, filtering on `iuqer` returns **exactly one** result at `0x004313D0`.*

*Referenced from a **single** function, `FUN_00408140`. The decision is centralised — no redundant or obfuscated second check.*


undefined4 killswitch_check(void)
{
pcVar3 = s_http://www.iuqerfsodp9ifjaposdfj_004313d0;
pcVar4 = local_50;
for (iVar2 = 0xe; iVar2 != 0; iVar2 = iVar2 + -1) { // copy URL to stack
*(undefined4 *)pcVar4 = *(undefined4 *)pcVar3;
pcVar3 = pcVar3 + 4; pcVar4 = pcVar4 + 4;
}
uVar1 = InternetOpenA(0,1,0,0,0); // INTERNET_OPEN_TYPE_DIRECT
iVar2 = InternetOpenUrlA(uVar1,local_50,0,0,0x84000000,0); // RELOAD | NO_CACHE_WRITE
if (iVar2 == 0) { // ── URL UNREACHABLE ──
InternetCloseHandle(uVar1);
InternetCloseHandle(0);
FUN_00408090(); // → PAYLOAD: service install + worm
return 0;
}
// ── URL REACHABLE ──
InternetCloseHandle(uVar1); // → close handles
InternetCloseHandle(iVar2); // → return, do nothing
return 0;
}
**Every element is deliberate:**
- **The URL is copied to the stack before use.** Functionally unnecessary — the API would accept the
`.rdata` pointer directly. Its purpose is **anti-analysis**: a breakpoint on `InternetOpenUrlA`
shows a stack address rather than a recognisable data-section reference.
- **`INTERNET_OPEN_TYPE_DIRECT`** bypasses any proxy. On a corporate network the proxy is what would
log or block the request — so going direct makes the request *more likely to fail* in a proxied
enterprise, **biasing the branch toward detonation exactly where the target is most valuable**.
- **`0x84000000`** = `INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE` — forces a fresh,
uncached answer every time.
- **`if (iVar2 == 0)`** is the whole decision. **The response content is never examined** — nothing is
read, no status code checked, no body parsed. *Any* reply satisfies the check, which is exactly why
Run A terminated the instant INetSim answered.
- **`FUN_00408090` is the payload entry point** — it references `OpenSCManagerA` and
`StartServiceCtrlDispatcherA`. The kill switch is the gate in front of everything else.
**On design intent:** the mechanism is *inverted sandbox detection*. Its weakness is that the domain
is a **single hardcoded external dependency gating all malicious behaviour** — anyone who registers it
can reach every infected host worldwide. It is a case study in an evasion technique that is clever in
the small and catastrophic in the large.
### 4.4 Ghidra — service creation and persistence




undefined4 FUN_00407c40(void)
{
sprintf(local_104, "%s -m security", &DAT_0070f760);
hSCManager = OpenSCManagerA(NULL, NULL, 0xf003f);
if (hSCManager != NULL) {
hService = CreateServiceA(hSCManager,
"mssecsvc2.0", // lpServiceName
"Microsoft Security Center (2.0) Service", // lpDisplayName
0xf01ff, // SERVICE_ALL_ACCESS
0x10, // SERVICE_WIN32_OWN_PROCESS
2, // SERVICE_AUTO_START ← persistence
1, // SERVICE_ERROR_NORMAL
local_104, // lpBinaryPathName
NULL, NULL, NULL, NULL, NULL);
if (hService != NULL) { StartServiceA(hService,0,NULL); CloseServiceHandle(hService); }
CloseServiceHandle(hSCManager);
}
}
| Parameter | Value | Intent |
|---|---|---|
| `lpServiceName` | **`mssecsvc2.0`** | Reads as "Microsoft Security Service" — passes a visual scan of `services.msc` |
| `lpDisplayName` | **`Microsoft Security Center (2.0) Service`** | Impersonates a real security component. Deleting it *looks* like disabling security |
| `dwServiceType` | `0x10` `WIN32_OWN_PROCESS` | Own process, not shared in `svchost` |
| **`dwStartType`** | **`2` `AUTO_START`** | **Starts at every boot, before user logon — this is the persistence** |
| `dwErrorControl` | `1` `ERROR_NORMAL` | Logs failures but does not halt boot — avoids attention if it crashes |
| `lpBinaryPathName` | `" -m security"` | Implements the **two-mode design** predicted from the imports |
**Three conclusions:**
1. **Persistence and privilege escalation are the same operation.** `CreateServiceA` with a `NULL`
account defaults to `LocalSystem` — so the service starts as **SYSTEM** at every boot with no
separate escalation step. The SYSTEM process chain in §3.2 is the observed consequence of *this
single call*.
2. **The naming is defensive, not decorative.** Choosing a *security* service specifically maximises
an administrator's hesitation to disable it.
3. **The `-m security` argument confirms the two-mode design** — one binary, two behaviours, selected
by command-line argument.
**Service name `mssecsvc2.0` was read directly from the disassembly, not assumed.**
### 4.5 The archive password and extraction routine


*`FindResourceA` has exactly **one** reference — the payload is unpacked in a single routine.*

Location 0071f5d0
Code Unit db 57h (byte[3514368][62764])
String "WNcry@2ol7"
**The addressing detail is the most precise finding of this phase.** Ghidra places the password at
**offset 62,764 inside the 3,514,368-byte embedded resource** — exactly the size of resource `R`/1831.

*`FUN_00407a20` opens the malware's **own** image with `GENERIC_READ`, reads its entire size, and processes it in a heap buffer — how a self-contained dropper treats its own file as a data source. The `FILE_ATTRIBUTE_SYSTEM` flag hides dropped files from Explorer under default settings.*
### 4.6 Recovered C2 and ransom text

gx7ekbenv2riucmf[.]onion 57g7spgrzlojinas[.]onion xxlvbrloxvriy2c5[.]onion
76jdd2ir2embyv47[.]onion cwwnhwhlz52maqm7[.]onion
hxxps://dist[.]torproject[.]org/torbrowser/6.5.1/tor-win32-0.2.9.10.zip
**Five Tor hidden-service addresses recovered by hand, with no network contact.** The addresses are
stored in **plaintext** inside a layer already protected by three layers of encryption — the author
judged the outer layers sufficient. Five redundant addresses provide resilience against takedown, and
the Tor Project URL is a **fallback** if the bundled client fails. These are **v2 onion addresses**
(16 characters), which dates the campaign — v2 has since been deprecated in favour of 56-character v3.

*`r.wnry` with `sprintf` format specifiers intact for the amount, Bitcoin address and decryptor filename.*
Two lines are social engineering rather than technique:
- *"You may need to disable your antivirus for a while"* — a **defence-evasion instruction disguised
as helpful troubleshooting**, arriving at the moment the victim is most compliant.
- *"Nobody will trust us if we cheat users"* — an explicit **reputation argument**, presenting the
operation as a going concern. Combined with free-sample decryption and a *Contact Us* channel, the
extortion is deliberately packaged as a **transaction** rather than an attack, because a victim who
believes they are dealing with a rational counterparty is more likely to pay.
## MITRE ATT&CK Mapping
| Tactic | Technique | Evidence |
|---|---|---|
| Initial Access | T1566.001 Spearphishing Attachment | `invoice_greenanimals.pdf.exe` double-extension lure |
| Execution | T1569.002 Service Execution | Service created and started; chain runs as SYSTEM |
| Persistence | T1543.003 Windows Service | `mssecsvc2.0`, `SERVICE_AUTO_START` (§4.4) |
| Privilege Escalation | T1543.003 | Service runs as `LocalSystem` → SYSTEM (§3.2) |
| Defense Evasion | **T1497 Virtualization/Sandbox Evasion** | **Kill switch, demonstrated both directions (§3.1)** |
| Defense Evasion | T1036.005 Masquerading | "DiskPart" / security-service impersonation |
| Defense Evasion | T1140 Deobfuscate/Decode Files | Encrypted ZIP in `.rsrc`, password `WNcry@2ol7` |
| Defense Evasion | T1622 Debugger Evasion | `IsDebuggerPresent` (§2.5) |
| Discovery | T1046 Network Service Discovery | Mass TCP/445 scanning (§3.5) |
| Discovery | T1016 System Network Config Discovery | `GetAdaptersInfo` import |
| Lateral Movement | T1210 Exploitation of Remote Services | MS17-010 / CVE-2017-0144, -0147 |
| Command & Control | T1090.003 Multi-hop Proxy | Bundled Tor client; five `.onion` addresses |
| **Impact** | **T1486 Data Encrypted for Impact** | Encryption I/O (§3.2); ransom UI |
| **Impact** | **T1490 Inhibit System Recovery** | `vssadmin` / `wbadmin` / `bcdedit`; VSSVC activity |
| Impact | T1491.001 Internal Defacement | Desktop wallpaper replaced (`b.wnry`) |
## Indicators of Compromise
### Host
| Type | Indicator |
|---|---|
| SHA-256 | `24d004a104d4d54034dbcffc2a4b19a11f39008a575aa614ea04703480b1022c` |
| SHA-1 | `e889544aff85ffaf8b0d0da705105dee7c97fe26` |
| MD5 | `db349b97c37d22f5ea1d1841e3c89eb4` |
| imphash | `9ecee117164e0b870a53dd187cdd7174` |
| Working directory | `C:\ProgramData\\` (observed: `rgltpxbykpoo197`) |
| Dropped executables | `tasksche.exe`, `taskdl.exe`, `taskse.exe`, `@WanaDecryptor@.exe`, `taskhsvc.exe` |
| Dropped data files | `b.wnry`, `c.wnry`, `f.wnry`, `r.wnry`, `s.wnry`, `t.wnry`, `u.wnry` |
| Key artefacts | `00000000.pky`, `00000000.eky`, `00000000.res` |
| **Service name** | **`mssecsvc2.0`** |
| **Service display name** | **`Microsoft Security Center (2.0) Service`** |
| Ransom note | `@Please_Read_Me@.txt` |
| Encrypted extension | `.WNCRY` (temporary: `.WNCRYT`) |
| Archive password | `WNcry@2ol7` (letters `o`, `l` — not digits) |
| Masquerade filenames | `lhdfrgui.exe`, `diskpart.exe` |
### Network
| Type | Indicator |
|---|---|
| Kill-switch domain | `iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea[.]com` |
| Behaviour | High-volume TCP/445 SYN to random public IPv4 addresses |
| C2 (Tor v2) | `gx7ekbenv2riucmf[.]onion` |
| C2 (Tor v2) | `57g7spgrzlojinas[.]onion` |
| C2 (Tor v2) | `xxlvbrloxvriy2c5[.]onion` |
| C2 (Tor v2) | `76jdd2ir2embyv47[.]onion` |
| C2 (Tor v2) | `cwwnhwhlz52maqm7[.]onion` |
| Bitcoin | `13AM4VW2dhxYgXeQepoHkHSQuy6NgaEb94` |
| Exploited CVEs | CVE-2017-0144, CVE-2017-0147 (MS17-010) |
Machine-readable indicators: [`iocs/iocs.csv`](iocs/iocs.csv)
## YARA Detection Rule
Full rule: [`iocs/wannacry.yar`](iocs/wannacry.yar)
rule WannaCry_Dropper_Static
{
meta:
author = "Ali Mehdi Jafeeri"
description = "WannaCry outer dropper - masquerade, SMBv1 downgrade, nested payload"
reference = "SHA-256 24d004a104d4d54034dbcffc2a4b19a11f39008a575aa614ea04703480b1022c"
date = "2026-07-21"
strings:
// SMBv1 downgrade negotiation - the worm component
$smb1 = "PC NETWORK PROGRAM 1.0" ascii
$smb2 = "LANMAN1.0" ascii
$smb3 = "Windows for Workgroups 3.1a" ascii
$smb4 = "NT LM 0.12" ascii
$smb5 = "LM1.2X002" ascii
// Kill-switch domain
$ks = "iuqerfsodp9ifjaposdfjhgosurijfaewrwergwea" ascii
// Payload component names
$c1 = "c.wnry" ascii
$c2 = "t.wnry" ascii
$c3 = "s.wnry" ascii
$c4 = "b.wnry" ascii
// Masquerade metadata
$m1 = "lhdfrgui.exe" ascii wide
$m2 = "diskpart.exe" ascii wide
condition:
uint16(0) == 0x5A4D // MZ
and filesize > 3MB and filesize < 4MB
and (
$ks // kill switch alone is conclusive
or (4 of ($smb*) and 2 of ($c*))
or (all of ($m*) and 3 of ($smb*))
)
}
**Design reasoning.** The rule anchors on the **SMBv1 dialect strings** rather than the ransom note or
`.WNCRY` extension. Those latter strings live in the *innermost encrypted layer* and are **invisible in
the delivered file**, so a rule built on them would fail against the artefact a defender actually
intercepts. The dialect strings sit in the readable outer layer. Requiring co-occurrence of both
masquerade filenames plus SMB dialects guards against false positives — a legitimate SMB
implementation would not also claim to be two different Microsoft disk utilities.
## Defensive Recommendations
Each recommendation is the direct counter to a mechanism demonstrated above — an observed dependency
read backwards.
| Control | Why it works against this sample |
|---|---|
| **Patch MS17-010 / disable SMBv1** | The propagation mechanism has exactly **one** dependency. The dialect list (§2.3) shows it must negotiate *down* to SMBv1 to attack. The failed propagation in §3.5 is the proof. |
| **Block outbound TCP/445 at the perimeter** | §3.5 shows the worm scanning the public internet on 445. No legitimate traffic requires port 445 to leave an organisational network. |
| **Offline, immutable backups** | §3.4 shows shadow copies and the backup catalog are deliberately destroyed. **Any backup the malware can reach as SYSTEM is not a backup.** |
| **Restrict & monitor service creation** | Persistence and privilege escalation are the same act (§4.4). Creation of an auto-start service is a high-fidelity alert. |
| **Alert on shadow-copy deletion** | `vssadmin delete shadows /all` from a non-backup process is almost never legitimate, and fires **immediately before impact** — an ideal last-line trigger. |
| **Network segmentation** | A worm that cannot route cannot spread. The containment that held in this lab is the same control that limits blast radius in production. |
| **Enforce code signing, not appearances** | The sample passes visual inspection as a Microsoft utility and **fails signature verification** (§2.2). Verify cryptographic identity, not displayed names. |
## What I Learned
Beyond the findings, three methodological lessons generalise past this sample:
**1. Concealment strategy dictates recovery strategy.** RedLine packed its *code* and forced a memory
dump; WannaCry contained its *cargo* and left its brain readable. Recognising which kind of protection
you face — packer vs. container — tells you immediately whether to reach for a resource editor or a
debugger. That single distinction saved hours here.
**2. A sandbox that simulates the internet too well defeats itself.** The kill switch is inverted
sandbox detection, and my default lab configuration silently satisfied it. If I had run the sample
once and written "no observable behaviour," I would have been confidently wrong. **Always ask what
external dependency a sample checks before concluding it is inert.**
**3. Centralised decisions are single points of failure — for both sides.** The kill switch was
simultaneously WannaCry's cleverest anti-analysis feature and its fatal flaw: one hardcoded,
unauthenticated check gating all malicious behaviour. When analysing any sample, *"what one thing does
the whole payload depend on?"* is now among my first questions, because that thing is both the
author's best idea and the defender's best lever.
I also kept a set of **attacker's-design-rationale notes** alongside the technical write-up — working
through *why* each choice was made and what trade-off it accepted. Reconstructing the engineering
reasoning turned this from cataloguing artefacts into actually understanding the sample.
## References & Prior Work
WannaCry is among the most extensively documented malware families in existence, and every indicator
in this report has been public since 2017. **Nothing here claims novel discovery of the family.**
What this repository documents is my **own end-to-end analysis**: my isolated lab, my sample, my tool
runs, my screenshots, my Ghidra session, and — the part I consider genuinely my own contribution — the
**paired kill-switch experiment** designed to demonstrate the branch in both directions before
confirming it in the disassembly.
Methodology draws on standard practical malware-analysis workflow (the PMAT-style static → dynamic →
reverse-engineering progression), and public WannaCry write-ups were consulted for orientation. All
findings presented were independently reproduced and verified in my own environment; where a result
was ambiguous or a tool artefact (see the FLOSS `IPC$` note in §2.3), I have said so explicitly rather
than presenting it as clean.
**Sample source:** [MalwareBazaar](https://bazaar.abuse.ch/) · **Vulnerability:** [MS17-010](https://learn.microsoft.com/en-us/security-updates/securitybulletins/2017/ms17-010) · **Framework:** [MITRE ATT&CK](https://attack.mitre.org/)
## Repository Contents
.
├── README.md ← this analysis
├── images/ ← 64 evidence screenshots
├── report/
│ └── WannaCry_Malware_Analysis_Report.docx ← formatted report
├── iocs/
│ ├── iocs.csv ← machine-readable indicators
│ └── wannacry.yar ← YARA detection rule
└── LICENSE
**No malware samples or extracted payload components are included in this repository.**
Analysis by **Ali Mehdi Jafeeri** · July 2026
Published for educational and defensive purposes. Indicators are provided for detection engineering only.
Published for educational and defensive purposes. Indicators are provided for detection engineering only.