Tutorial Hacking JavaScript Games with Hooks, Example: ShellShock.io

Hexui Undetected CSGO Cheats PUBG Accounts

TDStuart

Dank Tier Donator
Full Member
Mar 28, 2020
10
348
0
Game Name
Shell Shockers
Anticheat
N/A
How long you been coding/hacking?
4 years of coding
Coding Language
JavaScript
Javascript based games, something you certainly don't see everyday but Shell Shockers is worth a look. Shell Shockers is a egg-based FPS multiplayer shooter game. The game is based inside your browser and can be accessed by visiting shellshock.io, now it is a game based off of ones aiming ability so let's see if a shellshock.io aimbot is even possible. The game begins by allowing you to select a weapon from a pretty basic set including assault rifle, shotgun, and various scoped rifles including a long distance sniper. The game allows you to choose from multiple location-based servers for improved connectivity. Each player in the game is basically an egg and you are separated into two teams to race around the map and destroy each other. Due to the multiplayer nature of the game many people have sought out a shell shockers hack to use in the game.

Now normally we deal with games that run locally on our machine and we browse and edit areas of memory to achieve our goal, but Shell Shockers is a browser based java game so we have to take a different approach. Unlike C++ you can actually view some of the Java code by simply pulling back the curtain, in other words using Google Chrome Dev tools to view some of the source. As you can already tell this is much easier than reverse engineering a game without the source code! We can look directly at the source code and find our player object without having to scan for it in cheat engine. Once we find our local play object we can hook a few functions so that we can read the data from our object and of course display it on the screen. We hook two methods in our code to get our data and once we have our position data and our player data we can even create a java based shell shockers aimbot! You can tweak this code to include some aimbot prediction and the only tools we used were Chrome and our IDE! Shellshock.io aimbot also includes a toggle key so that you may switch off your hack if you need to remain discreet

A quick note that this is going to go into the specifics of how I hacked this game, if you want more general knowledge on hacking browser-based games then I recommend checking out this forum post I made.

How to Hack Shell Shockers (We will be doing aimbot)

Ok, so the first thing you really want to do is check out the code for the game. To do that I am going to open up Google Chromes Dev tools, then navigate to the Sources tab to see what files our game may be running. If we look under the shellshock.io domain we have a folder called src and under that is a file called "shellshock.js".

As we can see the code is minified and hard to read, luckily chrome has a built-in js beautifier. Click the "{}" Icon at the bottom of the code window. You should now see this:

Cool, we can now see the code a lot better. Although unfortunately for us it is over 200 thousand lines (most of that being position vectors for map data). So let's try to find our player object. The obvious thing to do is search for the player. We get about 200 results, cool. Luckily for us, the second search result shows a class prototype that has some player object values exposed.

We could grab data from here, but as you can see it says Ae.prototype.fireThis which suggests that this function will only be called when a player shoots a gun. So let's try to grab where the original player class is initialized. Using the information we have about the player class we know it has a property called this.dx so what I did is I searched for this.dx =. I got 11 results and I found this large function that gets passed an object and creates a bunch of properties.

As you can see in the code this is likely our player class. The function name has been minimized and it is now called function nt(e, t) instead of like "playerObject". Alright so now that we have the player class initializer function, we want to see what calls it. We can do that by searching for new nt. We know it starts with the keyword new as that is how you initialize a class in javascript.

Ok, so we now have a function, function Ai, that is passed an object, e, and creates the player object. If you look at the code closely you can see it saves the newly created player object to an array called _r. This is going to be our player list array. Right below this function is, function Si. This function is responsible for removing the player object from the array.

Ok, so now we actually need to start doing some hacking now that we have something to grab from the code. I am going to be using hooks to grab this information. You can read more about js hooks in the forum post linked at the top. Since all of these functions have been wrapped in a function all of these variables are in a local scope, meaning we can't directly access the player list array. Finding the hook is actually pretty tricky, we have to take a step back and look at the player class function. In that function, we see that it passes the player object to another function, this.actor = new Io(this),. If we look at that function we can see that it calls a function that is part of the rendering engine Babylon, and Babylon is a global object, so we can hook it this.bodyMesh = this.scene.cloneMesh("egg", this.mesh),. The Babylon prototype is: BABYLON.Scene.prototype.cloneMesh. So all we have to do is hook the cloneMesh function before our player is added.



Ok so here is my hook code:

JavaScript:
let currentPlayerList = [];
const oldCloneMesh = BABYLON.Scene.prototype.cloneMesh;
BABYLON.Scene.prototype.cloneMesh = function(){
    if (arguments[0] == "egg"){
        // We Are Cloning the Egg Mesh in player constructor!
        // Grab the player object by getting the arguments passed to function Io!
        let playerObj = arguments.callee.caller.arguments[0];
        // Make sure this is a player
        if (playerObj.id == null || playerObj.id == -1){return oldCloneMesh.apply(this, arguments);}
        // Add the player to our list
        currentPlayerList[playerObj.id] = playerObj;

        // The function Si to remove the player calls playerObj.actor.remove (aka Io.prototype.remove)
        // We are going to hook that function call!
        // Note : arguments.callee.caller references the Io function.
        let oldRemove = arguments.callee.caller.prototype.remove;
        arguments.callee.caller.prototype.remove = function(){
            // Make sure this is a player
            if (!this.player.id){return oldRemove.apply(this, arguments);}

            // Remove the player from our list
            delete currentPlayerList[this.player.id];
            // Apply to original
            return oldRemove.apply(this, arguments);
        }
    }
    return oldCloneMesh.apply(this, arguments);
}
As you can see the hook is fairly complex. We end up hooking two methods, the cloneMesh, and the remove functions. With the arguments.callee.caller we can reference previous functions in the callstack and their arguments. The playerList will update when we are in a game, but if we leave the game it does not reset. I will deal with this problem down below. Our last real problem is that we don't know which player is ours. The game deals with that by having a variable (in this case "y") that has the id of our player. So it turns out this also gets pretty complicated lmao. Here is the code that sets the y value:

As we can see it is watching the WebSocket.prototype.onmessage for a message. The Le.gameJoined is a packet code with the value of 0. We can hook the WebSocket.prototype.onmessage function but we need to clone the packet "decrypt" function ourselves. So here is the packet "decrypt" function clone:

JavaScript:
let packetDecryptClone = {
    init: function(e) {
        this.buffer = new Uint8Array(e),
        this.idx = 0
    },
    unPackInt8U: function() {
        var e = this.idx;
        return this.idx++,
        this.buffer[e]
    }
}
Now we can use this function to intercept and decrypt the packets. Here is my example:

JavaScript:
let myPlayerId = 0;
function messageHook(){
    if (arguments[0].data instanceof ArrayBuffer){
        // Lets use our packetDecryptClone
        let decrypt = packetDecryptClone;
        decrypt.init(arguments[0].data);
        let packetType = decrypt.unPackInt8U();
        if (packetType == 0){ // Check if gameJoined Packet
            myPlayerId = decrypt.unPackInt8U();
        }
    }
}
let oldOnMessageSetter = Object.getOwnPropertyDescriptors(WebSocket.prototype).onmessage.set;
Object.defineProperty(WebSocket.prototype, 'onmessage', {
    set: function(){
        let originalFunc = ()=>{};
        if (typeof arguments[0] == "function"){
            originalFunc = arguments[0];
            arguments[0] = function(){
                messageHook.apply(this, arguments); // Send a copy of the values to our message hook
                originalFunc.apply(this, arguments);
            }
        }
        oldOnMessageSetter.apply(this, arguments);
    }
});
As you can see above we set myPlayerId to the id we get from the packets. Using this we can also fix currentPlayerList not updating when a new game starts. Since we know when a new game starts we can now clear the array when the game starts.
JavaScript:
function messageHook(){
    if (arguments[0].data instanceof ArrayBuffer){
        // Lets use our packetDecryptClone
        let decrypt = packetDecryptClone;
        decrypt.init(arguments[0].data);
        let packetType = decrypt.unPackInt8U();
        if (packetType == 0){ // Check if gameJoined Packet
            myPlayerId = decrypt.unPackInt8U(); // Update my player id
            currentPlayerList = []; // Clear my player list
        }
    }
}
Alright, now that we have all our hooking code we can wrap it together in a package. Let's use tampermonkey to inject our hook code. You will see that I modify the hook to wait for BABYLON to be loaded. Here is the tampermonkey script with all of our hooks:
JavaScript:
// ==UserScript==
// @name         Shell Shock Aimbot Example
// @version      1
// @description  Hooks shellshock functions to make an aimbot
// @author       TDStuart
// @match        https://shellshock.io/
// @grant        none
// ==/UserScript==

(function() {
    let currentPlayerList = [];
    let myPlayerId = 0;
    window.currentPlayerList = currentPlayerList;
    window.myPlayerId = myPlayerId;
    let packetDecryptClone = {
        init: function(e) {
            this.buffer = new Uint8Array(e),
                this.idx = 0
        },
        unPackInt8U: function() {
            var e = this.idx;
            return this.idx++,
                this.buffer[e]
        }
    }
    function doHook(){
        const oldCloneMesh = window.BABYLON.Scene.prototype.cloneMesh;
        window.BABYLON.Scene.prototype.cloneMesh = function(){
            if (arguments[0] == "egg"){
                // We Are Cloning the Egg Mesh in player constructor!
                // Grab the player object by getting the arguments passed to function Io!
                let playerObj = arguments.callee.caller.arguments[0];
                // Make sure this is a player
                if (playerObj.id == null || playerObj.id == -1){return oldCloneMesh.apply(this, arguments);}
                // Add the player to our list
                currentPlayerList[playerObj.id] = playerObj;

                // The function Si to remove the player calls playerObj.actor.remove (aka Io.prototype.remove)
                // We are going to hook that function call!
                // Note : arguments.callee.caller references the Io function.
                let oldRemove = arguments.callee.caller.prototype.remove;
                arguments.callee.caller.prototype.remove = function(){
                    // Make sure this is a player
                    if (!this.player.id){return oldRemove.apply(this, arguments);}

                    // Remove the player from our list
                    delete currentPlayerList[this.player.id];
                    // Apply to original
                    return oldRemove.apply(this, arguments);
                }
            }
            return oldCloneMesh.apply(this, arguments);
        }
        function messageHook(){
            if (arguments[0].data instanceof ArrayBuffer){
                // Lets use our packetDecryptClone
                let decrypt = packetDecryptClone;
                decrypt.init(arguments[0].data);
                let packetType = decrypt.unPackInt8U();
                if (packetType == 0){ // Check if gameJoined Packet
                    myPlayerId = decrypt.unPackInt8U(); // Get my player id
                    currentPlayerList = []; // Clear the player list
                }
            }
        }
        let oldOnMessageSetter = Object.getOwnPropertyDescriptors(WebSocket.prototype).onmessage.set;
        Object.defineProperty(WebSocket.prototype, 'onmessage', {
            set: function(){
                let originalFunc = ()=>{};
                if (typeof arguments[0] == "function"){
                    originalFunc = arguments[0];
                    arguments[0] = function(){
                        messageHook.apply(this, arguments); // Send a copy of the values to our message hook
                        originalFunc.apply(this, arguments);
                    }
                }
                oldOnMessageSetter.apply(this, arguments);
            }
        });
    }
    let checkForBabylon = setInterval(()=>{
        if (window.BABYLON && window.BABYLON.Scene && window.BABYLON.Scene.prototype.cloneMesh){
            clearInterval(checkForBabylon);
            doHook();
        }
    }, 100);
})();
Note: That the code is wrapped in a local function as tampermonkey will automatically grab the window object and pass it to the function.
Ok so now that we have the player list and my player, we can make an aimbot! Let's make our aimbot code. We will use the g key as our aimbot toggle key.
This game does handle yaw and pitch weird so you have to correct that in the aimbot. Anyway here is our final aimbot code:
JavaScript:
// ==UserScript==
// @name         Shell Shock Aimbot Example
// @version      1
// @description  Hooks shellshock functions to make an aimbot
// @author       TDStuart
// @match        https://shellshock.io/
// @grant        none
// ==/UserScript==

(function() {
    let currentPlayerList = [];
    let myPlayerId = 0;
    window.hackData = {
        get currentPlayerList(){return currentPlayerList;},
        get myPlayerId(){return myPlayerId;}
    };
    //
    // Aimbot Settings
    const fov = 20;
    const toggleKey = "KeyV";
    const aimCheckDelay = 1; // In milliseconds
    //
    // Aimbot Code
    let aimToggled = false;
    function doAimbot(){
        let myPlayer = currentPlayerList[myPlayerId]; // Get my player object
        let players = currentPlayerList.filter(e=>{return (e.id != myPlayerId)}); // Filter the array
        if (!myPlayer || !players){return;} // Make sure these both exist!
        let aimPlayer = {}; // Aim player will be best calculated target so far
        let aimPlayerAngle = 999; // Will be used to store the closest player angle to our crosshair so far
        let aimPlayerYawPitch = {yaw: 0, pitch: 0}; // Store the yaw and pitch to aim at the aim player
        //
        const adjustedYaw = Math.radRange(Math.PI / 2 - myPlayer.yaw); // Correct my yaw for calcs
        const adjustedPitch = -myPlayer.pitch; // Correct my pitch for calcs
        const cosPitch = Math.cos(adjustedPitch); // Get cosPitch
        const lookVec = new window.BABYLON.Vector3(cosPitch * Math.cos(adjustedYaw), Math.sin(adjustedPitch), cosPitch * Math.sin(adjustedYaw)).normalize(); // Get my look vec
        const posVec = new window.BABYLON.Vector3(myPlayer.x, myPlayer.y + 0.3, myPlayer.z); // Get my position vec
        //
        for (let player of players){
            if (player == null || player.id == null){continue;} // Check if player object exists
            if (player.isDead()){continue;} // Check if player is dead
            //
            const otherVec = new window.BABYLON.Vector3(player.x, player.y + 0.3 - 0.072, player.z); // Calculate their position vec
            const diffVec = otherVec.subtract(posVec).normalize(); // Calculate the difference and normalize
            const angle = Math.acos(window.BABYLON.Vector3.Dot(lookVec, diffVec)); // Get the angle (in radians)
            const angleDeg = angle * 180 / Math.PI; // Get the angle in degrees (for fov)
            console.log(player.name, angleDeg);
            //
            if (angleDeg <= fov && angleDeg < aimPlayerAngle){
                aimPlayer = player;
                aimPlayerAngle = angleDeg;
                const targetVectorNormalized = diffVec; // Set targetVectorNormalized
                const newYaw = Math.radRange(-Math.atan2(targetVectorNormalized.z, targetVectorNormalized.x) + Math.PI / 2); // Get my new yaw
                const newPitch = Math.clamp(-Math.asin(targetVectorNormalized.y), -1.5, 1.5); // Get my new pitch
                aimPlayerYawPitch.yaw = newYaw;
                aimPlayerYawPitch.pitch = newPitch;
            }
        }
        //
        if (aimPlayer.id != null){ // If the aimPlayer was set it should have an id!
            // Ok we can set our yaw and pitch now!
            myPlayer.yaw = aimPlayerYawPitch.yaw;
            myPlayer.pitch = aimPlayerYawPitch.pitch;
        }
    }
    let aimLoop; // Define our aimloop variable
    document.addEventListener("keydown", (e)=>{
        if (e.code == toggleKey){ // Check if this key pressed is our aim toggle
            aimToggled = !aimToggled;
            if (aimToggled){ // If aimbot enabled set a loop to do the aimbot
                aimLoop = setInterval(doAimbot, aimCheckDelay);
            }else { // If aimbot is disabled clear the loop
                clearInterval(aimLoop);
            }
        }
    });
    //
    // Hooks
    let packetDecryptClone = {
        init: function(e) {
            this.buffer = new Uint8Array(e),
                this.idx = 0
        },
        unPackInt8U: function() {
            var e = this.idx;
            return this.idx++,
                this.buffer[e]
        }
    }
    function doHook(){
        const oldCloneMesh = window.BABYLON.Scene.prototype.cloneMesh;
        window.BABYLON.Scene.prototype.cloneMesh = function(){
            if (arguments[0] == "egg"){
                // We Are Cloning the Egg Mesh in player constructor!
                // Grab the player object by getting the arguments passed to function Io!
                let playerObj = arguments.callee.caller.arguments[0];
                // Make sure this is a player
                if (playerObj.id == null || playerObj.id == -1){return oldCloneMesh.apply(this, arguments);}
                // Add the player to our list
                currentPlayerList[playerObj.id] = playerObj;

                // The function Si to remove the player calls playerObj.actor.remove (aka Io.prototype.remove)
                // We are going to hook that function call!
                // Note : arguments.callee.caller references the Io function.
                let oldRemove = arguments.callee.caller.prototype.remove;
                arguments.callee.caller.prototype.remove = function(){
                    // Make sure this is a player
                    if (!this.player.id){return oldRemove.apply(this, arguments);}

                    // Remove the player from our list
                    delete currentPlayerList[this.player.id];
                    // Apply to original
                    return oldRemove.apply(this, arguments);
                }
            }
            return oldCloneMesh.apply(this, arguments);
        }
        function messageHook(){
            if (arguments[0].data instanceof ArrayBuffer){
                // Lets use our packetDecryptClone
                let decrypt = packetDecryptClone;
                decrypt.init(arguments[0].data);
                let packetType = decrypt.unPackInt8U();
                if (packetType == 0){ // Check if gameJoined Packet
                    myPlayerId = decrypt.unPackInt8U(); // Get my player id
                    currentPlayerList = []; // Clear the player list
                }
            }
        }
        let oldOnMessageSetter = Object.getOwnPropertyDescriptors(WebSocket.prototype).onmessage.set;
        Object.defineProperty(WebSocket.prototype, 'onmessage', {
            set: function(){
                let originalFunc = ()=>{};
                if (typeof arguments[0] == "function"){
                    originalFunc = arguments[0];
                    arguments[0] = function(){
                        messageHook.apply(this, arguments); // Send a copy of the values to our message hook
                        originalFunc.apply(this, arguments);
                    }
                }
                oldOnMessageSetter.apply(this, arguments);
            }
        });
    }
    let checkForBabylon = setInterval(()=>{
        if (window.BABYLON && window.BABYLON.Scene && window.BABYLON.Scene.prototype.cloneMesh){
            clearInterval(checkForBabylon);
            doHook();
        }
    }, 100);
})();

Conclusion Time!
Honestly, the aimbot isn't that good, but that's because I didn't want to add my full prediction aimbot into this example (bullets in shell shockers move really slow so you need prediction). I also have never done an aimbot hack on this game using hooks. For my main hack I actually just download the code, edit it, and re-inject it, along with the help of some fancy tools I made. This is because the hack I made for the game is about 2000 lines long and at the time I didn't feel like making hooks for each thing I needed.
 
Last edited by a moderator:

Rake

I'm not your friend
Administrator
Jan 21, 2014
13,306
79,068
2,484
Really cool, thank you for sharing, I always wanted to make userscripts but I'm too lazy to learn
 

TDStuart

Dank Tier Donator
Full Member
Mar 28, 2020
10
348
0
Really cool, thank you for sharing, I always wanted to make userscripts but I'm too lazy to learn
Honestly, it isn't difficult, especially since you are good at programming. The only tricky thing is getting a feel for what you should be looking for to hook if you are going that route.
 

Xploit

Ride the Pony
Trump Tier Donator
Dank Tier Donator
Nobleman
Dec 27, 2016
147
1,568
1
I feel I'm sorta experienced in JavaScript and flash hacking but your tutorial taught me alot. I never thought of going this route too hook into functions. Nice tutorial bro👍
 

TDStuart

Dank Tier Donator
Full Member
Mar 28, 2020
10
348
0
Just a note the video got removed from youtube somehow... wtf. I will either re-upload it or make a new video.
 
Community Mods