โครงการนี้มุ่งมั่นที่จะให้คำอธิบายที่สมบูรณ์และดำเนินการอีกครั้งของ WhatsApp Web API ซึ่งในที่สุดจะนำไปสู่ไคลเอนต์ที่กำหนดเอง whatsapp web ทำงานภายในโดยใช้ websockets; โครงการนี้ก็ทำได้เช่นกัน
ไม่จำเป็นต้องติดตั้งหรือจัดการเวอร์ชัน Python และโหนดไฟล์ shell.nix กำหนดสภาพแวดล้อมที่มีการอ้างอิงทั้งหมดที่รวมอยู่ในการเรียกใช้โครงการนี้
มีไฟล์ .envrc ในโฟลเดอร์รูทที่เรียกว่าโดยอัตโนมัติเมื่อ cd ing (เปลี่ยนไดเรกทอรี) เป็นโครงการหากมีการติดตั้งโปรแกรม direnv พร้อมกับ nix คุณควรได้รับเอาต์พุตเช่นนี้:
> 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ในรูทโครงการ
ก่อนที่คุณจะเรียกใช้แอปพลิเคชันตรวจสอบให้แน่ใจว่าคุณติดตั้งซอฟต์แวร์ต่อไปนี้:
await async )pip ต่อไปนี้:websocket-client และ git+https://github.com/dpallot/simple-websocket-server.git สำหรับทำหน้าที่เป็นเซิร์ฟเวอร์ websocket และไคลเอนต์curve25519-donna และ pycrypto สำหรับการเข้ารหัสpyqrcode สำหรับการสร้างรหัส QRprotobuf สำหรับการอ่านและการเขียนรูปแบบการสนทนาไบนารีcurve25519-donna ต้องใช้ Microsoft Visual C ++ 9.0 และคุณต้องคัดลอก stdint.h ลงใน C:UsersYOUR USERNAMEAppDataLocalProgramsCommonMicrosoftVisual C++ for Python9.0VCinclude ก่อนที่จะเริ่มแอปพลิเคชันเป็นครั้งแรกให้เรียกใช้ npm install -f เพื่อติดตั้งโหนดและ pip install -r requirements.txt สำหรับการพึ่งพา Python ทั้งหมด
สุดท้ายในที่สุดก็เปิดใช้งานเพียงแค่เรียกใช้ npm start บน Linux ที่ใช้ Linux และ npm run win บน Windows การใช้แฟนซี concurrently และ nodemon Magic ส่วนประกอบท้องถิ่นทั้งสามจะเริ่มต้นหลังจากซึ่งกันและกันและเมื่อคุณแก้ไขไฟล์โมดูลที่เปลี่ยนแปลงจะรีสตาร์ทโดยอัตโนมัติเพื่อใช้การเปลี่ยนแปลงโดยอัตโนมัติ
การเพิ่มล่าสุดคือเวอร์ชันของรูทีนถอดรหัสที่แปลไปยัง JavaScript ในเบราว์เซอร์ เรียกใช้ node index_jsdemo.js (เพียงแค่ต้องการเพราะเบราว์เซอร์ไม่อนุญาตให้เปลี่ยนส่วนหัว HTTP สำหรับ websockets) จากนั้นเปิด client/login-via-js-demo.html เป็นไฟล์ปกติในเบราว์เซอร์ใด ๆ เอาต์พุตคอนโซลควรแสดงข้อความไบนารีที่ถอดรหัสหลังจากสแกนรหัส QR
Adiwajshing สร้าง Baileys ซึ่งเป็นไลบรารีโหนดที่ใช้ whatsapp web API
Ndunks ทำ reimplementation typeScript ที่ WAJS
P4KL0NC4T สร้าง Kyros ซึ่งเป็นแพ็คเกจ Python ที่ใช้ whatsapp Web API
ด้วย whatsappweb-rs, Wiomoc สร้างไคลเอนต์ whatsapp ใน Rust
Rhymen สร้าง Go-Whatsapp ซึ่งเป็นแพ็คเกจ GO ที่ใช้ WHATSAPP Web API
Vzaramel สร้าง whatsappweb-clj ไลบรารี clojure ที่ใช้ whatsapp web api
โครงการถูกจัดระเบียบด้วยวิธีต่อไปนี้ หมายเหตุพอร์ตที่ใช้แล้วและตรวจสอบให้แน่ใจว่าพวกเขาไม่ได้ใช้งานที่อื่นก่อนเริ่มแอปพลิเคชัน
whatsapp เว็บเข้ารหัสข้อมูลโดยใช้อัลกอริทึมที่แตกต่างกันหลายประการ เหล่านี้รวมถึง AES 256 CBC, Curve25519 เป็นโครงการข้อตกลง Key Diffie-Hellman, HKDF สำหรับการสร้างความลับที่ใช้ร่วมกันและ HMAC ที่ใช้ร่วมกันกับ SHA256
การเริ่มต้นเซสชันเว็บ WhatsApp เกิดขึ้นเพียงแค่เชื่อมต่อกับหนึ่งในเซิร์ฟเวอร์ WebSocket ที่ wss://w[1-8].web.whatsapp.com/ws ( wss:// หมายความว่าการเชื่อมต่อ WebSocket ปลอดภัย; w[1-8] หมายความว่าหมายเลขใด ๆ ระหว่าง 1 และ 8 สามารถติดตาม w ) ตรวจสอบให้แน่ใจว่าเมื่อสร้างการเชื่อมต่อส่วนหัว HTTP Origin: https://web.whatsapp.com จะถูกตั้งค่ามิฉะนั้นการเชื่อมต่อจะถูกปฏิเสธ
เมื่อคุณส่งข้อความไปยัง WHATSAPP Web WebSocket พวกเขาจะต้องอยู่ในรูปแบบที่เฉพาะเจาะจง มันค่อนข้างง่ายและดูเหมือน messageTag,JSON , เช่น 1515590796,["data",123] โปรดทราบว่าเห็นได้ชัดว่าแท็กข้อความสามารถเป็นอะไรก็ได้ แอปพลิเคชันนี้ส่วนใหญ่ใช้การประทับเวลาปัจจุบันเป็นแท็กเพื่อให้มีเอกลักษณ์เล็กน้อย Whatsapp เองมักจะใช้แท็กข้อความเช่น s1 , 1234.--0 หรืออะไรทำนองนั้น เห็นได้ชัดว่าแท็กข้อความอาจไม่มีเครื่องหมายจุลภาค นอกจากนี้ วัตถุ JSON เป็นไปได้เช่นเดียวกับน้ำหนักบรรทุก
หากต้องการเข้าสู่ระบบที่ WebSocket ที่เปิดอยู่ให้ทำตามขั้นตอนเหล่านี้:
clientId ของคุณเองซึ่งจะต้องเป็น 16 ไบต์ที่เข้ารหัส base64 (เช่น 25 อักขระ) แอปพลิเคชั่นนี้ใช้ 16 ไบต์สุ่มเช่น base64.b64encode(os.urandom(16)) ใน PythonmessageTag,["admin","init",[0,3,2390],["Long browser description","ShortBrowserDesc"],"clientId",true]messageTag และ clientId ด้วยค่าที่คุณเลือกมาก่อน[0,3,2390] ระบุเวอร์ชันเว็บ WhatsApp ปัจจุบัน ค่าสุดท้ายเปลี่ยนแปลงบ่อยครั้ง มันควรจะเข้ากันได้ค่อนข้างย้อนหลัง"Long browser description" เป็นสตริงโดยพลการที่จะแสดงในแอพ WhatsApp ในรายการไคลเอนต์ WHATSAPP ที่ลงทะเบียนหลังจากที่คุณสแกนรหัส QR"ShortBrowserDesc" ยังไม่ได้รับการสังเกตที่ใดก็ได้ แต่ก็เป็นไปตามอำเภอใจเช่นกันstatus : ควรเป็น 200ref : ในแอปพลิเคชันสิ่งนี้ถือเป็นรหัสเซิร์ฟเวอร์; สำคัญสำหรับรุ่น QR ดูด้านล่างttl : คือปี 20000 บางทีเวลาหลังจากรหัส QR ไม่ถูกต้องupdate : ธงบูลีนcurr : เวอร์ชันเว็บ Whatsapp ปัจจุบันเช่น 0.2.7314time : การประทับเวลาที่เซิร์ฟเวอร์ตอบกลับเป็นมิลลิวินาทีแบบลอยตัวเช่น 1515592039037.0curve25519.Private()privateKey.get_public()ref จากขั้นตอนที่ 4base64.b64encode(publicKey.serialize())pyqrcode ) และสแกนโดยใช้แอพ WhatsAppttl )messageTag,["admin","Conn","reref"]status : ควรเป็น 200 (คนอื่น ๆ : 304 - นำกลับมาใช้ใหม่ก่อนหน้านี้ 429 - Ref Ref DENIED ใหม่)ref : อ้างอิงใหม่ttl : เวลาหมดอายุConn : Array มีวัตถุ JSON เป็นองค์ประกอบที่สองที่มีข้อมูลการเชื่อมต่อที่มีแอตทริบิวต์ต่อไปนี้และอีกมากมาย:battery : เปอร์เซ็นต์แบตเตอรี่ปัจจุบันของโทรศัพท์ของคุณbrowserToken : ใช้เพื่อออกจากระบบโดยไม่ต้องเชื่อมต่อ WebSocket ที่ใช้งานอยู่ (ยังไม่ได้ใช้งาน)clientToken : ใช้ในการกลับมาทำงานที่ปิดหรือที่รู้จักphone : วัตถุที่มีข้อมูลโดยละเอียดเกี่ยวกับโทรศัพท์ของคุณเช่น device_manufacturer , device_model , os_build_number , os_versionplatform : ระบบปฏิบัติการโทรศัพท์ของคุณเช่น androidpushname : ชื่อของคุณที่คุณให้ whatsappsecret (จำสิ่งนี้!)serverToken : ใช้ในการกลับมาใช้งานที่ปิดหรือที่รู้จักwid : หมายเลขโทรศัพท์ของคุณในรูปแบบการระบุแชท (ดูด้านล่าง)Stream : อาร์เรย์มีองค์ประกอบทั้งหมดสี่องค์ประกอบดังนั้นน้ำหนักบรรทุกทั้งหมดจึงเหมือน ["Stream","update",false,"0.2.7314"]Props : อาร์เรย์มีวัตถุ JSON เป็นองค์ประกอบที่สองที่มีคุณสมบัติหลายอย่างเช่น imageMaxKBytes (1024), maxParticipants (257), videoMaxEdge (960) และอื่น ๆsecret จาก Conn เป็น base64 และจัดเก็บเป็น secret ความลับที่ถอดรหัสนี้จะมีความยาว 144 ไบต์sharedSecret แอปพลิเคชันใช้งานโดยใช้ privateKey.get_shared_key(curve25519.Public(secret[:32]), lambda a:a)sharedSecret เป็น 80 ไบต์โดยใช้ HKDF เรียกค่านี้ sharedSecretExpandedHmacSha256(sharedSecretExpanded[32:64], secret[:32] + secret[64:]) เปรียบเทียบค่านี้เป็น secret[32:64] หากพวกเขาไม่เท่ากันให้ยกเลิกการเข้าสู่ระบบsharedSecretExpanded[64:] + secret[64:] เป็น keysEncryptedsharedSecretExpanded[:32] เป็นกุญแจสำคัญเช่นเก็บ AESDecrypt(sharedSecretExpanded[:32], keysEncrypted) keysDecryptedkeysDecrypted มีความยาว 64 ไบต์และมีสองปุ่มแต่ละอันยาว 32 ไบต์ encKey ใช้สำหรับการถอดรหัสข้อความไบนารีที่ส่งถึงคุณโดยเว็บเซิร์ฟเวอร์ WhatsApp หรือการเข้ารหัสข้อความไบนารีที่คุณส่งไปยังเซิร์ฟเวอร์ macKey เป็นสิ่งจำเป็นในการตรวจสอบข้อความที่ส่งถึงคุณ:encKey : keysDecrypted[:32]macKey : keysDecrypted[32:64]init ให้ตรวจสอบว่าคุณมี serverToken และ clientToken หรือไม่messageTag,["admin","login","clientToken","serverToken","clientId","takeover"]{"status": 200} สถานะอื่น ๆ :tos TOS ใน JSON: ถ้ามันเท่ากับหรือมากกว่า 2 คุณได้ละเมิด TOSserverToken และ clientToken เก่าหรือหมดอายุคุณจะถูกท้าทายเพื่อยืนยันว่าคุณยังมีคีย์การเข้ารหัสที่ถูกต้องmessageTag,["Cmd",{"type":"challenge","challenge":"BASE_64_ENCODED_STRING=="}]challenge String จาก Base64, ลงชื่อเข้าใช้กับ Mackey ของคุณเข้ารหัสกลับมาพร้อมกับ Base64 และส่ง messageTag,["admin","challenge","BASE_64_ENCODED_STRING==","serverToken","clientId"]{"status": 200} แต่มันไม่มีความหมายอะไรเลยgoodbye,,["admin","Conn","disconnect"]encKey ของคุณด้วย macKey ของคุณและเข้ารหัสด้วย Base64 สมมติว่ามันเป็น logoutToken ของคุณhttps://dyn.web.whatsapp.com/logout?t=browserToken&m=logoutTokenตอนนี้คุณมีสองปุ่มการตรวจสอบและถอดรหัสข้อความที่เซิร์ฟเวอร์ส่งถึงคุณนั้นค่อนข้างง่าย โปรดทราบว่านี่เป็นสิ่งจำเป็นสำหรับข้อความ ไบนารี เท่านั้น JSON ทั้งหมดที่คุณได้รับการเข้าพัก ข้อความไบนารีมี 32 ไบต์เสมอในตอนแรกที่ระบุการตรวจสอบ HMAC ทั้งข้อความ JSON และ ไบนารีมีแท็กข้อความในจุดเริ่มต้นของพวกเขาที่สามารถยกเลิกได้เช่นส่วนหลังจากตัวละครจุลภาคตัวแรกมีความสำคัญ
macKey (ที่นี่ messageContent เป็นข้อความไบนารี ทั้งหมด ): HmacSha256(macKey, messageContent[32:]) หากค่านี้ไม่เท่ากับ messageContent[:32] ข้อความที่ส่งถึงคุณโดยเซิร์ฟเวอร์นั้นไม่ถูกต้องและควรถูกยกเลิกencKey : AESDecrypt(encKey, messageContent[32:])ข้อมูลที่คุณได้รับในขั้นตอนสุดท้ายมีรูปแบบไบนารีซึ่งอธิบายไว้ในต่อไปนี้ แม้ว่ามันจะเป็นไบนารี แต่คุณยังสามารถเห็นสตริงได้หลายสายโดยเฉพาะเนื้อหาของข้อความที่คุณส่งนั้นค่อนข้างชัดเจน
Python Script backend/decoder.py ใช้คลาส MessageParser มันสามารถสร้างโครงสร้าง JSON จากข้อมูลไบนารีซึ่งข้อมูลยังคงจัดระเบียบในทางที่ค่อนข้างยุ่ง ส่วนเกี่ยวกับการจัดการโหนดด้านล่างจะหารือเกี่ยวกับวิธีการจัดระเบียบโหนดใหม่ในภายหลัง
MessageParser ในตอนแรกเพียงต้องการข้อมูลบางอย่างจากนั้นประมวลผลไบต์โดย BYTE เช่นเป็นสตรีม มันมีค่าคงที่สองสามอย่างและวิธีการมากมายที่สร้างขึ้นซึ่งกันและกัน
[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"]- สำหรับ . , สำหรับ 11 และ