surveillance-client/docs/surveillance-client-dpi-analysis.md
MaddoScientisto de077ca5d5 Add detailed analysis and documentation for RSNet.dll download functionality
- Introduced a comprehensive markdown document outlining the API surface of RSNet.dll related to downloading video files.
- Documented initialization, connection, record querying, and download processes.
- Provided insights into the exported functions, their parameters, and expected behaviors.
- Included practical implications and recommendations for implementing a downloader script using C# interop.
- Highlighted the necessary struct layouts and callback mechanisms for effective integration with the DLL.
2026-04-17 21:19:46 +02:00

389 lines
No EOL
13 KiB
Markdown

# Surveillance Client Server And Recorded Video Download Analysis
Date: 2026-04-17
Target binary: `Surveillance_client.exe`
Analysis method: Ghidra MCP against the already-open executable, with non-destructive analysis only. During this pass, allowed writes were limited to function renames and comments.
## Objective
Determine how the client connects to remote devices and how it searches for and downloads recorded video, with the practical goal of building a small standalone downloader that can fetch an arbitrary duration from an arbitrary camera.
## Executive Summary
- The EXE is mostly UI, orchestration, and request-shaping code.
- The real connection, record-query, playback-control, and download logic is implemented in `RSNET.DLL`.
- Local playback and file rendering are implemented in `RSPLAY.DLL`.
- The EXE contains a clear playback/download workflow that prepares request structures and hands them to `RSNET.DLL` exports.
- The current executable analysis is enough to map the high-level flow and the parameter shapes, but not enough to recover the on-the-wire protocol. For that, `RSNet.dll` needs to be loaded into Ghidra next.
## Installed Module Layout
The installed client directory contains the key vendor modules:
- `RSNet.dll`
- `RSPlay.dll`
- `QRSosAdapter.dll`
- `QtNetwork4.dll`
This matches the import thunks present in the EXE.
## Confirmed DLL Boundary
The following named functions in the EXE are import thunks, not native implementations:
- `RSNetQueryParam` at `0x006e96f0`
- `RSNetQueryParamEx` at `0x006e9720`
- `RSNetAlarmSubscribe` at `0x006e99a0`
- `RSPlayGetPlayFileTimeRange` at `0x006ea760`
Example:
- `RSNetQueryParam` is just `JMP dword ptr [0x0076e648]`
External symbol resolution in the loaded EXE confirms these libraries and exports:
### `RSNET.DLL`
Relevant exports confirmed in the import table:
- `RSNetConnectionStart`
- `RSNetConnectionStartEx`
- `RSNetQueryRecordEx`
- `RSNetStartRecordPlayEx`
- `RSNetReqRecordPlayCtrol`
- `RSNetStartDownloadRecord`
- `RSNetStartDownloadRecordEx`
- `RSNetStopDownloadRecord`
- `RSNetReqRecordData`
- `RSNetStopRecordPlay`
- `RSNetReplayRecordData`
- `RSNetReposRecordData`
- `RSNetGetDevInfo`
- `RSNetSearchDev`
### `RSPLAY.DLL`
Relevant playback exports confirmed in the import table:
- `RSPlayCreatePlayInstance`
- `RSPlaySetPlayWnd`
- `RSPlayStartPlay`
- `RSPlaySetCallbackMessageEx`
- `RSPlayImageCallback`
- `RSPlayOpenPlayFile`
- `RSPlayOpenPlayFileListEx`
- `RSPlayGetPlayFileTimeRange`
- `RSPlaySetCurPlayedTimeEx`
- `RSPlayInputNetFrame`
- `RSPlayStartlocalrecord`
- `RSPlayStoplocalrecord`
Interpretation:
- `RSNET.DLL` is the module that matters for device login, remote record queries, remote playback, and remote download.
- `RSPLAY.DLL` is mostly decode/render/local playback support, not the primary network protocol implementation.
## Confirmed EXE-Side Flow
### UI widgets involved
The strings and handlers in the EXE show a playback and file-download workflow:
- `Widget_Playback`
- `Widget_Playback_FileDownload`
- `Download By Date`
- `Download By Files`
- `DownLoad Start Time`
- `MainStream`
- `SubStream`
- `playback-download`
- `QRSAdapterReqRecordPlayCtrol`
Relevant strings also show signals/slots for:
- `searchRequest(QString)`
- `playbackResponse()`
- `fileDownload()`
- `download()`
- `stopdownload()`
### Functions renamed during analysis
The following EXE functions were renamed in the Ghidra database:
- `0x004a9690` -> `PlaybackFileDownloadWidget_connectSignals`
- `0x0049f570` -> `RecordPlaybackControl_toggleMode`
- `0x004afb30` -> `PlaybackFileDownloadWidget_download`
- `0x004b1be0` -> `PlaybackFileDownloadWidget_downloadByTime`
These names reflect verified behavior from decompilation.
## What The Download UI Does
### `PlaybackFileDownloadWidget_connectSignals`
This function wires the file-download UI:
- download button -> `download()`
- stop button -> `stopdownload()`
- cancel button -> `cancel()`
- date/time selector changes -> `GetFileNumAndSize(QDateTime)`
That establishes the UI entrypoint cleanly.
### `PlaybackFileDownloadWidget_download`
This is the main dispatcher for the download dialog.
It has two modes:
1. File-based download mode
2. Time-range download mode
In file-based mode it:
- iterates rows in the record table
- checks selected rows
- tracks row state in a per-row metadata structure
- starts a timer to process queued download items
In time-range mode it:
- reads the selected start and end `QDateTime` values from the UI
- iterates available remote record segments
- compares the requested interval against each segment
- computes overlap windows
- schedules one or more download requests accordingly
The function logs several debug markers:
- `DownloadByTime 111111111111111111`
- `DownloadByTime 222222222222222222`
- `DownloadByTime 33333333333333333333`
- `DownloadByTime 444444444444444444444`
Those correspond to different interval-overlap cases between the selected range and a returned recording segment.
### `PlaybackFileDownloadWidget_downloadByTime`
This function handles the actual by-time iteration across remote record segments.
It reconstructs begin/end timestamps from record metadata, converts them to `QDateTime`, compares them against the user-selected range, then dispatches a lower-level download request for the relevant slice.
The logic strongly suggests the client does not ask the device for an arbitrary time range in one step unless the selected window aligns with a single record segment. Instead, it walks matching segments and issues one or more segment-bounded download requests.
Implication for a standalone tool:
- arbitrary time-range download likely requires:
- querying the device for record segments first
- intersecting those with the requested interval
- issuing one or more download requests
- stitching or sequencing the resulting data if needed
## EXE-Side RSNET Wrapper Layer
The EXE contains thin wrappers around the `RSNET.DLL` APIs. These wrappers are useful because they reveal request structure shapes and callback wiring even though the real protocol lives in the DLL.
### Connection wrapper
`FUN_006eaf70`
Behavior:
- copies fields from a caller-supplied struct into a local 0x38-byte request block
- stores two callback/context values into the owning object
- calls `RSNetConnectionStartEx(&local_38)`
Interpretation:
- connection/login is performed through `RSNetConnectionStartEx`
- the caller passes a structured login/connect request, not loose scalar parameters
- the request likely includes address or P2P information, credentials, and callback context
### Remote record query wrapper
`FUN_006eb6f0`
Behavior:
- allocates a small 8-byte helper object from fields `param_2[0x12]` and `param_2[0x11]`
- copies roughly 0x60 bytes of request data into a local buffer
- assigns completion callbacks
- calls `RSNetQueryRecordEx(handle, &local_64)`
Interpretation:
- remote record search is performed with `RSNetQueryRecordEx`
- the query request is non-trivial and includes multiple fields beyond just channel and time
- two tail fields are treated specially and likely represent callback target/context or an auxiliary selection object
### Remote playback wrapper
`FUN_006e9e00`
Behavior:
- creates an `RSPlay` instance via `RSPlayCreatePlayInstance(1)`
- optionally binds the playback window handle via `RSPlaySetPlayWnd`
- starts local rendering via `RSPlayStartPlay`
- registers callback handlers with `RSPlaySetCallbackMessageEx` and `RSPlayImageCallback`
- fills a request block from the provided playback parameters
- calls `RSNetStartRecordPlayEx(connectionHandle, &local_40)`
Interpretation:
- remote recorded playback is a network operation through `RSNET.DLL`
- incoming data is then fed into an `RSPLAY.DLL` decode/render pipeline
- the standalone downloader probably does not need the `RSPLAY` path unless raw playback streaming is required instead of file download
### Download wrappers
`FUN_006e9810`
Behavior:
- allocates a 12-byte state object
- copies a compact request from the caller
- sets a callback function `FUN_006e8690`
- calls `RSNetStartDownloadRecord(handle, &local_14)`
`FUN_006e98b0`
Behavior:
- allocates a 12-byte state object
- copies a slightly larger request including an extra byte field
- sets callback `FUN_006e8730`
- calls `RSNetStartDownloadRecordEx(handle, &local_20)`
`FUN_006e9980`
Behavior:
- calls `RSNetStopDownloadRecord(downloadHandle)`
- frees the associated state object
Interpretation:
- there are at least two remote download entrypoints: base and extended
- the extended form includes one extra byte field that may be stream type, record type, or a mode selector
- both return or populate a download handle that is later passed to `RSNetStopDownloadRecord`
### Record data callback path
`FUN_006e85b0`
Behavior:
- if callback state is valid and `param_1 == 1`, it calls `RSNetReqRecordData(*param_2)`
- otherwise it posts a Qt event with type `0x3ed` to a target object
Interpretation:
- remote playback/download is at least partly callback-driven
- one callback state value appears to be a target `QObject`
- `RSNetReqRecordData` is likely a pull/continue/read-next mechanism for record data transfer
### Record playback control
`RecordPlaybackControl_toggleMode`
Behavior:
- toggles internal playback state based on UI mode values `0x10` through `0x13`
- logs `QRSAdapterReqRecordPlayCtrol`
- calls a helper that eventually dispatches `RSNetReqRecordPlayCtrol`
Interpretation:
- pause/resume/seek/speed or similar playback controls go through `RSNetReqRecordPlayCtrol`
- this is playback control, not initial search or initial download setup
## Current Reconstructed Download Pipeline
From the EXE alone, the likely pipeline is:
1. Establish device/session connection through `RSNetConnectionStartEx`
2. Query available recordings with `RSNetQueryRecordEx`
3. In the UI, present file/segment rows to the user
4. For time-based download, intersect the requested interval against returned record segments
5. For each needed segment or sub-range, call either `RSNetStartDownloadRecord` or `RSNetStartDownloadRecordEx`
6. Receive progress/data/completion via callback functions and Qt events
7. Stop transfer with `RSNetStopDownloadRecord` when complete or canceled
## What This Means For A Standalone Downloader
At a high level, the minimum downloader architecture is likely:
1. Login/connect request builder
2. Record query request builder
3. Segment intersection logic for arbitrary user time ranges
4. Download request builder
5. Callback/event loop or polling layer for transfer progress and completion
The most important unresolved details are all inside `RSNet.dll`:
- exact login request structure
- whether connection uses direct IP, media port, P2P ID, or multiple modes under one API
- exact query-record structure fields
- exact download request structure fields
- callback message formats and result codes
- file format or chunk framing of downloaded data
- authentication handshake and any encryption or session-key behavior
## What Is Known Versus Unknown
### Known from EXE analysis
- The EXE does not appear to implement the wire protocol itself.
- `RSNET.DLL` is the primary networking and remote-record module.
- `RSPLAY.DLL` handles playback/render support.
- The UI supports both file-based and time-based record download.
- Arbitrary time-range download is implemented as interval intersection over returned recording segments.
- The download path uses `RSNetStartDownloadRecord` or `RSNetStartDownloadRecordEx`.
- Record search uses `RSNetQueryRecordEx`.
- Record playback uses `RSNetStartRecordPlayEx` and `RSNetReqRecordPlayCtrol`.
### Unknown until `RSNet.dll` is analyzed
- packet layout and transport protocol
- exact authentication mechanism
- exact request structure fields
- whether the client speaks directly to the device, uses P2P relay logic, or switches transport modes internally
- how downloaded bytes are framed and persisted to disk
## Recommended Next Step
Load `RSNet.dll` into Ghidra next.
That is the correct next target if the goal is to build an independent downloader rather than automate the existing EXE.
After `RSNet.dll` is loaded, the next analysis pass should focus on:
- `RSNetConnectionStartEx`
- `RSNetQueryRecordEx`
- `RSNetStartDownloadRecord`
- `RSNetStartDownloadRecordEx`
- `RSNetReqRecordData`
- `RSNetStopDownloadRecord`
- any credential, P2P, relay, encryption, or session-management helpers they call
## Credentials And Live Validation
Credentials are not required yet for the current static pass.
They will become useful later for one of these reasons:
- validating a reconstructed downloader against a real target
- comparing dynamic behavior with captured traffic
- understanding whether connection mode changes based on device type, P2P ID, or network reachability
If live testing becomes necessary, credentials should be stored in an untracked local file such as a new `.gitignore`d notes/config file inside the repository or workspace.
## Analysis State Notes
- Earlier in the session, a few function definitions were accidentally deleted by using the wrong MCP endpoint on the writable program. Those functions were restored immediately.
- A repository instruction file now explicitly forbids destructive Ghidra actions during analysis unless the user explicitly requests them.
- Current repository instruction file: `.github/copilot-instructions.md`