From 50e44abaff47794ced6841f77cbe719e9b37313d Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Tue, 20 Jun 2023 17:30:35 -0600 Subject: [PATCH] client spoof back --- src/index.js | 97 +++++++++++++++++++++++++++--------------- src/lib/clientSpoof.js | 62 +++++++++++++++++++-------- 2 files changed, 106 insertions(+), 53 deletions(-) diff --git a/src/index.js b/src/index.js index 0cfe744..8a95668 100644 --- a/src/index.js +++ b/src/index.js @@ -83,9 +83,9 @@ process.stdin.setEncoding("utf8"); client.once("ready", function () { console.log( "Logged in as: " + - chalk.yellow(`${client.user.username} (${client.user.id})`) + chalk.yellow(`${client.user?.username} (${client.user?.id})`) ); - comcord.state.nameLength = client.user.username.length + 2; + comcord.state.nameLength = (client.user?.username?.length ?? 0) + 2; listGuilds(); @@ -242,10 +242,7 @@ if ( config.allowUserAccounts == "true" && !(token ?? config.token).startsWith("Bot ") ) { - console.log("User account support pending rewrite."); - process.exit(1); - - /*if (fetch == null) { + if (fetch == null) { console.log("Node v18+ needed for user account support."); process.exit(1); } @@ -253,48 +250,78 @@ if ( (async function () { comcord.clientSpoof = require("./lib/clientSpoof"); const superProperties = await comcord.clientSpoof.getSuperProperties(); + comcord.clientSpoof.superProperties = superProperties; + comcord.clientSpoof.superPropertiesBase64 = Buffer.from( + JSON.stringify(superProperties) + ).toString("base64"); - console.log("% Allowing non-bot tokens to connect"); - const connectLines = client.connect.toString().split("\n"); - connectLines.splice(0, 4); - connectLines.splice(-1, 1); + // FIXME: is there a way we can string patch functions without having to + // dump locals into global + global.MultipartData = require("@projectdysnomia/dysnomia/lib/util/MultipartData.js"); + global.SequentialBucket = require("@projectdysnomia/dysnomia/lib/util/SequentialBucket.js"); + global.DiscordHTTPError = require("@projectdysnomia/dysnomia/lib/errors/DiscordHTTPError.js"); + global.DiscordRESTError = require("@projectdysnomia/dysnomia/lib/errors/DiscordRESTError.js"); + global.Zlib = require("node:zlib"); + global.HTTPS = require("node:https"); + global.HTTP = require("node:http"); + global.GatewayOPCodes = Constants.GatewayOPCodes; + global.GATEWAY_VERSION = Constants.GATEWAY_VERSION; - const newConnect = new client.connect.constructor(connectLines.join("\n")); - client.connect = newConnect.bind(client); + client.getGateway = async function getGateway() { + return {url: "wss://gateway.discord.gg"}; + }; console.log("% Injecting headers into request handler"); - client.rest.handler.options.userAgent = `Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/${superProperties.client_version} Chrome/91.0.4472.164 Electron/13.6.6 Safari/537.36`; - client.rest.handler._request = client.rest.handler.request.bind( - client.rest.handler - ); - client.rest.handler.request = async function (options) { - options.headers = options.headers ?? {}; - options.headers["X-Super-Properties"] = - await comcord.clientSpoof.getSuperPropertiesBase64(); + client.requestHandler.userAgent = superProperties.browser_user_agent; + const requestFunction = client.requestHandler.request.toString(); + const newRequest = requestFunction + .replace( + "this.userAgent,", + 'this.userAgent,\n"X-Super-Properties":comcord.clientSpoof.superPropertiesBase64,' + ) + .replace("._token", '._token.replace("Bot ","")'); + if (requestFunction === newRequest) + throw new Error("Failed to patch request"); + client.requestHandler.request = new Function( + "method", + "url", + "auth", + "body", + "file", + "_route", + "short", + `return (function ${newRequest}).apply(this,arguments)` + ).bind(client.requestHandler); - return await this._request.apply(this, [options]); - }.bind(client.rest.handler); - - console.log("% Setting gateway connection properties"); - client.shards.options.connectionProperties = superProperties; - - console.log("% Injecting application into READY payload"); + console.log("% Injecting shard spawning"); client.shards._spawn = client.shards.spawn.bind(client.shards); client.shards.spawn = function (id) { const res = this._spawn.apply(this, [id]); const shard = this.get(id); if (shard) { - shard._onDispatch = shard.onDispatch.bind(shard); - shard.onDispatch = async function (packet) { + const identifyFunction = shard.identify.toString(); + const newIdentify = identifyFunction + .replace( + /properties: {\n\s+.+?\n\s+.+?\n\s+.+?\n\s+}\n/, + "properties: comcord.clientSpoof.superProperties\n" + ) + .replace(/\s+intents: this.client.shards.options.intents,/, ""); + if (identifyFunction === newIdentify) + throw new Error("Failed to patch identify"); + shard.identify = new Function( + `(function ${newIdentify}).apply(this, arguments)` + ); + shard._wsEvent = shard.wsEvent; + shard.wsEvent = function (packet) { if (packet.t == "READY") { packet.d.application = {id: CLIENT_ID, flags: 565248}; } - const ret = await this._onDispatch.apply(this, [packet]); + const ret = this._wsEvent.apply(this, [packet]); if (packet.t == "READY") { for (const guild of packet.d.guilds) { - await this._onDispatch.apply(this, [ + this._wsEvent.apply(this, [ { t: "GUILD_CREATE", d: guild, @@ -304,15 +331,15 @@ if ( } return ret; - }.bind(shard); + }; } return res; - }.bind(client.shards); + }; console.log("% Connecting to gateway now"); await client.connect(); - })();*/ + })(); } else { client.connect(); } @@ -364,7 +391,7 @@ setInterval(function () { } else { console.log(timeString); } - comcord.state.nameLength = client.user.username.length + 2; + comcord.state.nameLength = (client.user?.username?.length ?? 0) + 2; sentTime = true; } else if (seconds > 2 && sentTime) { sentTime = false; diff --git a/src/lib/clientSpoof.js b/src/lib/clientSpoof.js index d67cf17..6b78a75 100644 --- a/src/lib/clientSpoof.js +++ b/src/lib/clientSpoof.js @@ -51,7 +51,7 @@ async function getBuildNumber() { return buildNumber; } -async function getClientVersion() { +/*async function getClientVersion() { if (comcord.state.cachedClientVersion) { return comcord.state.cachedClientVersion; } @@ -63,15 +63,43 @@ async function getClientVersion() { comcord.state.cachedClientVersion = clientVersion; return clientVersion; +}*/ + +async function getBrowserInfo() { + let targetOS; + switch (process.platform) { + case "win32": + default: + targetOS = "windows"; + break; + case "darwin": + targetOS = "mac os"; + break; + case "linux": + targetOS = "linux"; + break; + } + + const data = await fetch( + `https://cdn.jsdelivr.net/gh/ray-lothian/UserAgent-Switcher/v2/firefox/data/popup/browsers/firefox-${encodeURIComponent( + targetOS + )}.json` + ).then((res) => res.json()); + data.sort((a, b) => Number(b.browser.major) - Number(a.browser.major)); + const target = data[0]; + + return {ua: target.ua, version: target.browser.version}; } async function getSuperProperties() { const buildNumber = await getBuildNumber(); - const clientVersion = await getClientVersion(); + // const clientVersion = await getClientVersion(); + const browserInfo = await getBrowserInfo(); let _os; switch (process.platform) { case "win32": + default: _os = "Windows"; break; case "darwin": @@ -80,28 +108,26 @@ async function getSuperProperties() { case "linux": _os = "Linux"; break; - default: - _os = process.platform; } const props = { - os: _os, - browser: "Discord Client", - releaseChannel: "stable", - client_version: clientVersion, - os_version: os.release(), - os_arch: os.arch(), - system_locale: "en-US", + browser: "Firefox", + browser_user_agent: browserInfo.ua, + browser_version: browserInfo.version, client_build_number: buildNumber, client_event_source: null, + device: "", + os: _os, + os_version: os.release(), + //os_arch: os.arch(), + referrer: "", + referrer_current: "", + referring_domain: "", + referring_domain_current: "", + release_channel: "stable", + system_locale: "en-US", }; return props; } -async function getSuperPropertiesBase64() { - return Buffer.from(JSON.stringify(await getSuperProperties())).toString( - "base64" - ); -} - -module.exports = {getSuperProperties, getSuperPropertiesBase64}; +module.exports = {getSuperProperties};