- 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.
389 lines
No EOL
13 KiB
Markdown
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` |