How vphoned Exposes Keychain, Clipboard, and File Access Services over vsock

vphoned exposes iOS keychain, clipboard, and file-system services to the macOS host via a length-prefixed JSON protocol over AF_VSOCK on port 1337, with each service registering dedicated command handlers that perform native operations and stream responses back through the socket.

vphoned is the iOS-side daemon that powers the VPhone virtualization stack. Running inside the guest virtual machine, it listens on vsock port 1337 and translates JSON commands from the host into native iOS API calls. This architecture enables seamless, programmatic access to sensitive system services without requiring complex network bridging or摆脱了 the sandbox constraints typically associated with mobile virtualization.

The vsock Wire Protocol Architecture

The communication layer relies on a simple length-prefixed JSON wire-format defined in vphoned_protocol.h and vphoned_protocol.m. The daemon binds to port 1337 (matching the host-side VPhoneControl client) and processes incoming messages through a unified dispatch loop.

Every request must include a unique id field for correlation. The host writes a 4-byte length prefix followed by the JSON payload, and vphoned responds using vp_make_response() to ensure the same id is echoed back. For binary transfers—such as clipboard images or file contents—the daemon first writes a JSON header describing the payload, then streams the raw bytes.

Keychain Service Implementation

The keychain handler is implemented in vphoned_keychain.m via the vp_handle_keychain_command function. When the daemon receives a request with {"t":"keychain"}, it performs direct SQLite reads of /var/Keychains/keychain-2.db to enumerate items.

For entries the daemon is entitled to access, it optionally uses SecItemCopyMatching to extract sensitive metadata. A helper function safe_value converts CoreFoundation types (CFStrings, CFData) into JSON-safe objects before serialization. The response contains an items array where each entry specifies the security class, account, service, value, creation dates, and data protection level.

Clipboard Service Implementation

Clipboard operations are handled in vphoned_clipboard.m by vp_handle_clipboard_command. Because the daemon cannot link UIKit directly at build time, it dynamically loads the framework at runtime using dlopen and resolves UIPasteboard symbols via objc_msgSend.

The service supports bidirectional text and image transfer. For clipboard_get requests, the handler reads generalPasteboard, retrieves the string and image properties, and encodes image data as PNG. When images are present, vphoned streams the binary PNG data immediately after the JSON header. The clipboard_set command updates the pasteboard and returns the new change_count to ensure atomic operations.

File Access Service Implementation

File-system access is managed in vphoned_files.m through vp_handle_file_command. The implementation bridges high-level NSFileManager calls for directory listings with low-level POSIX I/O (open, read, write, stat, mkstemp, rename) for robust file transfer.

Four primary commands are supported:

  • file_list: Returns JSON with an entries array containing name, type, size, permissions, and modification time
  • file_get: Sends a JSON header with the file size, then streams content in 32 KB chunks
  • file_put: Receives binary data after the JSON header and writes to a temporary file before atomic rename
  • file_mkdir: Creates directories and returns a status confirmation

Protocol Flow and Message Lifecycle

The request-response cycle follows a strict sequence to ensure reliability:

  1. Connection: VPhoneControl (host-side Swift client) opens an AF_VSOCK socket to the guest on port 1337
  2. Request Framing: The host writes a 4-byte big-endian length prefix followed by the UTF-8 JSON payload
  3. Dispatch: vphoned reads the length, parses the JSON, and routes to the appropriate handler (vp_handle_keychain_command, vp_handle_clipboard_command, or vp_handle_file_command)
  4. Response Generation: The handler builds a response dictionary using vp_make_response(), preserving the request id for correlation
  5. Transmission: For simple replies, the daemon writes length-prefixed JSON. For binary payloads (images, files), it writes the JSON header first, then calls write() to stream raw bytes using vp_write_message utilities

Swift Client Implementation Examples

The host-side client in VPhoneControl.swift wraps the vsock connection. Below are practical implementations for each service.

Requesting the full keychain database:

let ctrl = VPhoneControl()
let request: [String: Any] = ["t": "keychain", "id": UUID().uuidString]
if let reply = try await ctrl.send(json: request),
   let items = reply["items"] as? [[String: Any]] {
    for item in items {
        print("\(item["service"] ?? ""): \(item["account"] ?? "")")
    }
}

Reading clipboard contents with binary image handling:

let clipboardReq: [String: Any] = ["t": "clipboard_get", "id": UUID().uuidString]
if let reply = try await ctrl.send(json: clipboardReq) {
    print("Text:", reply["text"] ?? "")
    if let hasImage = reply["has_image"] as? Bool, hasImage {
        let pngData = try await ctrl.readBinaryPayload()
        // Process UIImage(data: pngData)
    }
}

Listing directories on the guest file system:

let listReq: [String: Any] = ["t": "file_list", "path": "/var/mobile", "id": UUID().uuidString]
if let reply = try await ctrl.send(json: listReq),
   let entries = reply["entries"] as? [[String: Any]] {
    for entry in entries {
        print("\(entry["name"]!): \(entry["type"]!) (\(entry["size"]!) bytes)")
    }
}

Summary

  • vphoned operates as an iOS daemon listening on AF_VSOCK port 1337, exposing keychain, clipboard, and file-system services to the macOS host
  • The wire protocol uses length-prefixed JSON with mandatory correlation IDs, defined in vphoned_protocol.h and implemented via vp_make_response()
  • Keychain access (vphoned_keychain.m) reads /var/Keychains/keychain-2.db directly via SQLite and SecItemCopyMatching
  • Clipboard operations (vphoned_clipboard.m) dynamically load UIKit at runtime to interact with UIPasteboard, supporting both text and PNG image streaming
  • File transfer (vphoned_files.m) combines NSFileManager metadata with POSIX I/O, streaming file contents in 32 KB chunks after JSON headers
  • The host-side VPhoneControl.swift client manages connection lifecycle and provides Swift-native async/await interfaces for all three services

Frequently Asked Questions

How does vphoned handle binary data like images or large files?

Binary data transmission follows a two-phase protocol. The daemon first writes a JSON header containing metadata—such as size, has_image, or change_count—using the standard length-prefixed framing. Immediately following the JSON, it writes the raw binary payload (PNG bytes for clipboard images, file chunks for transfers) directly to the vsock socket. The host-side client calls readBinaryPayload() or equivalent to consume exactly the number of bytes specified in the header before parsing the next JSON message.

What security model protects the keychain database access?

The keychain service in vphoned_keychain.m performs direct SQLite queries against /var/Keychains/keychain-2.db rather than using high-level APIs exclusively. While this provides comprehensive enumeration, the daemon relies on iOS entitlement checks and the virtual machine's sandbox to restrict access. Items requiring specific keychain access groups may still return empty results unless the daemon possesses the necessary entitlements, and SecItemCopyMatching is used as a fallback for entitled items to ensure proper decryption of sensitive values.

Why does the clipboard service use dynamic loading instead of static linking?

The clipboard implementation dynamically loads UIKit via dlopen and resolves UIPasteboard symbols at runtime because vphoned runs as a system daemon without the full GUI framework environment. Static linking against UIKit would introduce dependency failures and bloat in a non-graphical context. By using objc_msgSend to invoke generalPasteboard, string, and image selectors, the daemon maintains a lightweight footprint while still accessing the shared pasteboard that bridges between iOS applications and the host macOS environment.

Can the file service handle concurrent operations from multiple host processes?

The current vphoned_files.m implementation uses standard POSIX file operations and NSFileManager without explicit file-locking mechanisms in the provided source. Each request is processed sequentially by the main dispatch loop in the daemon. While the protocol supports unique request IDs for correlation, concurrent file modifications from multiple host connections could result in race conditions. For production use, the host-side VPhoneControl client should serialize file operations or implement application-level locking when multiple processes target the same guest paths simultaneously.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →