# Frida Hook Pass: aahClientManaged Integrated Read Scenario: ```powershell powershell.exe -NoProfile -ExecutionPolicy Bypass -File .\scripts\Attach-AahClientManagedFridaCapture.ps1 -TagName OtOpcUaParityTest_001.Counter -LookbackMinutes 1440 -MaxRows 1 ``` Artifacts: - Script: `scripts\frida\aahclientmanaged-open-query.js` - Runner: `scripts\Attach-AahClientManagedFridaCapture.ps1` - Latest capture: `docs\reverse-engineering\frida-aahclientmanaged-attach-read-latest.ndjson` ## Result The attach-based workflow is the reliable Frida path for this mixed CLR/native assembly: 1. Start Windows PowerShell. 2. Load `current\aahClientManaged.dll`. 3. Sleep briefly before creating `HistorianAccess`. 4. Attach Frida to the already-loaded process. 5. Install hooks at candidate RVAs. 6. Continue the native integrated read. Frida confirmed: - target process architecture: `x64` - loaded module: `aahClientManaged.dll` - module base for latest run: `0x7ffd4a600000` - module size: `17166336` - path: `C:\Users\dohertj2\Desktop\histsdk\current\aahClientManaged.dll` Hooks were installed for candidate method RVAs including: - `CClientInfo.SerializeOpenConnectionInParams*` - `CHistoryConnectionWCF.OpenConnection*` - `HistorianClient.StartQuery` - `HistorianClient.StartDataQuery` - `ClientApp.StartDataQuery` - `Query.StartDataQuery` - `CRetrievalConnectionWCF.StartQuery2` - `QueryColumnSelector.SelectNonSummaryColumns` - `QueryColumnSelector.Save` - `QueryColumnSelector.GetColumnSelectorFlags` - `HistorianClient.GetNextRow` - `HistorianClient.GetNextRow` No hook entry/leave events fired during the successful read. ## Interpretation The `methods` command RVAs are not reliable executable hook targets for the successful path. They are CLR/mixed-mode method body locations or stubs, not the actual runtime addresses invoked after CLR/JIT/mixed-mode dispatch. Frida can see the module and can install some interceptors at `base + RVA`, but those addresses are not reached by the wrapper scenario. The successful read still proves the scenario: - integrated auth opens - `GetConnectionStatus` settles - `HistoryQuery.StartQuery` succeeds - `MoveNext` returns one row for `OtOpcUaParityTest_001.Counter` An expanded pass based on decompiled call targets produced the same result: hooks installed for MethodDef RVAs such as `HistorianClient.StartDataQuery` (`0x4160C4`), `Query.StartDataQuery` (`0x41CACC`), `QueryColumnSelector.SelectNonSummaryColumns` (`0x1EE34`), and `HistorianClient.GetNextRow` (`0x42F818`), but no entry/leave callbacks fired. Treat MethodDef RVAs from the mixed-mode assembly as insufficient for Frida interception without resolving the actual CLR/native dispatch target. Runtime method-pointer dumps from `tools\AVEVA.Historian.NativeTraceHarness --dump-method-pointers ...` provide a better target class, but not stable RVAs. `RuntimeHelpers.PrepareMethod` plus `MethodHandle.GetFunctionPointer()` exposes process-local CLR/JIT entry addresses for methods such as `.HistorianClient.StartDataQuery`, `.CRetrievalConnectionWCF.StartQuery2`, and `.HistorianClient.GetNextRow`. The current artifacts mark all of these as `FunctionPointerInModule = false`, so they must be discovered in the same process that will be hooked. `scripts\Attach-NativeTraceHarnessRuntimePointerCapture.ps1` now automates that same-process attempt. It starts `NativeTraceHarness`, writes a runtime pointer snapshot immediately before `StartQuery`, pauses, generates a temporary Frida script with the absolute addresses, and attaches to the still-paused process. The latest local direct history run installed 37 absolute hooks from `runtime-method-pointers-before-history-start-latest.json`; the read succeeded, but no hook `enter`/`leave` callbacks fired. This rules out raw Frida interception of `MethodHandle.GetFunctionPointer()` addresses as the next primary capture route. ## Decompiled Query Call Facts `ArchestrA.HistoryQuery.StartQuery`: - Calls `BaseQuery.GetClient`. - Calls `EndQuery` before starting a new query. - Converts tag names to a native UTF-16 pointer vector. - Builds a selected-column stream as: - `ushort` value `1` - 8-byte `QueryColumnSelector` payload - Calls `HistorianClient.StartDataQuery` with retrieval mode, query format `0`, summary type `0`, tag count, tag pointer vector, UTC FILETIME start/end, resolution, deadbands, time zone `UTC`, version/interpolation/timestamp/ quality/value-selector/aggregation enum values, option/filter buffers, selected-column byte count and pointer, max states, output query handle, and `SError`. `ArchestrA.EventQuery.StartQuery`: - Calls `BaseQuery.GetClient`. - Calls `EndQuery` before starting a new event query. - Normalizes `EventCount = 0` to `100000` and enables a continue-query mode. - Calls `HistorianClient.StartEventQuery` with UTC FILETIME start/end, event count, skip count, event order as `ushort`, query type value `1`, filter pointer, time zone `UTC`, output query handle, and `SError`. ## Confirmed Managed Contract Shape Decompilation confirms these WCF byte-buffer contracts: - `HistoryServiceContract.IHistoryServiceContract2.OpenConnection2` - `HistoryServiceContract.IHistoryServiceContract2.ExchangeKey` - `HistoryServiceContract.IHistoryServiceContract2.ValidateClientCredential` - `RetrievalServiceContract.IRetrievalServiceContract2.StartQuery2` - `RetrievalServiceContract.IRetrievalServiceContract2.GetNextQueryResultBuffer2` - `RetrievalServiceContract.IRetrievalServiceContract2.EndQuery2` `StartQuery2` signature: ```text bool StartQuery2( uint clientHandle, ushort queryRequestType, uint requestSize, byte[] pRequestBuff, out uint responseSize, out byte[] pResponseBuff, ref uint queryHandle, out uint errSize, out byte[] err) ``` `GetNextQueryResultBuffer2` signature: ```text bool GetNextQueryResultBuffer2( uint clientHandle, uint queryHandle, out uint resultSize, out byte[] pResultBuff, out uint errSize, out byte[] err) ``` ## Next Hook Direction Use profiler/API-boundary interception rather than raw Frida function-pointer interception: - CLR profiler / managed method rewrite for `HistoryServiceContract.*` and `RetrievalServiceContract.*` calls. - API Monitor or Detours-style hooks at the lower native/WCF DLL boundary. - Focus first on capturing managed byte-array arguments to: - `OpenConnection2` - `ExchangeKey` - `ValidateClientCredential` - `StartQuery2` - `GetNextQueryResultBuffer2` ## WCF Diagnostics Attempt `tools\AVEVA.Historian.NativeTraceHarness` attempted classic .NET Framework `System.ServiceModel` diagnostics and message logging while running the same successful integrated read. No `.svclog` file was produced, even though the scenario succeeded. This is negative evidence that the native wrapper path does not expose a managed WCF client pipeline in the harness AppDomain that can be captured with ordinary WCF config diagnostics. A profiler/method-rewrite hook remains the likely buffer-capture route. ## Event Add-Tag Hook Pass Scenario: ```powershell .\tools\AVEVA.Historian.NativeTraceHarness\bin\Debug\net481\AVEVA.Historian.NativeTraceHarness.exe --scenario event --server-name localhost --tcp-port 32568 --connection-wait-seconds 15 --pre-open-sleep-seconds 15 --max-rows 1 --lookback-minutes 1440 ``` Artifacts: - Script: `scripts\frida\aahclientmanaged-open-query.js` - Latest capture: `docs\reverse-engineering\frida-aahclientmanaged-event-addtag-latest.ndjson` - Native result: `docs\reverse-engineering\native-event-addtag-frida-child.json` The native event query still succeeded and returned one event row. The expanded hook list installed hooks for the event default-tag path: - `HistorianAccess.CreateDefaultEventTag` at method RVA `0x43c2d4` - `HistorianAccess.AddTagInternal` at method RVA `0x43be68` - `HistorianClient.AddHistorianTag` at method RVA `0x417c18` - `HistorianClient.ConvertEventTagToTagMetadata` at method RVA `0x417b68` - `CTagMetadata.Save>` at method RVA `0x1044dc` The capture has hook-install events and some hook-install failures for other candidates, but still no enter/leave events. This confirms the same limitation as the history pass: MethodDef RVAs from this mixed-mode assembly are not the actual runtime entry points for the successful wrapper path. The next capture mechanism needs CLR method rewriting/profiling or a lower native boundary such as Winsock/API Monitor, not raw `base + RVA` Frida interceptors. ## Event Winsock/IPŠ” Pass `tools\AVEVA.Historian.NativeTraceHarness` now supports `--pre-load-sleep-seconds`, allowing Frida to attach before `aahClientManaged.dll` is loaded. Running the event scenario with that preload pause produced: - `docs\reverse-engineering\winsock-event-preload-localhost-latest.ndjson` - native event open succeeded - `StartQuery` succeeded - one event row returned - no Historian-port `connect`/`send`/`recv` events - no tracked named-pipe or interesting file `ReadFile`/`WriteFile` payloads For the local event scenario, the successful wrapper path is therefore not visible through the current Winsock or named-pipe hook set. Keep the preload pause because it is useful for future lower-level hooks, but the next byte capture should target CLR method arguments, API Monitor at the native/WCF DLL boundary, or a real remote/relay path where traffic leaves the process. The remote relay path does expose event-mode transport, but it still stops at the security layer. With endpoint-host rewriting enabled, event mode sends `/HistCert` over `application/ssl-tls`, then repeats `/Hist-Integrated` over `application/negotiate` with NTLMSSP messages. The server returns a short reject record before the native harness reaches connected state, so this path has endpoint/security evidence but still no query payload evidence. ## aahClient Export Hook Pass `scripts\frida\aahclient-exports.js` and `scripts\Attach-NativeTraceHarnessAahClientExportCapture.ps1` hook the procedural `mdas_*` exports from `aahClient.dll` if that DLL is loaded. The first local direct history run attached before `aahClientManaged.dll` load and the native read succeeded, but Frida never observed `aahClient.dll` being loaded and installed no export hooks. A separate `-DumpLoadedModules` run of the older PowerShell harness also showed only `aahClientManaged.dll` among the current AVEVA DLL set during a successful wrapper read. That rules out `aahClient.dll` exports as the immediate capture boundary for the active wrapper path. The `mdas_*` exports may still describe a separate native client ABI, but they are not the calls currently made by `HistoryQuery.StartQuery` in this harness. ## System Boundary Hook Pass `scripts\frida\aahclientmanaged-system-boundary.js` and `scripts\Attach-NativeTraceHarnessSystemBoundaryCapture.ps1` hook the imported system/API boundary used by `aahClientManaged.dll`: file I/O, `NtCreateFile`, `NtReadFile`, `NtWriteFile`, `NtDeviceIoControlFile`, DNS, exported Winsock connect/send/recv APIs, `WSAIoctl`, `mswsock` extension exports, Secur32, Crypt32, and NetAPI. Local direct history and same-machine remote-IP reads still succeeded without file/pipe/socket/security callbacks beyond hook installation. This reinforces that the local Historian path is optimized below the query surface we need. The Debian relay run adds one sharper fact. The relay accepted connections from the Windows host, and the Windows TCP owner monitor attributed the established connection to the `AVEVA.Historian.NativeTraceHarness` PID. Even with hooks installed in that same PID before `OpenConnection`, no exported Winsock, `WSAIoctl`, `mswsock`, or `NtDeviceIoControlFile` callbacks fired. The relay connection still reset before the harness reached `ConnectedToServer = true`. That makes raw Frida export hooks insufficient for the remaining transport capture. The next local capture mechanism should be ETW/netsh/WFP/kernel-level network tracing, API Monitor/Detours below the wrapper, or CLR profiler/IL instrumentation inside the mixed-mode assembly.