該項目旨在提供WhatsApp Web API的完整描述和重新實現,最終將導致自定義客戶端。 WhatsApp Web使用Websocket在內部工作;這個項目也是如此。
無需安裝或管理Python和節點版本,文件shell.nix定義了一個環境,其中包含所有依賴項以運行此項目。
根文件夾中有一個.envrc文件,當cd ING(更改目錄)到項目時,該文件將自動稱為“ direnv”,如果與nix一起安裝了direnv ,則應獲得這樣的輸出:
> cd ~ /dev/whatsapp
Installing node modules
npm WARN prepare removing existing node_modules/ before installation
> [email protected] install /home/rainy/dev/whatsapp/node_modules/fsevents
> node-gyp rebuild
make: Entering directory ' /home/rainy/dev/whatsapp/node_modules/fsevents/build '
SOLINK_MODULE(target) Release/obj.target/.node
COPY Release/.node
make: Leaving directory ' /home/rainy/dev/whatsapp/node_modules/fsevents/build '
> [email protected] postinstall /home/rainy/dev/whatsapp/node_modules/nodemon
> node bin/postinstall || exit 0
added 310 packages in 3.763s
Done.
$$ $$ $$ $$
$$ | $ $$ | $$ | $$ |
$$ | $$ $ $$ | $$$$$$ $ $$$$$$ $$$$$$ $$$$$$ $ $$$$$$ $$$$$$ $$$$$$
$$ $$ $$ $ $ | $$ __ $$ _ ___ $$ \ _ $$ _ | $$ _____ | _ ___ $$ $$ __ $$ $$ __ $$
$$$$ _ $$$$ | $$ | $$ | $$$$$$ $ | $$ | $ $$$$ $ $$$$$$ $ | $$ / $$ | $$ / $$ |
$$ $ / $ $$ | $$ | $$ | $$ __ $$ | $$ | $$ _ ___ $$ $$ __ $$ | $$ | $$ | $$ | $$ |
$$ / $ $ | $$ | $$ | $ $$$$$$ | $ $$ $ | $$$$$$ $ | $ $$$$$$ | $$$$$$ $ | $$$$$$ $ |
_ _/ _ _ | _ _ | _ _ | _ ______ | _ ___/ _ ______/ _ ______ | $$ ____/ $$ ____/
$$ | $$ |
$$ | $$ |
_ _ | _ _ |
Node v13.13.0
Python 2.7.17
Try running server with: npm start
[nix-shell: ~ /dev/whatsapp]$ 如果您不使用direnv或只是想手動進入構建環境:
nix-shell在項目根中
在運行應用程序之前,請確保安裝了以下軟件:
async await語法)pip軟件包:websocket-client和git+https://github.com/dpallot/simple-websocket-server.git充當WebSocket服務器和客戶端。curve25519-donna和pycrypto用於加密內容。pyqrcode 。protobuf用於閱讀和編寫二進制對話格式。curve25519-donna上需要Microsoft Visual C ++ 9.0,並且您需要將stdint.h複製到C:UsersYOUR USERNAMEAppDataLocalProgramsCommonMicrosoftVisual C++ for Python9.0VCinclude 。在首次啟動應用程序之前,請運行npm install -f以安裝所有python依賴性的所有節點和pip install -r requirements.txt 。
最後,最終啟動它,只需在基於Linux的OS和npm run win Windows上運行npm start啟動即可。 concurrently使用幻想和nodemon魔術,所有三個本地組件都將在彼此之後啟動,並且在您編輯文件時,更改的模塊將自動重新啟動以應用更改。
最近的添加是解密程序的一個版本,該版本轉化為瀏覽器JavaScript。運行node index_jsdemo.js (僅需要瀏覽器,因為瀏覽器不允許更改WebSockets的HTTP標頭),然後在任何瀏覽器中打開client/login-via-js-demo.html作為普通文件。掃描QR代碼後,控制台輸出應顯示解密的二進制消息。
Adiwajshing創建了實現WhatsApp Web API的節點庫Baileys。
ndunks在WAJS進行了打字稿的重新實現。
P4KL0NC4T創建了Kyros,這是一個實現WhatsApp Web API的Python軟件包。
使用WhatsAppWeb-RS,Wiomoc在Rust中創建了WhatsApp Web客戶端。
Rhymen創建了Go-Whatsapp,這是一個實現WhatsApp Web API的GO軟件包。
Vzaramel創建了Whatsappweb-Clj,一個Clojure庫,實現了WhatsApp Web API。
該項目是通過以下方式組織的。注意使用的端口,並確保在啟動應用程序之前在其他地方不使用它們。
WhatsApp Web使用幾種不同的算法對數據進行加密。其中包括AES 256 CBC,Curve25519作為Diffie-Hellman密鑰協議方案,HKDF,用於與SHA256生成擴展的共享秘密和HMAC。
通過僅連接到wss://w[1-8].web.whatsapp.com/ws ( wss://表示Websocket Connection; w[1-8]表示任何數字之間的任何數字之間的任何數字介於w )中,就可以啟動WhatsApp Web會話發生。另外,請確保在建立連接時,http標題Origin: https://web.whatsapp.com設置,否則連接將被拒絕。
當您將消息發送到WhatsApp Web WebSocket時,它們需要採用特定格式。它非常簡單,看起來像messageTag,JSON ,例如1515590796,["data",123] 。請注意,顯然消息標籤可以是任何東西。該應用程序主要使用當前的時間戳作為標籤,只是為了有點唯一。 WhatsApp本身通常使用s1 1234.--0類的消息標籤或類似的東西。顯然,消息標籤可能不包含逗號。此外,JSON對象也是可能的,還有有效載荷。
要在開放的Websocket上登錄,請按照以下步驟:
clientId ,該客戶端需要為16個基本編碼字節(即25個字符)。此應用程序僅使用16個隨機字節,即Python中的IE base64.b64encode(os.urandom(16)) 。messageTag,["admin","init",[0,3,2390],["Long browser description","ShortBrowserDesc"],"clientId",true] 。messageTag和clientId[0,3,2390]部分指定當前的WhatsApp Web版本。最後一個值經常變化。不過,它應該是相當倒數的。"Long browser description"是一個任意的字符串,掃描QR代碼後,將在已註冊的WhatsApp Web客戶端列表中顯示在WhatsApp應用程序中。"ShortBrowserDesc" ,但也是任意的。status :應為200ref :在應用程序中,這被視為服務器ID;對於QR一代很重要,請參見下文ttl :IS 20000,也許是QR碼之後的時間無效update :布爾國旗curr :當前的WhatsApp Web版本,例如0.2.7314time :服務器響應的時間戳,如浮點毫秒,例如1515592039037.0curve25519.Private()生成自己的私鑰。privateKey.get_public()獲取公鑰。ref屬性base64.b64encode(publicKey.serialize())pyqrcode ),並使用WhatsApp應用程序掃描它。ttl )時最多要求5個新服務器參考。messageTag,["admin","Conn","reref"]來做到這一點。status :應為200(其他:304-重複使用以前的參考,429-新裁判被拒絕)ref :新參考ttl :到期時間Conn :數組將JSON對像作為第二個元素,其中包含以下屬性等等的連接信息:battery :當前手機的電池百分比browserToken :用於未經活動的WebSocket連接(尚未實現)註銷(尚未實現)clientToken :用於恢復封閉會議又名“記住我”(尚未實現)phone :一個具有device_manufacturer device_model的詳細os_build_number os_version對象platform :您的手機操作系統,例如androidpushname :您提供的WhatsApp的名稱secret (記住這個!)serverToken :用於恢復封閉會議又名“記住我”(尚未實現)wid :您的聊天標識格式的電話號碼(請參閱下文)Stream :數組總共有四個元素,因此整個有效負載就像["Stream","update",false,"0.2.7314"]Props :數組包含JSON對像作為第二個元素,具有多個屬性,例如imageMaxKBytes (1024), maxParticipants (257), videoMaxEdge (960)等Conn的secret解碼為Base64,然後將其存儲為secret 。這個解碼的秘密將長144個字節。sharedSecret 。該應用程序使用privateKey.get_shared_key(curve25519.Public(secret[:32]), lambda a:a)進行應用程序。sharedSecret擴展到80字節。調用此值sharedSecretExpanded 。HmacSha256(sharedSecretExpanded[32:64], secret[:32] + secret[64:])來做到這一點。將這個值與secret[32:64]進行比較。如果它們不相等,請中止登錄名。sharedSecretExpanded[64:] + secret[64:]按keysEncrypted 。sharedSecretExpanded[:32]作為密鑰的AE進行加密鍵,IE Store AESDecrypt(sharedSecretExpanded[:32], keysEncrypted)如keysDecrypted 。keysDecrypted變量為64個字節長,包含兩個鍵,每個鍵長32個字節。 encKey用於解密由WhatsApp Web服務器發送給您的二進制消息或將您發送到服務器發送的二進制消息。需要macKey來驗證發送給您的消息:encKey : keysDecrypted[:32]macKey : keysDecrypted[32:64]init命令後,檢查您是否具有serverToken和clientToken 。messageTag,["admin","login","clientToken","serverToken","clientId","takeover"]{"status": 200} 。其他狀態:tos字段:如果它等於或大於2,則違反了TOSserverToken和clientToken時,您將面臨挑戰,以確認您仍然具有有效的加密密鑰。messageTag,["Cmd",{"type":"challenge","challenge":"BASE_64_ENCODED_STRING=="}]challenge字符串,與您的Mackey簽名,用base64對其進行編碼,然後發送messageTag,["admin","challenge","BASE_64_ENCODED_STRING==","serverToken","clientId"]{"status": 200}響應,但這一無所有。goodbye,,["admin","Conn","disconnect"] 。macKey簽名encKey ,然後用Base64對其進行編碼。假設這是您的logoutToken 。https://dyn.web.whatsapp.com/logout?t=browserToken&m=logoutToken現在,您已經擁有了兩個鍵,驗證和解密服務器發送給您的消息非常容易。請注意,這僅是二進制消息所需的,您收到的所有JSON都始終如一。二進制消息在開始時總是具有32個字節,以指定HMAC校驗和。 JSON和Binary Messages在他們的一開始就具有一個消息標籤,可以丟棄,即僅在第一個逗號字符很重要之後的部分。
macKey哈希實際消息內容來驗證消息(這裡是整個二進制消息): HmacSha256(macKey, messageContent[32:]) messageContent如果此值不等於messageContent[:32] ,則服務器發送給您的消息無效,應丟棄。encKey解密消息內容: AESDecrypt(encKey, messageContent[32:]) 。您在最後一步中獲得的數據具有以下二進制格式。即使它是二進制的,您仍然可以看到其中的幾個字符串,尤其是您發送的消息的內容很明顯。
Python backend/decoder.py實現MessageParser類。它能夠從二進制數據中創建一個JSON結構,其中數據仍然以凌亂的方式組織。關於節點處理的部分將討論以後如何重新組織節點。
MessageParser最初只需要一些數據,然後通過字節來處理它,即作為流。它有幾個常數和許多彼此建立的方法。
[None,None,None,"200","400","404","500","501","502","action","add", "after","archive","author","available","battery","before","body", "broadcast","chat","clear","code","composing","contacts","count", "create","debug","delete","demote","duplicate","encoding","error", "false","filehash","from","g.us","group","groups_v2","height","id", "image","in","index","invis","item","jid","kind","last","leave", "live","log","media","message","mimetype","missing","modify","name", "notification","notify","out","owner","participant","paused", "picture","played","presence","preview","promote","query","raw", "read","receipt","received","recipient","recording","relay", "remove","response","resume","retry","s.whatsapp.net","seconds", "set","size","status","subject","subscribe","t","text","to","true", "type","unarchive","unavailable","url","user","value","web","width", "mute","read_only","admin","creator","short","update","powersave", "checksum","epoch","block","previous","409","replaced","reason", "spam","modify_tag","message_info","delivery","emoji","title", "description","canonical-url","matched-text","star","unstar", "media_key","filename","identity","unread","page","page_count", "search","media_message","security","call_log","profile","ciphertext", "invite","gif","vcard","frequent","privacy","blacklist","whitelist", "verify","location","document","elapsed","revoke_invite","expiration", "unsubscribe","disable"]- 10 .對於15的11和