이 프로젝트는 WhatsApp Web API의 완전한 설명과 재 구현을 제공하고 결국 사용자 정의 클라이언트로 이어질 계획입니다. WhatsApp 웹은 내부적으로 websockets를 사용하여 작동합니다. 이 프로젝트도 마찬가지입니다.
파이썬 및 노드 버전을 설치하거나 관리 할 필요가 없습니다. 파일 shell.nix 이 프로젝트를 실행하기 위해 모든 종속성이 포함 된 환경을 정의합니다.
루트 폴더에는 cd ing (변경 디렉토리) direnv 프로젝트를 변경할 때 자동으로 호출되는 .envrc 파일이 있습니다 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프로젝트 루트에서
응용 프로그램을 실행하기 전에 다음 소프트웨어가 설치되어 있는지 확인하십시오.
async await 구문이 사용됨)pip 패키지가 설치된 Python 2.7 :websocket-client 및 git+https://github.com/dpallot/simple-websocket-server.git WebSocket 서버 및 클라이언트 역할을합니다.curve25519-donna 및 pycrypto .pyqrcode .protobuf .C:UsersYOUR USERNAMEAppDataLocalProgramsCommonMicrosoftVisual C++ for Python9.0VCinclude stdint.h curve25519-donna 처음으로 응용 프로그램을 시작하기 전에 모든 Python 종속성에 대해 모든 노드 및 pip install -r requirements.txt 설치하려면 npm install -f 실행하십시오.
마지막으로 마침내 시작하려면 Linux 기반 OS 및 npm run win 에서 npm start 실행하여 Windows에서 실행하십시오. concurrently Fancy와 nodemon Magic을 사용하면 세 가지 로컬 구성 요소가 모두 서로 시작되며 파일을 편집하면 변경된 모듈이 자동으로 다시 시작되어 변경 사항을 적용합니다.
최근 추가는 브라우저 내 JavaScript로 번역 된 암호 해독 루틴의 버전입니다. node index_jsdemo.js client/login-via-js-demo.html 하십시오 (브라우저에서 websockets에 대한 HTTP 헤더 변경을 허용하지 않기 때문에 필요한 경우). 콘솔 출력은 QR 코드를 스캔 한 후 해독 된 이진 메시지를 표시해야합니다.
Adiwajshing은 WhatsApp 웹 API를 구현하는 노드 라이브러리 인 Baileys를 만들었습니다.
Ndunks는 Wajs에서 TypeScript Remementation을 만들었습니다.
P4KL0NC4T는 WhatsApp 웹 API를 구현하는 파이썬 패키지 인 Kyros를 만들었습니다.
WhatsAppWeb-rs와 함께 Wiomoc은 Rust에서 WhatsApp 웹 클라이언트를 만들었습니다.
Rhymen은 WhatsApp 웹 API를 구현하는 GO 패키지 인 Go-Whatsapp을 만들었습니다.
Vzaramel은 Clojure 라이브러리 인 WhatsAppWeb-Clj를 만들었습니다.
이 프로젝트는 다음과 같은 방식으로 구성됩니다. 중고 포트에 주목하고 응용 프로그램을 시작하기 전에 다른 곳에서 사용하지 않는지 확인하십시오.
WhatsApp 웹은 여러 다른 알고리즘을 사용하여 데이터를 암호화합니다. 여기에는 AES 256 CBC, CURVE25519가 Diffie-Hellman 키 계약 체계로, HKDF를 SHA256과 함께 확장 공유 비밀 및 HMAC를 생성하는 HKDF가 포함됩니다.
WhatsApp 웹 세션 시작 wss://w[1-8].web.whatsapp.com/ws ( wss:// 은 WebSocket 연결이 보안임을 의미합니다. w[1-8] 1에서 8 사이의 숫자가 w 를 따를 수 있음을 의미합니다. 또한 연결을 설정할 때 HTTP 헤더 Origin: https://web.whatsapp.com 설정되어 있으면 연결이 거부됩니다.
WhatsApp WebSocket에 메시지를 보내면 특정 형식이어야합니다. 그것은 매우 간단하고 messageTag,JSON , 예를 들어 1515590796,["data",123] 처럼 보입니다. 메시지 태그는 무엇이든 될 수 있습니다. 이 응용 프로그램은 주로 현재 타임 스탬프를 태그로 사용합니다. WhatsApp 자체는 종종 s1 , 1234.--0 과 같은 메시지 태그를 사용합니다. 분명히 메시지 태그에는 쉼표가 포함되어 있지 않을 수 있습니다. 또한 JSON 객체 와 페이로드가 가능합니다.
열린 WebSocket에 로그인하려면 다음 단계를 따르십시오.
clientId 생성하십시오. 이 응용 프로그램은 파이썬에서 16 개의 무작위 바이트, 즉 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" QR 코드를 스캔 한 후 등록 된 WhatsApp 웹 클라이언트 목록의 WhatsApp 앱에 표시되는 임의의 문자열입니다."ShortBrowserDesc" 아직 어느 곳에서나 관찰되지 않았지만 임의적입니다.status : 200 여야합니다ref : 응용 프로그램에서 이것은 서버 ID로 취급됩니다. QR 생성에 중요합니다. 아래를 참조하십시오ttl : 20000, 아마도 QR 코드가 유효하지 않은 시간 일 것입니다.update : 부울 깃발curr : 현재 WhatsApp 웹 버전, 예를 들어 0.2.7314time : 서버가 부동 소수점 밀리 초로 응답 한 타임 스탬프, 예를 들어 1515592039037.0curve25519.Private() 로 자신의 개인 키를 생성하십시오.privateKey.get_public() 에서 공개 키를 얻으십시오.ref 속성base64.b64encode(publicKey.serialize())pyqrcode 사용)로 바꾸고 WhatsApp 앱을 사용하여 스캔하십시오.ttl ).messageTag,["admin","Conn","reref"] 보내서 수행하십시오.status : 200 명이어야합니다 (기타 : 304- 이전 Ref, 429- 새로운 심판 거부)ref : 새로운 참조ttl : 만료 시간Conn : 배열은 다음 속성을 포함하는 연결 정보가있는 두 번째 요소로 JSON 객체를 포함합니다.battery : 휴대 전화의 현재 배터리 백분율browserToken : 활성 WebSocket 연결없이 로그 아웃하는 데 사용됩니다 (아직 구현되지 않음)clientToken : 폐쇄 세션을 재개하는 데 사용되는 "기억 나"(아직 구현되지 않음)phone : 휴대 전화에 대한 자세한 정보가있는 객체, 예 : device_manufacturer , device_model , os_build_number , os_versionplatform : 전화 OS, 예를 들어 androidpushname : WhatsApp을 제공 한 이름secret (이것을 기억하십시오!)serverToken : 폐쇄 된 세션을 재개하는 데 사용되는 "기억 나"(아직 구현되지 않음)wid : 채팅 식별 형식의 전화 번호 (아래 참조)Stream : 배열은 총 4 개의 요소가 있으므로 전체 페이로드 ["Stream","update",false,"0.2.7314"]Props : 배열은 imageMaxKBytes (1024), maxParticipants (257), videoMaxEdge (960) 등과 같은 여러 속성을 가진 두 번째 요소로 JSON 객체를 포함합니다.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] 키로 사용하여 AESDecrypt(sharedSecretExpanded[:32], keysEncrypted) keysDecrypted 함께 aes를 사용하여 해독해야합니다.keysDecrypted 변수의 길이는 64 바이트이며 각각 32 바이트 길이의 두 개의 키를 포함합니다. encKey WhatsApp 웹 서버에서 보내는 바이너리 메시지를 해독하는 데 사용되거나 서버에 보내는 이진 메시지를 암호화하는 데 사용됩니다. macKey 귀하에게 보낸 메시지를 확인하기 위해 필요합니다.encKey : keysDecrypted[:32]macKey : keysDecrypted[32:64]init Command를 보내면 serverToken 과 clientToken 있는지 확인하십시오.messageTag,["admin","login","clientToken","serverToken","clientId","takeover"]{"status": 200} 으로 응답해야합니다. 기타 상태 :tos 필드를 점검 : 2보다 동일하면 TOS를 위반 한 것입니다.serverToken 및 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 에 게시물 요청을 보내십시오이제 서버가 보낸 메시지를 검증하고 해독하는 두 개의 키를 갖추 었으므로 매우 쉽습니다. 이것은 이진 메시지에만 필요합니다. 이진 메시지에는 항상 HMAC 체크섬을 지정하는 처음에 32 바이트가 있습니다. JSON 과 바이너리 메시지는 처음에는 버릴 수있는 메시지 태그를 가지고 있습니다. 즉, 첫 번째 쉼표 문자 후 부분 만 중요합니다.
macKey 로 실제 메시지 내용을 해싱하여 메시지를 확인하십시오 (여기서 messageContent 전체 이진 메시지입니다) : HmacSha256(macKey, messageContent[32:]) . 이 값이 messageContent[:32] 와 같지 않으면 서버가 귀하에게 보내는 메시지가 유효하지 않아 폐기해야합니다.encKey 사용하여 메시지 내용을 해독합니다 : AESDecrypt(encKey, messageContent[32:]) .최종 단계에서 얻는 데이터에는 다음에 설명 된 이진 형식이 있습니다. 이진이지만 여전히 여러 줄을 볼 수 있습니다. 특히 보낸 메시지의 내용은 분명합니다.
Python Script 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 . 11 및