يعتزم هذا المشروع توفير وصف كامل وإعادة تنفيذ واجهة برمجة تطبيقات WhatsApp ، والتي ستؤدي في النهاية إلى عميل مخصص. يعمل WhatsApp Web داخليًا باستخدام WebSockets ؛ هذا المشروع يفعل كذلك.
ليست هناك حاجة لتثبيت أو إدارة إصدارات Python و Node ، ويحدد File 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 لتوليد رمز الاستجابة السريعة.protobuf لقراءة وكتابة تنسيق المحادثة الثنائية.curve25519-donna يتطلب Microsoft Visual C ++ 9.0 وتحتاج إلى نسخ stdint.h إلى C:UsersYOUR USERNAMEAppDataLocalProgramsCommonMicrosoftVisual C++ for Python9.0VCinclude . قبل بدء التطبيق لأول مرة ، قم بتشغيل npm install -f لتثبيت جميع pip install -r requirements.txt .
أخيرًا ، لإطلاقه أخيرًا ، قم فقط بتشغيل npm start على نظام التشغيل OS المستند إلى Linux و npm run win على Windows. باستخدام Fancy concurrently وسحر nodemon ، سيتم بدء جميع المكونات المحلية الثلاثة بعد بعضها البعض ، وعندما تقوم بتحرير ملف ، سيتم إعادة تشغيل الوحدة التي تم تغييرها تلقائيًا لتطبيق التغييرات.
إضافة حديثة هي نسخة من روتين فك التشفير المترجمة إلى JavaScript في المتصفح. قم بتشغيل node index_jsdemo.js (مطلوب فقط لأن المتصفحات لا تسمح بتغيير رؤوس HTTP لـ WebSockets) ، ثم فتح client/login-via-js-demo.html كملف عادي في أي متصفح. يجب أن يُظهر إخراج وحدة التحكم رسائل ثنائية فك التشفير بعد مسح رمز الاستجابة السريعة.
أنشأ Adiwajshing Baileys ، مكتبة العقدة التي تنفذ واجهة برمجة تطبيقات WhatsApp.
قام Ndunks بإعادة تخطيط TypeScript في WAJS.
أنشأ P4KL0NC4T Kyros ، وهي حزمة Python التي تنفذ واجهة برمجة تطبيقات WhatsApp Web.
مع WhatsAppWeb-RS ، أنشأ Wiomoc عميل ويب WhatsApp في Rust.
أنشأت القافية go-whatsapp ، وهي حزمة GO التي تنفذ واجهة برمجة تطبيقات WhatsApp.
VZaramel أنشأ WhatsAppWeb-Clj ، مكتبة Clojure تقوم بتنفيذ واجهة برمجة تطبيقات WhatsApp Web.
يتم تنظيم المشروع بالطريقة التالية. لاحظ المنافذ المستخدمة وتأكد من عدم استخدامها في مكان آخر قبل بدء التطبيق.
يقوم WhatsApp Web بتشفير البيانات باستخدام عدة خوارزميات مختلفة. وتشمل هذه AES 256 CBC ، Curve25519 كمخطط اتفاقية Diffie-Hellman الرئيسية ، HKDF لإنشاء السر المشترك الممتد و HMAC مع SHA256.
يحدث بدء جلسة ويب WhatsApp بمجرد الاتصال بأحد خوادم WebSocket الخاصة به على wss://w[1-8].web.whatsapp.com/ws ( wss:// يعني أن اتصال WebSocket آمن ؛ w[1-8] يعني أن أي رقم بين 1 و 8 يمكن أن يتبع w ). تأكد أيضًا من أنه عند إنشاء الاتصال ، يتم تعيين Origin: https://web.whatsapp.com ، وإلا سيتم رفض الاتصال.
عندما ترسل رسائل إلى WhatsApp Web WebSocket ، يجب أن تكون بتنسيق محدد. إنه بسيط للغاية ويبدو مثل messageTag,JSON ، على سبيل المثال 1515590796,["data",123] . لاحظ أنه على ما يبدو أن علامة الرسالة يمكن أن تكون أي شيء. يستخدم هذا التطبيق في الغالب الطابع الزمني الحالي كعلامة ، فقط لتكون فريدة من نوعها بعض الشيء. غالبًا ما يستخدم WhatsApp نفسه علامات الرسائل مثل s1 ، 1234.--0 أو شيء من هذا القبيل. من الواضح أن علامة الرسالة قد لا تحتوي على فاصلة. بالإضافة إلى ذلك ، فإن كائنات JSON ممكنة وكذلك الحمولة النافعة.
لتسجيل الدخول في WebSocket مفتوح ، اتبع هذه الخطوات:
clientId الخاص بك ، والذي يجب أن يكون 16 بايت مشفرة (أي 25 حرفًا). يستخدم هذا التطبيق فقط 16 بايت عشوائي ، IE base64.b64encode(os.urandom(16)) في بيثون.messageTag,["admin","init",[0,3,2390],["Long browser description","ShortBrowserDesc"],"clientId",true] .messageTag و clientId بالقيم التي اخترتها من قبل[0,3,2390] إصدار ويب WhatsApp الحالي. تتغير القيمة الأخيرة بشكل متكرر. يجب أن تكون متوافقة مع الوراء على الرغم من."Long browser description" عبارة عن سلسلة تعسفية سيتم عرضها في تطبيق WhatsApp في قائمة عملاء WhatsApp المسجلين بعد مسح رمز الاستجابة السريعة."ShortBrowserDesc" في أي مكان حتى الآن ولكنه تعسفي أيضًا.status : يجب أن يكون 200ref : في التطبيق ، يتم التعامل مع هذا كمعرف الخادم ؛ مهم لجيل QR ، انظر أدناهttl : IS 20000 ، ربما الوقت بعد رمز الاستجابة السريعة يصبح غير صالحupdate : علم منطقيcurr : إصدار ويب WhatsApp الحالي ، على سبيل المثال 0.2.7314time : الطابع الزمني الذي استجاب الخادم في ، كمللي ثانية عائمة ، على سبيل المثال 1515592039037.0curve25519.Private() .privateKey.get_public() .ref من الخطوة 4base64.b64encode(publicKey.serialize())pyqrcode ) ومسحها ضوئيًا باستخدام تطبيق WhatsApp.ttl ).messageTag,["admin","Conn","reref"] .status : يجب أن يكون 200 (آخرون: 304 - إعادة استخدام المرجع السابق ، 429 - رفض جديد)ref : المرجع الجديدttl : وقت انتهاء الصلاحيةConn : Array يحتوي على كائن JSON كعنصر ثانٍ مع معلومات الاتصال التي تحتوي على السمات التالية وغيرها الكثير:battery : نسبة البطارية الحالية لهاتفكbrowserToken : يستخدم لتسجيل الخروج دون اتصال WebSocket النشط (لم يتم تنفيذه بعد)clientToken : تستخدم لاستئناف الجلسات المغلقة الملقب "تذكرني" (لم يتم تنفيذها بعد)phone : كائن يحتوي على معلومات مفصلة حول هاتفك ، على سبيل المثال device_manufacturer ، device_model ، os_build_number ، os_versionplatform : نظام تشغيل هاتفك ، على سبيل المثال androidpushname : اسم لك قدمت WhatsAppsecret (تذكر هذا!)serverToken : تستخدم لاستئناف الجلسات المغلقة الملقب "تذكرني" (لم يتم تنفيذها بعد)wid : رقم هاتفك بتنسيق تعريف الدردشة (انظر أدناه)Stream : Array يحتوي على أربعة عناصر في المجموع ، وبالتالي فإن الحمولة الكاملة مثل ["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. استدعاء هذه القيمة sharedSecretExpanded .HmacSha256(sharedSecretExpanded[32:64], secret[:32] + secret[64:]) . قارن هذه القيمة secret[32:64] . إذا لم تكن متساوية ، فقم بإحباط تسجيل الدخول.sharedSecretExpanded[64:] + secret[64:] كما keysEncrypted .sharedSecretExpanded[:32] كمفتاح ، أي متجر AESDecrypt(sharedSecretExpanded[:32], keysEncrypted) keysDecrypted .keysDecrypted 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 في JSON: إذا كان مساوياً أو أكبر من 2 ، فقد انتهكت TOSserverToken القديم أو منتهي clientToken ، سيتم تحديك لتأكيد أنه لا يزال لديك مفاتيح تشفير صالحة.messageTag,["Cmd",{"type":"challenge","challenge":"BASE_64_ENCODED_STRING=="}]challenge Decode من 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 و BINARY على علامة رسالة في بدايتها التي يمكن تجاهلها ، أي فقط الجزء بعد أن يكون شخصية الفاصلة الأولى مهمة.
macKey (هنا messageContent هي الرسالة الثنائية بأكملها ): HmacSha256(macKey, messageContent[32:]) . إذا كانت هذه القيمة لا تساوي messageContent[:32] ، فإن الرسالة المرسلة إليك بواسطة الخادم غير صالحة ويجب التخلص منها.encKey : AESDecrypt(encKey, messageContent[32:]) .البيانات التي تحصل عليها في الخطوة النهائية لها تنسيق ثنائي موصوف في ما يلي. على الرغم من أنها ثنائية ، لا يزال بإمكانك رؤية عدة سلاسل فيه ، وخاصة محتوى الرسائل التي أرسلتها واضحًا تمامًا هناك.
يقوم Python Script backend/decoder.py بتنفيذ فئة MessageParser . إنه قادر على إنشاء بنية JSON من البيانات الثنائية التي لا تزال البيانات منظمة بطريقة فوضوية إلى حد ما. سيناقش القسم المتعلق بمعالجة العقدة أدناه كيفية إعادة تنظيم العقد بعد ذلك.
يحتاج MessageParser في البداية إلى بعض البيانات ثم يقوم بمعالجتها Byte by 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"]- لمدة 10 ، . لمدة 11 و