From 2cbe4b714bfe6627910f25cd867f1531fa1b28c3 Mon Sep 17 00:00:00 2001 From: jeyawn Date: Sun, 14 Dec 2025 11:19:23 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtmp=E5=A4=8D=E6=9D=82?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=B8=8B=E6=8B=89=E6=B5=81=20C2=20=E4=B8=8D?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E5=AF=BC=E8=87=B4=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E6=96=AD=E5=BC=80=E7=9A=84bug=20(#4598)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix https://github.com/ZLMediaKit/ZLMediaKit/issues/4591 原因分析: C2不正确导致拉流校验不通过 --------- Co-authored-by: xiongguangjie --- src/Rtmp/RtmpProtocol.cpp | 120 +++++++++++++++++++++++++++++--------- src/Rtmp/RtmpProtocol.h | 4 +- 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index 191fcbed..daf43564 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -26,11 +26,35 @@ using namespace toolkit; #define S2_FMS_KEY_SIZE 68 #define C1_OFFSET_SIZE 4 + #ifdef ENABLE_OPENSSL #include "Util/SSLBox.h" #include #include +static uint8_t FMSKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, + 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, + 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, + 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 + 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, + 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, + 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae +}; // 68 + +static uint8_t FPKey[] = { + 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, + 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 + 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, + 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, + 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, + 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE +}; // 62 + static string openssl_HMACsha256(const void *key, size_t key_len, const void *data, size_t data_len){ std::shared_ptr out(new char[32], [](char *ptr) { delete[] ptr; }); unsigned int out_len; @@ -329,8 +353,16 @@ const char* RtmpProtocol::handle_S0S1S2(const char *data, size_t len, const func } // 发送 C2 [AUTO-TRANSLATED:e51c339e] // Send C2 - const char *pcC2 = data + 1; - onSendRawData(obtainBuffer(pcC2, C1_HANDSHARK_SIZE)); + uint8_t *pS1 = (uint8_t*)data + 1; + RtmpHandshake c2(0); + memcpy(&c2, pS1, sizeof(c2)); +#ifdef ENABLE_OPENSSL + if(pS1[4] >=3){ // 复杂握手计算c2 + handle_S1_complex((char*)pS1, c2); + } +#endif + + onSendRawData(obtainBuffer(&c2, C1_HANDSHARK_SIZE)); // 握手结束 [AUTO-TRANSLATED:9df763ff] // Handshake finished _next_step_func = [this](const char *data, size_t len) { @@ -408,7 +440,7 @@ void RtmpProtocol::handle_C1_complex(const char *data){ check_C1_Digest(digest, c1_joined); send_complex_S0S1S2(0, digest); -// InfoL << "schema0"; +// InfoL << "schema0"; } catch (std::exception &) { // 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f] // It seems that flash never uses schema1 @@ -426,40 +458,70 @@ void RtmpProtocol::handle_C1_complex(const char *data){ check_C1_Digest(digest, c1_joined); send_complex_S0S1S2(1, digest); -// InfoL << "schema1"; +// InfoL << "schema1"; } catch (std::exception &) { -// WarnL << "try rtmp complex schema1 failed:" << ex.what(); + //WarnL << "try rtmp complex schema1 failed:" << ex.what(); handle_C1_simple(data); } } } -#if !defined(u_int8_t) -#define u_int8_t unsigned char -#endif // !defined(u_int8_t) +void RtmpProtocol::check_S1_Digest(const std::string &digest,const std::string &data){ + auto sha256 = openssl_HMACsha256(FMSKey, S1_FMS_KEY_SIZE, data.data(), data.size()); + if (sha256 != digest) { + throw std::runtime_error("digest mismatched"); + } else { + InfoL << "check rtmp complex handshark success!"; + } +} -static u_int8_t FMSKey[] = { - 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, - 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, - 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, - 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 - 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, - 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, - 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, - 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae -}; // 68 +void RtmpProtocol::handle_S1_complex(const char *data,RtmpHandshake &c2){ -static u_int8_t FPKey[] = { - 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, - 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, - 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 - 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, - 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, - 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, - 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE -}; // 62 + const char *s1_start = data; + const char *schema_start = s1_start + 8; + char *digest_start; + std::string digest; + try { + /* c1s1 schema0 + time: 4bytes + version: 4bytes + key: 764bytes + digest: 764bytes + */ + digest = get_C1_digest((uint8_t *) schema_start + C1_SCHEMA_SIZE, &digest_start); + string s1_joined(s1_start, C1_HANDSHARK_SIZE); + s1_joined.erase(digest_start - s1_start, C1_DIGEST_SIZE); + check_S1_Digest(digest, s1_joined); + //InfoL << "schema0"; + } catch (std::exception &ex) { + // 貌似flash从来都不用schema1 [AUTO-TRANSLATED:2c6d140f] + // It seems that flash never uses schema1 + //WarnL << "try rtmp complex schema0 failed:" << ex.what(); + try { + /* c1s1 schema1 + time: 4bytes + version: 4bytes + digest: 764bytes + key: 764bytes + */ + digest = get_C1_digest((uint8_t *) schema_start, &digest_start); + string s1_joined(s1_start, C1_HANDSHARK_SIZE); + s1_joined.erase(digest_start - s1_start, C1_DIGEST_SIZE); + check_S1_Digest(digest, s1_joined); + //send_complex_S0S1S2(1, digest); + //InfoL << "schema1"; + } catch (std::exception &ex) { + WarnL << "try rtmp complex schema1 failed:" << ex.what(); + return; + } + } + + //InfoL << "send complex C2"; + auto c2_key = openssl_HMACsha256(FPKey, sizeof(FPKey), digest.data(), digest.size()); + std::string c2_str((char*)(&c2), sizeof(c2)- C1_DIGEST_SIZE); + auto c2_digest = openssl_HMACsha256(c2_key.data(), c2_key.size(), c2_str.data(), c2_str.size()); + memcpy(c2.random + RANDOM_LEN - C1_DIGEST_SIZE, c2_digest.data(), C1_DIGEST_SIZE); +} void RtmpProtocol::check_C1_Digest(const string &digest,const string &data){ auto sha256 = openssl_HMACsha256(FPKey, C1_FPKEY_SIZE, data.data(), data.size()); diff --git a/src/Rtmp/RtmpProtocol.h b/src/Rtmp/RtmpProtocol.h index 7a12495c..be51bbb1 100644 --- a/src/Rtmp/RtmpProtocol.h +++ b/src/Rtmp/RtmpProtocol.h @@ -63,14 +63,16 @@ protected: void sendRtmp(uint8_t type, uint32_t stream_index, const std::string &buffer, uint32_t stamp, int chunk_id); void sendRtmp(uint8_t type, uint32_t stream_index, const toolkit::Buffer::Ptr &buffer, uint32_t stamp, int chunk_id); toolkit::BufferRaw::Ptr obtainBuffer(const void *data = nullptr, size_t len = 0); - + private: void handle_C1_simple(const char *data); #ifdef ENABLE_OPENSSL + void handle_S1_complex(const char *data, RtmpHandshake &c2); void handle_C1_complex(const char *data); std::string get_C1_digest(const uint8_t *ptr,char **digestPos); std::string get_C1_key(const uint8_t *ptr); void check_C1_Digest(const std::string &digest,const std::string &data); + void check_S1_Digest(const std::string &digest,const std::string &data); void send_complex_S0S1S2(int schemeType,const std::string &digest); #endif //ENABLE_OPENSSL