Files
dohertj2 c95824a65d Initial commit: managed .NET 10 AVEVA Historian SDK + reverse-engineering toolkit
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:

- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass

Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.

Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 06:31:48 -04:00

148 lines
7.7 KiB
Markdown

# Capture Workflow
Use the reverse-engineering CLI to keep captures repeatable:
```powershell
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- manifest
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- exports current\aahClient.dll
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- mark history-raw
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- wcf-probe 10.100.0.48 32568
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- wcf-tag-info 10.100.0.48 32568 OtOpcUaParityTest_001.Counter
```
To probe the certificate-secured history endpoint with fully managed WCF/MDAS:
```powershell
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- wcf-cert-probe localhost 32568
dotnet run --project tools\AVEVA.Historian.ReverseEngineering -- wcf-cert-probe 10.100.0.48 32568 localhost
```
The optional final argument supplies the expected endpoint DNS identity. On the
current development Historian, the remote endpoint presents certificate identity
`localhost`, so the explicit identity is required when connecting by IP address.
Windows built-in packet capture may miss local Historian traffic. For local
native-wrapper probes, use the Frida harness:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Attach-NativeTraceHarnessWinsockCapture.ps1 -Scenario history -ServerName localhost -RetrievalMode Full -TagName OtOpcUaParityTest_001.Counter -LookbackMinutes 1440 -MaxRows 1
```
The Frida harness attaches before `OpenConnection` and hooks:
- Winsock `connect`, `WSAConnect`, `send`, `recv`, `WSASend`, and `WSARecv`
- `CreateFileW`, `ReadFile`, `WriteFile`, and `CloseHandle`
- `NtCreateFile`, `NtReadFile`, and `NtWriteFile`
For native trace harness captures that must hook before `aahClientManaged.dll`
loads, pass a preload pause:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Attach-NativeTraceHarnessWinsockCapture.ps1 -Scenario event -PreLoadSleepSeconds 8 -AttachDelaySeconds 0 -OutputPath .\docs\reverse-engineering\winsock-event-preload-localhost-latest.ndjson
```
The latest preload local event pass still produced no Winsock or tracked pipe
payloads even though native event open and `StartQuery` succeeded.
Artifacts should be treated as diagnostic metadata. The script records byte
counts and short hex prefixes only; do not add raw credential/session buffers
to the repo.
Current local result: `localhost`, `127.0.0.1`, and the machine LAN IP all
complete native reads without observed client-process socket or pipe payloads.
That suggests local native reads are not exercising the remote transport path.
To force the native client onto a remote TCP path through the Debian test box:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Run-DebianHistorianRelayCapture.ps1 -SshUser dohertj2 -SshHost 10.100.0.35 -TargetHost 10.100.0.48 -OutputPath .\docs\reverse-engineering\debian-relay-history-latest.ndjson -HarnessOutputPath .\docs\reverse-engineering\native-trace-harness-via-debian-relay-latest.json
```
This starts a temporary Python TCP relay on `10.100.0.35:32568` forwarding to
`10.100.0.48:32568`, runs the native harness against `10.100.0.35`, pulls back
the relay log, and cleans up the remote process. The relay logs connection
events, byte counts, and 16-byte hex prefixes only.
Current relay result: the native client reaches the remote Net.TCP/WCF preamble
and authentication exchange, but the relayed session is rejected before
`OpenConnection` becomes connected. This gives transport evidence but not query
request/response buffers yet. Matching ArchestrA logs identify the relayed
target as `Server(10.100.0.35)` and show `Transport with Certificate` security,
so the relay is not transparent at the certificate/identity layer.
For event mode, the rewritten relay shows the same security boundary but a
clear endpoint sequence:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Run-DebianHistorianRelayCapture.ps1 -SshUser dohertj2 -SshHost 10.100.0.35 -TargetHost 10.100.0.48 -RewriteEndpointHost -Scenario event -OutputPath .\docs\reverse-engineering\debian-relay-rewrite-event-latest.ndjson -HarnessOutputPath .\docs\reverse-engineering\native-trace-harness-via-debian-relay-rewrite-event-latest.json
```
Observed event relay sequence:
- `/HistCert` preamble with `application/ssl-tls`
- TLS-style records
- repeated `/Hist-Integrated` preambles with `application/negotiate`
- NTLMSSP type 1/2/3 messages
- 13-byte server rejection/reset before connected state
Adding `--direct-connection` for event mode does not bypass the relay; event
direct emits the same endpoint sequence and still fails before connected state.
To test whether native connection flags change that security choice, add extra
harness arguments:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Run-DebianHistorianRelayCapture.ps1 -SshUser dohertj2 -SshHost 10.100.0.35 -TargetHost 10.100.0.48 -OutputPath .\docs\reverse-engineering\debian-relay-history-direct-latest.ndjson -HarnessOutputPath .\docs\reverse-engineering\native-trace-harness-via-debian-relay-direct-latest.json -HarnessExtraArgs @("--direct-connection")
```
Observed result: once the reverse harness forces the private
`directConnection` backing field, the native read succeeds and the relay records
only its own startup line. This is useful for native parity snapshots, but it
bypasses the remote transport evidence needed for the managed driver.
To collect Windows packet metadata for the relay path without storing raw
payload bytes:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Run-PktmonDebianRelayCapture.ps1 -Scenario history -SshUser dohertj2 -SshHost 10.100.0.35 -TargetHost 10.100.0.48 -TagName OtOpcUaParityTest_001.Counter -LookbackMinutes 1440 -MaxRows 1 -OutputPrefix .\docs\reverse-engineering\pktmon-debian-relay-history-latest
```
This script:
- adds a pktmon TCP filter for `10.100.0.35:32568`
- starts pktmon with flags `0x00e`, intentionally omitting raw packet bytes
- runs the Debian relay harness scenario
- converts the ETL to text/stat metadata
- deletes the ETL file before writing the summary
Current pktmon result: the metadata capture records TCP flows between
`10.100.0.48` and `10.100.0.35:32568` with no payload bytes retained. This is
useful for timing, directions, ports, and reset behavior, but still not enough
to reconstruct query buffers.
To correlate relay TCP ownership on Windows while running Frida system-boundary
hooks:
```powershell
powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Attach-SystemBoundaryViaDebianRelay.ps1 -Scenario history -SshUser dohertj2 -SshHost 10.100.0.35 -TargetHost 10.100.0.48 -TagName OtOpcUaParityTest_001.Counter -LookbackMinutes 1440 -MaxRows 1 -OutputPath .\docs\reverse-engineering\system-boundary-via-debian-relay-history-latest.ndjson
```
Current system-boundary relay result: the Windows TCP owner monitor attributes
the established relay connection to `AVEVA.Historian.NativeTraceHarness`, but
Frida hooks on exported Winsock calls, `WSAIoctl`, `mswsock`, file APIs, and
`NtDeviceIoControlFile` still record no transport callbacks. Treat this as
negative evidence for further export-level Frida work.
For each scenario:
1. Start Wireshark and API Monitor.
2. Emit a `mark <scenario-name>` line and note the timestamp.
3. Run the same operation through the native SDK/client.
4. Save raw captures outside the repo.
5. Add only sanitized binary frames or decoded notes under `fixtures/protocol`.
The production SDK must not reference this harness or any AVEVA native binary.
After a capture is sanitized, add parser tests before enabling the corresponding
operation in `Historian2020ProtocolDialect`.