docs(grpc-events): token scheme fully RE'd via dnlib — aahCryptV2 (MD5-keyed RC4 + prefix)
Loaded dnlib in PowerShell (ILSpy crashes on the mixed-mode assembly) and scanned
the IL to recover the entire v8 token construction:
- <Module>::CHistoryConnectionGrpc.GetClientKey drives the ECDH: ECDiffieHellmanCng
{KeyDerivationFunction=Hash, HashAlgorithm=SHA256, KeySize=256} -> ExchangeKey ->
CngKey.Import(serverPub, EccPublicBlob) -> DeriveKeyMaterial = SHA256(shared secret),
the 32-byte client key.
- aahClientCommon.CClientBase.ConfigureOpenConnection (the lone GetClientKey caller)
builds the 26-byte token via HistorianCrypto.NRC4_V2.aahCryptV2 = a custom MD5-keyed
RC4 stream cipher with a version prefix:
* body/HashData = MD5 (verified by the round constants 0xd76aa478... + shifts 7/12/17/22)
* prepare_key = RC4 KSA from a 16-byte MD5 key
* enc_buffer = MD5 -> key, then rc4encrypt; enc prepends PrefixV2/InnerPrefixV2
(the constant 0x8e token marker)
So token = prefix + RC4(plaintext, key=MD5(keyMaterial)), keyMaterial tied to the
SHA256(ECDH secret) client key. 100% reproducible in pure managed code (RC4+MD5).
Remaining (next cycle): read ConfigureOpenConnection's exact key/plaintext/prefix bytes,
implement aahCryptV2 managed-side, set the v8 token, live-test. Frida CNG + dnlib are
the RE path; nothing AVEVA is shipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -269,10 +269,36 @@ Hooked Windows CNG (`bcrypt.dll`/`ncrypt.dll`) while the native harness ran a re
|
||||
output). It is built in managed code between the `DeriveKeyMaterial` call and the openParameters
|
||||
assembly.
|
||||
|
||||
**Next step:** ILSpy cannot decompile the mixed-mode assembly (full-assembly and `<Module>` both crash,
|
||||
exit 70). Use **dnlib** (IL-level, won't choke on the native parts) to dump the `<Module>` method that
|
||||
references `ECDiffieHellmanCng.DeriveKeyMaterial` and read the post-derive token construction, then
|
||||
implement it managed-side and re-test (non-destructive).
|
||||
**dnlib IL extraction 2026-06-23 — the token scheme is fully reverse-engineered.** ILSpy can't
|
||||
decompile the mixed-mode assembly (crashes), but loading `dnlib` in PowerShell and scanning the IL
|
||||
recovered the whole construction:
|
||||
|
||||
- **`<Module>::CHistoryConnectionGrpc.GetClientKey`** is the ECDH driver: `new ECDiffieHellmanCng()`
|
||||
→ `KeyDerivationFunction = Hash`, `HashAlgorithm = SHA256`, `KeySize = 256` →
|
||||
`GrpcHistoryClient.ExchangeKey(strHandle, ourPubKey.ToByteArray(), out serverPub, out err)` →
|
||||
`CngKey.Import(serverPub, CngKeyBlobFormat.EccPublicBlob)` → **`DeriveKeyMaterial`** = the 32-byte
|
||||
client key = **`SHA256(ECDH shared secret)`**. (So our managed side should derive the key the same
|
||||
way — `ECDiffieHellman` raw agreement then SHA256, or equivalently `DeriveKeyFromHash(..., SHA256)`.)
|
||||
- **The 26-byte token is built by `aahClientCommon.CClientBase.ConfigureOpenConnection`** (the lone
|
||||
caller of `GetClientKey`) using the **`HistorianCrypto.NRC4_V2.aahCryptV2`** scheme — a custom
|
||||
**MD5-keyed RC4 stream cipher with a version prefix**:
|
||||
- `aahCryptV2.body`/`HashData` = **MD5** (verified: the IL loads MD5 round constants `0xd76aa478`…
|
||||
and rotates 7/12/17/22).
|
||||
- `aahCryptV2.prepare_key` = standard **RC4 KSA** seeding the 256-byte S-box from a **16-byte (MD5)**
|
||||
key (`std.array<unsigned char,16>`).
|
||||
- `aahCryptV2.enc_buffer` = `MD5(...)` → key, then **`rc4encrypt`** the body; `enc` prepends a
|
||||
scheme **prefix** (`NRC4_V2.PrefixV2` / `InnerPrefixV2`) — the constant `0x8e` token marker.
|
||||
- `from_GUID` keys the cipher from a GUID string.
|
||||
|
||||
So the token = `prefix + RC4(plaintext, key = MD5(keyMaterial))`, where the key material ties back to
|
||||
the `SHA256(ECDH secret)` client key. **This is 100% reproducible in pure managed code** (RC4 + MD5
|
||||
are ~40 lines; nothing AVEVA ships).
|
||||
|
||||
**Remaining to finish (next cycle):** read `ConfigureOpenConnection`'s exact wiring (which value is
|
||||
MD5'd for the RC4 key, what plaintext is encrypted, the exact prefix bytes — a little more dnlib IL),
|
||||
implement `aahCryptV2` (RC4+MD5+prefix) managed-side, set the v8 token = that, and live-test
|
||||
(non-destructive). The offline correlation data (one run's derived key + token + openParameters) is
|
||||
captured under `artifacts/.../` to validate the managed reproduction before going live.
|
||||
|
||||
**2 of 3 layers cleared** (key exchange + client key); the 3rd (token construction) is localized to a
|
||||
specific managed method, pending dnlib extraction. ExchangeKey + the v8 serializer are committed; the
|
||||
|
||||
Reference in New Issue
Block a user