Is your feature request related to a problem?/你的请求是否与某个问题相关?
The daemon binds its CLI socket to a fixed, well-known filesystem path:
Because this is a filesystem-namespace UNIX socket, the path string is visible to any unprivileged process that reads /proc/net/unix (or /proc/self/net/unix). Local integrity scanners can match this constant string as a one-line signature to identify the framework, even when every other hooking surface is otherwise hidden.
This is not about defeating one specific app. Any scanner that greps /proc/net/unix for a constant, well-known path will match it. The fixed path is therefore a stable, version-independent fingerprint.
As a concrete example, Duck Detector (build 495) reports exactly one LSPosed signal on a device where all other surfaces are clean:
[DANGER] LSPosed
Verdict: 1 high-risk LSPosed signal(s)
Runtime:
Unix sockets: 1 hit(s)
(0000000000000000: 00000002 00000000 00010000 0001 01 23706 /data/adb/lspd/.cli_sock)
Every other LSPosed check on the same card is clean (class load, ClassLoader chain, XposedBridge fields, package catalog, stack trace, hook callbacks, Binder bridge, zygote permissions, logcat leaks, dirty sepolicy, native maps), which isolates the socket path string as the sole signal.
Describe the solution you'd like/描述你想要的解决方案
Make the CLI socket path non-deterministic so the constant string no longer exists as a stable fingerprint. A few options, in increasing order of change:
-
Randomize the socket name at runtime (preferred).
Generate a random per-boot name and keep it as the single source of truth in FileSystem, so CliSocketServer and Cli continue to agree without further changes. The literal .cli_sock string no longer exists as a constant on disk.
-
Use an abstract-namespace socket.
LocalSocketAddress.Namespace.ABSTRACT avoids a filesystem path entirely. Trade-off: abstract sockets are not governed by file permissions, so the existing token check becomes the only gate, and the name still appears in /proc/net/unix (as @name), so it would also need to be non-predictable.
-
Keep a fixed name but make it configurable, so users who don't need a stable external endpoint can opt into a randomized one.
I lean toward option 1 because it keeps the filesystem-permission layer, keeps the existing token auth (CLI_TOKEN_MSB / CLI_TOKEN_LSB) intact, and only touches how socketPath is produced.
The relevant code paths:
- daemon/src/main/kotlin/org/matrix/vector/daemon/data/FileSystem.kt — val socketPath: Path = basePath.resolve(".cli_sock")
- daemon/src/main/kotlin/org/matrix/vector/daemon/env/CliSocketServer.kt — binds via LocalSocketAddress(cliSocketPath, LocalSocketAddress.Namespace.FILESYSTEM)
- daemon/src/main/kotlin/org/matrix/vector/daemon/Cli.kt — connects via FileSystem.socketPath.toString()
Since both ends reference FileSystem.socketPath, centralizing name generation there keeps server and client consistent automatically.
Additional context/其他信息
- The CLI channel already authenticates with a per-build token (CLI_TOKEN_MSB / CLI_TOKEN_LSB), so the fixed path carries no security role — it is purely an address. Making it unpredictable does not weaken the auth model.
- The only external compatibility risk is third-party tooling or scripts that assume the literal path /data/adb/lspd/.cli_sock. If that is a real concern, option 3 (configurable, default unchanged) would preserve compatibility.
duck_detector_report.txt
- Environment: Duck Detector 2026.07.30-d1e9257cab46 (build 495), Samsung SM-A325N, Android 13, SDK 33.
Is your feature request related to a problem?/你的请求是否与某个问题相关?
The daemon binds its CLI socket to a fixed, well-known filesystem path:
Because this is a filesystem-namespace UNIX socket, the path string is visible to any unprivileged process that reads /proc/net/unix (or /proc/self/net/unix). Local integrity scanners can match this constant string as a one-line signature to identify the framework, even when every other hooking surface is otherwise hidden.
This is not about defeating one specific app. Any scanner that greps /proc/net/unix for a constant, well-known path will match it. The fixed path is therefore a stable, version-independent fingerprint.
As a concrete example, Duck Detector (build 495) reports exactly one LSPosed signal on a device where all other surfaces are clean:
Every other LSPosed check on the same card is clean (class load, ClassLoader chain, XposedBridge fields, package catalog, stack trace, hook callbacks, Binder bridge, zygote permissions, logcat leaks, dirty sepolicy, native maps), which isolates the socket path string as the sole signal.
Describe the solution you'd like/描述你想要的解决方案
Make the CLI socket path non-deterministic so the constant string no longer exists as a stable fingerprint. A few options, in increasing order of change:
Randomize the socket name at runtime (preferred).
Generate a random per-boot name and keep it as the single source of truth in FileSystem, so CliSocketServer and Cli continue to agree without further changes. The literal .cli_sock string no longer exists as a constant on disk.
Use an abstract-namespace socket.
LocalSocketAddress.Namespace.ABSTRACT avoids a filesystem path entirely. Trade-off: abstract sockets are not governed by file permissions, so the existing token check becomes the only gate, and the name still appears in /proc/net/unix (as @name), so it would also need to be non-predictable.
Keep a fixed name but make it configurable, so users who don't need a stable external endpoint can opt into a randomized one.
I lean toward option 1 because it keeps the filesystem-permission layer, keeps the existing token auth (CLI_TOKEN_MSB / CLI_TOKEN_LSB) intact, and only touches how socketPath is produced.
The relevant code paths:
Since both ends reference FileSystem.socketPath, centralizing name generation there keeps server and client consistent automatically.
Additional context/其他信息
duck_detector_report.txt