- 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
As you can see in the code this is likely our player class. The function name has been minimized and it is now called
Ok, so we now have a function,
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,
Ok so here is my hook code:
As you can see the hook is fairly complex. We end up hooking two methods, the cloneMesh, and the remove functions. With the
As we can see it is watching the
Now we can use this function to intercept and decrypt the packets. Here is my example:
As you can see above we set
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:
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:
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.
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);
}
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]
}
}
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);
}
});
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
}
}
}
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);
})();
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: