GDevelop¶
GDevelop is an open-source, no-code/low-code 2D game engine that exports HTML5 games using Pixi.js for rendering. On Android, GDevelop games are wrapped in Apache Cordova (or Capacitor in newer exports), placing the entire game runtime and logic inside assets/www/ as plaintext JavaScript. The engine compiles its visual event system into a single monolithic gd.js file -- the primary reverse engineering target.
Architecture¶
Engine Stack¶
| Layer | Component | Role |
|---|---|---|
| Game Logic | gd.js |
Compiled event sheets -- all game behavior in one file |
| Runtime | gdjs-evtsext-*.js |
Extension behaviors (physics, pathfinding, etc.) |
| Rendering | pixi.js / pixi-renderers/*.js |
Pixi.js 2D rendering (WebGL/Canvas) |
| Audio | howler.min.js |
Audio playback via Howler.js |
| Platform | Cordova / Capacitor WebView | android.webkit.WebView hosting the HTML5 game |
| Android Shell | Cordova activity | Java bootstrap loading index.html |
Compilation Model¶
GDevelop's visual event sheets (drag-and-drop logic blocks) compile into JavaScript functions at export time. Each scene's events become a set of functions in gd.js, with a consistent naming pattern:
gdjs.<SceneName>Code.func-- main scene event functionsgdjs.<SceneName>Code.eventsList*-- event handler arraysgdjs.RuntimeScene-- base scene management classgdjs.RuntimeObject-- base object class
The compiled output is verbose but structured. Variable names and scene/object names from the GDevelop editor are preserved as string identifiers in the generated code, making the logic straightforward to follow.
Identification¶
| Indicator | Location |
|---|---|
assets/www/gd.js |
Compiled game logic (primary target) |
assets/www/pixi-renderers/*.js |
GDevelop's Pixi.js renderer modules |
assets/www/libs/pixi.js or assets/www/pixi.min.js |
Pixi.js rendering library |
assets/www/howler.min.js |
Howler.js audio library |
assets/www/index.html |
Entry point referencing gd.js |
assets/www/data.json |
Game project data (scenes, objects, resources) |
gdjs.RuntimeScene in JS |
GDevelop runtime namespace |
gdjs.evtTools.* in JS |
GDevelop event tools namespace |
Quick check:
Version Detection¶
GDevelop exports include a version comment or gdjs.projectData object containing the engine version at the top of gd.js.
Code Location¶
Primary Files¶
| Path | Content | Size (typical) |
|---|---|---|
assets/www/gd.js |
All compiled game logic, runtime classes, event handlers | 500KB -- 5MB+ |
assets/www/data.json |
Project metadata, scene definitions, object properties, resource lists | 50KB -- 1MB |
assets/www/gdjs-evtsext-*.js |
Extension code (physics, tween, pathfinding, etc.) | 10KB -- 100KB each |
assets/www/pixi-renderers/pixi.js |
Pixi.js rendering (standard, unmodified) | ~500KB |
assets/www/resources/ |
Game assets (images, audio, fonts, JSON data) | Varies |
Code Structure in gd.js¶
The gd.js file contains both the GDevelop runtime engine and the compiled game logic:
| Section | Content |
|---|---|
gdjs.RuntimeGame |
Game initialization, scene management, global variables |
gdjs.RuntimeScene |
Scene lifecycle, object management, layer system |
gdjs.RuntimeObject |
Base object class with position, angle, visibility |
gdjs.SpriteRuntimeObject |
Sprite-specific behavior (animations, collision) |
gdjs.Variable |
Variable system (game, scene, object scope) |
gdjs.evtTools |
Built-in event tools (camera, sound, storage, network) |
gdjs.<SceneName>Code |
Per-scene compiled event handlers |
Extraction & Analysis¶
Basic Extraction¶
Code Beautification¶
gd.js is typically minified in production exports:
Identifying Game Logic¶
After beautification, search for scene-specific code:
GDevelop preserves original scene and object names as string literals. Search for game-specific identifiers:
Project Data Analysis¶
The data.json file contains the full project structure:
python3 -c "import json; d=json.load(open('extracted/assets/www/data.json')); print(json.dumps(d.get('properties',{}), indent=2))"
This reveals scene names, global variables, resource file paths, and extension dependencies.
Network Communication¶
GDevelop's built-in network actions use gdjs.evtTools.network:
Storage Analysis¶
GDevelop uses gdjs.evtTools.storage for local persistence (backed by localStorage):
grep -n "evtTools\.storage\|localStorage\|writeNumberInJSONFile\|writeStringInJSONFile" extracted/assets/www/gd.js
Hooking Strategy¶
WebView JavaScript Injection¶
Inject JavaScript through the Cordova WebView:
Java.perform(function() {
Java.choose("android.webkit.WebView", {
onMatch: function(webview) {
webview.evaluateJavascript(
"var _origCreate = gdjs.RuntimeScene.prototype.createObject;" +
"gdjs.RuntimeScene.prototype.createObject = function(name) {" +
" console.log('[GDevelop] Creating object: ' + name);" +
" return _origCreate.apply(this, arguments);" +
"};",
null
);
},
onComplete: function() {}
});
});
Variable Monitoring¶
Java.perform(function() {
Java.choose("android.webkit.WebView", {
onMatch: function(webview) {
webview.evaluateJavascript(
"var _origSetValue = gdjs.Variable.prototype.setNumber;" +
"gdjs.Variable.prototype.setNumber = function(val) {" +
" console.log('[GDevelop] Variable set: ' + val);" +
" return _origSetValue.call(this, val);" +
"};",
null
);
},
onComplete: function() {}
});
});
Scene Lifecycle Hooks¶
Java.perform(function() {
Java.choose("android.webkit.WebView", {
onMatch: function(webview) {
webview.evaluateJavascript(
"var _origLoadScene = gdjs.RuntimeGame.prototype._doLoadScene;" +
"gdjs.RuntimeGame.prototype._doLoadScene = function(name, data) {" +
" console.log('[GDevelop] Loading scene: ' + name);" +
" return _origLoadScene.apply(this, arguments);" +
"};",
null
);
},
onComplete: function() {}
});
});
Direct JS Modification¶
As with all Cordova-wrapped games, direct modification is the simplest approach:
- Extract
assets/www/from APK - Edit
gd.jsor any other file - Repackage, re-sign, install
Obfuscation & Protection¶
Default State¶
GDevelop exports produce unobfuscated JavaScript by default. The compiled event code preserves:
- Scene names as string literals
- Object names as string identifiers
- Variable names in accessor calls
- Extension and behavior names
Possible Protections¶
| Technique | Description | Bypass |
|---|---|---|
| JavaScript minification | Name mangling via Terser/UglifyJS | Beautify -- structure still readable |
| javascript-obfuscator | Control flow flattening, string encoding | Standard JS deobfuscation tools |
| Cordova plugin protection | Native license checks via Cordova plugins | Hook Java layer |
| Custom encryption | Encrypted data.json or resources |
Key must be in JS -- trace loading code |
In practice, GDevelop games rarely employ additional obfuscation beyond the default minification. The target audience (no-code developers) seldom adds custom protection layers.
RE Difficulty Assessment¶
| Aspect | Rating |
|---|---|
| Code format | Plaintext JavaScript |
| Readability | High -- scene/object names preserved |
| String extraction | Trivial |
| Control flow recovery | Full (standard JS) |
| Patching | Trivial -- edit JS directly |
| Data modification | Trivial -- JSON project data |
| Obfuscation ceiling | Low -- rarely applied |
| Overall difficulty | Easy |
GDevelop games on Android are fully transparent. The compiled event system produces verbose, structured JavaScript that preserves the original scene and object names from the visual editor. Combined with the Cordova wrapper providing no additional protection, analysis requires only a text editor and a JavaScript beautifier.