Microsoft Active Protection Service — Windows Defender's Cloud Brain
How MAPS integrates with Microsoft Defender Antivirus — the cloud brain behind endpoint protection
MAPS (Microsoft Advanced Protection Service, formerly "SpyNet") is the cloud protection backend for Microsoft Defender Antivirus. It's not a separate product — it's the cloud component that every Defender installation communicates with to deliver real-time verdicts on suspicious files, URLs, and behaviors. When cloud protection is enabled (the default), Defender queries MAPS before allowing unknown executables to run.
| Feature | MAPS Role | User Impact |
|---|---|---|
| Cloud-Delivered Protection | Real-time file reputation via hash lookup | Unknown files blocked/allowed in <1 second |
| Block at First Sight (BAFS) | Synchronous cloud query with execution hold | Zero-day files blocked before first execution |
| Emergency Dynamic Intelligence | FASTPATH signature blobs pushed per-client | New threat protection in minutes vs hours |
| Automatic Sample Submission | Cloud requests file upload via SAS URI | Unknown files sent to Azure sandbox for analysis |
| SmartScreen URL Filtering | URL reputation queries via MAPS protocol | Phishing/malware URLs blocked in browser |
| Network Protection | Network connection telemetry to MAPS | C2/malicious IP connections detected |
| AMSI Cloud Analysis | Script content submitted for cloud verdict | Obfuscated PowerShell/VBS malware caught |
| Tamper Protection Telemetry | Heartbeat type 10 reports tampering | Alerts on attempts to disable Defender |
Key stat: With cloud protection enabled (Windows 10 E5), emergency dynamic intelligence updates can deliver fixes for malware within minutes instead of waiting for the next scheduled definition update. Microsoft recommends keeping cloud protection always on.
End-to-end system architecture from client to cloud
| Component | DLL | Role |
|---|---|---|
| Engine | mpengine.dll | Bond schema definitions (372 fields), payload construction, FASTPATH parsing |
| Communication | MpCommu.dll | HTTP transport, version header encoding, TLS, endpoint selection |
| Service | MpSvc.dll | Heartbeat manager (12 subtypes), scan orchestration, scheduling |
| Client | MpClient.dll | WMI/COM interface, configuration, trigger exports |
Microsoft's binary serialization protocol — the wire format of all MAPS traffic
┌─────────┬──────────┐ │ 7 6 5 │ 4 3 2 1 0│ 1 byte field header ├─────────┼──────────┤ │ delta │ bond_type│ └─────────┴──────────┘ delta 1-5: field_id = previous_id + delta (inline) delta 6: field_id = next_byte (uint8 absolute) delta 7: field_id = next_two_bytes (uint16 LE absolute)
BT_BOOL=2 (1 byte)BT_UINT8=3 (1 byte)BT_UINT16=4 (varint)BT_UINT32=5 (varint)BT_UINT64=6 (varint)BT_FLOAT=7 (4 bytes LE)BT_DOUBLE=8 (8 bytes LE)
BT_INT8=14 (zigzag byte)BT_INT16=15 (zigzag varint)BT_INT32=16 (zigzag varint)BT_INT64=17 (zigzag varint)BT_STRING=9 (varint len + UTF-8)BT_WSTRING=18 (varint chars + UTF-16LE)BT_STRUCT=10 (fields...BT_STOP)BT_LIST=11 (type + count + values)BT_SET=12 (same as list)BT_MAP=13 (ktype+vtype+count)BT_STOP=0 / BT_STOP_BASE=1
Value 300 (0x12C): Byte 1: 0xAC = 0b_1_0101100 (continuation + low 7 bits: 44) Byte 2: 0x02 = 0b_0_0000010 (final + high bits: 2) Result: (2 << 7) | 44 = 256 + 44 = 300 ✓
The outer wrapper that encapsulates all MAPS payloads
43 42 01 00 ← CB marshal header ("CB" + version 1) Outer Struct { F5: STRING = schema_name ← "Microsoft.ProtectionServices.Entities.Raw.SpynetReportEntity" BT_STOP_BASE ← Base class terminator #1 BT_STOP_BASE ← Base class terminator #2 (resets field counter) F10: LIST<LIST<INT8>> = [[]] ← Runtime type info (empty) F20: STRUCT { ← Inner payload wrapper F5: STRING = schema_name ← Schema name repeated BT_STOP_BASE ← Base class terminator #1 BT_STOP_BASE ← Base class terminator #2 [SpynetReport fields...] ← Actual payload data BT_STOP ← End inner struct } BT_STOP ← End outer struct
Microsoft.ProtectionServices.Entities.Raw.SpynetReportEntity
This schema name is mandatory. The server validates it and returns HTTP 500 for unrecognized schemas.
43 42 01 00 ← "CB\x01\x00" marshal header A9 3D 4D 69 63 72 6F ... ← F5 STRING: schema name (61 bytes) 01 ← BT_STOP_BASE 01 ← BT_STOP_BASE CB 0B 0E 01 00 ← F10: LIST<LIST<INT8>> = [[]] EA 3D 4D 69 63 72 6F ... ← F20.F5 STRING: schema name again 01 01 ← Two more BT_STOP_BASE [payload fields...] ← SpynetReport Bond fields 00 ← BT_STOP (inner) 00 ← BT_STOP (outer)
HTTPS request/response format matching real Defender traffic
POST /wdcp.svc/bond/submitreport HTTP/1.1 Host: wdcp.microsoft.com Content-Type: application/bond Accept: application/bond Accept-Charset: utf-8 User-Agent: MpCommunication Connection: Keep-Alive X-MS-MAPS-CUSTOMERTYPE: Consumer X-MS-MAPS-OSVERSION: a0000000065f4 X-MS-MAPS-PLATFORMVERSION: 40012659a0005 X-MS-MAPS-ENGINEVERSION: 10001659a0001 Content-Length: <size>
Versions are packed as 64-bit hex: (major<<48) | (minor<<32) | (build<<16) | revision
| Version String | Hex Encoding | Type |
|---|---|---|
10.0.26100 | a0000000065f4 | OS Version (3-part) |
4.18.26010.5 | 40012659a0005 | Platform Version |
1.1.26010.1 | 10001659a0001 | Engine Version |
X-MS-MAPS-CUSTOMERTYPE: ConsumerAuthorization: Bearer <AAD_TOKEN>cab96880-db5b-.../WdCpSrvc.asmx → 404Complete request/response cycle for cloud file reputation
SpynetReport {
F20: Revision = LIST<INT16>[3] ← Schema version
F30: MachineGuid = STRING ← Client identity (UUID4)
F40: AvSigVersion = STRING ← "1.445.126.0"
F60: EngineVersion = STRING ← "1.1.26010.1"
F150: OsVer = STRING ← "10.0.0.0"
F170: OsBuild = LIST<UINT32>[26100]
F200: GeoId = LIST<INT32>[244] ← US
F300: AppVersion = STRING ← "4.18.26010.5"
F390: FileReportElements = LIST<LIST<STRUCT>> [
FileReport {
F10: Revision = LIST<INT16>[1]
F20: Index = LIST<INT16>[1]
F30: CoreReport = LIST<STRUCT> [{
F20: FileName = "malware.exe"
F30: FileSystem = "NTFS"
F40: Size = LIST<INT64>[file_size]
F80: MD5 = "44d88612fea..."
F90: SHA1 = "3395856ce81..."
F100: SHA256 = "275a021bbf..."
F330: ReportType = "2" ← SYNC_LOWFI
}]
}
]
F1050: EngineReportGuid = STRING (UUID)
}
How Defender queries cloud for URL/domain safety ratings
UrlReport = LIST<STRUCT>[1] { ← Must be LIST, not STRUCT (HTTP 500 otherwise!) F3: UrlReportGuid = STRING (UUID) F6: UrlList = STRUCT { F3: Urls = LIST<STRUCT> [ UrlElement { F20: Url = "https://example.com" ← Only field set by engine // F10: Order — NOT set by engine // F21: Url_Scrubbed — NOT set by engine } ] } F9: UrlContext = STRUCT { ... } ← Optional F12: SigSeq = "0" ← Mandatory F15: SigSha = "" ← Mandatory // F18: ReportOnly — omitted when querying }
| Field | Type | Description |
|---|---|---|
| Url | STRING | The queried URL |
| Determination | UINT8 | 0=Unknown, 1=Clean, 2=Malicious, 3=Phishing |
| Confidence | UINT8 | 0-100 confidence score |
| TTL | UINT8 | Cache time-to-live (short) |
| TTLlong | UINT64 | Extended TTL value |
12 heartbeat subtypes for connectivity, telemetry, and lifecycle events
Heartbeat is indicated by F90: IsHeartBeat = 1 in SpynetReport, plus F920/F930 subtype fields.
| Value | Name | Trigger |
|---|---|---|
| 0 | STILL_ALIVE | Periodic timer (default) |
| 1 | SETUP | First run / initial setup |
| 2 | UNINSTALL | MpTriggerHeartbeatOnUninstall |
| 3 | ERROR | MpTriggerErrorHeartbeatReport |
| 4 | POLICY_CHANGE | Configuration/Group Policy update |
| 5 | BROWSER | MpSendBrowserHeartbeat |
| 6 | EXCLUSION | Exclusion list change |
| 7 | CLEANUP | Post-remediation cleanup |
| 8 | SIGNATURE_UPDATE | After signature update |
| 9 | PLATFORM_UPDATE | After platform/engine update |
| 10 | TAMPER_PROTECT | Tamper protection event |
| 11 | REBOOT | Post-reboot heartbeat |
F90: IsHeartBeat = UINT8(1) ← Flags this as heartbeat F920: StillAliveHB = LIST<UINT8>[type] ← Heartbeat subtype (0-11) F930: HBControlGroup = LIST<UINT8>[0] ← A/B test group F940: HBSubtype = UINT8 ← Additional subtype info F290: Membership = "2" ← Report type string
Zero-tolerance cloud blocking for unknown files
| Level | Value | Behavior |
|---|---|---|
| OFF/DEFAULT | 0 | No cloud checking |
| MODERATE | 1 | Normal blocking threshold |
| HIGH | 2 | Aggressive blocking (default for MAPS) |
| HIGH_PLUS | 4 | Very aggressive blocking |
| ZERO_TOLERANCE | 6 | Maximum sensitivity — BAFS mode |
Note: Values 3, 5, 7+ are not documented. The fuzzer probes these gaps for hidden behavior.
Cloud-triggered file upload to Azure Blob Storage for detonation
PUT <blob_sas_uri> HTTP/1.1 Content-Type: application/octet-stream x-ms-blob-type: BlockBlob Content-Encoding: gzip ← If compression requested x-ms-meta-compression: gzip Content-Length: <size>
Decoding the Bond response from MAPS cloud
SubmitSpynetReportResult {
F5: Schema = "Microsoft.ProtectionServices.Entities.Raw.SubmitSpynetReportResult"
F6: SpynetReportResponse = LIST<STRUCT>[1] {
F3: Revision = UINT8(5) ← Protocol version
F6: SampleRate = INT32(1) ← Telemetry sampling
F9: SampleRequests = LIST<STRUCT> ← Upload requests (if any)
F12: SignaturePatches = LIST<STRUCT> ← FASTPATH signatures!
F15: UrlResponse = STRUCT ← URL reputation result
F18: ThreatDetailElements = LIST<STRUCT>
F21: CertificateResponse = STRUCT
F24: OnboardingResponse = STRUCT
}
F10: RuntimeType = STRUCT ← Runtime type info
}
| Scenario | Size | Contents |
|---|---|---|
| Clean / unknown file | 88 bytes | Revision=5, SampleRate=1 |
| Known threat + FASTPATH | ~488 bytes | 88 base + 385B FASTPATH blob |
| Known threat (cached) | 88 bytes | FASTPATH already delivered to this GUID |
VDM TLV format — real-time threat definitions delivered from cloud
┌──────────┬──────────┬───────────┬──────────────────┐ │ type (1) │ size_lo │ size_hi │ payload (size) │ │ 1 byte │ 1 byte │ 2 bytes │ variable │ └──────────┴──────────┴───────────┴──────────────────┘ size = size_lo | (size_hi << 8) ← 24-bit little-endian
| Type | Name | Description |
|---|---|---|
0x5C | THREAT_BEGIN | Start of threat group: ThreatID (4B) + Flags (4B) + DetectionName |
0x5D | THREAT_END | End of threat group: ThreatID (4B) |
0x67 | STATIC | Hash-based detection: CRC32 + MD5 + SHA1 |
0x80 | KCRCE | CRC-based detection entry |
0xAA | FASTPATH_DATA | Metadata: compilation timestamp + config flags |
0xAB | FASTPATH_SDN | Static Detection Name |
0xD8 | FASTPATH_TDN | Threat Detection Name |
0xDA | FASTPATH_SDN_EX | Extended SDN |
0xEC | ENVELOPE | Encrypted detection logic (AES, 256B typical) |
0xEE | OUTER_WRAPPER | V3 FASTPATH outer type with stream headers |
[0xEC ENVELOPE] 256 bytes — Encrypted detection logic (RSA-2048 + AES) [0xAA FASTPATH_DATA] 20 bytes — Compilation timestamp, config flags [0x5C THREAT_BEGIN] 47 bytes — ThreatID=2147519003 "Virus:DOS/EICAR_Test_File" [0x67 STATIC] 38 bytes — SHA1 + MD5 + CRC32 of EICAR [0x5D THREAT_END] 4 bytes — ThreatID=2147519003 (closing)
372 Bond fields reverse-engineered from mpengine.dll RTTI vtable analysis
| Ordinal | Name | Type | Purpose |
|---|---|---|---|
| F10 | ReportTime | INT64 | .NET DateTime ticks |
| F20 | Revision | LIST<INT16> | Schema version (3) |
| F30 | MachineGuid | STRING | Client UUID identity |
| F40-F80 | Version strings | STRING | AV sig, engine, NRI versions |
| F90 | IsHeartBeat | UINT8 | Heartbeat flag |
| F150-F220 | System info | mixed | OS, geo, locale, CPU |
| F280 | ProductGuid | STRING | Session GUID |
| F390 | FileReportElements | LIST<LIST<STRUCT>> | File scan reports |
| F580 | IsMsftInternal | UINT8 | Microsoft internal flag |
| F590 | TestHook | STRING | Test/debug hook |
| F920-F940 | Heartbeat fields | mixed | HB type, control group |
| F1190 | NetworkConnElements | LIST<STRUCT> | Network telemetry V1 |
| F1275 | AmsiUacInfos | LIST<STRUCT> | UAC elevation telemetry |
| F1373 | CloudBlockLevel | UINT32 | Block sensitivity |
| F1542 | UrlReport | LIST<STRUCT> | URL reputation query |
Complete endpoint map with geo-affinity and failover
| Endpoint | URL | Purpose |
|---|---|---|
| Production | wdcp.microsoft.com | Main MAPS cloud |
| Failover | wdcpalt.microsoft.com | Backup if primary fails |
| Pre-Production | fastpath.wdcpppe.microsoft.com | Testing/PPE environment |
| FASTPATH Prod | fastpath.wdcp.microsoft.com | Dynamic signature delivery |
| FASTPATH PPE | fastpath.wdcpppe.microsoft.com | Testing signatures |
unitedstates.cp.wd.microsoft.comeurope.cp.wd.microsoft.comunitedkingdom.cp.wd.microsoft.comaustralia.cp.wd.microsoft.com| Path | Protocol | Status |
|---|---|---|
/wdcp.svc/bond/submitreport | Bond CompactBinaryV1 | Active |
/wdcp.svc/submitReport | Legacy REST | Unknown |
/wdcp.svc/entraReport | Entra ID/Enterprise | Enterprise |
/WdCpSrvc.asmx | WS-Security SOAP | Dead (404) |
Where to look for hidden features, undocumented behavior, and security gaps
| Module | What It Probes | Looking For |
|---|---|---|
hidden | IsMsftInternal, TestHook, IsBeta, QueryOnly, MapsOrigin, VdiType, IsPassiveMode, AsimovDeviceTicket, PartnerGuid, multi-file, compression | Hidden features unlocked by flags |
paths | 70+ URL paths | Undiscovered WCF/REST endpoints |
report-types | ReportType 0-65535 | Undocumented report types beyond 1-7 |
headers | 50+ HTTP header variations | Debug/preview/insider header gates |
fields | 200+ SpynetReport field ordinals | Server reaction to unknown fields |
schemas | 20+ Bond schema names | Alternative schema acceptance |
endpoints | 25+ hostname variations | Undiscovered regional endpoints |
versions | OS, engine, platform, sig versions | Version-gated features |
customer-types | 30+ customer type values | Role-gated functionality |
block-levels | CloudBlockLevel 0-255 | Undocumented sensitivity levels |
heartbeat-types | HeartbeatType 0-255 | Undocumented heartbeat subtypes |
bond-types | Wire type mismatches | Parser edge cases and crashes |
response-fields | Diverse requests → response analysis | Novel response field ordinals |
# Quick test — hidden feature probes python fuzz_maps.py hidden # Full sweep with proxy inspection python fuzz_maps.py --proxy http://127.0.0.1:8080 --no-verify --verbose all # Specific module with slow rate python fuzz_maps.py --delay 2.0 fields # Pre-production endpoint (more permissive) python fuzz_maps.py --ppe report-types