surveillance-client/docs/surveillance-client-rsnet-download-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

29 KiB

RSNet Download Analysis

Date: 2026-04-17

Target binary: RSNet.dll

Related binaries:

  • Surveillance_client.exe
  • RSPlay.dll

Analysis method: Ghidra MCP against the loaded RSNet.dll, using read-only inspection plus function renames/comments only.

Objective

Determine enough of the RSNet.dll API and behavior to build a simple script that can:

  1. initialize the DLL
  2. connect/login to a device
  3. query recorded segments for a camera
  4. request download of a chosen interval or file segment
  5. wait for completion and retrieve the saved file

Bottom Line

At this point, the DLL has been analyzed far enough to outline a realistic first-pass downloader script that calls RSNet.dll directly with ctypes or similar FFI.

What is established:

  • RSNet.dll owns the real connection, login, record-query, playback, and record-download logic.
  • RSNet.dll performs its own socket I/O and background worker management.
  • the EXE is mainly a UI and adapter layer that fills structs and hands them to RSNet.dll
  • the download worker inside RSNet.dll writes data locally through an internal record-file layer rather than requiring the caller to manually process frame packets
  • a simple standalone script is plausible without reversing the entire wire protocol, because the DLL already encapsulates the transport

What is not fully nailed down yet:

  • exact C struct definitions for all exported APIs
  • the full callback signature set and all event codes
  • exact output container selection logic between AVI, MP4, or the vendor private format

Even with those gaps, there is now enough to define the likely call sequence and the minimum reverse-engineering targets for a first prototype.

Exported API Surface Relevant To Downloading

Confirmed exports in RSNet.dll:

  • RSNetInit at 0x1001ae30
  • RSNetSetEncription at 0x1001b570
  • RSNetConnectionStart at 0x1001c350
  • RSNetConnectionStartEx at 0x1001c3a0
  • RSNetQueryRecord at 0x1001c640
  • RSNetAsyncQueryRecord at 0x1001c660
  • RSNetQueryRecordEx at 0x1001c680
  • RSNetAsyncQueryRecordEx at 0x1001c6a0
  • RSNetAsyncQueryRecordStop at 0x1001c6c0
  • RSNetStartDownloadRecord at 0x1001c780
  • RSNetStartDownloadRecordEx at 0x1001c810
  • RSNetStartRecordPlay at 0x1001c870
  • RSNetStartRecordPlayEx at 0x1001c9a0
  • RSNetReqRecordData at 0x1001ca00
  • RSNetReqRecordPlayCtrol at 0x1001ca40
  • RSNetRelease at 0x1001b4e0

Interpretation:

  • a standalone downloader should use the DLL exports directly
  • there is no need to reproduce the underlying socket protocol if the goal is just to automate download on Windows where the vendor DLL is available

Initialization

RSNetInit

RSNetInit:

  • initializes logging from RSNet.ini
  • calls WSAStartup(0x202, ...)
  • prepares global DLL state

Practical implication:

  • any standalone script must call RSNetInit() before any connection or query function

RSNetSetEncription

RSNetSetEncription(value) simply stores a global value.

Practical implication:

  • there is some configurable encryption mode or toggle
  • it may need to be set explicitly for some device families, but this has not yet been proven necessary for the basic flow

Connection / Login

Public entry points

  • RSNetConnectionStart
  • RSNetConnectionStartEx

Both functions:

  • allocate a per-device/session object of size 0xe98
  • initialize internal locks, events, strings, and worker state
  • copy caller-provided login parameters into the session object
  • queue a background login/connection worker
  • return a session handle pointer on success, or NULL on failure

RSNetConnectionStart

This is the simpler path and likely the better first target for a prototype.

From decompilation, the input struct used by RSNetConnectionStart appears to have this rough layout:

struct RSNET_CONNECT_REQ_BASE {
    char *address_or_id;   // offset 0x00
    uint16_t port;         // offset 0x04
    char *username;        // offset 0x08
    char *password;        // offset 0x0c
    uint32_t field10;      // offset 0x10
    uint32_t field14;      // offset 0x14
    uint32_t field18;      // offset 0x18
    uint32_t field1c;      // offset 0x1c
    uint32_t field20;      // offset 0x20
    uint32_t field24;      // offset 0x24
    uint32_t field28;      // offset 0x28
};

The DLL copies these into its internal session object as:

  • destination string at session +0x04
  • port at session +0x20
  • username at session +0x28
  • password at session +0x44
  • several mode or callback related fields at +0x68 .. +0x80

RSNetConnectionStartEx

The extended path supports more than direct IP login. Decompiled behavior shows:

  • a connection mode field at request index 0x0b
  • if that mode is zero, the DLL treats the first string as the device address directly
  • if non-zero, it stores the first string in an alternate field and forces the target address to 127.0.0.1
  • when mode is 1 or 10, it copies an additional string from request index 0x0c

Interpretation:

  • RSNetConnectionStartEx likely supports P2P / relay / localhost-tunneled connection modes
  • the simple downloader should avoid the Ex path initially unless the target system requires P2P rather than direct device access

Login worker behavior

The session worker at 0x100074b0:

  • attempts login / session setup
  • posts state changes through callback or message mechanisms
  • sends heartbeats every ~5 seconds after login
  • dispatches status notifications with integer codes

Observed status/event codes from the worker:

  • 2 on one failure path
  • 3 on disconnect/connection-lost path
  • 0x65
  • 0x79
  • 0x7a

Strings in the DLL map login-related status messages to names such as:

  • RSNetMsgLoginRequest
  • RSNetMsgLoginSuccess
  • RSNetMsgLoginUserLogined
  • RSNetMsgLoginNoUserName
  • RSNetMsgLoginPasswordError
  • RSNetMsgLoginNoRight
  • RSNetMsgOverMaxUser
  • RSNetMsgLoginUserDisable
  • RSNetMsgLoginFail

Practical implication:

  • a script should assume login is asynchronous
  • a callback or polling/wait mechanism is required before issuing record queries

Record Query

Public entry points

  • RSNetQueryRecord
  • RSNetQueryRecordEx

The simpler function is:

RSNetQueryRecord(session, query_req)

which routes into FUN_10005ca0.

The extended function is:

RSNetQueryRecordEx(session, query_req)

which routes into FUN_10005eb0.

Behavior

Both paths:

  • build a request message with message subtype 0x6f
  • send it through the session object
  • wait up to 20 seconds for a response
  • decode the returned payload into caller-facing record structures through callback helpers

The record-query dispatcher distinguishes at least two output styles using the first field of the query struct:

  • 0x65
  • 0x66

Those two cases use different callbacks:

  • FUN_100046b0
  • FUN_10004750

FUN_100046b0 iterates a result array in units of 0x1c bytes.

FUN_10004750 iterates a result array in units of 0x0c bytes.

Interpretation:

  • one query result format is likely the full record segment list (0x1c bytes each)
  • the other is likely a compact per-day/per-file summary format (0x0c bytes each)

For a downloader, the 0x1c record entries are the important one because the EXE uses 0x1c-byte records when building playback and download requests.

Likely record entry format

The download worker consumes entries of size 0x1c and treats them like timestamped record descriptors.

From earlier EXE analysis and DLL behavior, each 0x1c entry likely contains:

  • start date/time fields
  • end date/time fields
  • channel or stream selection data
  • a record type flag or subtype

This matches how the EXE performed time-range intersection over returned recording segments.

Download

Public entry points

  • RSNetStartDownloadRecord
  • RSNetStartDownloadRecordEx

These are the core APIs for a standalone downloader.

RSNetStartDownloadRecord

This is the simpler form.

The EXE-side wrapper showed this request shape:

struct RSNET_DOWNLOAD_REQ_BASE {
    uint32_t field00;
    uint32_t field04;
    uint32_t field08;
    uint32_t callback_or_ctx;
    uint32_t callback_user;
};

Inside RSNet.dll, FUN_10010330 adapts this into the extended worker request by supplying defaults:

  • count or mode fields are normalized
  • an extra byte field is forced to zero
  • a local_14 = 1 flag is inserted before forwarding to the common worker builder

Interpretation:

  • the base function is just a convenience wrapper around the extended worker
  • for a script, RSNetStartDownloadRecord is probably easier to prototype first

RSNetStartDownloadRecordEx

This is the common implementation target.

The decompiled request consumer FUN_100103b0 proves the worker expects at least:

  • two required non-zero fields at offsets +0x04 and +0x08
  • a count field at +0x0c
  • an extra mode byte at +0x10
  • a sequence of seven 32-bit values copied from offsets +0x00 .. +0x18 into the worker object

Then it:

  • allocates count * 0x1c bytes for one array
  • allocates count * 0x108 bytes for a second array
  • copies source arrays from worker request indices 0x19 and 0x1a
  • queues a background worker thread beginning at 0x100105b0

Interpretation:

  • the extended request supports batch download of one or more 0x1c record descriptors
  • each record also has an associated 0x108 byte metadata block, likely including the destination path or output-file information

This is a strong indication that a standalone downloader can submit one selected record entry at a time rather than needing a large complex batch.

Download Worker Behavior

The worker at 0x100105b0 is the core of remote file download.

Key verified behaviors:

  • waits on session/login readiness
  • opens a socket and configures timeouts
  • sends a request with message subtype:
    • 0x83 in one mode
    • 0x196 in another mode
  • copies one or more 0x1c record entries into the outgoing message
  • optionally handles an additional payload block depending on mode
  • receives a response header and payload
  • processes download status and progress messages
  • writes or routes the resulting data into an internal record-file path

Important status/message strings in the DLL:

  • RSNetMsgDownloadRecordClosed
  • RSNetMsgDownloadRecordStoreFail
  • RSNetMsgDownloadRecordNoFile
  • RSNetMsgDownloadRecordOK
  • RSNetMsgDownloadRecordPercent
  • RSNetMsgDownloadRecordFail

Observed message codes in the worker:

  • 0x132
  • 0x133
  • 0x142
  • 0x145

Interpretation:

  • the worker sends progress and completion back to the caller via callback/message dispatch
  • success/failure is not just the return value of RSNetStartDownloadRecord*; the returned handle only means the worker was started

Does The DLL Save The File Itself?

Yes, that appears to be the case.

Evidence:

  • the download worker is large and manages long-running transfer state
  • the DLL contains internal record-file classes:
    • .?AVCBaseRecordFile@@
    • .?AVCRSAVIRecordFile@@
    • .?AVCRSMP4RecordFile@@
    • .?AVCRSPrivateRecordFile@@
  • the DLL also contains AVI-writing error strings
  • cleanup paths include an object with a FILE * at offset 0x54 and a file/path buffer with small-string optimization behavior

Practical implication:

  • the script most likely does not need to manually receive frame payloads and write them itself
  • instead, it should provide the request/path metadata that the DLL expects, start the download, and wait for the worker to finish writing the file

Callback And Handle Lifetime

Session handle

The session handle returned by RSNetConnectionStart*:

  • owns socket state
  • runs a worker thread
  • dispatches status via callback or window message

Important internal callback-related session fields:

  • callback window/message at offsets around +0x68 and +0x6c
  • callback function at +0x78
  • callback user/context at +0x70

Download handle

The download handle returned by RSNetStartDownloadRecord* is a heap object with:

  • session pointer at +0x20
  • event handle at +0x2c
  • worker state, copied record entries, and additional metadata

There is a tiny internal stop/cleanup wrapper around 0x1001ca81 that:

  • validates the handle
  • calls a stop routine
  • destroys the download object via FUN_1001cab0

So a full script should ideally also locate or call the exported stop-download function when cleanup is needed. The original EXE import table proves that such an export exists, even though the current symbol recovery in this DLL session did not label it cleanly.

Practical Downloader Strategy

The simplest viable approach is not to reverse the raw network protocol. It is to call the vendor DLL directly.

C# Interop Feasibility

Yes. C# can load RSNet.dll as an unmanaged native library and call its exports directly.

The DLL is a good fit for P/Invoke or NativeLibrary.GetExport-based interop because the key exports are ordinary x86 functions, not COM interfaces or C++ object methods.

ABI details confirmed from RSNet.dll

  • architecture: x86/little/32
  • pointer size: 4
  • compiler family: Visual Studio
  • export names are undecorated in the import table used by the EXE
  • the relevant exports use callee stack cleanup, which matches StdCall

Observed export epilogues:

  • RSNetInit -> RET
  • RSNetRelease -> RET
  • RSNetConnectionStart -> RET 0x4
  • RSNetConnectionStartEx -> RET 0x4
  • RSNetQueryRecord -> RET 0x8
  • RSNetQueryRecordEx -> RET 0x8
  • RSNetStartDownloadRecord -> RET 0x8
  • RSNetStartDownloadRecordEx -> RET 0x8
  • RSNetReqRecordData -> RET 0x4

Practical implication:

  • the first C# prototype should treat the exported functions as CallingConvention.StdCall
  • the process must be 32-bit

Runtime constraints for a C# host process

The managed host should be built and run as:

  • x86, not AnyCPU
  • Windows only
  • from the vendor application directory, or with that directory added to the DLL search path

The safest deployment model is:

  • place the C# test app in the same directory as RSNet.dll
  • or launch it with the current directory set to i:/Apps/Surveillance_client
  • or call SetDllDirectory / AddDllDirectory before loading RSNet.dll

Why that matters:

  • RSNet.dll depends on the VC++ runtime and the rest of the vendor installation layout
  • it also expects RSNet.ini nearby for logging/config initialization
  • strings inside the DLL reference RSP2PClient.exe and RSP2PDaemon.exe, so P2P mode likely depends on those companion files being present

For direct-IP downloading, the minimum runtime context is likely smaller than the full client install, but the easiest first run is still to execute from the installed client folder.

There are two realistic options.

Option 1: Static DllImport

Use this once the calling convention and signatures are stable enough.

Advantages:

  • simplest code
  • easiest to read
  • easiest to debug once structs are correct

Disadvantages:

  • more brittle while struct layouts are still being discovered

Option 2: NativeLibrary.Load plus delegates

Use this during the reverse-engineering phase.

Advantages:

  • lets you experiment with signatures more safely
  • avoids hard failure at process load if a dependency is missing
  • makes it easy to log export resolution one function at a time

Disadvantages:

  • more boilerplate

For the current stage, Option 2 is the better choice.

C# Interop Types To Use

Handles

Treat these as opaque IntPtr values:

  • session handle returned by RSNetConnectionStart*
  • download handle returned by RSNetStartDownloadRecord*

Do not model them as managed classes or attempt to dereference them directly in C#.

Strings

Current evidence suggests the DLL uses ANSI char * strings, not UTF-16.

In C#:

  • use CharSet.Ansi
  • marshal strings as LPStr
  • for uncertain layouts, prefer IntPtr plus Marshal.StringToHGlobalAnsi

Struct packing

Use:

  • StructLayout(LayoutKind.Sequential, Pack = 4)

Reason:

  • this is a 32-bit Visual Studio binary using 4-byte pointers and ordinary 32-bit alignment

Callbacks

Use delegates marked with:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]

And keep the delegate alive for the entire native operation lifetime.

Practical rule:

  • store callback delegates in fields, not local variables

Otherwise the GC may collect them while the native worker thread still holds the callback pointer.

First-Pass C# API Shape

Based on the confirmed export behavior, a first interop layer can be modeled like this:

internal static class RsNetNative
{
  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
  internal static extern void RSNetInit();

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern void RSNetRelease();

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern void RSNetSetEncription(uint value);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern IntPtr RSNetConnectionStart(ref RsNetConnectRequest request);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern IntPtr RSNetConnectionStartEx(IntPtr request);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern int RSNetQueryRecord(IntPtr session, IntPtr queryRequest);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern int RSNetQueryRecordEx(IntPtr session, IntPtr queryRequest);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern IntPtr RSNetStartDownloadRecord(IntPtr session, IntPtr downloadRequest);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern IntPtr RSNetStartDownloadRecordEx(IntPtr session, IntPtr downloadRequest);

  [DllImport("RSNet.dll", CallingConvention = CallingConvention.StdCall)]
  internal static extern void RSNetReqRecordData(IntPtr recordPlayHandle);
}

Notes:

  • RSNetConnectionStart is the first function worth strongly typing
  • RSNetConnectionStartEx, RSNetQueryRecord*, and RSNetStartDownloadRecord* should initially stay as IntPtr request pointers until the layouts are fully proven
  • this reduces the chance of silent marshaling mistakes during the first prototype

First Strongly-Typed C# Struct Worth Trying

The current best candidate for a partial typed struct is the direct-IP login request used by RSNetConnectionStart.

Best current guess:

[StructLayout(LayoutKind.Sequential, Pack = 4)]
internal struct RsNetConnectRequest
{
  public IntPtr AddressAnsi;
  public ushort Port;
  public ushort Padding0;
  public IntPtr UsernameAnsi;
  public IntPtr PasswordAnsi;
  public uint Field10;
  public uint Field14;
  public uint Field18;
  public uint Field1C;
  public uint Field20;
  public uint Field24;
  public uint Field28;
}

Why this is reasonable:

  • the decompiled function copies a pointer at offset 0x00
  • a 16-bit port value is read from offset 0x04
  • string pointers are copied from offsets 0x08 and 0x0c
  • the remaining fields are treated as 32-bit values or callback-related fields

This struct should still be treated as provisional.

Likely C# Callback Shape

The session object stores either:

  • a callback window handle and message ID
  • or a callback function pointer plus a user/context value

The worker invokes the function callback in patterns like:

  • callback(code, userContext)
  • or posts a window message with PostMessageA(hwnd, msgId, code, userContext)

Best current guess for the login/status callback delegate:

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
internal delegate void RsNetStatusCallback(uint code, IntPtr userContext);

This is not fully proven, but it matches the currently observed call sites better than any multi-parameter alternative.

Why C# Is A Better Fit Than Raw Packet Reimplementation

For the current objective, C# plus unmanaged interop is likely better than reimplementing the network protocol because:

  • the vendor DLL already knows the login handshake
  • the vendor DLL already knows P2P versus direct-IP behavior
  • the vendor DLL already manages record-query formatting
  • the vendor DLL already performs the actual download worker loop
  • the vendor DLL appears to write the file itself

That cuts the problem down to:

  • loading the DLL safely
  • building the request structs correctly
  • handling callbacks/events
  • waiting for the output file to appear and complete

Layer 1: Native bootstrap

Responsibilities:

  • locate the vendor install directory
  • set DLL search path
  • call RSNetInit
  • optionally call RSNetSetEncription

Layer 2: Session wrapper

Responsibilities:

  • allocate unmanaged ANSI strings for device address, username, password
  • build the login request
  • call RSNetConnectionStart
  • keep the callback delegate alive
  • expose a Task or ManualResetEventSlim for login completion

Layer 3: Record query wrapper

Responsibilities:

  • build an unmanaged query struct
  • invoke RSNetQueryRecord
  • collect returned 0x1c entries into managed record models
  • expose them to higher-level range selection code

Layer 4: Download wrapper

Responsibilities:

  • choose the target record entry or entries
  • build the native download request and per-item metadata
  • call RSNetStartDownloadRecord
  • monitor status/progress callbacks
  • resolve the final output file path

Layer 5: Cleanup

Responsibilities:

  • call the native stop/cleanup entrypoints when known
  • free all unmanaged strings and unmanaged buffers
  • call RSNetRelease on process shutdown

What a minimal C# prototype should look like

The first prototype should not attempt the whole workflow at once.

Do it in stages:

  1. Load RSNet.dll successfully from C# in an x86 process.
  2. Call RSNetInit() and verify it does not crash.
  3. Call RSNetConnectionStart(...) with a direct-IP request and log callback codes.
  4. Confirm which callback code means login success.
  5. Add RSNetQueryRecord(...) and dump raw 0x1c entries.
  6. Only then attempt RSNetStartDownloadRecord(...).

That staged approach is important because the unstable part is the native struct layout, not the export resolution.

Risks Specific To C# Interop

The main failure modes are:

  • running as AnyCPU or x64 and failing to load the 32-bit DLL
  • incorrect calling convention
  • incorrect struct packing
  • wrong ANSI versus Unicode marshaling
  • GC moving or collecting delegates or unmanaged buffers too early
  • freeing unmanaged strings before the async worker is finished using them
  • assuming login is synchronous when it is not

Operational rule:

  • keep every unmanaged buffer and every callback delegate alive until the native session or download handle has definitively finished

Implementation Checklist For A C# Version

Before coding:

  • confirm the C# host runs as x86
  • run from the vendor install folder or configure DLL search paths explicitly
  • keep a copy of the vendor folder structure intact for first tests

During interop setup:

  • use StdCall
  • use CharSet.Ansi
  • use Pack = 4
  • represent unknown native structs as IntPtr first
  • keep callback delegates pinned by strong references

During login:

  • prefer RSNetConnectionStart over RSNetConnectionStartEx
  • use direct IP and media port first
  • do not assume immediate success after the function returns a non-null session handle

During record query:

  • expect asynchronous or callback-driven result delivery
  • log raw bytes for each 0x1c record entry before trying to reinterpret every field semantically

During download:

  • start with one record entry, not a batch
  • expect completion via status event rather than via the return value alone
  • inspect the output directory for the file the DLL creates

What Still Needs To Be Recovered For A Clean C# Implementation

The unresolved items are the same as for Python FFI, but C# can tolerate them if opaque pointers are used first.

The most important remaining tasks are:

  1. exact RSNetConnectionStart request struct validation
  2. exact query-request struct layout
  3. exact query callback prototype
  4. exact download-request struct layout
  5. exact per-item 0x108 metadata layout
  6. exact stop-download export signature
  7. exact mapping from numeric event codes to login/query/download states

Revised Assessment

Yes, a C# implementation is realistic.

It is probably one of the better options if the end goal is a Windows-only utility that uses the vendor binary as-is.

The current blocker is not can C# do it? but how much of the request structs do we want to strongly type before writing the prototype?

At this point, a cautious C# prototype is feasible if it:

  • uses x86
  • calls the DLL with StdCall
  • uses direct IP login first
  • treats most request blobs as unmanaged buffers until the layouts are fully validated
  1. Load RSNet.dll with ctypes.WinDLL
  2. Call RSNetInit()
  3. Optionally call RSNetSetEncription(...) if required by the target device family
  4. Build a direct-IP login struct for RSNetConnectionStart
  5. Wait until the async login callback reports success
  6. Build a record-query request for RSNetQueryRecord
  7. Collect returned 0x1c record entries
  8. Pick the entries intersecting the user-requested time range
  9. Build a single-entry download request and call RSNetStartDownloadRecord
  10. Wait for RSNetMsgDownloadRecordOK or a terminal failure event
  11. Locate the output file written by the DLL
  12. Release/cleanup handles

Why direct-IP first

The Ex login path clearly has extra logic for P2P or relay modes.

To keep the prototype small:

  • use direct device IP / media port first
  • defer P2P-only targets until after the basic downloader works

Best Current Guess At Minimal Script Design

Language

Python with ctypes is the fastest path.

C# is now a credible alternative for a Windows-only implementation that loads the vendor DLL directly.

Components

  1. DLL loader and function prototypes
  2. callback function declared with ctypes.WINFUNCTYPE
  3. login request struct for RSNetConnectionStart
  4. query request struct for RSNetQueryRecord
  5. query result callback handling 0x1c entries
  6. download request struct for RSNetStartDownloadRecord
  7. event loop or polling wait for download completion

Important caveat

The exact ctypes.Structure layouts are not fully proven yet.

So the first implementation should be treated as an iterative prototype, not a guaranteed drop-in script.

Still, the reverse engineering so far is enough to support the following plan:

  • prototype direct login with RSNetConnectionStart
  • verify callback codes for login success and failure
  • prototype RSNetQueryRecord with a callback that logs each 0x1c result entry
  • once one real query works, use a single returned record entry as input to RSNetStartDownloadRecord

That is now a tractable task.

Gaps That Still Matter Before Writing The Actual Script

These are the highest-value unresolved items:

  1. Exact layout of the RSNetConnectionStart input struct
  2. Exact layout of the RSNetQueryRecord request struct and callback prototype
  3. Exact layout of the RSNetStartDownloadRecord request struct, especially the destination-file metadata
  4. Exact numeric callback codes corresponding to:
    • login success
    • login failure types
    • query complete
    • download progress
    • download success
    • download failure
  5. Exact output file naming and format-selection rules

The next pass should focus on making the prototype script concrete:

  1. Rename key RSNet.dll internals:

    • FUN_100050a0
    • FUN_100051f0
    • FUN_10005ca0
    • FUN_10005eb0
    • FUN_10010330
    • FUN_100103b0
    • FUN_100105b0
    • FUN_100074b0
  2. Recover the exact login callback prototype from the session object fields around +0x68, +0x6c, +0x70, and +0x78

  3. Recover the exact query callback prototype used by result handlers FUN_100046b0 and FUN_10004750

  4. Identify the structure at size 0x108 associated with each download item

  5. Identify where the output file path is copied into the download-item metadata

  6. Validate the call sequence against a real device using credentials

Current Assessment

The original objective was to analyze until it became possible to figure out how to make a simple script that downloads a video file.

That threshold has been reached in architectural terms:

  • use RSNet.dll directly
  • direct-IP login first
  • query recordings first
  • submit one record entry into the download API
  • let the DLL write the file locally
  • monitor callback/status events for completion

The remaining work is no longer “what is the path?” but “what are the exact struct layouts?”

That is a much narrower problem and should be solvable in a follow-up pass, especially with live credentials or one round of prototype-and-adjust testing.