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

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.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

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 .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