Dieses Projekt beabsichtigt, eine vollständige Beschreibung und Neuimplementierung der WhatsApp-Web-API bereitzustellen, die schließlich zu einem benutzerdefinierten Client führt. WhatsApp Web funktioniert intern mit Websockets. Dieses Projekt tut auch.
Die Datei shell.nix definiert keine Umgebung mit allen Abhängigkeiten, die für die Ausführung dieses Projekts enthalten sind, nicht installieren oder verwalten.
Es gibt eine .envrc -Datei im cd , der beim Projekt automatisch aufgerufen wird. Wenn das Programm direnv zusammen mit nix installiert ist, sollten Sie eine solche Ausgabe erhalten:
> 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]$ Wenn Sie direnv nicht verwenden oder nur manuell in die Build -Umgebung einsteigen möchten:
nix-shellin der Projektwurzel
Bevor Sie die Anwendung ausführen können, stellen Sie sicher, dass die folgende Software installiert ist:
async await die Syntax aufwächst)pip -Paketen installiert:websocket-client und git+https://github.com/dpallot/simple-websocket-server.git für das Handeln als WebSocket-Server und als Client.curve25519-donna und pycrypto für das Verschlüsselungsmaterial.pyqrcode für die QR -Codegenerierung.protobuf zum Lesen und Schreiben des binären Konversationsformats.curve25519-donna benötigt Microsoft Visual C ++ 9.0 und Sie müssen stdint.h in C:UsersYOUR USERNAMEAppDataLocalProgramsCommonMicrosoftVisual C++ for Python9.0VCinclude kopieren. Führen Sie vor dem ersten Starten der Anwendung npm install -f aus, um alle Knoten- und pip install -r requirements.txt zu installieren. Txt für alle Python -Abhängigkeiten.
Um es endlich zu starten, führen Sie einfach npm start unter Linux -basiertem OS- und npm run win unter Windows aus. Mit der concurrently und nodemon Magie werden alle drei lokalen Komponenten nacheinander gestartet. Wenn Sie eine Datei bearbeiten, werden das geänderte Modul automatisch neu gestartet, um die Änderungen anzuwenden.
Eine aktuelle Ergänzung ist eine Version der Entschlüsselungsroutine, die in den Browser-JavaScript übersetzt wurde. Führen Sie node index_jsdemo.js aus (nur benötigt, weil Browser nicht zulassen, dass die HTTP-Header für WebSockets geändert werden) und dann client/login-via-js-demo.html als normale Datei in jedem Browser öffnen. Die Konsolenausgabe sollte nach dem Scannen des QR -Codes entschlüsselte Binärnachrichten angezeigt werden.
Adiwajshing hat Baileys erstellt, eine Knotenbibliothek, die die WhatsApp -Web -API implementiert.
Ndunks hat bei WAJS eine Typscript -Neuimplementierung durchgeführt.
P4KL0NC4T hat Kyros erstellt, ein Python -Paket, das die WhatsApp -Web -API implementiert.
Mit WhatsAppweb-Rs hat Wiomoc einen WhatsApp-Web-Client in Rost erstellt.
Reimen erstellte Go-WhatsApp, ein Go-Paket, das die WhatsApp-Web-API implementiert.
Vzaramel erstellt WhatsAppweb-clj, eine Clojure-Bibliothek, die die WhatsApp-Web-API implementiert.
Das Projekt ist auf folgende Weise organisiert. Beachten Sie die gebrauchten Ports und stellen Sie sicher, dass sie vor Beginn der Anwendung nicht an anderer Stelle verwendet werden.
WhatsApp -Web verschlüsselt die Daten mit mehreren verschiedenen Algorithmen. Dazu gehören AES 256 CBC, CURVE25519 als Diffie-Hellman-Schlüsselvereinbarungsschema, HKDF zur Generierung des erweiterten gemeinsam genutzten Geheimnisses und HMAC mit SHA256.
Das Starten der WhatsApp-Websitzung erfolgt, indem nur eine Verbindung zu einem seiner WebSocket-Server unter wss://w[1-8].web.whatsapp.com/ws w[1-8] w wss:// bedeutet, dass die WebSocket-Verbindung sicher ist; Stellen Sie außerdem sicher, dass bei der Erstellung der Verbindung der HTTP -Header Origin: https://web.whatsapp.com festgelegt wird, andernfalls wird die Verbindung abgelehnt.
Wenn Sie Nachrichten an ein WhatsApp -Websocket senden, müssen sie in einem bestimmten Format sein. Es ist ganz einfach und sieht aus wie messageTag,JSON , z. B. 1515590796,["data",123] . Beachten Sie, dass anscheinend das Nachrichten -Tag alles sein kann. Diese Anwendung verwendet hauptsächlich den aktuellen Zeitstempel als Tag, nur um ein bisschen einzigartig zu sein. WhatsApp selbst verwendet oft Nachrichten-Tags wie s1 , 1234.--0 oder ähnliches. Offensichtlich enthält das Nachrichten -Tag möglicherweise kein Komma. Zusätzlich sind JSON -Objekte sowohl möglich als auch für die Nutzlast.
Befolgen Sie die folgenden Schritte, um sich bei einem offenen WebSocket anzumelden:
clientId , die 16 Base64-kodierte Bytes (dh 25 Zeichen) sein muss. Diese Anwendung verwendet nur 16 zufällige Bytes, dh base64.b64encode(os.urandom(16)) in Python.messageTag,["admin","init",[0,3,2390],["Long browser description","ShortBrowserDesc"],"clientId",true] .messageTag und clientId durch die Werte ersetzen, die Sie zuvor ausgewählt haben[0,3,2390] gibt die aktuelle WhatsApp -Webversion an. Der letzte Wert ändert sich häufig. Es sollte allerdings ziemlich gut kompatibel sein."Long browser description" ist eine beliebige Zeichenfolge, die in der WhatsApp -App in der Liste der registrierten WhatsApp -Web -Clients nach dem Scannen des QR -Codes angezeigt wird."ShortBrowserDesc" wurde noch nirgendwo beobachtet, ist aber ebenfalls willkürlich.status : sollte 200 seinref : In der Anwendung wird dies als Server -ID behandelt. wichtig für die QR -Generation, siehe untenttl : IS 20000, vielleicht die Zeit nach dem QR -Code ungültigupdate : Eine Boolesche Flaggecurr : Die aktuelle WhatsApp -Webversion, z. B. 0.2.7314time : Der Zeitstempel des Servers reagierte mit Floating-Punkt-Millisekunden, z. B. 1515592039037.0curve25519.Private() .privateKey.get_public() .ref -Attribut aus Schritt 4base64.b64encode(publicKey.serialize())pyqrcode ) und scannen Sie sie mit der WhatsApp -App.ttl ) vorhanden sind.messageTag,["admin","Conn","reref"] senden.status : Sollte 200 sein (andere: 304 - Vorherige Ref, 429 - neuer Schiedsrichter abgelehnt)ref : New Refttl : AblaufzeitConn : Array enthält JSON -Objekt als zweites Element mit Verbindungsinformationen, die die folgenden Attribute und vieles mehr enthalten:battery : Der aktuelle Batterieanteil Ihres TelefonsbrowserToken : Wird verwendet, um sich ohne aktive WebSocket -Verbindung anzumelden (noch nicht implementiert)clientToken : Wird verwendet, um geschlossene Sitzungen wieder aufzunehmen, auch als "Erinnere dich an mich" (noch nicht implementiert)phone : Ein Objekt mit detaillierten Informationen zu os_version Telefon os_build_number device_model device_manufacturerplatform : Ihr Telefon -Betriebssystem, z. B. androidpushname : Der Name von Ihnen, den Sie WhatsApp angegeben habensecret (erinnere dich daran!)serverToken : Wird verwendet, um geschlossene Sitzungen wieder aufzunehmen, auch als "Erinnere dich an mich" (noch nicht implementiert)wid : Ihre Telefonnummer im Chat -Identifikationsformat (siehe unten)Stream : Array hat insgesamt vier Elemente, daher ist die gesamte Nutzlast wie ["Stream","update",false,"0.2.7314"]Props : Array enthält ein JSON -Objekt als zweites Element mit mehreren Eigenschaften wie imageMaxKBytes (1024), maxParticipants (257), videoMaxEdge (960) und anderensecret von Conn als Base64 und speichern Sie es als secret . Dieses dekodierte Geheimnis wird 144 Bytes lang sein.sharedSecret . In der Anwendung wird privateKey.get_shared_key(curve25519.Public(secret[:32]), lambda a:a) verwendet.sharedSecret auf 80 Bytes mit HKDF. Nennen Sie diesen Wert sharedSecretExpanded .HmacSha256(sharedSecretExpanded[32:64], secret[:32] + secret[64:]) berechnen. Vergleichen Sie diesen Wert mit secret[32:64] . Wenn sie nicht gleich sind, lehnen Sie die Anmeldung ab.sharedSecretExpanded[64:] + secret[64:] als keysEncrypted .sharedSecretExpanded[:32] als Schlüssel entschlüsselt werden, dh speichern AESDecrypt(sharedSecretExpanded[:32], keysEncrypted) als keysDecrypted .keysDecrypted -Variable ist 64 Bytes lang und enthält zwei Schlüssel, jeweils 32 Bytes. Der encKey wird zum Entschlüsseln von Binärnachrichten verwendet, die Sie vom WhatsApp -Webserver gesendet oder binäre Nachrichten, die Sie an den Server senden, verschlüsseln. Der macKey wird benötigt, um die an Sie gesendeten Nachrichten zu validieren:encKey : keysDecrypted[:32]macKey : keysDecrypted[32:64]init -Befehl, ob Sie serverToken und clientToken haben.messageTag,["admin","login","clientToken","serverToken","clientId","takeover"]{"status": 200} antworten. Andere Status:tos -Feld im JSON: Wenn es gleich oder größer als 2 ist, haben Sie TOS verletztserverToken und clientToken verwenden, werden Sie aufgefordert, zu bestätigen, dass Sie weiterhin gültige Verschlüsselungsschlüssel haben.messageTag,["Cmd",{"type":"challenge","challenge":"BASE_64_ENCODED_STRING=="}]challenge -String von Base64, unterschreiben Sie sie mit Ihrem Mackey, codieren Sie sie mit Base64 zurück und senden Sie messageTag,["admin","challenge","BASE_64_ENCODED_STRING==","serverToken","clientId"]{"status": 200} antworten, aber es bedeutet nichts.goodbye,,["admin","Conn","disconnect"] .encKey mit Ihrem macKey und codieren Sie sie mit Base64. Nehmen wir an, es ist Ihr logoutToken .https://dyn.web.whatsapp.com/logout?t=browserToken&m=logoutTokenNachdem Sie die beiden Schlüssel haben, ist es recht einfach, Nachrichten zu validieren und zu entschlüsseln, die der Server an Sie gesendet wird. Beachten Sie, dass dies nur für binäre Nachrichten benötigt wird. Alle, die Sie erhalten, bleibt einfach. Die binären Nachrichten haben zu Beginn immer 32 Bytes, die die HMAC -Prüfsumme angeben. Sowohl JSON- als auch binäre Nachrichten haben zu ihrem Start, der weggeworfen werden kann, dh nur der Teil nach dem ersten Komma -Charakter ist signifikant.
macKey -Makey (hier messageContent ist die gesamte Binärnachricht): HmacSha256(macKey, messageContent[32:]) . Wenn dieser Wert nicht gleich messageContent[:32] ist, ist die vom Server gesendete Nachricht ungültig und sollte verworfen werden.encKey : AESDecrypt(encKey, messageContent[32:]) .Die Daten, die Sie im letzten Schritt erhalten, haben ein binäres Format, das im Folgenden beschrieben wird. Auch wenn es Binär ist, können Sie immer noch mehrere Saiten darin sehen, insbesondere der Inhalt der von Ihnen gesendeten Nachrichten.
Das Python -Skript backend/decoder.py implementiert die MessageParser -Klasse. Es ist in der Lage, eine JSON -Struktur aus binären Daten zu erstellen, in denen die Daten noch ziemlich chaotisch organisiert sind. In dem Abschnitt über die folgende Notenbehandlung wird erläutert, wie die Knoten anschließend neu organisiert werden.
MessageParser benötigt zunächst nur einige Daten und verarbeitet es dann Byte nach Byte, dh als Stream. Es hat ein paar Konstanten und viele Methoden, die alle aufeinander aufbauen.
[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"]- für 10 , . für 11 und