该项目旨在提供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和