이는 Dahua P2P 프로토콜을 통한 RTSP 구현의 개념 증명입니다. Dahua 및 파생 카메라/NVR과 함께 작동합니다.
Dahua P2P 프로토콜은 Dahua 장치에 대한 원격 액세스에 사용됩니다. 이는 Android의 gDMSS Lite 또는 Windows의 SmartPSS, KBiVMS와 같은 Dahua 앱에서 일반적으로 사용됩니다.
내 특정 시나리오에서는 KBVision CCTV 시스템이 있습니다. KBiVMS 클라이언트를 사용하여 카메라에 액세스할 수 있지만 주로 Windows가 아닌 플랫폼을 사용합니다. 따라서 저는 보다 광범위하게 지원되는 RTSP 클라이언트를 사용하여 비디오를 스트리밍하기 위한 대체 옵션을 살펴보고 싶었습니다. 결과적으로 저는 Dahua P2P 프로토콜을 다시 구현하는 실험을 하기로 결정했습니다.
src/*.rs
- Rust 소스 파일Cargo.toml
- Rust 종속성main.py
- 메인 스크립트helpers.py
- 도우미 기능requirements.txt
- Python 종속성dh-p2p.lua
- Dahua P2P 프로토콜용 Wireshark 해부기 비동기 프로그래밍과 메시지 전달 패턴을 활용하여 Rust 구현을 더욱 효율적이고 유연하게 만듭니다.
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
DH-P2P의 Python 구현은 간단하고 직접적인 접근 방식입니다. 빠르고 쓰기 쉬운 특성으로 인해 초안 작성 및 테스트 목적으로 사용됩니다. 또한 구현이 더욱 선형적이고 하향식 실행 흐름을 따르므로 이해하기가 더 쉽습니다. 널리 사용되는 프로그래밍 언어인 Python은 개발자의 접근성과 친숙성에 더욱 기여합니다.
# 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 "
채널 생성 시 인증이 필요한 장치에서 스크립트를 사용하려면 -t 1
옵션을 사용하세요.
--debug
모드에서 실행하거나 --type
> 0인 경우 USERNAME
및 PASSWORD
인수는 필수입니다. 또한 디버그 모드가 활성화되면 ffplay
시스템 경로에 있는지 확인하십시오.
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
및 -rtsp_transport tcp
옵션을 사용하면 더 효과적으로 작업할 수 있습니다. 프로토콜을 리버스 엔지니어링하기 위해 Windows에서 Wireshark와 KBiVMS V2.02.0을 클라이언트로 사용했습니다. dh-p2p.lua
dissector를 사용하면 Wireshark의 프로토콜을 더 쉽게 볼 수 있습니다.
RTSP 클라이언트의 경우 신호를 더 쉽게 제어하기 위해 VLC 또는 ffplay를 사용할 수 있습니다.
그래프 LR
앱[[이 스크립트]]
서비스[Easy4IPCloud]
장치[카메라/NVR]
앱 -- 1 --> 서비스
서비스 -- 2 --> 장치
앱 <-. 3 .-> 장치
Dahua P2P 프로토콜은 P2P 핸드셰이크로 시작됩니다. 이 프로세스에는 타사 서비스인 Easy4IPCloud를 통해 일련 번호(SN)를 사용하여 장치를 찾는 작업이 포함됩니다.
그래프 LR
장치[카메라/NVR]
앱[[이 스크립트]]
클라이언트1[RTSP 클라이언트 1]
클라이언트2[RTSP 클라이언트 2]
클라이언트n[RTSP 클라이언트 n]
클라이언트1 -- TCP --> 앱
클라이언트2 - TCP -> 앱
클라이언트 -- TCP --> 앱
앱 <-. UDPnPTCP 프로토콜 .-> 장치
P2P 핸드셰이크 후에 스크립트는 포트 554에서 RTSP 연결을 수신하기 시작합니다. 클라이언트 연결 시 스크립트는 PTCP 프로토콜 내에서 새 영역을 시작합니다. 기본적으로 이 스크립트는 클라이언트와 장치 사이의 터널 역할을 하여 PTCP 캡슐화를 통한 통신을 촉진합니다.
시퀀스 다이어그램
참가자 A는 이 스크립트로
참가자 B(Easy4IPCloud)
참가자 C1을 P2P 서버로 사용
참가자 C2를 릴레이 서버로 사용
참가자 C3을 에이전트 서버로 사용
참가자 D를 카메라/NVR로 사용
A->>B: /프로브/p2psrv
B-->>A: ;
A->>B: /online/p2psrv/{SN}
B-->>A: p2psrv 정보
A->>C1: /프로브/장치/{SN}
C1-->>A: ;
A->>B: /온라인/릴레이
B-->>A: 릴레이 정보
A->>B: /device/{SN}/p2p-채널 (*)
평가
A->>C2: /릴레이/에이전트
C2-->>A: 에이전트 정보 + 토큰
A->>C3: /relay/start/{토큰}
C3-->>A: ;
끝
B-->>A: 장치 정보
A->>B: /device/{SN}/relay-channel + 에이전트 정보
C3-->>A: 서버 Nat 정보!
A->>C3: PTCP 동기화
A->>C3: PTCP 요청 부호
C3-->>A: PTCP 기호
A->>D: PTCP 핸드셰이크 (*)
참고 : (*)
로 표시된 연결과 장치에 대한 모든 후속 연결은 모두 동일한 UDP 로컬 포트를 사용해야 합니다.
PTCP(PhonyTCP)는 Dahua가 개발한 독점 프로토콜입니다. 이는 UDP 패킷 내에 TCP 패킷을 캡슐화하여 NAT 뒤에 있는 장치와 클라이언트 사이에 터널을 생성하는 역할을 합니다.
PTCP에 대한 공식 문서는 제공되지 않습니다. 여기에 제공된 정보는 리버스 엔지니어링을 기반으로 합니다.
PTCP 패킷 헤더는 아래에 설명된 것처럼 고정된 24바이트 구조입니다.
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
: 상수 값 PTCP
.sent
및 recv
: 각각 보내고 받은 바이트 수를 추적합니다.pid
: 패킷 ID입니다.lmid
: 로컬 ID입니다.rmid
: 이전에 수신된 패킷의 로컬 ID입니다.패킷 본문은 패킷 유형에 따라 크기(0, 4, 12바이트 이상)가 다릅니다. 그 구조는 다음과 같습니다:
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
: 패킷 유형을 지정합니다.len
: data
필드의 길이입니다.realm
: 연결의 영역 ID입니다.padding
: 패딩 바이트, 항상 0으로 설정됩니다.data
: 패킷 데이터입니다.패킷 유형:
0x00
: SYN, 본문은 항상 4바이트 0x00030100
입니다.0x10
: TCP 데이터. 여기서 len
TCP 데이터의 길이입니다.0x11
: 바인딩 포트 요청.0x12
: 데이터가 CONN
또는 DISC
인 연결 상태입니다.realm
이 0으로 설정됨):0x13
: 하트비트, 여기서 len
항상 0입니다.0x17
0x18
0x19
: 인증.0x1a
: 0x19
이후 서버 응답.0x1b
: 0x1a
이후의 클라이언트 응답. 이 프로젝트는 다음 프로젝트와 사람들로부터 영감을 받고 영향을 받았습니다.