Il s'agit d'une preuve de concept d'implémentation du protocole RTSP sur Dahua P2P. Il fonctionne avec Dahua et les caméras/NVR dérivés.
Le protocole Dahua P2P est utilisé pour l'accès à distance aux appareils Dahua. Il est couramment utilisé par les applications Dahua telles que gDMSS Lite sur Android ou SmartPSS, KBiVMS sous Windows.
Dans mon scénario spécifique, j'ai un système de vidéosurveillance KBVision. Bien que je puisse accéder aux caméras à l'aide du client KBiVMS, j'utilise principalement des plates-formes non Windows. Par conséquent, je voulais explorer des options alternatives pour diffuser la vidéo à l’aide d’un client RTSP, qui est plus largement pris en charge. En conséquence, j'ai décidé d'expérimenter la réimplémentation du protocole Dahua P2P.
src/*.rs
- Fichiers sources RustCargo.toml
- Dépendances Rustmain.py
- Script principalhelpers.py
- Fonctions d'assistancerequirements.txt
- Dépendances Pythondh-p2p.lua
- Dissecteur Wireshark pour le protocole Dahua P2P Implémentation de Rust utilisant une programmation asynchrone et un modèle de transmission de messages, ce qui la rend plus efficace et flexible.
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
L'implémentation Python de DH-P2P est une approche simple et directe. Il est utilisé à des fins de rédaction et de test en raison de sa nature rapide et facile à écrire. De plus, la mise en œuvre est plus linéaire et suit un flux d’exécution descendant, ce qui la rend plus facile à comprendre. Python, étant un langage de programmation populaire, contribue en outre à son accessibilité et à sa familiarité avec les développeurs.
# 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 "
Pour utiliser le script avec un appareil qui nécessite une authentification lors de la création d'un canal, utilisez l'option -t 1
.
Lors de l'exécution en mode --debug
ou lorsque --type
> 0, les arguments USERNAME
et PASSWORD
sont obligatoires. De plus, assurez-vous que ffplay
se trouve dans le chemin système lorsque le mode débogage est activé.
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
et -rtsp_transport tcp
Pour l'ingénierie inverse du protocole, j'ai utilisé Wireshark et KBiVMS V2.02.0 comme client sous Windows. En utilisant le dissecteur dh-p2p.lua
, vous pouvez voir le protocole dans Wireshark plus facilement.
Pour le client RTSP, VLC ou ffplay peuvent être utilisés pour un contrôle plus facile des signaux.
graphique LR
Application[[Ce script]]
Service[Easy4IPCloud]
Appareil[Caméra/NVR]
Application -- 1 --> Service
Service -- 2 --> Appareil
Application <-. 3 .-> Appareil
Le protocole Dahua P2P démarre par une poignée de main P2P. Ce processus consiste à localiser l'appareil à l'aide de son numéro de série (SN) via un service tiers, Easy4IPCloud :
graphique LR
Appareil[Caméra/NVR]
Application[[Ce script]]
Client1[Client RTSP 1]
Client2[Client RTSP 2]
Clientn[Client RTSP n]
Client1 -- TCP --> Application
Client2 -- TCP --> Application
Client - TCP -> Application
Application <-. Protocole UDPnPTCP .-> Appareil
Après la négociation P2P, le script commence à écouter les connexions RTSP sur le port 554. Lors de la connexion d'un client, le script initie un nouveau domaine au sein du protocole PTCP. Essentiellement, ce script sert de tunnel entre le client et l'appareil, facilitant la communication via l'encapsulation PTCP.
diagramme de séquence
participant A comme Ce script
participant B en tant que Easy4IPCloud
participant C1 en tant que serveur P2P
participant C2 en tant que serveur relais
participant C3 en tant que serveur agent
participant D comme caméra/NVR
A ->>B : /probe/p2psrv
B-->>A : ;
A ->>B : /online/p2psrv/{SN}
B -->>A : informations p2psrv
A ->> C1 : /probe/device/{SN}
C1-->>A : ;
A ->>B : /en ligne/relais
B -->>A : infos relais
A ->>B : /device/{SN}/p2p-channel (*)
par
A->>C2 : /relais/agent
C2 -->>A : informations sur l'agent + jeton
A ->>C3 : /relay/start/{token}
C3-->>A : ;
fin
B -->>A : informations sur l'appareil
A->>B : /device/{SN}/relay-channel + informations sur l'agent
C3-->>A : Infos Nat du serveur !
A ->> C3 : SYNCHRONISATION PTCP
A->>C3 : signature de la demande PTCP
C3-->>A : signe PTCP
A ->>D : prise de contact PTCP (*)
Remarque : Les deux connexions marquées d'un (*)
et toutes les connexions ultérieures à l'appareil doivent utiliser le même port local UDP.
PTCP (PhonyTCP) est un protocole propriétaire développé par Dahua. Il sert à encapsuler les paquets TCP dans des paquets UDP, permettant la création d'un tunnel entre un client et un périphérique derrière un NAT.
Veuillez noter que la documentation officielle pour PTCP n'est pas disponible. Les informations fournies ici sont basées sur l’ingénierie inverse.
L'en-tête du paquet PTCP est une structure fixe de 24 octets, comme indiqué ci-dessous :
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
: Une valeur constante, PTCP
.sent
et recv
: suivez le nombre d’octets envoyés et reçus, respectivement.pid
: l'ID du paquet.lmid
: L'ID local.rmid
: L'ID local du paquet précédemment reçu.La taille du corps du paquet varie (0, 4, 12 octets ou plus) en fonction du type de paquet. Sa structure est la suivante :
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
: Spécifie le type de paquet.len
: La longueur du champ data
.realm
: L’ID de domaine de la connexion.padding
: octets de remplissage, toujours définis sur 0.data
: Les données du paquet.Types de paquets :
0x00
: SYN, le corps fait toujours 4 octets 0x00030100
.0x10
: données TCP, où len
est la longueur des données TCP.0x11
: demande de port de liaison.0x12
: État de la connexion, où les données sont soit CONN
, soit DISC
.realm
défini sur 0) :0x13
: Heartbeat, où len
est toujours 0.0x17
0x18
0x19
: Authentification.0x1a
: Réponse du serveur après 0x19
.0x1b
: Réponse du client après 0x1a
. Ce projet a été inspiré et influencé par les projets et les personnes suivants :