Layout:
- src/ .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
MxAsbClient, probes, tests, harnesses. Executable spec.
- design/ Architectural plan for the Rust port (M0–M6), error
model, protocol invariants, risks (R1–R16), adversarial
review log (review.md).
- rust/ Rust workspace. M0 skeleton + M1 codec parity.
mxaccess-codec: 215 unit tests + 2 cross-implementation
parity tests (byte-identical against .NET reference).
Other crates are M0 stubs awaiting M2+.
- captures/ Frida + netsh + pcap evidence per CLAUDE.md
("captures are evidence, not throwaway logs").
- analysis/ Decompiled C# (frida/proxy/decompiled-*),
Ghidra exports for native DLLs (`exports/` only —
working state at `projects/` and AVEVA's input
binaries at `input/` are gitignored).
- docs/ Reverse-engineering reference docs.
- tools/ Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/ Rust CI: fmt + build + test + clippy on Windows.
- LICENSE MIT (Joseph Doherty, 2026).
Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly
Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2.5 KiB
Transport correlation
This note records the current boundary between the native adapter body format and localhost transport.
Combined captures
The combined runner starts Npcap loopback capture, then launches the harness under Frida:
analysis\scripts\run_frida_loopback_capture.ps1
Helper scripts:
analysis\scripts\map_frida_to_tcp.py
analysis\scripts\parse_dcerpc_streams.py
analysis\scripts\decode_mixed_local_stream.py
Capture 043
captures\043-frida-loopback-write-test-int-115
This writes TestChildObject.TestInt = 115. It proved exact Frida adapter
bodies are not copied verbatim to TCP, but the scalar value 115 was ambiguous
because it also matched DCE/RPC call IDs in the same window.
Capture 044
captures\044-frida-loopback-write-test-int-123456789
This writes a distinctive value:
TestChildObject.TestInt = 123456789
Results:
| Needle | Result |
|---|---|
raw little-endian 123456789 |
not found anywhere in the full pcap payload scan |
exact 40-byte Frida PutRequest body |
not found in reassembled TCP streams |
exact 86-byte Frida TransferData body |
not found in reassembled TCP streams |
| exact 88-byte Frida callback body | not found in reassembled TCP streams |
mixed 127.0.0.1:57415 <-> 57433 stream |
parsed, raw value not found |
DCE/RPC ::1:49704 streams |
parsed 452 PDUs, raw value not found in request/response stubs |
Generated files:
captures\044-frida-loopback-write-test-int-123456789\frida-to-tcp-map.tsv
captures\044-frida-loopback-write-test-int-123456789\dcerpc-stream-pdus.tsv
captures\044-frida-loopback-write-test-int-123456789\mixed-stream-57415-to-57433.tsv
captures\044-frida-loopback-write-test-int-123456789\mixed-stream-57433-to-57415.tsv
Implication
The CNmxAdapter::PutRequest and CNmxAdapter::TransferData buffers are an
internal adapter representation, not the TCP wire format. The wire transport
does not expose the write value as plain little-endian scalar bytes for this
distinctive-value capture.
The next reverse-engineering step is to decode the structural bridge between adapter bodies and transport messages:
- Correlate Frida call timestamps to DCE/RPC call IDs and mixed-stream record windows.
- Decode DCE/RPC NDR stubs for the observed context/opnum pairs.
- Hook deeper in
NmxSvc.exearoundCNmxControler::TransferDataandCNmxService::TransferDataso both sides of the adapter-to-service boundary can be compared before TCP serialization.