-
-
Notifications
You must be signed in to change notification settings - Fork 98
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Obfuscate GDScript in production export #4220
Comments
Related to #1407. I think the engine should minify scripts in release mode exports to reduce the PCK size, but obfuscation is much more complex to do correctly. In contrast, minification is much simpler and doesn't risk breaking as often, since it's mostly about removing comments and non-significant whitespace. Obfuscating a language like GDScript is potentially very difficult due to String-based signal connections, Object's That said, I don't think minification would be relevant by default, since scripts are exported to bytecode already ( |
Unfortunately breaking both the So both of these included security features don't actually do anything to protect a games exported scripts or technology. It took me less than 5 seconds to get access to a standard exported script. If I have to dig for the encryption key, the first time it will take me around 5 minutes and after that also less than 5 seconds. Here is an example of one of my exported (production export) scripts after decompilation: |
I think this is a task which should be taken step by step. We build a basic framework for obfuscating tokens at export, which uses a script-wide name search. Tokens such as A broad whitelist of what to avoid obfuscating would help tremendously (i.e. never Another way to provide increased security would be to allow customisation of the the location of the encryption key in the exported binary, although perhaps this warrants a separate proposal. |
This sounds flat out impossible. At least with the full scope of GDScript. Example: a.call("%s_%s" % [b, c]) I don't see how you could obfuscate GDScript code without breaking a construct like this. |
@zinnschlag JS actually doesn't mutate those unless you set the mangle level really high. For your example, a basic obfuscation might look like: 0_1230a.call("%s_%s" % [0_1243a, 0_1550a]) By default I think a good first level would be obfuscation of Perhaps merging all GDscript files into a single large |
My goal isn't to "protect" anything; it's just to optimize the export. Remember that Godot already has script encryption ( |
Yep, I understood, this proposal is about protection of assets, and not about performance.
The |
If you obfuscation function names then the example I gave won't work any more, since it constructs a function name dynamically at run time. |
Ah I see you mean the naming of the function. In this case I recommend having the ability to exclude items from the obfuscation. Javascript does this with the |
So I've been jamming on this idea in Python and I seem to have got at least some progress: Before, taken from godot-simple-state After (running without issues): Currently it runs a system-wide search for things to replace, and supports The obfuscation code needs a bit of a cleanup but I think this is at least an example of what we could do in-engine before export. |
I pushed the work I made so far here: https://github.com/tavurth/godot-gdscript-obfuscator So far it's not working on the full repository, as I will look into this more when I'm closer to my project release. |
I agree this would be very useful. I'm currently creating a puzzle game with procedural puzzle generation, which I've worked on sporadically for several years, and I'd like to protect the generation code as much as possible to prevent clones just ripping off my approach. The type of puzzles I'm creating aren't new, but I believe my generation code is unique and produces much better results than currently available alternatives. Obfuscation seems the perfect way of achieving this. |
Is the lack of this feature just a matter of being low priority, are people against it or is there another problem implementing? Isn't this a major detriment to people making commercial games in Godot? I did some reading on this but didn't find a clear answer whether decompiled gdc files retain the original identifier names or not? Not that it makes a big difference but if they do then I would love to see obfuscation. |
You can see my screenshot above, this is what files look like after you decompile them, comments stripped but everything else the same as before: When I posted this on reddit around 60% of people were leaving extremely negative feedback, with people yelling at me for starting work on it. I found that a bit confusing, but my conclusion was that a large proportion of godot devs are either:
I still feel like this is an extremely necessary part of the godot ecosystem. We don't have to pay anything to Godot for using this engine in exported games, but creating a cool new game currently can easily be ripped off, changed some way and released on a market where the legal system has no effect. (I'm sure you can imagine such markets). |
The lack of a feature in Godot is 90% of the time caused by a lack of people willing to do it. Also, obfuscation causes some code slowdown, meaning this potential feature should be disabled by default. I think that for a large number of projects, performance is still more important than protection. Obfuscation is not a very reliable way to protect the code, it's just a way to make it harder to crack. Obfuscation won't help if the cracker has enough desire and deobfuscation tools (which will definitely appear over time). You can try some third party tools to protect your code (DRM). Creating such tools is a difficult and ethically controversial goal for an open source project. I'm not sure if obfuscation that doesn't have an optimization goal (such as minimizing local variable names, constant folding, function inlining, etc.) is acceptable for an open source project. Although I understand your desire to protect your work and that it is quite common in game development. |
I guess this is just a problem for all languages that use byte code. Maybe my way of thinking is outdated but it feels wrong that somebody can just run a program and have direct access to the source code to easily modify various parts of the game inside godot engine itself and claim as their own? If that starts to happen enough times the idea of ownership gets very blurry. Like with people making rom hacks they have to at least put in effort in order to do these things, and they don't have access to the original tools. I'm not interested in DRM though. |
Thankfully in Europe & most western countries the legal system takes care of this for you. If you are wondering if someone has copied your Godot game you can just decompile their source and find your lines of code directly to use in the lawsuit 🤷 However there are quite a few jurisdictions of the world where your lawsuit would land on deaf ears, or even worse have you labelled as the copier rather than the original owner.
I haven't tested with my project above yet, but since I've reduced the byte size of each individual symbol in the obfuscated script, it would probably work with some small percentage performance increase. |
In my opinion, if we can't get obfuscation option in Godot because of various downsides, then |
An example of why we need this: https://www.reddit.com/r/gamedev/comments/ufa7q6/my_game_got_copied_changed_and_uploaded_to_google/ |
If you are already compiling your own export template (e.g. in order to turn off some unused modules), changing the structure of GDScript byte code might be a good alternative to obfuscation. e.g. You can swap the order of constant count and identifier count fields when dumping and reading GDScript byte code. Compile an editor and an export template with the modified engine. Export the project with the custom editor and export template. Then your exported project won't be able to be decompiled by a generic tool and is script-kiddie proof. |
i dont have knowledge but having both in the default engine as an optional option if possible to exist, would be a unique step than other game engines have. |
I wish this feature too. Use the |
You can think a reverse engineering tool as a special kind of export template that iterates through all the resources instead of executing the game logic. Using a stock export template means the tool does not need to do anything that takes effort, as the export template's logic is open sourced. You have to compile your own export template in order to hide what it does I think. |
I was shocked to see the RE tool exposed everything in my game, except for comments. Best to come to terms with it now than later I guess... It would be nice to have a basic level of obfuscation for gdscript/tscn, doesn't have to be extreme, just a basic level would thwart most copycats |
To be fair, this type of problem already exists in some of popular game engines that use the same kind of build process, such as Unity. The only thing different is that on Unity there is already third party tool that obfuscates code, and even built-in IL2CPP tool that mitigates basic reverse engineering efforts. I have been in some of gaming groups and they indeed also do reverse engineering game files and have exposed its contents regularly. It's so trivial to do already and you only have few options to mitigate it. In Godot's case (and few of open source game engines) is much more severe since the build process is already been made in public, so anyone can just observe a build process and find a way to reverse it. In fact, even encrypting the game engine and entire PCK file won't help that much (there is already a tool being made to find a key and decrypt game files) Plus, GDScript is also one of the weakest point of entire game engine in terms of protection. It doesn't even get minified, and it isn't a compiled code (although the exported script is a bytecode, but labels are still preserved) . It has some trade-offs especially in performance. There aren't many things we can do to to mitigate any of reverse engineering efforts with a game engine that has simple build process. Even minification won't help that much. I only need minification feature to save some of disk space, such as internal function labels. |
Probably adding obfuscation/minification is not a good solution compared to transpiling? Example: #3069 Imagine you could compile GDScript into C++ for release builds, would that be superior way of tripping up decompilers? |
@naturally-intelligent people have already tried using C++ transpilers to make RE harder with tools like unity, but this isn't the point of it at all. C++ transpiling is a useful way of squeezing extra performance out of a game when it's needed without messing with C++ stuff directly, it's not meant at making RE harder. |
Intermediate representation would actually go a bit further, if I read the post correctly. What you quoted is a human-readable version of Godot 3.x scripts in exported projects (the actual file would be binary, but still a 1-to-1 mapping). Intermediate representation further obfuscates your scripts because you are including a precompiled version of your GDScript rather than the script itself. On top of being in binary as well, it doesn't store local variable names since it doesn't need them. Plus the code is made up of simpler instructions (I imagine it would be like reading Assembly instead of just plain C), making it much harder to follow, certainly enough for your average script kiddie. Regardless, there haven't been any updates on the IR idea lately, though the engine team appears to be aware of it. It may be on the backburner while this proposal is discussed, which would supersede intermediate representation, and a lot of other things discussed here. Personally I think having the option to offline compile GDScript to a DLL/.so as discussed in Juan's proposal would be sufficient obfuscation, and if we can get performance uplifts to go with it even better. We'll just have to wait and see if other engine contributors agree. |
This comment was marked as off-topic.
This comment was marked as off-topic.
Making a competitive multiplayer game that I plan to update frequently, I wanted to expend on SysError99's comment. I understand some persons like to keep control of what runs on their computer. So, client-side, I decided against using an intrusive anti-cheat in my game. But knowing how much of a plague are cheats in competitive games, doing nothing to prevent hackers from accessing the source code of the game would be a serious disrespect of legitimate players / paying customers. I do not want to have the "the competitive game that released without an anti-cheat but with the source code" reputation. Unfortunately, the engine using it's own formats and even scripting language, there is no existing tool for me to implement this in my release pipeline. And encryption does close to nothing when the key is stored alongside the data it encrypts. |
Hey there! You can use compiled languages like C# and C++, which are officially supported, to hide sensitive implementation detail. You don't have to use GDScript at all, or limit it to parts which are not critical for security reasons.
There is no way around it with encryption. Anything that you encrypt needs to run on the end-user machine. So that machine must be able to decode the encrypted files to execute them. |
This seems a little weird for a language which was built as the main entry point to the engine. 🤔 GDscript is awesome, I see there's a pull request above to bring this proposal close to a close! I think once that's merged we suddenly have a whole host of abilities to better protect ourselves. I hope the new influx of unity C# developers does not reduce the amount of effort spent on GDscript 😔 |
Well, we support many languages, and GDScript is mainly a scripting language. Not everything should be done with a scripting language. Arguably, only scripting should be done with a scripting language 🙃 In any case, I was only addressing a certain concern in the comment above, because you are not actually limited to GDScript with Godot. |
Agree, it's a bit disturbing to see a response that comes across as "Don't use GDScript if you want X language-agnostic feature" Huh? My entire game is GDScript. Is it going to become a second class language? |
Also the fact that there are plenty of great C# decompilers out there, I would say it's a moot point when it comes to code security against hackers anyway. The main current way to avoid C# decompilation is obfuscation, as can be seen from reading the above stack overflow. Lets just get GDscript close to something we can mutate by our own custom tooling, and then it's possible to be more secure than C#'s questionably "secure" compilation. As an aside I saw this recently. Good job Google! Maybe a little later we can run GDscript while keeping it encrypted 😅 |
Yes, but with unity, they use the IL2cpp |
GDScript will be supported as long as enough users and contributors are interested in it. Increasing interest in C# does not decrease interest in GDScript. Moreover, different teams maintain these languages.
This confirms the complexity of the task: even a compiled language like C# is relatively easy to decompile to the source code (or something close to it) if no third-party protection tools have been used. Additionally, as stated above, GDScript cannot safely remove/rename member symbols (this is only possible for locals).
Obfuscation is not just a missing feature, it is something that is difficult to implement due to the way GDScript is designed. A lot of work needs to be done just to make obfuscation possible. Some things like intermediate representation and compilation optimizations can help, but it will only be a side effect, not the primary goal. Essentially, obfuscation is quite conflicting with the design goals of GDScript. Maintainability, bug fixes, language features and performance will always take higher priority.
This is already possible. Potentially, you could create an export plugin that would rewrite the GDScript source code with obfuscation. As a POC, see my plugin gdscript-preprocessor. Given that you'll need a GDScript parser, it might be easier to do this as a C++ module to use the existing parser. |
Was not aware of export plugins. That's a fantastic POC, thanks for creating and sharing. It definitely brings to question how much of this should be natively supported by Godot vs. living in the plugin ecosystem, and after seeing this POC I'm feeling less convinced that this is a Godot responsibility. |
I should also bring this up about obfuscation in common. Sometimes simply stripping/minifying entire labels may already help with the protection since users need to figure out what they do logically. Some game engines or tools that offer integrated obfuscators are possible simply because the way they are designed is simply suitable for minification/obfuscation. In GameMaker, you cannot even wire up interfaces with strings since it expects most of the stuff to be static. When the project gets exported, it knows everything and can alternate source code safely. In contrast, it's virtually impossible to alternate source code in Godot without some serious source rewrite. I learned this the hard way while developing obfuscators for Godot and it turns out that it's very difficult to alternate source code safely, even with along the project source code itself due to the way Godot is developed. It may be possible but it's unavoidable that we need to fork the project, making it support minification/obfuscation exclusively. I doubt anyone would want to take on the task, pretty opposite from the way it gets demanded. Ultimately, while this issue may not be resolved, any of Godot projects being under long-running development are simply doomed. I suggest everyone demanding for the solution to start looking up any other scripting languages that can be easily modified to confuse hackers and easily integrated into Godot. It's pretty common practice even in medium-large companies to do it. Lua is also supported in Godot by the community. JavaScript can also be a viable option given that it has a lot of obfuscation and minification tools. If these aren't really options, Godot may unfortunately not be the right tool for you. |
I remember reading a reddit thread where someone made something in Rust, a compiler that can take your GDScripts and it will spits out binary. After digging my history, this was the thread, the repository, but sadly it never went far and now archived. |
Another use case for this: When working professionally with a client. I'd like to release a playable build before contract payment is completed. Ideally quite often. I would however like to validate my privacy of sources, and ensure I don't have too much work left on the table if a client chooses to run away with my source code and find another (cheaper) developer to build off my architecture. Currently the best ways to freelance in Godot appear to be:
|
I think this isn't so hard, even Python and Lua have good obfuscators, examples are pyarmor and sourcedefender for python, which are premium, and hyperion (there are some deobfuscators for this one), that's free, for lua there is luraph for premium and prometheus for free |
@nacho00112 I will note all of the tools you named appear to be third-party tools: they're handled by separate groups/people from their respective programming languages. And if I had to guess why that's the case, I wouldn't say it's coincidence. Because, even if we assume that obfuscation is easy, a proper obfuscator tool would take time away from development of the programming language (or in this case, development of the game engine). So, if you want an obfuscator for Godot, I invite you to work on one. That way, people who want obfuscation can have it, without it taking away from engine development time. |
Are those languages you mention strictly name based? Do they rely on interfaces with fixed names like |
I've thinking about it, one could make an obfuscator that adds to and modifies the existing control flow with, randomized, unreadable and confusing false control flow with false variable names and all to avoid deobfuscators due to indeterminated results, with the ability to control how big the resulting obfuscated file is, the bigger the more false control flow quantity, just imagine a 1MB file filled with that, one couldn't know where the actual code is, and on top of that, a feature to convert directories of source code to one single file with everything embedded, but these are no more than ideas, implementing it is the real problem, the false control flow must look real, hyperion does something like this I'm mentioning those languages because they have reputation of being readable, so making good obfuscation for them is impressive to me |
IMO, we can crack any language. but for godot. use re-tools. we can anti-project. just a minutes. we can open the .godot project. not only gdscript. like, we pulish a godot game. but just use re-tools. we like pulished my godot project. |
With godotengine/godot#87634 merged, I think we now have very rudimentary obfuscation back. At the very least it strips comments. |
It's not that simple. The problem with out-of-the-box open-source obfuscators is that most of them are all reversible or at least maintain plenty of characteristics of the original source code especially original labels, since they are all required to be remained the same as the nature of 🦆 (dynamically typed) programming languages that cannot determine types outside its runtime. While it's true that it's somewhat not difficult to implement some sort of obfuscators similar to whatever these open source obfuscators offer, it's also not difficult to also reverse the process since the process is also open-source. For example, JavaScript obfuscators already got reversed and deobfuscators for multiple of open-source procedures being made available. The outcome becomes just not as valuable to be implemented in the core or even in general if that matters. The problem got a lot worse with Godot because not only that GDScript is a 🦆 language, but Godot Engine itself also heavily relies on human-readable string references since it's the best approach for source maintainability and readability. Not to mention that Godot is supposed to be supported by multiple development environments and portable via GDExtension, doing some sort of AIO-integration like what GameMaker does is also very difficult to closely impossible to achieve without breaking compatibility (again) and another huge load of maintenance efforts. In other words, Godot may need to reinvent the wheel all over again to support that feature.
Did you mean that if this feature is merged into the core? No This is just a proof of concept (that does work) of what it'd look like for the source code that's ideally in exported projects, especially on how "intermediate representation" would work. The The horrible side effect is that the source tracker must know every single user-defined labels and must be able to distinguish between Godot labels and user's labels in order to alternate source code safely and not butchering user's code randomly. Fortunately Godot's integrated docs made made it easy, and this this project is able to alternate source code safely for the entire project. Things left are just developing a new "programming language" that supports this proof of concept work. However the way that this project is written is absolutely unfriendly to use, and I won't gonna open source it because of this exact reason. It's in JavaScript in form of "source generator" syntaxes, and it's absolutely horrendous to read. Yes, there are alot better ways to handle this with total of possibilities of it to be in the core, but I bet it won't gonna be in the core until at least Godot 6, judging by how contributors not treating it as a necessary feature. |
Describe the project you are working on
Any project which can be released publicly where the developer might not want to have their code or structure implementation easily stolen.
Describe the problem or limitation you are having in your project
Currently Godot supports encryption of the exported files (if you use a custom build of the export templates)
However this is not really good enough. Almost every successful engine will at some point have a decompiler for the exported assets. (see ILSpy).
In-fact, due to the open source nature of Godot, I think it would be relatively simple to write a script-dumper, that uses the encryption to first de-crypt the files and then just dump them to some output folder once they've been loaded into memory.
Many others have also been worried about this issue, quite often with those issues being closed when the discussion goes more towards DRM or encryption.
❗️ This is not a discussion about DRM or Encryption ❗️
godotengine/godot#39115
godotengine/godot#24716
godotengine/godot#19790
Describe the feature / enhancement and how it helps to overcome the problem or limitation
When exporting to production, exported GDscript files can be obfuscated with the names mangled.
In this case, adding encryption would add a layer of security, and if someone does decide to extract the files from the pck, they are left with an unintelligible mess of obfuscated code.
This additional security would put godot at the top end of protecting game assets, and likely bring users to our community.
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
An example implementation can be found here
Here is a sample:
And obfuscated:
If this enhancement will not be used often, can it be worked around with a few lines of script?
This should probably be core, because it could be part of the standard export pipeline.
Is there a reason why this should be core and not an add-on in the asset library?
I may work on an external plugin for this, but it would require duplicating the repository before every export, as this would mangle all script files.
This is not something which fits with a good export pipeline.
The text was updated successfully, but these errors were encountered: