- 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.
13 KiB
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.DLLexports. - 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.dllneeds to be loaded into Ghidra next.
Installed Module Layout
The installed client directory contains the key vendor modules:
RSNet.dllRSPlay.dllQRSosAdapter.dllQtNetwork4.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:
RSNetQueryParamat0x006e96f0RSNetQueryParamExat0x006e9720RSNetAlarmSubscribeat0x006e99a0RSPlayGetPlayFileTimeRangeat0x006ea760
Example:
RSNetQueryParamis justJMP dword ptr [0x0076e648]
External symbol resolution in the loaded EXE confirms these libraries and exports:
RSNET.DLL
Relevant exports confirmed in the import table:
RSNetConnectionStartRSNetConnectionStartExRSNetQueryRecordExRSNetStartRecordPlayExRSNetReqRecordPlayCtrolRSNetStartDownloadRecordRSNetStartDownloadRecordExRSNetStopDownloadRecordRSNetReqRecordDataRSNetStopRecordPlayRSNetReplayRecordDataRSNetReposRecordDataRSNetGetDevInfoRSNetSearchDev
RSPLAY.DLL
Relevant playback exports confirmed in the import table:
RSPlayCreatePlayInstanceRSPlaySetPlayWndRSPlayStartPlayRSPlaySetCallbackMessageExRSPlayImageCallbackRSPlayOpenPlayFileRSPlayOpenPlayFileListExRSPlayGetPlayFileTimeRangeRSPlaySetCurPlayedTimeExRSPlayInputNetFrameRSPlayStartlocalrecordRSPlayStoplocalrecord
Interpretation:
RSNET.DLLis the module that matters for device login, remote record queries, remote playback, and remote download.RSPLAY.DLLis 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_PlaybackWidget_Playback_FileDownloadDownload By DateDownload By FilesDownLoad Start TimeMainStreamSubStreamplayback-downloadQRSAdapterReqRecordPlayCtrol
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_connectSignals0x0049f570->RecordPlaybackControl_toggleMode0x004afb30->PlaybackFileDownloadWidget_download0x004b1be0->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:
- File-based download mode
- 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
QDateTimevalues 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 111111111111111111DownloadByTime 222222222222222222DownloadByTime 33333333333333333333DownloadByTime 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]andparam_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
RSPlayinstance viaRSPlayCreatePlayInstance(1) - optionally binds the playback window handle via
RSPlaySetPlayWnd - starts local rendering via
RSPlayStartPlay - registers callback handlers with
RSPlaySetCallbackMessageExandRSPlayImageCallback - 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.DLLdecode/render pipeline - the standalone downloader probably does not need the
RSPLAYpath 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 callsRSNetReqRecordData(*param_2) - otherwise it posts a Qt event with type
0x3edto a target object
Interpretation:
- remote playback/download is at least partly callback-driven
- one callback state value appears to be a target
QObject RSNetReqRecordDatais 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
0x10through0x13 - 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:
- Establish device/session connection through
RSNetConnectionStartEx - Query available recordings with
RSNetQueryRecordEx - In the UI, present file/segment rows to the user
- For time-based download, intersect the requested interval against returned record segments
- For each needed segment or sub-range, call either
RSNetStartDownloadRecordorRSNetStartDownloadRecordEx - Receive progress/data/completion via callback functions and Qt events
- Stop transfer with
RSNetStopDownloadRecordwhen complete or canceled
What This Means For A Standalone Downloader
At a high level, the minimum downloader architecture is likely:
- Login/connect request builder
- Record query request builder
- Segment intersection logic for arbitrary user time ranges
- Download request builder
- 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.DLLis the primary networking and remote-record module.RSPLAY.DLLhandles 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
RSNetStartDownloadRecordorRSNetStartDownloadRecordEx. - Record search uses
RSNetQueryRecordEx. - Record playback uses
RSNetStartRecordPlayExandRSNetReqRecordPlayCtrol.
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:
RSNetConnectionStartExRSNetQueryRecordExRSNetStartDownloadRecordRSNetStartDownloadRecordExRSNetReqRecordDataRSNetStopDownloadRecord- 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 .gitignored 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