dart_vm_injector 1.0.0
dart_vm_injector: ^1.0.0 copied to clipboard
Ultra-pro Dart package for injecting and evaluating expressions in live Dart VM isolates via VM Service (WebSocket/JSON-RPC), designed for advanced debugging, live inspection, hot-patching, and securi [...]
dart_vm_injector π¬ #
β οΈ Security Warning
This package opens a direct, unrestricted channel to the live Dart VM heap β including all in-memory secrets (passwords, API tokens, private keys, PII).
Use only with explicit authorisation on systems you own or are permitted to inspect.
Inject and evaluate arbitrary Dart expressions inside live, running Dart VM Isolates without stopping, restarting, or modifying the target application.
Powered by the official Dart VM Service Protocol (WebSocket + JSON-RPC 2.0).
β¨ Features #
| Feature | Description |
|---|---|
| π Connect | WebSocket connection to any live Dart VM |
| π Discover | List all running Isolates with full metadata |
| π Inject | Evaluate any valid Dart expression in a live Isolate |
| ποΈ Watch | Continuous polling streams for any variable or expression |
| π‘ Stream | Subscribe to VM stdout / stderr / events in real-time |
| π Inspect | Deep heap inspection via getObject / getInstances |
| π‘οΈ Type-safe | Strongly-typed API with meaningful exception hierarchy |
| π Stats | Per-session latency and injection counters |
β οΈ Requirements #
This package works only with JIT-compiled Dart programs running with the
--enable-vm-service flag.
| Scenario | Works? |
|---|---|
dart run your_app.dart |
β |
dart run --enable-vm-service your_app.dart |
β |
dart run --observe your_app.dart |
β |
| Flutter debug mode | β |
| Flutter profile mode | β |
dart compile exe your_app.dart (AOT) |
β |
| Flutter release mode | β |
π Quick Start #
1. Add to pubspec.yaml #
dependencies:
dart_vm_injector: ^1.0.0
2. Start your target app with VM Service enabled #
dart run --enable-vm-service your_app.dart
You will see:
The Dart VM service is listening on http://127.0.0.1:8181/FmMJoBzneFU=/
3. Inject an expression #
import 'package:dart_vm_injector/dart_vm_injector.dart';
void main() async {
// One-shot injection β connects, evaluates, disconnects
final result = await VmInjector.inject(
3495637284, // isolate number from startup output
'userService.currentUser?.email ?? "none"',
token: 'FmMJoBzneFU', // from the VM service URL
);
print(result.valueAsString); // admin@example.com
print(result.latency); // Duration(milliseconds: 12)
}
π API Reference #
One-shot static methods #
// Inject into a specific Isolate and disconnect immediately
final r = await VmInjector.inject(isolateNumber, expression, token: '...');
// Inject into the main Isolate (auto-detected)
final r = await VmInjector.injectMain(expression, token: '...');
Persistent session #
// Open a reusable session
final injector = await VmInjector.connect(
host: '127.0.0.1',
port: 8181,
token: 'YourToken',
);
try {
// List Isolates
final isolates = await injector.listIsolates();
// Evaluate expressions
final r = await injector.evaluateInIsolate(12345, '2 + 2');
final r = await injector.evaluateInMainIsolate('"hello"');
final r = await injector.evaluateInNamedIsolate('worker', 'queue.length');
// Evaluate in a paused stack frame
final r = await injector.evaluateInFrame(12345, 0, 'localVar');
// Watch a variable (continuous polling stream)
await for (final e in injector.watch(12345, 'requestCount', interval: 1.seconds)) {
print('requestCount = ${e.value}');
}
// Live VM streams
injector.stdout.listen(print);
injector.stderr.listen(print);
injector.vmEvents.listen((e) => print(e.kind));
// Deep object inspection
final obj = await injector.getObject(12345, result.instanceId!);
final instances = await injector.getInstances(12345, 'objects/class/42');
// Lifecycle control
await injector.pauseIsolate(12345);
await injector.resumeIsolate(12345);
// Statistics
print(injector.stats);
} finally {
await injector.dispose();
}
π¬ How It Works #
Your code dart_vm_injector Dart VM (JIT)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
VmInjector.inject()
β
βββΊ WebSocket handshake ββββββββββββββββββΊ VM Service (port 8181)
β β
βββΊ getVM() JSON-RPC βββββββββββββββββββββββββΊ β list isolates
β ββββββββββββββββββββ IsolateRef[] ββββββββββ€
β β
βββΊ getIsolate(id) βββββββββββββββββββββββββββΊ β get root library
β ββββββββββββββββββββ Isolate{rootLib} ββββββ€
β β
βββΊ evaluate(isolateId, libId, expr) βββββββββΊ β
β JIT compiler
β ββββββββββββββββββ
β β parse expr β
β β β AST β
β β β kernel IR β
β β β bytecode β
β β execute in heap β
β ββββββββββββββββββ
β βββββββββββββ InstanceRef{valueAsString} βββ€
β
βββΊ InjectionResult{valueAsString, valueKind, instanceId}
The evaluate RPC is implemented by the incremental Dart compiler embedded
in every JIT runtime. It compiles the expression in the scope of a specific
Library, executes the resulting bytecode on the target Isolate's thread, and
returns an InstanceRef β a reference to the live heap object.
π§ Finding the Isolate Number #
The Isolate number is the numeric portion of the Isolate ID (isolates/XXXXXXXXXX).
Method 1 β listIsolates() (recommended) #
final injector = await VmInjector.connect(host: 'localhost', port: 8181, token: '...');
final isolates = await injector.listIsolates();
for (final iso in isolates) {
print('${iso.number} ${iso.name}'); // 3495637284 main
}
Method 2 β Observatory UI #
Open http://127.0.0.1:8181/FmMJoBzneFU=/ in a browser β Isolates tab.
Method 3 β dart:developer #
import 'dart:developer' as dev;
void main() {
print(dev.Service.getIsolateID(Isolate.current));
// prints: isolates/3495637284
}
π Security Model #
What the VM Service exposes #
| Capability | Risk |
|---|---|
| Read any live variable | π΄ High β secrets, tokens, PII |
| Write any live variable | π΄ High β bypass auth, corrupt state |
| Call any function | π΄ High β arbitrary side effects |
| Trigger GC | π‘ Medium β service disruption |
| Kill Isolate | π΄ High β DoS |
| Read call stacks | π‘ Medium β logic exposure |
Defence in depth #
- Never run
--enable-vm-serviceon public-facing production servers. - Restrict the VM Service port with firewall rules (
iptables,ufw, security groups). - Never use
--disable-service-auth-codesβ the random token is your only layer of protection. - Audit all injections β log every expression evaluated in a tamper-proof audit trail.
- Prefer
--observe(profile mode) over debug when you need live inspection β it is slightly faster. - Use TLS tunnelling (e.g. SSH port-forward) when connecting to remote VMs.
ποΈ Package Structure #
dart_vm_injector/
βββ lib/
β βββ dart_vm_injector.dart β Barrel export
β βββ src/
β βββ vm_injector.dart β VmInjector β main class
β βββ vm_connection.dart β WebSocket connection management
β βββ isolate_resolver.dart β Isolate discovery & resolution
β βββ expression_evaluator.dart β evaluate / evaluateInFrame / invoke
β βββ result_parser.dart β InstanceRef β InjectionResult
β βββ stream_listener.dart β Live VM event streams
β βββ models/
β β βββ injection_result.dart
β β βββ isolate_info.dart
β β βββ vm_connection_config.dart
β β βββ vm_connection_stats.dart
β β βββ watch_event.dart
β βββ exceptions/
β β βββ vm_connection_exception.dart
β β βββ isolate_not_found_exception.dart
β β βββ injection_failed_exception.dart
β β βββ aot_mode_exception.dart
β β βββ vm_timeout_exception.dart
β βββ utils/
β βββ logger.dart
β βββ websocket_helper.dart
βββ example/
β βββ basic_inject.dart
β βββ list_isolates.dart
β βββ watch_variable.dart
β βββ listen_stdout.dart
β βββ advanced_inspect.dart
β βββ server_example/
β βββ target_server.dart β Shelf server to inject into
β βββ injector_client.dart β Client that injects into it
βββ test/
βββ models_test.dart
βββ exceptions_test.dart
βββ result_parser_test.dart
βββ websocket_helper_test.dart
π§ VM Service RPC Reference #
| RPC | Used by | Risk |
|---|---|---|
getVM |
listIsolates() |
Low |
getIsolate |
All evaluate calls | Low |
evaluate |
evaluateInIsolate() |
Very High |
evaluateInFrame |
evaluateInFrame() |
Very High |
invoke |
ExpressionEvaluator.invokeMethod() |
High |
getObject |
getObject() |
Medium |
getInstances |
getInstances() |
High |
streamListen |
stdout, stderr, events |
Low |
streamCancel |
dispose() |
Low |
pause |
pauseIsolate() |
High |
resume |
resumeIsolate() |
High |
getVersion |
Connection setup | Low |
π§βπ» Legitimate Use Cases #
- β IDE plugin development β power live variable inspection panels.
- β Custom debuggers β build domain-specific debuggers for Dart servers.
- β Staging environment inspection β debug issues without deploying new code.
- β Developer tooling β CLI tools for Dart server introspection.
- β Authorised penetration testing β assess VM Service exposure on client systems.
- β Hot-patch tooling β apply emergency in-memory patches in JIT environments.
- β Forensics β post-incident memory inspection on isolated, authorised systems.
β Out of Scope #
- β Unauthorized access to any system.
- β Production systems without explicit operator consent.
- β AOT-compiled Flutter release or standalone binaries.
- β Exfiltrating user data.
π License #
MIT Β© 2026 β See LICENSE.