surveillance-client/docs/surveillance-client-dpi-analysis.md

389 lines
13 KiB
Markdown
Raw Normal View History

# Surveillance Client Server And Recorded Video Download Analysis
2026-03-25 16:41:55 +01:00
Date: 2026-04-17
2026-03-25 16:41:55 +01:00
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.
2026-03-25 16:41:55 +01:00
## 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.
2026-03-25 16:41:55 +01:00
## 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.
2026-03-25 16:41:55 +01:00
## Installed Module Layout
2026-03-25 16:41:55 +01:00
The installed client directory contains the key vendor modules:
2026-03-25 16:41:55 +01:00
- `RSNet.dll`
- `RSPlay.dll`
- `QRSosAdapter.dll`
- `QtNetwork4.dll`
2026-03-25 16:41:55 +01:00
This matches the import thunks present in the EXE.
2026-03-25 16:41:55 +01:00
## Confirmed DLL Boundary
2026-03-25 16:41:55 +01:00
The following named functions in the EXE are import thunks, not native implementations:
2026-03-25 16:41:55 +01:00
- `RSNetQueryParam` at `0x006e96f0`
- `RSNetQueryParamEx` at `0x006e9720`
- `RSNetAlarmSubscribe` at `0x006e99a0`
- `RSPlayGetPlayFileTimeRange` at `0x006ea760`
2026-03-25 16:41:55 +01:00
Example:
2026-03-25 16:41:55 +01:00
- `RSNetQueryParam` is just `JMP dword ptr [0x0076e648]`
2026-03-25 16:41:55 +01:00
External symbol resolution in the loaded EXE confirms these libraries and exports:
2026-03-25 16:41:55 +01:00
### `RSNET.DLL`
2026-03-25 16:41:55 +01:00
Relevant exports confirmed in the import table:
2026-03-25 16:41:55 +01:00
- `RSNetConnectionStart`
- `RSNetConnectionStartEx`
- `RSNetQueryRecordEx`
- `RSNetStartRecordPlayEx`
- `RSNetReqRecordPlayCtrol`
- `RSNetStartDownloadRecord`
- `RSNetStartDownloadRecordEx`
- `RSNetStopDownloadRecord`
- `RSNetReqRecordData`
- `RSNetStopRecordPlay`
- `RSNetReplayRecordData`
- `RSNetReposRecordData`
- `RSNetGetDevInfo`
- `RSNetSearchDev`
2026-03-25 16:41:55 +01:00
### `RSPLAY.DLL`
2026-03-25 16:41:55 +01:00
Relevant playback exports confirmed in the import table:
2026-03-25 16:41:55 +01:00
- `RSPlayCreatePlayInstance`
- `RSPlaySetPlayWnd`
- `RSPlayStartPlay`
- `RSPlaySetCallbackMessageEx`
- `RSPlayImageCallback`
- `RSPlayOpenPlayFile`
- `RSPlayOpenPlayFileListEx`
- `RSPlayGetPlayFileTimeRange`
- `RSPlaySetCurPlayedTimeEx`
- `RSPlayInputNetFrame`
- `RSPlayStartlocalrecord`
- `RSPlayStoplocalrecord`
2026-03-25 16:41:55 +01:00
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
2026-03-25 16:41:55 +01:00
The function logs several debug markers:
2026-03-25 16:41:55 +01:00
- `DownloadByTime 111111111111111111`
- `DownloadByTime 222222222222222222`
- `DownloadByTime 33333333333333333333`
- `DownloadByTime 444444444444444444444`
2026-03-25 16:41:55 +01:00
Those correspond to different interval-overlap cases between the selected range and a returned recording segment.
2026-03-25 16:41:55 +01:00
### `PlaybackFileDownloadWidget_downloadByTime`
2026-03-25 16:41:55 +01:00
This function handles the actual by-time iteration across remote record segments.
2026-03-25 16:41:55 +01:00
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.
2026-03-25 16:41:55 +01:00
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.
2026-03-25 16:41:55 +01:00
Implication for a standalone tool:
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
## EXE-Side RSNET Wrapper Layer
2026-03-25 16:41:55 +01:00
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)`
2026-03-25 16:41:55 +01:00
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
2026-03-25 16:41:55 +01:00
### Remote record query wrapper
2026-03-25 16:41:55 +01:00
`FUN_006eb6f0`
2026-03-25 16:41:55 +01:00
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:
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
### Remote playback wrapper
2026-03-25 16:41:55 +01:00
`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)`
2026-03-25 16:41:55 +01:00
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)`
2026-03-25 16:41:55 +01:00
`FUN_006e98b0`
2026-03-25 16:41:55 +01:00
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
2026-03-25 16:41:55 +01:00
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`
2026-03-25 16:41:55 +01:00
Behavior:
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
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:
2026-03-25 16:41:55 +01:00
- pause/resume/seek/speed or similar playback controls go through `RSNetReqRecordPlayCtrol`
- this is playback control, not initial search or initial download setup
2026-03-25 16:41:55 +01:00
## Current Reconstructed Download Pipeline
2026-03-25 16:41:55 +01:00
From the EXE alone, the likely pipeline is:
2026-03-25 16:41:55 +01:00
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
2026-03-25 16:41:55 +01:00
## What This Means For A Standalone Downloader
2026-03-25 16:41:55 +01:00
At a high level, the minimum downloader architecture is likely:
2026-03-25 16:41:55 +01:00
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
2026-03-25 16:41:55 +01:00
The most important unresolved details are all inside `RSNet.dll`:
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
## What Is Known Versus Unknown
2026-03-25 16:41:55 +01:00
### Known from EXE analysis
2026-03-25 16:41:55 +01:00
- 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`.
2026-03-25 16:41:55 +01:00
### Unknown until `RSNet.dll` is analyzed
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
## Recommended Next Step
2026-03-25 16:41:55 +01:00
Load `RSNet.dll` into Ghidra next.
2026-03-25 16:41:55 +01:00
That is the correct next target if the goal is to build an independent downloader rather than automate the existing EXE.
2026-03-25 16:41:55 +01:00
After `RSNet.dll` is loaded, the next analysis pass should focus on:
2026-03-25 16:41:55 +01:00
- `RSNetConnectionStartEx`
- `RSNetQueryRecordEx`
- `RSNetStartDownloadRecord`
- `RSNetStartDownloadRecordEx`
- `RSNetReqRecordData`
- `RSNetStopDownloadRecord`
- any credential, P2P, relay, encryption, or session-management helpers they call
2026-03-25 16:41:55 +01:00
## Credentials And Live Validation
2026-03-25 16:41:55 +01:00
Credentials are not required yet for the current static pass.
2026-03-25 16:41:55 +01:00
They will become useful later for one of these reasons:
2026-03-25 16:41:55 +01:00
- 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
2026-03-25 16:41:55 +01:00
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.
2026-03-25 16:41:55 +01:00
## Analysis State Notes
2026-03-25 16:41:55 +01:00
- 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`