m0n1t0r Architecture
Architecture
Overview
m0n1t0r is a cross-platform C2 framework written in Rust with a React web dashboard. It uses a client-server model where agents connect back to the server over TLS, and operators interact through a REST/WebSocket API served alongside a single-page web application.
1 | ┌───────────┐ TLS (remoc RPC) ┌───────────────┐ REST / WebSocket ┌────────────┐ |
Workspace Structure
1 | m0n1t0r/ |
Communication Model
Client ↔ Server: Bidirectional Async RPC
The transport layer uses TLS (rustls) on port 27853. On top of TLS, remoc establishes typed bidirectional channels with MessagePack serialization.
Connection handshake:
- Client initiates TLS connection. The CA certificate is embedded in the client binary at compile time (
certs/ca.crt). remoc::Connect::io()creates two typed channel pairs over the TLS stream.- Server sends a
ServerClientRPC stub to the client; client sends aClientClientRPC stub to the server. - Both sides can now invoke methods on each other asynchronously.
- Server registers the client in
ServerMapand broadcasts aConnectEvent::Connectnotification.
The client auto-reconnects every 10 seconds on connection failure.
Server ↔ UI: REST + WebSocket
Actix-web serves the HTTP API on port 10801 at /api/v1/. WebSocket endpoints are used for:
- Interactive shell sessions (
/client/{addr}/process/interactive) - Remote desktop streaming (
/client/{addr}/rd/stream/*) - Server event notifications (
/server/notification)
Server Architecture
Entry Point
The server runs two concurrent tasks:
- TLS Listener (
conn::run) — Accepts client connections on port 27853, establishes remoc channels, tracks clients inServerMap. - HTTP API (
api::run) — Actix-web server on port 10801 with session middleware, CORS, and route handlers.
State Management
1 | pub struct ServerMap { |
API handlers look up clients in ServerMap, obtain the ClientClient RPC stub, and call methods through it. The get_agent! macro encapsulates this pattern:
1 | let (agent, _) = get_agent!(data, &addr, fs_agent)?; |
API Route Layout
1 | /api/v1/ |
Response Envelope
All API responses use a standard envelope:
1 | { "code": 0, "body": <T> } |
Errors are mapped to HTTP status codes in web/error.rs.
Client Architecture
Entry Point
The client binary hides its console window on Windows (#![windows_subsystem = "windows"]). In debug builds it connects to 127.0.0.1:27853; in release builds the server address is baked in via the M0N1T0R_DOMAIN environment variable at compile time.
Agent Pattern
The client implements the Client RPC trait from m0n1t0r-common. Each capability is exposed as a factory method that returns an RPC stub:
1 | async fn fs_agent(&self) -> AppResult<fs::AgentClient> { |
When the server-side API handler calls client.fs_agent(), the client spawns a new agent task and returns an RPC stub. The server then calls methods on that stub, which are executed on the client side.
Platform Dispatch
Platform-specific code is selected at compile time via feature flags and the declare_agents! macro:
1 | declare_agents!(general, [proxy, network, qq, rd], ["general", "macos", "linux", "winnt"]); |
Platform-specific modules:
| Directory | Platforms | Capabilities |
|---|---|---|
client/general/ |
All | proxy, network, remote desktop, QQ |
client/windows/ |
Windows | ETW patching, registry autorun, shellcode injection, drive enumeration |
client/unix/ |
Linux, macOS | systemd/launchd persistence |
Common Library
m0n1t0r-common defines the contract between server and client.
RPC Traits
All agent interfaces use the #[rtc::remote] macro from remoc, which generates serializable client/server stubs:
Client— Top-level trait: version info, system info, agent factory methods, self-updatefs::Agent— File operations: list, read, write, delete, drives, rename, copyprocess::Agent— Process list, execute, interactive shell, kill, shellcode injectionproxy::Agent— TCP connect, TCP forward (for SOCKS5/port forwarding)rd::Agent— Display enumeration, video streaming (VP9/MPEG-TS encoded frames)network::Agent— File download to clientqq::Agent— QQ protocol integrationautorun::Agent— Persistence mechanismscharset::Agent— Charset conversion (Windows ANSI ↔ UTF-8)
Error Handling
A typed error hierarchy using thiserror, fully serializable for RPC transmission:
1 | Error |
Web UI
Tech Stack
React 19 + TypeScript + Vite + Ant Design (dark theme) + React Router. Uses bun as the package manager.
Structure
1 | src/ |
Routing
1 | / Dashboard |
Remote Desktop Streaming
The UI supports three stream formats via WebSocket:
- RGB — Raw pixel data
- YUV — Raw YUV frames
- MPEG1Video — MPEG-TS container decoded by
@cycjimmy/jsmpeg-player(jsmpeg)
Build System
Platform Feature Flags
Mutually exclusive, required at compile time:
1 | cargo build --features macos -r # macOS |
TLS Certificate Generation
cargo xtask -c generates a self-signed CA and end-entity certificate. Environment variables control the certificate subject fields (M0N1T0R_DOMAIN, M0N1T0R_COUNTRY, etc.). The CA cert is embedded in the client at compile time for certificate pinning.
C++ FFI
Platform-specific native code is built with xmake and linked via the cxx crate (m0n1t0r-cpp-*-lib).
Deployment
Docker (Multi-Stage Build)
1 | Stage 1 (server-builder): Rust nightly + vcpkg → server binary |
nginx serves the UI on port 80 and proxies /api/ to the server on port 10801. Port 27853 is exposed for client TLS connections.
CI/CD
- build.yml — Matrix build across macOS, Linux, Windows. Installs Rust, xmake, vcpkg dependencies. Produces binary artifacts.
- docker.yml — Builds Docker image and pushes to
ghcr.io/mmitsuha/m0n1t0r:nightlyon master push.
Design Principles
- Contract-first: RPC traits in
m0n1t0r-commondefine the interface; server and client implement against the same contract. - Agent-per-capability: Each feature (fs, process, proxy, rd) is an independent agent with its own RPC channel, allowing fine-grained lifecycle management.
- Platform abstraction via compile-time dispatch: Feature flags and macros select platform-specific implementations without runtime overhead.
- Size-optimized release builds:
opt-level = "z", LTO, single codegen unit, symbol stripping,panic = abort, optional UPX compression. - Typed errors across boundaries: All errors are serializable and transmitted over RPC, then mapped to HTTP status codes at the API layer.