So I was grinding Granblue Fantasy for years (don’t judge me), and like most sane people, I was using Viramate - this browser extension that made the game actually playable. Quality of life improvements, some automation like keybindings, you know the drill.
Then one day? BOOM. Detected. Banned. RIP. (I thankfully did not get banned, but my friends did.)
Obviously I had to know how they caught people, so I dove headfirst into their obfuscated JavaScript mess. What I found was… actually pretty clever? Let me break down this wild anti-cheat system that’s probably watching you right now.
The Fundamental Problem
Here’s the thing about browser games: you’re completely fucked from a security standpoint.
When your entire game runs in someone’s browser, you’ve basically handed them the keys to the Granblue kingdom. They can:
- Modify any code they want
- Intercept every network request
- Mess with DOM elements
- Speed up/slow down execution
- Inject whatever scripts they feel like
You literally cannot stop them. The best you can do is notice when they’re being sketchy and snitch to the server.
Multiple modules?
Here’s where it gets wild - they don’t just have one anti-cheat system. They have three separate modules (util/od
, util/ob
, and util/oc
) all doing basically the same shit but with different detection codes and timing patterns.
Why? Probably to make reverse engineering more annoying and to have redundancy in case someone figures out how to disable one module.
The Obfuscation Game
First thing you notice in their code? Every single string is built character by character:
var joinStrings = function() {
return $.makeArray(arguments).join("");
};
// Instead of "length", they do this:
var length = joinStrings("l", "e", "n", "g", "t", "h");
// Instead of "XMLHttpRequest", they build:
var xhr = joinStrings("X", "M", "L", "H", "t", "t", "p", "R", "e", "q", "u", "e", "s", "t"); // wow such obfuscation
This isn’t some galaxy-brain security measure - any decent debugger will show you the real strings at runtime. It’s just trying to dodge automated scanners that look for obvious patterns.
Like, if you’re scanning for code that checks “XMLHttpRequest” modifications, you won’t find that exact string sitting there in plain text. Sneaky, but not exactly bulletproof.
The Snitch System(s)
The core of their whole operation is this batching system that collects evidence before tattling. But here’s the kicker - each module reports to different endpoints:
// Module 'od' - reports to "od/query"
sendData("query", payload);
// Module 'ob' - reports to "ob/r"
sendData("r", payload);
// Module 'oc' - reports to "oc/s" (and doesn't even send!)
sendData("s", payload); // Missing $.ajax() call - builds config but never sends
Each module has different timing too:
- od: 3011ms delay before reporting
- ob: 5011ms delay before reporting
- oc: 3011ms delay (but broken - never actually sends)
Every sketchy thing you do gets a unique code. Instead of immediately screaming “CHEATER!” to the server, they wait and send batch reports. Smart for avoiding backend floods and making it harder to correlate your actions with reports.
Click Pattern Analysis
// od & oc modules: 5011ms window
tapCount = ((event.x || event.y) && dateNow() - lastTime < 5011) ? 0 : tapCount + 1;
// ob module: 7011ms window
tapCount = ((event.x || event.y) && dateNow() - lastTime < 7011) ? 0 : tapCount + 1;
But here’s where it gets more sophisticated. They’re not just watching for multiple clicks - they’re systematically capturing coordinates for every meaningful game action:
// Pattern used across multiple game actions:
tB = [M.FIXED_VALUE_X + t.x, M.FIXED_VALUE_Y + t.y, actionType];
// Found in:
// - onTapStartDirection (starting auto)
// - AddNormalAttackQueue (attacks)
// - popShowAbility (abilities)
// - renderPopSummonDetail (summons)
// - changeAutoMode (changing auto)
This is their information gathering golden egg. They have a systematic approach where every significant game action requires realistic coordinate data.
Translation: If you’re a bot that just sends API calls without proper tap coordinates, you’re fucked and will trigger 1001/1100. The server expects every attack, ability use, and auto-battle toggle to come with believable interaction data.
This is also how they build up a profile about you.
Performance Monitoring
This is where it gets interesting. They’re watching for weird frame rates with different codes per module:
// od & oc: code 7001, ob: code 1700
// All trigger if FPS > 35
return window.createjs && window.createjs.Ticker &&
window.createjs.Ticker.getFPS() > 35;
But the really clever part is how they detect timing manipulation (od/oc use 7002, ob uses 2700):
var currentFPS = window.createjs.Ticker.getFPS();
var currentInterval = window.createjs.Ticker.getInterval();
// Temporarily mess with the interval
window.createjs.Ticker.setInterval(currentInterval + 100);
var isManipulated = (window.createjs.Ticker.getFPS() == currentFPS);
window.createjs.Ticker.setInterval(currentInterval);
They literally change the frame interval and see if the FPS actually responds. If someone’s overridden the timing functions to speed-hack the game, this test will expose them. That’s actually pretty genius.
The Hall of Fame (Known Tool Detection)
They’ve got a whole hit list of tools they’re specifically looking for, with different codes per module:
// RIP Viramate - od/oc: 8001, ob: 1800
'script[src^="chrome-extension://fgpokpknehglcioijejfeebigdnbnokj"]'
// Various other tools with different codes:
'[id^=gbfTool]' // od/oc: 9001, ob: 1900
'[id^=guraburu]' // od/oc: 9003, ob: 3900
'input[id*=boss_mode_1]' // od/oc: 9005, ob: 5900
Viramate was the big one - that Chrome extension ID is burned into their system forever across all three modules. The other selectors hunt for DOM elements that only exist when specific cheating tools are running.
Network Shenanigans Detection
// od/oc: 10102, ob: 21010
return window.hookAjax || window.unHookAjax || window._ahrealxhr;
This catches people using AJAX hooking libraries to intercept and modify network requests. Different modules use different codes for the same detection.
Code Integrity Checks (The Paranoid Stuff)
They’re constantly checking if you’ve tampered with core JavaScript objects:
var originalXHRLength = (window.XMLHttpRequest + "").length;
// Later...
return originalXHRLength !== (window.XMLHttpRequest + "").length;
By comparing the string representation of objects, they can detect if you’ve replaced or modified them. They do this for game-specific code too, especially in raids (probably because that’s where the juiciest automation happens).
The Monitoring Loop (Always Watching)
All checks run continuously with exponential backoff, but different modules have different special cases:
var validateWithRetry = function(code, delay, increment, checkFunction) {
var check = function() {
if (checkFunction()) {
queueQuery(code);
} else {
// od & oc: 4001 exception, ob: 1400 exception
if (4001 !== code) { // or 1400 for ob
delay += increment;
}
setTimeout(check, delay);
}
};
setTimeout(check, delay);
};
Most checks start every 5-7 seconds and back off if nothing suspicious is detected. The special codes (4001 for od/oc, 1400 for ob) maintain constant intervals - these are the baseline “nothing detected” states.
Debug Mode? In My Browser Game?
var hashPrefix = window.location.hash.split("/")[0];
return hashPrefix !== "debug";
This one’s a bit confusing at first glance. The function returns true
when you’re NOT in debug mode, which means:
- Normal players: Function returns
true
, so they continuously report the “normal player” baseline codes (4001 for od/oc, 1400 for ob) - Debug mode users: Function returns
false
, so they stop sending the baseline codes
Basically, if you’re in debug mode, you go silent on the baseline reporting. The system expects normal players to constantly ping with these codes, so if you stop sending them, that’s suspicious in itself.
Plot Twist: One Module is Broken
Here’s the hilarious part - module oc
is completely broken! It builds the AJAX config but never actually calls $.ajax()
. So it’s doing all the detection work, queuing up reports, but never sending them anywhere.
Either this is:
- A backup system that’s disabled
- A honeypot to confuse reverse engineers
- Someone fucked up the code and never noticed
- It’s meant to be enabled later via some other mechanism
The Complete Cheat Sheet
Here’s what each detection code means across all three modules:
Module ‘od’ & ‘oc’ (identical codes):
1001 - Abnormal clicking patterns
2002 - XMLHttpRequest tampering (od only - oc missing)
2003 - Raid code modifications (od only - oc missing)
4001 - Normal player baseline (continuously sent when NOT in debug mode)
7001 - Suspicious FPS (above 35)
7002 - Timing system fuckery
8001 - Viramate detected (F in chat)
8002 - Marketing/tracking element detection
9001 - GBF Tool detected
9002 - GFE tool detected
9003 - Guraburu tool detected
9004 - TKE tool detected
9005 - Boss mode/temporary_small in DOM detected
10102 - AJAX hooking detected
Module ‘ob’ (different codes for same detections):
1100 - Abnormal clicking patterns
1400 - Normal player baseline (continuously sent when NOT in debug mode)
1700 - Suspicious FPS (above 35)
1800 - Viramate detected
1900 - GBF Tool detected
2700 - Timing system fuckery
2800 - Marketing/tracking element detection
2900 - GFE tool detected
3900 - Guraburu tool detected
4900 - TKE tool detected
5900 - Boss mode/temporary_small in DOM detected
21010 - AJAX hooking detected
Pro tip: If you see reports going to multiple endpoints with different codes for the same behavior, you know they’re running redundant detection systems.
So… Is It Actually Worth It?
Look, this system obviously isn’t stopping anyone who really wants to cheat. Running three near-identical modules with different codes is overkill for what amounts to basic detection.
What it’s actually doing is:
- Scaring off casual cheaters through complexity and fear
- Collecting intel on what tools are popular (from multiple angles)
- Raising the barrier to entry for wannabe cheaters
- Providing redundancy in case one module gets defeated
- Making reverse engineering more annoying with multiple code paths
Browser game security is fundamentally about making cheating more annoying than just playing the damn game properly. The real security happens server-side with rate limiting, sanity checks, and statistical analysis.
This client-side stuff? It’s just the bouncer at the door - weeding out the obvious troublemakers and taking notes on everyone else for the real security team to review later.
Pretty clever for a gacha game, not gonna lie. Even if one of the modules is completely busted.