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

> Discover how vphoned exposes iOS keychain, clipboard, and file services over vsock. Learn about its length-prefixed JSON protocol and command handlers for native operations on port 1337.

- Repository: [Lakr/vphone-cli](https://github.com/Lakr233/vphone-cli)
- Tags: internals
- Published: 2026-09-08

---

**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`](https://github.com/Lakr233/vphone-cli/blob/main/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`](https://github.com/Lakr233/vphone-cli/blob/main/VPhoneControl.swift) wraps the vsock connection. Below are practical implementations for each service.

Requesting the full keychain database:

```swift
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:

```swift
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:

```swift
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`](https://github.com/Lakr233/vphone-cli/blob/main/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.