Dies ist eine Proof-of-Concept-Implementierung von RTSP über das Dahua P2P-Protokoll. Es funktioniert mit Dahua und abgeleiteten Kameras/NVRs.
Das Dahua P2P-Protokoll wird für den Fernzugriff auf Dahua-Geräte verwendet. Es wird häufig von Dahua-Apps wie gDMSS Lite auf Android oder SmartPSS, KBiVMS unter Windows verwendet.
In meinem speziellen Szenario habe ich ein KBVision CCTV-System. Obwohl ich über den KBiVMS-Client auf die Kameras zugreifen kann, verwende ich hauptsächlich Nicht-Windows-Plattformen. Daher wollte ich alternative Möglichkeiten zum Streamen des Videos mithilfe eines RTSP-Clients erkunden, der allgemeiner unterstützt wird. Aus diesem Grund beschloss ich, mit der Neuimplementierung des Dahua P2P-Protokolls zu experimentieren.
src/*.rs
– Rust-QuelldateienCargo.toml
– Rust-Abhängigkeitenmain.py
– Hauptskripthelpers.py
– Hilfsfunktionenrequirements.txt
– Python-Abhängigkeitendh-p2p.lua
– Wireshark-Dissektor für das Dahua P2P-Protokoll Die Rust-Implementierung nutzt asynchrone Programmierung und Nachrichtenübermittlungsmuster, was sie effizienter und flexibler macht.
A PoC implementation of TCP tunneling over Dahua P2P protocol.
Usage: dh-p2p [OPTIONS] <SERIAL>
Arguments:
<SERIAL> Serial number of the camera
Options:
-p, --port <[bind_address:]port:remote_port>
Bind address, port and remote port. Default: 127.0.0.1:1554:554
-h, --help
Print help
Die Python-Implementierung von DH-P2P ist ein einfacher und unkomplizierter Ansatz. Aufgrund seiner schnellen und einfachen Schreibweise wird es für Entwurfs- und Testzwecke verwendet. Darüber hinaus ist die Implementierung linearer und folgt einem Top-Down-Ausführungsablauf, was das Verständnis erleichtert. Da Python eine beliebte Programmiersprache ist, trägt es zusätzlich zu seiner Zugänglichkeit und Vertrautheit bei Entwicklern bei.
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Run
python main.py [CAMERA_SERIAL]
# Stream (e.g. with ffplay) rtsp://[username]:[password]@127.0.0.1/cam/realmonitor?channel=1&subtype=0
ffplay -rtsp_transport tcp -i " rtsp://[username]:[password]@127.0.0.1/cam/realmonitor?channel=1&subtype=0 "
Um das Skript mit einem Gerät zu verwenden, das beim Erstellen eines Kanals eine Authentifizierung erfordert, verwenden Sie die Option -t 1
.
Bei der Ausführung im --debug
Modus oder wenn --type
> 0 ist, sind die Argumente USERNAME
und PASSWORD
obligatorisch. Stellen Sie außerdem sicher, dass sich ffplay
im Systempfad befindet, wenn der Debug-Modus aktiviert ist.
usage: main.py [-h] [-u USERNAME] [-p PASSWORD] [-d] serial
positional arguments:
serial Serial number of the camera
options:
-h, --help show this help message and exit
-d, --debug Enable debug mode
-t TYPE, --type TYPE Type of the camera
-u USERNAME, --username USERNAME
Username of the camera
-p PASSWORD, --password PASSWORD
Password of the camer
ffplay
und -rtsp_transport tcp
Für das Reverse Engineering des Protokolls habe ich Wireshark und KBiVMS V2.02.0 als Client unter Windows verwendet. Mit dem Dissektor dh-p2p.lua
können Sie das Protokoll in Wireshark einfacher sehen.
Für den RTSP-Client kann zur einfacheren Steuerung der Signale entweder VLC oder ffplay verwendet werden.
Grafik LR
App[[Dieses Skript]]
Dienst[Easy4IPCloud]
Gerät[Kamera/NVR]
App -- 1 --> Service
Service -- 2 --> Gerät
App <-. 3 .-> Gerät
Das Dahua P2P-Protokoll wird mit einem P2P-Handshake initiiert. Bei diesem Vorgang wird das Gerät anhand seiner Seriennummer (SN) über einen Drittanbieterdienst, Easy4IPCloud, lokalisiert:
Grafik LR
Gerät[Kamera/NVR]
App[[Dieses Skript]]
Client1[RTSP-Client 1]
Client2[RTSP-Client 2]
Clientn[RTSP-Client n]
Client1 – TCP –> App
Client2 – TCP –> App
Clientn -- TCP --> App
App <-. UDPnPTCP-Protokoll .-> Gerät
Nach dem P2P-Handshake beginnt das Skript, auf Port 554 auf RTSP-Verbindungen zu warten. Bei der Verbindung eines Clients initiiert das Skript einen neuen Bereich innerhalb des PTCP-Protokolls. Im Wesentlichen dient dieses Skript als Tunnel zwischen dem Client und dem Gerät und erleichtert die Kommunikation durch PTCP-Kapselung.
Sequenzdiagramm
Teilnehmer A als Dieses Skript
Teilnehmer B als Easy4IPCloud
Teilnehmer C1 als P2P-Server
Teilnehmer C2 als Relay Server
Teilnehmer C3 als Agent Server
Teilnehmer D als Kamera/NVR
A->>B: /probe/p2psrv
B-->>A: ;
A->>B: /online/p2psrv/{SN}
B-->>A: p2psrv-Info
A->>C1: /probe/device/{SN}
C1-->>A: ;
A->>B: /online/relay
B-->>A: Relais-Info
A->>B: /device/{SN}/p2p-channel (*)
Par
A->>C2: /relay/agent
C2-->>A: Agenteninformationen + Token
A->>C3: /relay/start/{token}
C3-->>A: ;
Ende
B-->>A: Geräteinformationen
A->>B: /device/{SN}/relay-channel + Agenteninformationen
C3-->>A: Server Nat Info!
A->>C3: PTCP SYN
A->>C3: PTCP-Anforderungszeichen
C3-->>A: PTCP-Zeichen
A->>D: PTCP-Handshake (*)
Hinweis : Beide mit (*)
markierten Verbindungen und alle nachfolgenden Verbindungen zum Gerät müssen denselben lokalen UDP-Port verwenden.
PTCP (PhonyTCP) ist ein proprietäres Protokoll, das von Dahua entwickelt wurde. Es dient der Kapselung von TCP-Paketen in UDP-Paketen und ermöglicht so die Erstellung eines Tunnels zwischen einem Client und einem Gerät hinter einem NAT.
Bitte beachten Sie, dass keine offizielle Dokumentation für PTCP verfügbar ist. Die hier bereitgestellten Informationen basieren auf Reverse Engineering.
Der PTCP-Paket-Header ist eine feste 24-Byte-Struktur, wie unten beschrieben:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| magic |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| sent |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| recv |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| lmid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rmid |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
magic
: Ein konstanter Wert, PTCP
.sent
und recv
: Verfolgen Sie die Anzahl der gesendeten bzw. empfangenen Bytes.pid
: Die Paket-ID.lmid
: Die lokale ID.rmid
: Die lokale ID des zuvor empfangenen Pakets.Die Größe des Paketkörpers variiert je nach Pakettyp (0, 4, 12 Byte oder mehr). Seine Struktur ist wie folgt:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| type | len |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| realm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
type
: Gibt den Pakettyp an.len
: Die Länge des data
.realm
: Die Realm-ID der Verbindung.padding
: Füllbytes, immer auf 0 gesetzt.data
: Die Paketdaten.Pakettypen:
0x00
: SYN, der Körper ist immer 4 Bytes 0x00030100
.0x10
: TCP-Daten, wobei len
die Länge der TCP-Daten ist.0x11
: Bindungsportanfrage.0x12
: Verbindungsstatus, wobei die Daten entweder CONN
oder DISC
sind.realm
auf 0 gesetzt ist):0x13
: Heartbeat, wobei len
immer 0 ist.0x17
0x18
0x19
: Authentifizierung.0x1a
: Serverantwort nach 0x19
.0x1b
: Client-Antwort nach 0x1a
. Dieses Projekt wurde von folgenden Projekten und Personen inspiriert und beeinflusst: