From 5e58b46e6952fe215cad6dd859963e6244b168f7 Mon Sep 17 00:00:00 2001 From: lolcat Date: Thu, 6 Mar 2025 21:42:34 -0500 Subject: [PATCH] the zuccing begins --- .gitignore | 3 + README.md | 13 + login.js | 837 ++++++++++++++++++++++++++++++++++++++++++++++++ util_decoder.js | 32 ++ 4 files changed, 885 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 login.js create mode 100644 util_decoder.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e710a67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +package.json +package-lock.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..6ae694f --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# Facebook messenger bot +Shitty facebook messenger bot unofficial API written in nodeJS. This mimicks the requests of a browser. + +## Roadmap +[x] Login (with user&pass) +[x] Connect to websocket +[x] Send messages (sort of) +[ ] Receive messages +[ ] Get friend list +[ ] Eh who gives a shit i give up writing this readme + +## License +wtfpl diff --git a/login.js b/login.js new file mode 100644 index 0000000..a796110 --- /dev/null +++ b/login.js @@ -0,0 +1,837 @@ +const { fetch } = require("fetch-h2"); +const tls = require("tls"); +const crypto = require('crypto'); +const nacl = require('tweetnacl'); +const sealedbox = require('tweetnacl-sealedbox-js'); +const zlib = require("zlib"); +const mqtt = require("mqtt-packet"); +const WebSocket = require("ws"); +const user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0'; + +// config +config = { + email: "yourmail@gmail.com", + pass: "yourpass" +}; + +// firefox bypass +const agent = { + secureProtocol: "TLS_method", + minVersion: "TLSv1.2", + maxVersion: "TLSv1.3", + ciphers: [ + "TLS_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-CHACHA20-POLY1305" + ].join(":"), + honorCipherOrder: true +}; + +// consts +const POST_URLENCODE = 0; +const POST_JSON = 1; +const POST_RAW = 2; + +// +// Helper functions +// + +function merge_headers(headers){ + + var base_headers = { + 'User-Agent': user_agent, + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'en-US,en;q=0.5', + 'Accept-Encoding': 'gzip', + 'Referer': 'https://www.messenger.com/', + //'Content-Type': 'application/x-www-form-urlencoded', + //'Content-Length': '317', + 'Origin': 'https://www.messenger.com', + 'DNT': '1', + 'Sec-GPC': '1', + //'Connection': 'keep-alive', + //'Cookie': 'datr=Kh69Z0FC3H-Stoj1M5vR7tXx; wd=1011x1000', + 'Upgrade-Insecure-Requests': '1', + 'Sec-Fetch-Dest': 'document', + 'Sec-Fetch-Mode': 'navigate', + 'Sec-Fetch-Site': 'same-origin', + 'Sec-Fetch-User': '?1', + 'Priority': 'u=0, i', + 'TE': 'trailers' + }; + + for(let key in headers){ + + base_headers[key] = headers[key]; + } + + return base_headers; +} + +function make_guid(){ + var date = Date.now(); + + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { + const r = Math.floor((date + Math.random() * 16) % 16); + return (c == 'x' ? r : (r & 7) | 8).toString(16); + }); +} + +function make_otid(){ + + const rand = Math.floor(Math.random() * 4294967295); + const str = ('0000000000000000000000' + rand.toString(2)).slice(-22); + const msgs = Date.now().toString(2) + str; + return binaryToDecimal(msgs); +} + +function binaryToDecimal(data){ + let ret = ''; + while (data !== '0') { + let end = 0; + let fullName = ''; + let i = 0; + for (; i < data.length; i++) { + end = 2 * end + parseInt(data[i], 10); + if (end >= 10) { + fullName += '1'; + end -= 10; + } else { + fullName += '0'; + } + } + ret = end.toString() + ret; + data = fullName.slice(fullName.indexOf('1')); + } + return ret; +} + +function http_build_query(array){ + + var str = []; + + for(let key in array){ + + str.push(encodeURIComponent(key) + "=" + encodeURIComponent(array[key])); + } + + return str.join("&"); +} + +async function get_data(url, headers = {}){ + + const data = await fetch( + url, + { + headers: merge_headers(headers), + httpsOptions: agent, + allowForbiddenHeaders: true + } + ); + + const body = await data.text(); + + return { + status: data.status, + headers: data.headers._data, + data: body + }; +} + +async function post_data(url, data, encode_as, headers = {}){ + + switch(encode_as){ + + case POST_URLENCODE: + data = http_build_query(data); + headers["Content-Type"] = "application/x-www-form-urlencoded"; + break; + + case POST_JSON: + data = JSON.stringify(data); + headers["Content-Type"] = "application/json"; + break; + } + + headers["Content-Length"] = data.length; + + const res = await fetch( + url, + { + method: "POST", + headers: merge_headers(headers), + body: data, + httpsOptions: agent, + allowForbiddenHeaders: true + } + ); + + const body = await res.text(); + + return { + status: res.status, + headers: res.headers._data, + data: body + }; +} + +// +// Facebook login +// + +init_login(); +async function init_login(){ + + login(get_init_data()); +} + + +async function get_init_data(){ + + console.log("(info) Getting FB public key"); + + // + // Step 1 + // get keyId, publicKey, initial_request_id & other shits + // + var keys = + await get_data( + "https://www.messenger.com/" + ); + + var keyId = keys.data.match(/"keyId":([0-9]+)/); + if(keyId === null){ + + throw new Error("Could not grep keyId"); + } + + keyId = parseInt(keyId[1]); + console.log("(debug) Got keyId=" + keyId); + + // get publicKey + var publicKey = keys.data.match(/"publicKey":"([A-Za-z0-9]+)"/); + if(publicKey === null){ + + throw new Error("Could not grep publicKey"); + } + console.log("(debug) publicKey=" + publicKey[1]); + + // convert public key to binary + publicKey = publicKey[1]; + + // get initial_request_id + var initial_request_id = keys.data.match(/(?:name|id)=\"initial_request_id\" value=\"([A-Za-z0-9_-]+)\"/); + if(initial_request_id === null){ + + throw new Error("Could not grep initial_request_id"); + } + + console.log("(debug) Got initial_request_id=" + initial_request_id[1]); + initial_request_id = initial_request_id[1]; + + // get lgnrnd + var lgnrnd = keys.data.match(/(?:name|id)=\"lgnrnd\" value=\"([A-Za-z0-9_-]+)\"/); + if(lgnrnd === null){ + + throw new Error("Could not grep initial_request_id"); + } + + console.log("(debug) Got lgnrnd=" + lgnrnd[1]); + lgnrnd = lgnrnd[1]; + + // get LSD token + var lsd = keys.data.match(/\["LSD"(?:,\[\])?,{"token":"([A-Za-z0-9_-]+)"}/); + if(lsd === null){ + + throw new Error("Could not grep lsd"); + } + + console.log("(debug) Got lsd=" + lsd[1]); + lsd = lsd[1]; + + return { + keyId: keyId, + publicKey: publicKey, + initial_request_id: initial_request_id, + lgnrnd: lgnrnd, + lsd: lsd + }; +} + + +async function login(init){ + + init = await init; + + // + // Step 2. + // Encrypt form data + // + const time = Math.floor(Date.now() / 1000); + const key = crypto.randomBytes(32); + const iv = Buffer.alloc(12, 0); + + // Create AES-GCM cipher + const cipher = crypto.createCipheriv("aes-256-gcm", key, iv); + cipher.setAAD(Buffer.from(time.toString(), "utf-8")); + + // Encrypt password + let encryptedPassword = cipher.update(config.pass, "utf8", "binary"); + encryptedPassword += cipher.final("binary"); + encryptedPassword = Buffer.from(encryptedPassword, "binary"); + const cipherTag = cipher.getAuthTag(); + + // Encrypt AES key with public key + const publicKey_d = Buffer.from(init.publicKey, "hex"); + const sealedBox = sealedbox.seal(key, publicKey_d); + const encryptedKey = Buffer.from(sealedBox); + + // Build final payload + const keyIdBuffer = Buffer.from([1, init.keyId]); + const lengthBuffer = Buffer.alloc(2); + lengthBuffer.writeUInt16LE(encryptedKey.length, 0); + + const data = Buffer.concat([ + keyIdBuffer, + lengthBuffer, + encryptedKey, + cipherTag, + encryptedPassword + ]); + + // Base64 encode and format + const encrypted_data = data.toString("base64"); + + // + // Step 3 + // Send login req + // + + console.log("(info) Initiating login in 10 seconds using " + encrypted_data); + setTimeout(async function(){ + + console.log("(info) Sending login information"); + var redirect = + await post_data( + "https://www.messenger.com/login/password/", + { + jazoest: "21035", + lsd: init.lsd, + initial_request_id: init.initial_request_id, + timezone: "300", + lgndim: btoa('{"w":1920,"h":1080,"aw":1920,"ah":1080,"c":24}'), + lgnrnd: init.lgnrnd, + lgnjs: "n", + email: config.email, + pass: "#PWD_BROWSER:5:" + time + ":" + encrypted_data, + default_persistent: "" + }, + POST_URLENCODE, + { + "Cookie": "datr=m5vDZzGXLy2_XEtAGDrB6Cgi; wd=1920x1080" + } + ); + + // + // Check if login was successful + // + if( + redirect.headers.has("location") === false || + ( + redirect.headers.get("location") != "https://www.messenger.com/" && + redirect.headers.get("location") != "https://www.messenger.com" + ) + ){ + + throw new Error("Did not obtain correct redirect value"); + } + + // + // Get cookies + // + if(redirect.headers.has("set-cookie") === false){ + + throw new Error("Did not obtain any cookies"); + } + + const cookies_raw = redirect.headers.get("set-cookie"); + var cookies = []; + + for(var i=0; i m[1]); + + if(clientIDs === null){ + + throw new Error("Could not grep clientID(s)"); + } + + console.log("(debug) Got " + clientIDs.length + " clientIDs: [" + clientIDs.join(", ") + "]"); + */ + + // get appIDs for websocket edges + var appID_status = main_page.data.match(/"appId":([0-9]+)/); + if(appID_status === null){ + + throw new Error("Failed to grep appId for status websocket"); + } + appID_status = parseInt(appID_status[1]); + console.log("(debug) Got appID_status " + appID_status); + + var appID_msg = main_page.data.match(/"appID":([0-9]+)/); + if(appID_msg === null){ + + throw new Error("Failed to grep appId for msg websocket"); + } + appID_msg = parseInt(appID_msg[1]); + console.log("(debug) Got appID_msg " + appID_msg); + + // get version + var version = main_page.data.match(/\\"version\\":([0-9]{4,})/); + if(version === null){ + + throw new Error("Failed to grep version"); + } + version = version[1]; + console.log("(debug) Got version " + version); + + // + // Connect to the websocket edges + // + + // connect to the status server (random GUID, no foreground) + //ws_connect("status", cookies, appID_status, make_guid(), false); + + // connect to the message server (defined GUID, has foreground, version) + ws_connect("msg", cookies, appID_status, make_guid(), true, version); +} + +async function ws_connect(ident, cookies, appID, edgeName, foreground, version){ + + var msg_c = 0 + var task_id = 1; + + // + // Split up cookies + // + cookies_s = cookies.split(";"); + cookies_a = []; + + for(var i=0; i