From bc93dac435df4762b6e9e0ce06a8455f33bd1f74 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 17 Nov 2019 22:23:51 +0000 Subject: [PATCH] big magic: --- README.md | 9 +- loader/chairloader.js | 46 +++++ loader/manifest.json | 8 + user.script.js | 411 --------------------------------------- wheelchair.js | 435 ++++++++++++++++++++++++++++++++++++++++++ wheelchair.min.js | 1 + 6 files changed, 495 insertions(+), 415 deletions(-) create mode 100644 loader/chairloader.js create mode 100644 loader/manifest.json delete mode 100644 user.script.js create mode 100644 wheelchair.js create mode 100644 wheelchair.min.js diff --git a/README.md b/README.md index 9829445..00abef2 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ 1.8.9 - Become a cripple, use a WheelChair... # Gold Standard WheelChair -Note: certain features have been removed but it should still destroy +Remove all exisiting scripts. Wheelchair is now a chromium based extension that will stop you from having to reinstalling scripts. -1. Install tamper monkey -2. Create new script -> paste the code from `user.script.js` and save +1. Navigate to your chromium browsers extensions page +2. Enable developer mode (probably a checkbox in the top right) +3. [Download this unpacked extension](https://github.com/hrt/WheelChair/releases/download/2.0/loader.zip) +4. Click on "Load unpacked extension" and select the downloaded zip file http://youtube.com/hrtbot [![WheelChair](https://i.imgur.com/FXvZhq3.png)](http://youtube.com/hrtbot) diff --git a/loader/chairloader.js b/loader/chairloader.js new file mode 100644 index 0000000..fb8815c --- /dev/null +++ b/loader/chairloader.js @@ -0,0 +1,46 @@ +/* + * much nicer injection than what extensions like tampermonkey offer us + * great against stack traces (VM:XX rather than ..extension/script.js or userscript..) + * sandboxed environment (unmodified native functions) + * faster than tamper monkeys "document_start" + * injects into all frames with high priority +*/ + +try { + (ttap)(hrt); +} catch(e) { + try { + let recursing = e.stack.match(/chairloader/g).length > 1; + if (!recursing) { + // must be synchronous to force execution before other scripts + // note: we fetch the same code for each iframe + let request = new XMLHttpRequest(); + request.open('GET', 'https://raw.githubusercontent.com/hrt/WheelChair/master/wheelchair.min.js', false); + request.send(null); + if (request.status != 200) { + console.error('Error GET wheelchair: ' + request.status); + } + + const unique_string = chrome.runtime.getURL('').match(/\/\/(\w{9})\w+\//)[1]; + let code = request.responseText.replace(/ttap#4547/g, unique_string); + + // inject our code into a new iframe to avoid using hooks placed by anti cheat + let frame = document.createElement('iframe'); + frame.setAttribute('style', 'display:none'); + document.documentElement.appendChild(frame); + let child = frame.contentDocument || frame.contentWindow.document; + let chair = document.createElement('script'); + chair.innerHTML = code; + child.documentElement.append(chair); + child.documentElement.remove(chair); + document.documentElement.removeChild(frame); + } + } catch (e) { + if (e instanceof DOMException) { + // expected for sandboxed iframes + console.warn(e); + } else { + throw e; + } + } +} \ No newline at end of file diff --git a/loader/manifest.json b/loader/manifest.json new file mode 100644 index 0000000..319ccb2 --- /dev/null +++ b/loader/manifest.json @@ -0,0 +1,8 @@ +{ + "name": "♿", + "version": "2.0.0", + "manifest_version": 2, + "description": "https://github.com/hrt/wheelchair", + "content_scripts": [ + {"matches":["https://krunker.io/", "https://krunker.io/*game*"],"run_at": "document_start","js": ["chairloader.js"],"all_frames":true,"match_about_blank":true}] +} \ No newline at end of file diff --git a/user.script.js b/user.script.js deleted file mode 100644 index 8ea8f91..0000000 --- a/user.script.js +++ /dev/null @@ -1,411 +0,0 @@ -// ==UserScript== -// @name Krunker WheelChair -// @namespace https://github.com/hrt -// @version 1.8.9 -// @description WheelChair -// @author hrt x ttap -// @match *://krunker.io/* -// @run-at document-start -// @grant none -// ==/UserScript== - -(function() { - const replace = String.prototype.replace; - const original_call = Function.prototype.call; - - let anti_map = []; - - // hook toString to conceal all hooks - const original_toString = Function.prototype.toString; - let hook_toString = new Proxy(original_toString, { - apply: function(target, _this, _arguments) { - for (var i = 0; i < anti_map.length; i++) { - if (anti_map[i].from === _this) { - return target.apply(anti_map[i].to, _arguments); - } - } - return target.apply(_this, _arguments); - } - }); - // hide toString hook itself - anti_map.push({from: hook_toString, to: original_toString}); - Function.prototype.toString = hook_toString; - - let conceal_function = function(original_Function, hook_Function) { - anti_map.push({from: hook_Function, to: original_Function}); - }; - - // hook Object.getOwnPropertyDescriptors to hide variables from window - let hidden_globals = []; - const original_getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors; - let hook_getOwnPropertyDescriptors = new Proxy(original_getOwnPropertyDescriptors, { - apply: function(target, _this, _arguments) { - let descriptors = target.apply(_this, _arguments); - for (var i = 0; i < hidden_globals.length; i++) { - delete descriptors[hidden_globals[i]]; - } - return descriptors; - } - }); - Object.getOwnPropertyDescriptors = hook_getOwnPropertyDescriptors; - conceal_function(original_getOwnPropertyDescriptors, hook_getOwnPropertyDescriptors); - - let invisible_define = function(obj, key, value) { - hidden_globals.push(key); - Object.defineProperty(obj, key, { - enumberable: false, - configurable: false, - writable: true, - value: value - }); - }; - - let global_invisible_define = function(key, value) { - invisible_define(window, key, value); - }; - - // we generate random keys for global variables and make it almost impossible(?) - // for outsiders to find programatically - let keyMap = {}; - let genKey = function() { - // https://stackoverflow.com/questions/1349404/generate-random-string-characters-in-javascript - let a = new Uint8Array(20); - crypto.getRandomValues(a); - return 'hrt'+Array.from(a,x=>('0'+x.toString(16)).substr(-2)).join(''); - } - - keyMap['init'] = genKey(); - global_invisible_define(keyMap['init'], false); - - // drawVisuals gets overwritten later - place hook before anti cheat loads - let drawVisuals = function() {}; - const original_clearRect = CanvasRenderingContext2D.prototype.clearRect; - let hook_clearRect = new Proxy(original_clearRect, { - apply: function(target, _this, _arguments) { - target.apply(_this, _arguments); - drawVisuals(_this); - } - }); - conceal_function(original_clearRect, hook_clearRect); - CanvasRenderingContext2D.prototype.clearRect = hook_clearRect; - - // me, inputs, world, consts, math are objects the rest are key strings - let hrtCheat = function(me, inputs, world, consts, math, canSee, pchObjc, objInstances, isYou, recoilAnimY, mouseDownL, mouseDownR) { - /* re implements code that we overwrote to place hook */ - let controls = world.controls; - if (controls.scrollDelta) { - controls.skipScroll = controls.scrollToSwap; - if (!controls.scrollToSwap) { - controls.fakeKey(0x4e20,0x1); - } - } - controls.scrollDelta = 0; - controls.wSwap = 0; - /******************************************************/ - - const playerHeight = 11; - const crouchDst = 3; - const headScale = 2; - const hitBoxPad = 1; - const armScale = 1.3; - const chestWidth = 2.6; - const armInset = -.1; - const playerScale = (2 * armScale + chestWidth + armInset) / 2; - const SHOOT = 5, SCOPE = 6, xDr = 3, yDr = 2, JUMP = 7, CROUCH = 8; - let isEnemy = function(player) {return !me.team || player.team != me.team}; - let canHit = function(player) {return null == world[canSee](me, player.x3, player.y3 - player.crouchVal * crouchDst, player.z3)}; - let normaliseYaw = function(yaw) {return (yaw % Math.PI2 + Math.PI2) % Math.PI2;}; - let getDir = function(a, b, c, d) { - return Math.atan2(b - d, a - c); - }; - let getD3D = function(a, b, c, d, e, f) { - let g = a - d, h = b - e, i = c - f; - return Math.sqrt(g * g + h * h + i * i); - }; - let getXDire = function(a, b, c, d, e, f) { - let g = Math.abs(b - e), h = getD3D(a, b, c, d, e, f); - return Math.asin(g / h) * (b > e ? -1 : 1); - }; - - let dAngleTo = function(x, y, z) { - let ty = normaliseYaw(getDir(controls.object.position.z, controls.object.position.x, z, x)); - let tx = getXDire(controls.object.position.x, controls.object.position.y, controls.object.position.z, x, y, z); - let oy = normaliseYaw(controls.object.rotation.y); - let ox = controls[pchObjc].rotation.x; - let dYaw = Math.min(Math.abs(ty - oy), Math.abs(ty - oy - Math.PI2), Math.abs(ty - oy + Math.PI2)); - let dPitch = tx - ox; - return Math.hypot(dYaw, dPitch); - }; - let calcAngleTo = function(player) {return dAngleTo(player.x3, player.y3 + playerHeight - (headScale + hitBoxPad) / 2 - player.crouchVal * crouchDst, player.z3);}; - let calcDistanceTo = function(player) {return getD3D(player.x3, player.y3, player.z3, me.x, me.y, me.z)}; - let isCloseEnough = function(player) {let distance = calcDistanceTo(player); return me.weapon.range >= distance && ("Shotgun" != me.weapon.name || distance < 70) && ("Akimbo Uzi" != me.weapon.name || distance < 100);}; - let haveAmmo = function() {return !(me.ammos[me.weaponIndex] !== undefined && me.ammos[me.weaponIndex] == 0);}; - - // target selector - based on closest to aim - let closest = null, closestAngle = Infinity; - let players = world.players.list; - for (var i = 0; me.active && i < players.length; i++) { - let e = players[i]; - if (e[isYou] || !e.active || !e[objInstances] || !isEnemy(e)) { - continue; - } - - // experimental prediction removed - e.x3 = e.x; - e.y3 = e.y; - e.z3 = e.z; - - if (!isCloseEnough(e) || !canHit(e)) { - continue; - } - - let angle = calcAngleTo(e); - if (angle < closestAngle) { - closestAngle = angle; - closest = e; - } - } - // aimbot - let ty = controls.object.rotation.y, tx = controls[pchObjc].rotation.x; - if (closest) { - let target = closest; - let y = target.y3 + playerHeight - (headScale/* + hitBoxPad*/) / 2 - target.crouchVal * crouchDst; - if (me.weapon.nAuto && me.didShoot) { - inputs[SHOOT] = 0; - } else if (!me.aimVal) { - inputs[SHOOT] = 1; - inputs[SCOPE] = 1; - } else { - inputs[SCOPE] = 1; - } - - ty = getDir(controls.object.position.z, controls.object.position.x, target.z3, target.x3); - tx = getXDire(controls.object.position.x, controls.object.position.y, controls.object.position.z, target.x3, y, target.z3); - - // perfect recoil control - tx -= .3 * me[recoilAnimY]; - } else { - inputs[SHOOT] = controls[mouseDownL]; - inputs[SCOPE] = controls[mouseDownR]; - } - - // silent aim - inputs[xDr] = (tx % Math.PI2).round(3); - inputs[yDr] = (ty % Math.PI2).round(3); - - // auto reload - controls.keys[controls.reloadKey] = !haveAmmo() * 1; - - // bhop - inputs[JUMP] = (controls.keys[controls.jumpKey] && !me.didJump) * 1; - - // runs once to set up renders - if (!window[keyMap['init']]) { - window[keyMap['init']] = true; - - drawVisuals = function(c) { - let scalingFactor = arguments.callee.caller.caller.arguments[0]; - let perspective = arguments.callee.caller.caller.arguments[2]; - let scaledWidth = c.canvas.width / scalingFactor; - let scaledHeight = c.canvas.height / scalingFactor; - let worldPosition = perspective.camera.getWorldPosition(); - for (var i = 0; i < world.players.list.length; i++) { - let player = world.players.list[i]; - let e = players[i]; - if (e[isYou] || !e.active || !e[objInstances] || !isEnemy(e)) { - continue; - } - - // the below variables correspond to the 2d box esps corners - // note: we can already tell what ymin ymax is - let xmin = Infinity; - let xmax = -Infinity; - let ymin = Infinity; - let ymax = -Infinity; - let br = false; - for (var j = -1; !br && j < 2; j+=2) { - for (var k = -1; !br && k < 2; k+=2) { - for (var l = 0; !br && l < 2; l++) { - let position = e[objInstances].position.clone(); - position.x += j * playerScale; - position.z += k * playerScale; - position.y += l * (playerHeight - e.crouchVal * crouchDst); - if (!perspective.frustum.containsPoint(position)) { - br = true; - break; - } - position.project(perspective.camera); - xmin = Math.min(xmin, position.x); - xmax = Math.max(xmax, position.x); - ymin = Math.min(ymin, position.y); - ymax = Math.max(ymax, position.y); - } - } - } - - if (br) { - continue; - } - - xmin = (xmin + 1) / 2; - ymin = (ymin + 1) / 2; - xmax = (xmax + 1) / 2; - ymax = (ymax + 1) / 2; - - - c.save(); - // save and restore these variables later so they got nothing on us - const original_strokeStyle = c.strokeStyle; - const original_lineWidth = c.lineWidth; - const original_font = c.font; - const original_fillStyle = c.fillStyle; - - // perfect box esp - c.lineWidth = 5; - c.strokeStyle = 'rgba(255,50,50,1)'; - - let distanceScale = Math.max(.3, 1 - getD3D(worldPosition.x, worldPosition.y, worldPosition.z, e.x, e.y, e.z) / 600); - c.scale(distanceScale, distanceScale); - let xScale = scaledWidth / distanceScale; - let yScale = scaledHeight / distanceScale; - - c.beginPath(); - ymin = yScale * (1 - ymin); - ymax = yScale * (1 - ymax); - xmin = xScale * xmin; - xmax = xScale * xmax; - c.moveTo(xmin, ymin); - c.lineTo(xmin, ymax); - c.lineTo(xmax, ymax); - c.lineTo(xmax, ymin); - c.lineTo(xmin, ymin); - c.stroke(); - - // health bar - c.fillStyle = "rgba(255,50,50,1)"; - let barMaxHeight = ymax - ymin; - c.fillRect(xmin - 7, ymin, -10, barMaxHeight); - c.fillStyle = "#00FFFF"; - c.fillRect(xmin - 7, ymin, -10, barMaxHeight * (e.health / e.maxHealth)); - - // info - c.font = "60px Sans-serif"; - c.fillStyle = "white"; - c.strokeStyle='black'; - c.lineWidth = 1; - let x = xmax + 7; - let y = ymax; - c.fillText(e.name, x, y); - c.strokeText(e.name, x, y); - c.font = "30px Sans-serif"; - y += 35; - c.fillText(e.weapon.name, x, y); - c.strokeText(e.weapon.name, x, y); - y += 35; - c.fillText(e.health + ' HP', x, y); - c.strokeText(e.health + ' HP', x, y); - - c.strokeStyle = original_strokeStyle; - c.lineWidth = original_lineWidth; - c.font = original_font; - c.fillStyle = original_fillStyle; - c.restore(); - - // skelly chams - // note: this can be done better - if (e.legMeshes[0]) { - let material = e.legMeshes[0].material; - material.alphaTest = 1; - material.depthTest = false; - material.fog = false; - material.emissive.r = 1; - material.emissive.g = 1; - material.emissive.b = 1; - material.wireframe = true; - } - - } - }; - }; - }; - keyMap['hrtCheat'] = genKey(); - global_invisible_define(keyMap['hrtCheat'], hrtCheat); - - const handler = { - construct(target, args) { - // ttap#4547 - if (args.length == 2 && args[1].length > 1337) { - let script = args[1]; - - // anti anti chet & anti skid - const version = script.match(/\w+\['exports'\]=(0[xX][0-9a-fA-F]+);/)[1]; - if (version !== "0x597b") { - window[atob('ZG9jdW1lbnQ=')][atob('d3JpdGU=')](atob('VmVyc2lvbiBtaXNzbWF0Y2gg') + version); - window[atob('bG9jYX'+'Rpb24'+'=')][atob('aHJ'+'lZg='+'=')] = atob('aHR0cHM6'+'Ly9naXRodWIuY2'+'9tL2hydC93aGVlb'+'GNoYWly'); - } - - var canSee = "'"+script.match(/,this\['(\w+)'\]=function\(\w+,\w+,\w+,\w+,\w+\){if\(!\w+\)return!\w+;/)[1]+"'"; - var pchObjc = "'"+script.match(/\(\w+,\w+,\w+\),this\['(\w+)'\]=new \w+\['\w+'\]\(\)/)[1]+"'"; - var objInstances = "'"+script.match(/\[\w+\]\['\w+'\]=!\w+,this\['\w+'\]\[\w+\]\['\w+'\]&&\(this\['\w+'\]\[\w+\]\['(\w+)'\]\['\w+'\]=!\w+/)[1]+"'"; - var isYou = "'"+script.match(/,this\['\w+'\]=!\w+,this\['\w+'\]=!\w+,this\['(\w+)'\]=\w+,this\['\w+'\]\['length'\]=\w+,this\[/)[1]+"'"; - var recoilAnimY = "'"+script.match(/\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*1,this\['\w+'\]=\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,/)[1]+"'"; - var mouseDownL = "'"+script.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[1]+"'"; - var mouseDownR = "'"+script.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[2]+"'"; - - var inputs = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[2]; - var world = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[1]; - var consts = script.match(/\w+\['\w+'\]\),\w+\['\w+'\]\(\w+\['\w+'\],\w+\['\w+'\]\+\w+\['\w+'\]\*(\w+)/)[1]; - var me = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[3]; - var math = script.match(/\\x20\-50\%\)\\x20rotate\('\+\((\w+)\['\w+'\]\(\w+\[\w+\]\['\w+'\]/)[1]; - - - const code_to_overwrite = script.match(/(\w+\['\w+'\]&&\(\w+\['\w+'\]=\w+\['\w+'\],!\w+\['\w+'\]&&\w+\['\w+'\]\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0),!\w+\['\w+'\]&&\w+\['\w+'\]\['push'\]\(\w+\),\w+\['\w+'\]\(\w+,\w+,!\w*1,\w+\['\w+'\]\)/)[1]; - const ttapParams = [me, inputs, world, consts, math, canSee, pchObjc, objInstances, isYou, recoilAnimY, mouseDownL, mouseDownR].toString(); - let call_hrt = `window['` + keyMap['hrtCheat'] + `'](` + ttapParams + `)`; - - /* - pad to avoid stack trace line number detections - the script will have the same length as it originally had - */ - while (call_hrt.length < code_to_overwrite.length) { - call_hrt += ' '; - } - - const hooked_call = Function.prototype.call; - Function.prototype.call = original_call; - /* the bIg mod */ - script = replace.call(script, code_to_overwrite, call_hrt); - - /* Below are some misc features which I wouldn't consider bannable, third party clients could be using them */ - // all weapons trails on - script = replace.call(script, /\w+\['weapon'\]&&\w+\['weapon'\]\['trail'\]/g, "true") - - // color blind mode - script = replace.call(script, /#9eeb56/g, '#00FFFF'); - - // no zoom - script = replace.call(script, /,'zoom':.+?(?=,)/g, ",'zoom':1"); - - // script = replace.call(script, /(void this\['sendQueue'\]\['push'\]\(\[(\w+),(\w+)\]\);)/, '$1_[$2]=$3;'); - Function.prototype.call = hooked_call; - /***********************************************************************************************************/ - - // bypass modification check of returned function - const original_script = args[1]; - args[1] = script; - let mod_fn = new target(...args); - args[1] = original_script; - let original_fn = new target(...args); - conceal_function(original_fn, mod_fn); - return mod_fn; - } - return new target(...args); - } - }; - - // we intercept game.js at the `Function` generation level - const original_Function = Function; - let hook_Function = new Proxy(Function, handler); - conceal_function(original_Function, hook_Function); - Function = hook_Function; -})() \ No newline at end of file diff --git a/wheelchair.js b/wheelchair.js new file mode 100644 index 0000000..5512aca --- /dev/null +++ b/wheelchair.js @@ -0,0 +1,435 @@ +// note: this script gets injected into its own isolated context/iframe +// to console.log we would have to call window.top.console.log + +cripple_window(window.parent); +function cripple_window(_window) { + if (!_window) { + return; + } + + // state is shared across all frames + let shared_state = {functions_to_hide: [], strings_to_hide: [], hidden_globals: [], init: false, hrt: []}; + + let invisible_define = function(obj, key, value) { + shared_state.hidden_globals.push(key); + Object.defineProperty(obj, key, { + enumberable: false, + configurable: false, + writable: true, + value: value + }); + }; + + // unique to each user + const master_key = 'ttap#4547'; + if (!window.top[master_key]) { + // initialise top state + invisible_define(window.top, master_key, shared_state); + } else { + // restore + shared_state = window.top[master_key]; + } + + // hook toString to hide presence + const original_toString = _window.Function.prototype.toString; + let hook_toString = new Proxy(original_toString, { + apply: function(target, _this, _arguments) { + for (var i = 0; i < shared_state.functions_to_hide.length; i++) { + if (shared_state.functions_to_hide[i].from === _this) { + return target.apply(shared_state.functions_to_hide[i].to, _arguments); + } + } + + let ret = target.apply(_this, _arguments); + for (var i = 0; i < shared_state.strings_to_hide.length; i++) { + ret = ret.replace(shared_state.strings_to_hide[i].from, shared_state.strings_to_hide[i].to); + } + return ret; + } + }); + _window.Function.prototype.toString = hook_toString; + + let conceal_function = function(original_Function, hook_Function) { + shared_state.functions_to_hide.push({from: hook_Function, to: original_Function}); + }; + + let conceal_string = function(original_string, hook_string) { + shared_state.strings_to_hide.push({from: new RegExp(hook_string.replace(/([\[|\]|\(|\)|\*|\\|\.|\+])/g,'\\$1'), 'g'), to: original_string}); + }; + + // hook Object.getOwnPropertyDescriptors to hide variables from window + const original_getOwnPropertyDescriptors = _window.Object.getOwnPropertyDescriptors; + let hook_getOwnPropertyDescriptors = new Proxy(original_getOwnPropertyDescriptors, { + apply: function(target, _this, _arguments) { + try { + var descriptors = target.apply(_this, _arguments); + } catch (e) { + // modify stack trace to hide proxy + e.stack = e.stack.replace(/.*Object.*\n/g, ''); + throw e; + } + for (var i = 0; i < shared_state.hidden_globals.length; i++) { + delete descriptors[shared_state.hidden_globals[i]]; + } + return descriptors; + } + }); + _window.Object.getOwnPropertyDescriptors = hook_getOwnPropertyDescriptors; + + // drawVisuals gets overwritten later - place hook before anti cheat loads + let drawVisuals = function() {}; + const original_clearRect = _window.CanvasRenderingContext2D.prototype.clearRect; + let hook_clearRect = new Proxy(original_clearRect, { + apply: function(target, _this, _arguments) { + target.apply(_this, _arguments); + drawVisuals(_this); + } + }); + _window.CanvasRenderingContext2D.prototype.clearRect = hook_clearRect; + + // hook window.open to always return null (pop up blocker) + // otherwise we would have to also patch native functions in new window + const original_open = _window.open; + let hook_open = new Proxy(original_open, { + apply: function(target, _this, _arguments) { + return null; + } + }); + _window.open = hook_open; + + // me, inputs, world, consts, math are objects the rest are key strings + if (shared_state.hrt.length === 0) { + shared_state.hrt.push(function(me, inputs, world, consts, math) { + /******************************************************/ + /* re implements code that we overwrote to place hook */ + let controls = world.controls; + if (controls.scrollDelta) { + controls.skipScroll = controls.scrollToSwap; + if (!controls.scrollToSwap) { + controls.fakeKey(0x4e20,0x1); + } + } + controls.scrollDelta = 0; + controls.wSwap = 0; + /******************************************************/ + + const playerHeight = 11; + const crouchDst = 3; + const headScale = 2; + const hitBoxPad = 1; + const armScale = 1.3; + const chestWidth = 2.6; + const armInset = -.1; + const playerScale = (2 * armScale + chestWidth + armInset) / 2; + const SHOOT = 5, SCOPE = 6, xDr = 3, yDr = 2, JUMP = 7, CROUCH = 8; + const PI2 = Math.PI * 2; + let isEnemy = function(player) {return !me.team || player.team != me.team}; + let canHit = function(player) {return null == world[canSee](me, player.x3, player.y3 - player.crouchVal * crouchDst, player.z3)}; + let normaliseYaw = function(yaw) {return (yaw % PI2 + PI2) % PI2;}; + let getDir = function(a, b, c, d) { + return Math.atan2(b - d, a - c); + }; + let getD3D = function(a, b, c, d, e, f) { + let g = a - d, h = b - e, i = c - f; + return Math.sqrt(g * g + h * h + i * i); + }; + let getXDire = function(a, b, c, d, e, f) { + let g = Math.abs(b - e), h = getD3D(a, b, c, d, e, f); + return Math.asin(g / h) * (b > e ? -1 : 1); + }; + + let dAngleTo = function(x, y, z) { + let ty = normaliseYaw(getDir(controls.object.position.z, controls.object.position.x, z, x)); + let tx = getXDire(controls.object.position.x, controls.object.position.y, controls.object.position.z, x, y, z); + let oy = normaliseYaw(controls.object.rotation.y); + let ox = controls[pchObjc].rotation.x; + let dYaw = Math.min(Math.abs(ty - oy), Math.abs(ty - oy - PI2), Math.abs(ty - oy + PI2)); + let dPitch = tx - ox; + return Math.hypot(dYaw, dPitch); + }; + let calcAngleTo = function(player) {return dAngleTo(player.x3, player.y3 + playerHeight - (headScale + hitBoxPad) / 2 - player.crouchVal * crouchDst, player.z3);}; + let calcDistanceTo = function(player) {return getD3D(player.x3, player.y3, player.z3, me.x, me.y, me.z)}; + let isCloseEnough = function(player) {let distance = calcDistanceTo(player); return me.weapon.range >= distance && ("Shotgun" != me.weapon.name || distance < 70) && ("Akimbo Uzi" != me.weapon.name || distance < 100);}; + let haveAmmo = function() {return !(me.ammos[me.weaponIndex] !== undefined && me.ammos[me.weaponIndex] == 0);}; + + // target selector - based on closest to aim + let closest = null, closestAngle = Infinity; + let players = world.players.list; + for (var i = 0; me.active && i < players.length; i++) { + let e = players[i]; + if (e[isYou] || !e.active || !e[objInstances] || !isEnemy(e)) { + continue; + } + + // experimental prediction removed + e.x3 = e.x; + e.y3 = e.y; + e.z3 = e.z; + + if (!isCloseEnough(e) || !canHit(e)) { + continue; + } + + let angle = calcAngleTo(e); + if (angle < closestAngle) { + closestAngle = angle; + closest = e; + } + } + // aimbot + let ty = controls.object.rotation.y, tx = controls[pchObjc].rotation.x; + if (closest) { + let target = closest; + let y = target.y3 + playerHeight - (headScale/* + hitBoxPad*/) / 2 - target.crouchVal * crouchDst; + if (me.weapon.nAuto && me.didShoot) { + inputs[SHOOT] = 0; + } else if (!me.aimVal) { + inputs[SHOOT] = 1; + inputs[SCOPE] = 1; + } else { + inputs[SCOPE] = 1; + } + + ty = getDir(controls.object.position.z, controls.object.position.x, target.z3, target.x3); + tx = getXDire(controls.object.position.x, controls.object.position.y, controls.object.position.z, target.x3, y, target.z3); + + // perfect recoil control + tx -= .3 * me[recoilAnimY]; + } else { + inputs[SHOOT] = controls[mouseDownL]; + inputs[SCOPE] = controls[mouseDownR]; + } + + + // silent aim + inputs[xDr] = +(tx % PI2).toFixed(3); + inputs[yDr] = +(ty % PI2).toFixed(3); + + // auto reload + controls.keys[controls.reloadKey] = !haveAmmo() * 1; + + // bhop + inputs[JUMP] = (controls.keys[controls.jumpKey] && !me.didJump) * 1; + + // runs once + if (!shared_state.init) { + shared_state.init = true; + + drawVisuals = function(c) { + let scalingFactor = arguments.callee.caller.caller.arguments[0]; + let perspective = arguments.callee.caller.caller.arguments[2]; + let scaledWidth = c.canvas.width / scalingFactor; + let scaledHeight = c.canvas.height / scalingFactor; + let worldPosition = perspective.camera.getWorldPosition(); + for (var i = 0; i < world.players.list.length; i++) { + let player = world.players.list[i]; + let e = players[i]; + if (e[isYou] || !e.active || !e[objInstances] || !isEnemy(e)) { + continue; + } + + // the below variables correspond to the 2d box esps corners + // note: we can already tell what ymin ymax is + let xmin = Infinity; + let xmax = -Infinity; + let ymin = Infinity; + let ymax = -Infinity; + let br = false; + for (var j = -1; !br && j < 2; j+=2) { + for (var k = -1; !br && k < 2; k+=2) { + for (var l = 0; !br && l < 2; l++) { + let position = e[objInstances].position.clone(); + position.x += j * playerScale; + position.z += k * playerScale; + position.y += l * (playerHeight - e.crouchVal * crouchDst); + if (!perspective.frustum.containsPoint(position)) { + br = true; + break; + } + position.project(perspective.camera); + xmin = Math.min(xmin, position.x); + xmax = Math.max(xmax, position.x); + ymin = Math.min(ymin, position.y); + ymax = Math.max(ymax, position.y); + } + } + } + + if (br) { + continue; + } + + xmin = (xmin + 1) / 2; + ymin = (ymin + 1) / 2; + xmax = (xmax + 1) / 2; + ymax = (ymax + 1) / 2; + + + c.save(); + // save and restore these variables later so they got nothing on us + const original_strokeStyle = c.strokeStyle; + const original_lineWidth = c.lineWidth; + const original_font = c.font; + const original_fillStyle = c.fillStyle; + + // perfect box esp + c.lineWidth = 5; + c.strokeStyle = 'rgba(255,50,50,1)'; + + let distanceScale = Math.max(.3, 1 - getD3D(worldPosition.x, worldPosition.y, worldPosition.z, e.x, e.y, e.z) / 600); + c.scale(distanceScale, distanceScale); + let xScale = scaledWidth / distanceScale; + let yScale = scaledHeight / distanceScale; + + c.beginPath(); + ymin = yScale * (1 - ymin); + ymax = yScale * (1 - ymax); + xmin = xScale * xmin; + xmax = xScale * xmax; + c.moveTo(xmin, ymin); + c.lineTo(xmin, ymax); + c.lineTo(xmax, ymax); + c.lineTo(xmax, ymin); + c.lineTo(xmin, ymin); + c.stroke(); + + // health bar + c.fillStyle = "rgba(255,50,50,1)"; + let barMaxHeight = ymax - ymin; + c.fillRect(xmin - 7, ymin, -10, barMaxHeight); + c.fillStyle = "#00FFFF"; + c.fillRect(xmin - 7, ymin, -10, barMaxHeight * (e.health / e.maxHealth)); + + // info + c.font = "60px Sans-serif"; + c.fillStyle = "white"; + c.strokeStyle='black'; + c.lineWidth = 1; + let x = xmax + 7; + let y = ymax; + c.fillText(e.name, x, y); + c.strokeText(e.name, x, y); + c.font = "30px Sans-serif"; + y += 35; + c.fillText(e.weapon.name, x, y); + c.strokeText(e.weapon.name, x, y); + y += 35; + c.fillText(e.health + ' HP', x, y); + c.strokeText(e.health + ' HP', x, y); + + c.strokeStyle = original_strokeStyle; + c.lineWidth = original_lineWidth; + c.font = original_font; + c.fillStyle = original_fillStyle; + c.restore(); + + // skelly chams + // note: this can be done better + if (e.legMeshes[0]) { + let material = e.legMeshes[0].material; + material.alphaTest = 1; + material.depthTest = false; + material.fog = false; + material.emissive.g = 1; + material.wireframe = true; + } + + } + }; + }; + }) + } + + const handler = { + construct(target, args) { + try { + var original_fn = new target(...args); + } catch (e) { + // modify stack trace to hide proxy + e.stack = e.stack.replace(/.*Object.*\n/g, ''); + throw e; + } + + if (args.length == 2 && args[1].length > parseInt("1337 ttap#4547")) { + let script = args[1]; + + // anti anti chet & anti skid + const version = script.match(/\w+\['exports'\]=(0[xX][0-9a-fA-F]+);/)[1]; + if (version !== "0x597b") { + _window[atob('ZG9jdW1lbnQ=')][atob('d3JpdGU=')](atob('VmVyc2lvbiBtaXNzbWF0Y2gg') + version); + _window[atob('bG9jYX'+'Rpb24'+'=')][atob('aHJ'+'lZg='+'=')] = atob('aHR0cHM6'+'Ly9naXRodWIuY2'+'9tL2hydC93aGVlb'+'GNoYWly'); + } + + // note: this window is not the main window + window['canSee'] = script.match(/,this\['(\w+)'\]=function\(\w+,\w+,\w+,\w+,\w+\){if\(!\w+\)return!\w+;/)[1]; + window['pchObjc'] = script.match(/\(\w+,\w+,\w+\),this\['(\w+)'\]=new \w+\['\w+'\]\(\)/)[1]; + window['objInstances'] = script.match(/\[\w+\]\['\w+'\]=!\w+,this\['\w+'\]\[\w+\]\['\w+'\]&&\(this\['\w+'\]\[\w+\]\['(\w+)'\]\['\w+'\]=!\w+/)[1]; + window['isYou'] = script.match(/,this\['\w+'\]=!\w+,this\['\w+'\]=!\w+,this\['(\w+)'\]=\w+,this\['\w+'\]\['length'\]=\w+,this\[/)[1]; + window['recoilAnimY'] = script.match(/\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*1,this\['\w+'\]=\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,/)[1]; + window['mouseDownL'] = script.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[1]; + window['mouseDownR'] = script.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[2]; + + const inputs = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[2]; + const world = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[1]; + const consts = script.match(/\w+\['\w+'\]\),\w+\['\w+'\]\(\w+\['\w+'\],\w+\['\w+'\]\+\w+\['\w+'\]\*(\w+)/)[1]; + const me = script.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[3]; + const math = script.match(/\\x20\-50\%\)\\x20rotate\('\+\((\w+)\['\w+'\]\(\w+\[\w+\]\['\w+'\]/)[1]; + + + const code_to_overwrite = script.match(/(\w+\['\w+'\]&&\(\w+\['\w+'\]=\w+\['\w+'\],!\w+\['\w+'\]&&\w+\['\w+'\]\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0),!\w+\['\w+'\]&&\w+\['\w+'\]\['push'\]\(\w+\),\w+\['\w+'\]\(\w+,\w+,!\w*1,\w+\['\w+'\]\)/)[1]; + const ttapParams = [me, inputs, world, consts, math].toString(); + let call_hrt = `window.top['` + master_key + `'].hrt[0](` + ttapParams + `)`; + + /* + pad to avoid stack trace line:column number detection + the script will have the same length as it originally had + */ + if (call_hrt.length > code_to_overwrite.length) { + throw 'WHEELCHAIR: target function too small ' + [call_hrt.length, code_to_overwrite.length]; + } + let whitespaces = code_to_overwrite.match(/\s/g); + for (var i = 0; i < whitespaces && whitespaces.length; i++) { + call_hrt += whitespaces[i]; + } + while (call_hrt.length < code_to_overwrite.length) { + call_hrt += ' '; + } + + script = script.replace(code_to_overwrite, call_hrt); + conceal_string(code_to_overwrite, call_hrt); + + /***********************************************************************************************************/ + /* Below are some misc features which I wouldn't consider bannable */ + // all weapons trails on + script = script.replace(/\w+\['weapon'\]&&\w+\['weapon'\]\['trail'\]/g, "true") + + // color blind mode + script = script.replace(/#9eeb56/g, '#00FFFF'); + + // no zoom + script = script.replace(/,'zoom':.+?(?=,)/g, ",'zoom':1"); + /***********************************************************************************************************/ + // bypass modification check of returned function + const original_script = args[1]; + args[1] = script; + let mod_fn = new target(...args); + args[1] = original_script; + conceal_function(original_fn, mod_fn); + return mod_fn; + } + return original_fn; + } + }; + + // we intercept game.js at the `Function` generation level + const original_Function = _window.Function; + let hook_Function = new Proxy(original_Function, handler); + _window.Function = hook_Function; + + + conceal_function(original_open, hook_open); + conceal_function(original_clearRect, hook_clearRect); + conceal_function(original_getOwnPropertyDescriptors, hook_getOwnPropertyDescriptors); + conceal_function(original_toString, hook_toString); + conceal_function(original_Function, hook_Function); +} diff --git a/wheelchair.min.js b/wheelchair.min.js new file mode 100644 index 0000000..feadee0 --- /dev/null +++ b/wheelchair.min.js @@ -0,0 +1 @@ +function cripple_window(t){if(!t)return;let e={functions_to_hide:[],strings_to_hide:[],hidden_globals:[],init:!1,hrt:[]};var o,n,w;window.top["ttap#4547"]?e=window.top["ttap#4547"]:(o=window.top,n="ttap#4547",w=e,e.hidden_globals.push(n),Object.defineProperty(o,n,{enumberable:!1,configurable:!1,writable:!0,value:w}));const i=t.Function.prototype.toString;let a=new Proxy(i,{apply:function(t,o,n){for(var w=0;ww?-1:1)},f=function(t){return function(t,e,o){let n=s(h(a.object.position.z,a.object.position.x,o,t)),w=u(a.object.position.x,a.object.position.y,a.object.position.z,t,e,o),i=s(a.object.rotation.y),r=a[pchObjc].rotation.x,c=Math.min(Math.abs(n-i),Math.abs(n-i-l),Math.abs(n-i+l)),p=w-r;return Math.hypot(c,p)}(t.x3,t.y3+11-1.5-3*t.crouchVal,t.z3)},m=function(e){let o=function(e){return p(e.x3,e.y3,e.z3,t.x,t.y,t.z)}(e);return t.weapon.range>=o&&("Shotgun"!=t.weapon.name||o<70)&&("Akimbo Uzi"!=t.weapon.name||o<100)},y=null,g=1/0,d=n.players.list;for(var b=0;t.active&&bparseInt("1337 ttap#4547")){let s=n[1];const c=s.match(/\w+\['exports'\]=(0[xX][0-9a-fA-F]+);/)[1];"0x597b"!==c&&(t[atob("ZG9jdW1lbnQ=")][atob("d3JpdGU=")](atob("VmVyc2lvbiBtaXNzbWF0Y2gg")+c),t[atob("bG9jYXRpb24=")][atob("aHJlZg==")]=atob("aHR0cHM6Ly9naXRodWIuY29tL2hydC93aGVlbGNoYWly")),window.canSee=s.match(/,this\['(\w+)'\]=function\(\w+,\w+,\w+,\w+,\w+\){if\(!\w+\)return!\w+;/)[1],window.pchObjc=s.match(/\(\w+,\w+,\w+\),this\['(\w+)'\]=new \w+\['\w+'\]\(\)/)[1],window.objInstances=s.match(/\[\w+\]\['\w+'\]=!\w+,this\['\w+'\]\[\w+\]\['\w+'\]&&\(this\['\w+'\]\[\w+\]\['(\w+)'\]\['\w+'\]=!\w+/)[1],window.isYou=s.match(/,this\['\w+'\]=!\w+,this\['\w+'\]=!\w+,this\['(\w+)'\]=\w+,this\['\w+'\]\['length'\]=\w+,this\[/)[1],window.recoilAnimY=s.match(/\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*1,this\['\w+'\]=\w*1,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,this\['\w+'\]=\w*0,/)[1],window.mouseDownL=s.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[1],window.mouseDownR=s.match(/this\['\w+'\]=function\(\){this\['(\w+)'\]=\w*0,this\['(\w+)'\]=\w*0,this\['\w+'\]={}/)[2];const h=s.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[2],p=s.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[1],u=s.match(/\w+\['\w+'\]\),\w+\['\w+'\]\(\w+\['\w+'\],\w+\['\w+'\]\+\w+\['\w+'\]\*(\w+)/)[1],f=s.match(/\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0,!(\w+)\['\w+'\]&&\w+\['\w+'\]\['push'\]\((\w+)\),(\w+)\['\w+'\]/)[3],m=s.match(/\\x20\-50\%\)\\x20rotate\('\+\((\w+)\['\w+'\]\(\w+\[\w+\]\['\w+'\]/)[1],y=s.match(/(\w+\['\w+'\]&&\(\w+\['\w+'\]=\w+\['\w+'\],!\w+\['\w+'\]&&\w+\['\w+'\]\(\w+,\w*1\)\),\w+\['\w+'\]=\w*0,\w+\['\w+'\]=\w*0),!\w+\['\w+'\]&&\w+\['\w+'\]\['push'\]\(\w+\),\w+\['\w+'\]\(\w+,\w+,!\w*1,\w+\['\w+'\]\)/)[1];let g="window.top['ttap#4547'].hrt[0]("+[f,h,p,u,m].toString()+")";if(g.length>y.length)throw"WHEELCHAIR: target function too small "+[g.length,y.length];let d=y.match(/\s/g);for(var i=0;i