I’m considering building the game I am working on via IL2CPP rather than Mono with the main purpose of obfuscation. I’m relatively new to C# and a long time C++ developer. Seeing how incredibly easy it is to decompile C# code, I would either use a C# obfuscator or build to IL2CPP. Neither is bulletproof, but they should prevent easy/lazy hacking attempts. I’m not overly concerned about players cheating, more concerned with overall security with regards to access to the back end, etc. Any performance gains will be a plus
Anyhoo…
Are there any compelling reasons NOT to build using IL2CPP? Pitfalls, gotchas, known incompatibilities, etc.
IL2CPP doesn’t help you there and if someone tells you otherwise then don’t belive them, because it’s not true, sorry. Make sure that your server doesn’t trust anything the client sends and that your (important) messages are encrypted properly. People can “hack” your game by reading, changing, faking networking packets without reading a single line of your code if your server isn’t prepared for these kind of things.
It doesn’t help with hacking (the likes of cheat engine edit values in memory), but it certainly helps reduce wholesale game rips which happen mostly on Android, where someone decompile your game, replaces the art assets and re-upload as a new game. That’s how some shady companies manage to clone popular mobile Unity games few weeks after they release.
It won’t protect you from these companies either, it just makes it a littler harder, but there are already some tools out there to make it “easy enough” even if they used IL2CPP, like you said it happens to many mobile games and many mobile games already use IL2CPP. Im not going to name any of these tools or discord servers for obvious reasons but Im in a few modding discord servers and they don’t have any problem to mod IL2CPP games and rebuild them. Note: These modding discords don’t “steal” these games, they just mod the original games for fun and they only do it for single player games, but since it’s that “easy” for modders it isn’t any harder for companies who try to scam.
I’m aware of these sort of exploits and will take all appropriate measures. My motivation is for the logic of the code to be much harder for a human to interpret. For example, un-obfuscated C# code decompiles so looks very similar to the original. Obfuscated C# code will rename classes, variables and methods so it’s much harder for a human to interpret what is going on.
I assume that IL2CPP will generate assembly code and strip out class/variable and method names. Not sure if they can be easily reconstructed using the info that the binary may have with regards to Reflection. Please correct me if I’m wrong about class/variable/method names being stripped via IL2CPP.
So, the motivation, as mentioned, is to make it harder for a causal hacker to get any meaningful, human readable logic from the binary, not impossible. Un-obfuscated C# code seems like a “red carpet”.
KokkuHub, now that you mention it, preventing wholesale rips of the game code is a pretty good reason to use it as well. Thanks for the tip
I am also concerned that special cases may pop up using a C# obfuscator. In particular, if a 3rd party plugin is using Reflection and not handled by the obfuscator, then bugs may pop up and may be hard to identify.
So… yes… hacking and cheating is always possible. I don’t plan on spending too much time preventing it, but want to make sure I do due diligence. At this point it’s a matter of Mono+obfuscation vs. IL2CPP. So original question remains…
Are there any compelling reasons not to use IL2CPP over mono+obfuscation? I will use one or the other, and want the choice to be one that results in the least number of special case bugs.
Modding games is very different. Changing things like models and data files is easy, if the games use interpreted scripting that is also easy to change, but changing C++ code is a completely different story. The difference between decompiled C# and decompilled C++ is enormous.
With decompiled C# you get something that is very close to the original source code, with fields, methods, properties, and class names, which someone with average C# experience can turn into a buildable project with a few days of work.
Decompiled C++ is barely readable, and requires a lot more effort to become something you can build. Most names are mangled beyond recognition, stuff that used to be in separate libraries becomes a single jumbled mess, moreso if LTO was used. Unless you’re dealing with dlls, which need to preserve their public interfaces somehow, what you have is basically raw x86/x86/ARM machine code, and it’s very, very hard to work with that when you don’t have any source code.
AFAIK, native game modding is hard work that is done mostly out of love (or for cheating/piracy purposes). Using it as a basis for a shady game publishing business is not cost effective, because it’s much more time consuming and error-prone.
I think their point about modding communities existing is that the bar of entry isn’t all that high. So if you’re trying to subvert a company from duping your game with new assets, IL2CPP is hardly a roadblock for a company with money to throw at a problem, since teenagers on a discord can figure it out relatively easily. May that drive to do so comes from love or financial incentive… both are incentive enough to learn some basic tools and code.
Now if you’re not trying to thwart an asset swap.
OK… what are you trying to block?
Piracy? Depending on your anti-piracy measures, the difference between IL2CPP and CIL can range depending on how your anti-piracy measures work. That’s a way more complicated topic.
Intellectual property rights of code that you don’t want shared? This is more arguable for obfuscation and CIL… though many would argue that courts are where you protect that. Which ever, you do you boo.
The thing is what keeps getting mentioned is “casual hacker”… what is a “casual hacker” if not someone more skilled that those found in the modding community? What would a “casual hacker” be trying to exploit that you have in your code? Are you like storing passwords to your db in your code??? Don’t do that… llike R1PFake suggests, your server should NEVER trust any incoming message.
At the end of the day a “casual hacker” is either someone with so little skill they’re possibly just trying to hop into the mod community and wants to learn how code works and stuff. In which case the biggest exploit they’ll do is the equivalent of “cheating” in a 1-player game. So what. Let the cheaters cheat all they want.
Otherwise, they are more than a “casual hacker”, are being malicious, and have the tools/skill necessary to rip apart your IL2CPP/obfuscated code.
BUT
At the end of the day… you do you boo.
IL2CPP is pretty dang good as long as your not using some of the more aggressive stripping configurations. When you start using the more aggressive stripping aspects and you also rely on reflection/strings for stuff, you’ll possibly run into some issues.
Of course, that’s what a QA/testing pass is for.
And if you’re concerned about hackers exploiting your game… you should probably also have a robust QA/testing pass.
I would like to re-ask the question, because I’d love to know the answer.
What are you trying to protect via obfuscation OR IL2CPP?
I ask for 2 reasons:
we could maybe offer up some suggestions that directly address what it is you’re attempting to protect
Whenever I see threads about this it’s usually people arguing about how it’s useful/not useful. This back and forth argument about it (mind you I’m on the not-useful side, but I’m not going to tell you that you shouldn’t… you do you boo). But in that argument I’ve never had this question answered. It’s always this wishy washy “I know I can’t protect against skilled hackers, but what about these ‘casual hackers’?” What are these “casual hackers” doing that you don’t want them doing?
Cause honestly the only big ones I can think of (if cheating isn’t your concern).
Multiplayer Online Game - block this on the server side. Don’t trust any messages, and perform a binary check on their install (this could be as simple as hash the binary file, send it to server, and compare to known hashes… if they’ve edited your game, then it won’t be in the list of known valid hashes… of course they could inject code to subvert this, by sending valid hashes, but you could easily build up this concept to make it stronger. The depth of which though is a cost-benefit analysis which mirrors my argument about the next thing…)
Anti-Piracy - OK… yes… they could potentially strip out your anti-piracy, especially if you rely on really basic anti-piracy. This is like the only legit thing I can thing of being a legit reason to obfuscate (IL2CPP included as an obfuscation tactic). As it can be a “poor-mans” way to protect another “poor-mans” anti-piracy code.
But here’s my thing there…
Who is pirating your games?
You’re not selling some 60$ AAA title here. If you don’t have the money to invest in robust anti-piracy measures, you’re breaking into a small indie market. The kinds of people who are willing to go out and download a possibly infected version of your game off a russion torrent site… well… they weren’t going to buy your 5 dollar game in the first place! They’re willing to risk ending up on a botnet or worse to play your game! No sweat off your back, and it’s not like some customer is going to “accidentally” install a bootleg copy and blame you for the virus… they were the ones who went to the shady site and downloaded it. That’s on them. Cause it’s not like they’re going to print your game to disk, go down to the local flea market, and sell it to people who think it’s legit… no one has heard of “Kyle’s Indie Punk Game” before.
Which mirrors back to the online game play. Unless your some massive game with a huge player base… you should either be investing in robust anti-cheat methods based on the income your game is generating, or your game is so minor, it’s not like people are going to be scale exploiting your online experience.
…
Some Other Reason - I would love to know what it is. Because just like the Multiplayer, there is likely wayyyyy better ways to deal with it.
My main motivation is for securing a multiplayer online game. I’m using Phonton and Playfab for the networking. Not overly concerned about cheating, but some anti-cheating will be nice. More concerned about attacks that may affect the network as a whole. I’ll follow all of the recommended practices for securing Photon/Playfab, but I suspect that if the code is too easy to decompile ( as is un-obfuscated C3 mono), it may be too easy for a hacker to rip it apart, find an exploit, and do things that may affect other players negatively.
I realize that this is a general statement, since I am not experienced in hacking, I can’t say specifically what I am trying to stop. I simply want to establish the “best practices”. Ideally, it will be just difficult enough to discourage most from bothering. Beyond that, I’m not too concerned. The one thing that I am sure of is that the un-obfuscated decompiled c# code is unacceptable. It’s just way too easy to read and understand how the code works. If it’s hard, most will give up.
A part of the business model will be to have regular updates to the game, as new content will be constantly added. So, security measures can be changed on a regular basis, secret keys changed, yada yada. So if one build gets compromised, the next update should take care of it. Ideally, it will be too much trouble for someone to bother constantly hacking new builds.
So, in conclusion, my motivation is to employ “best practices” that will discourage easy/moderate hacking. Doing nothing is not a best practice, nor is doing something that will be overly time consuming and error prone.
Yeah, using IL2CPP will do nothing to bar cheaters. Most cheats don’t even touch the game files at all. Several popular multiplayer games are made in C++ and also have to deal with cheating.
The reason you should use IL2CPP is because it can improve performance, specially when you adapt your code to work with high-level code stripping. If you are using dedicated servers, you want your server instances to be as efficient as possible so you can run as many as you can per VCPU and lower costs.
Here is my line of thinking… it may be wrong, but it seems to make sense…
If the variable names/method names etc. and the result is assembly code, would it not make it more difficult to interpret the game logic? To hack a game, the hacker generally needs to understand what the logic is. Just because they have the tools to modify a CPP compiled binary, don’t mean that it is easy to decide what the logic is.
Again, since I am unfamiliar with the IL2CPP specifically, I’m not sure if this reasoning is correct. Can IL2CPP code be decompiled into a form where the original class/variable/method names are preserved? If not, that that is what I am looking for. If they can be reconstructed, then a obfuscation tool ( found good one on the Asset Store ) will do that and bob’s your uncle. Not sure if both work together. I’ll ask the developer about that.
If all of the names are meaningless, would it not be more difficult for a hacker to understand what to change and why? Yes they can go in, inspect variable values, remove/inject code and use whatever bag of tricks they have up their sleeves. But would it not be more difficult than un-obfuscated c# code?
The latter would obviously be more difficult for someone to understand it’s purpose while the former would be completely obvious.
If this assumption is incorrect, then mono + obfuscation tool may be a better approach, which will do this.
This is the purpose of the post… to find out if that assumption in correct or not. If I should protect the code at all, and/or use other techniques, etc. make interesting discussion, but I’d be very interested in knowing the answer to that core question.
All of the responses are very helpful and I thank you all for them. These discussions often go in the direction of “why do you want to do that”, or “you shouldn’t bother”, etc. That’s all good, but to make a proper decision, I’ll need that core info I am searching for
I just double checked the description of the obfuscator I am considering…
Apparently it does support IL2CPP builds, doing both is an option as well. My previous C++ experience lead me to believe that it would be redundant since names get stripped from the binary. But with everything going on with C# with regards to Reflection, I’m unsure about what is stripped in IL2CPP or not.
So good news is I’ll get what I’m looking for guaranteed. It’s just a question if the obfuscator is redundant or not.
Here’s the thing about hacking online multiplayer games.
You attack the weakest point. Like you said… doing nothing isn’t acceptable to you, but spending a ton of time is also not acceptable.
Well the same goes for hackers. They don’t want to muck around in your weird code. They want to attack the parts that are easiest to attack. Oh, you use Steam and I want to subvert the Steam account authentication to get around having to have a steam key? OK, they don’t attack your game specifically, they attack the Steam integration dll’s to subvert the Steam authentication (which is CPP). Why that? Because you crack one, you’ve cracked them all!
As for online play when it’s known you use things like Photon (not hard since a) you just said you did here on the internet and b) a simple spy of the packets being sent by your game will show messages formatted like the commonly used Photon). Well… you’d attack Photon. It’s a known system that is used in many games, meaning you can reuse vulnerabilities between games.
And obfuscating Photon wouldn’t help either… because why modify the binaries when I could just intercept the messages before they leave my computer and get sent to the server. Edit the information as I see fit, and then send it along. I can even send false messages to the server without the game even saying to do so, or inject messages into the game. No need to modify your game at all… just write up a program that listens on the outgoing/incoming port, consume all messages, and push them along as I see fit.
Cause here the thing… at the end of the day. Your code is AWFUL. It is a place I, the hacker, don’t want to spend my time. No matter how pretty you may consider your own code, anyone not familiar is going to go insane trying to read it. When I start a new job, I don’t just dive into the code and start mucking around… I have to take time to spelunk the code and familiarize myself with it. And I’m a software engineer with 15+ years of professional experience, 25+ years of hobby experience. And I still need several hours, if not days, to get familiar with a large code base.
Same goes for hackers… they don’t want to read your code. They want to exploit using the path of least resistance!
…
Back to your whole not wanting to spend too much extra time.
Well… sorry, obfuscation (IL2CPP sort of as well) is going to add time in that IF you’ve already tested. Well you’re going to have to test again. You’re going to have to make sure that the obfuscator or IL2CPP didn’t accidentally strip or rename something that breaks something you did in your code. Here I’m looking at reflection… and note there’s a lot of behind closed doors reflection that goes on in unity, especially if you use 3rd party libs that may not have taken obfuscators into consideration. Though IL2CPP is likely considered for most well known libs designed for Unity… but not guaranteed.
Point is, you’re going to have to do a thorough testing pass on that. Which is time. Which is money.
If it’s time and money you’ve already accounted for. Have at it. If you want the sound of mine… it’s not going to necessarily hurt you to obfuscate your code. And heck the IL2CPP will likely give you a performance boost!
But at the end of the day… it’s a paper bag around the problem you’re trying to solve. Sure now the hacker needs to rip open the paper bag… but they likely won’t, and even if they do, they likely have the skills needed to do that simple task. Since digging into your code base, even when written in clean C#, is still a chore and a half to do.
…
Give you an idea of how easy it is to work around an obfuscator.
If I’m spelunking a code base I don’t know, I don’t search it for text that MIGHT be the name of a function I’m concerned with (like searching for ‘Decrypt’). That’s hard. I have to guess names, and assume the dev spelled it correctly, and assume it’s English!
No… I attach a debugger.
And then I break at the moment where I’m most concerned. Oh, I want to find the ‘Decrypt Message’ method, ok, I run the program and send/receive a message, and then I break when that occurs (network activated?) and step through the code watching the call stack and what the memory is doing. Now I know where I am in the code, I can find all the methods/functions relavent to this code, and observe the memory for changes that I’m concerned about. I don’t care if the method is called AX13543uopfduuas_boogers. If it takes in a jarbled string and spits out a clean string, well, that’s probably a decrypt function!
And here’s the thing… this is why C++ is actually just as hackable. I don’t care what the function is named, heck in the compiled C++ code the function doesn’t have a name… it’s just an address in the program memory. But if that address when jumped to and returns had modified a jarbled string and made it clean… again, I know that’s the decrypt function!
Hell, C++ debugging tools are ROBUST. We’re talking about one of the most popular languages in the world. If I hack games, I probably know C++, and I probably am super familiar with the tools I have at my disposal.
Think about it… people reverse engineered the source code for Mario 64 from N64 binaries! It’s so accurate that when they recompile it the resulting binary is identical to the official binary. Now of course it took them a while, and it was a very dedicated fan base doing it, but they did it. Just people… it wasn’t like the community was super hackers, they were video game fans, just regular old joes. Sure they probably won’t put that level of effort into 100% reverse engineering your game… but that’s our point… no one is going to put in this effort! But if they really truly wanted to… I think they could reverse engineer the 0.1% of your code that is important to making whatever hack they want. Because finding the location of that code in your binary… is trivial… it requries a debugger and a copy of your game and to just pause and step through with the debugger when the in game event they want to exploit occurs.
And this is why when people come here with questions about their code, we often ask them if they “used the profiler” and “used the debugger”. And often times can even guess at the root cause based on our own experience without even seeing specifics.
lordofduct - I am aware of all of that. It takes no extra effort to build to IL2CPP and if the obfuscator asset works as advertised, it should take little effort as well. I simply do not want the code to be in a human readable form. That is the scope of my question. Everything else, as true as it is, is another topic.
It’s just not doing what you think it is… if I want to hack your online multiplayer, your code is the last place I’m going to look.
I was specifically referring to this part in your post (and is why I commented it in my post):
It’s wrong. That’s not how hackers hack. Just like that’s not how engineers spelunk code bases. We don’t just “read” the code and follow its logic. We run the code and observe its logic.
It makes me think of a joke I cracked while working a large Enterprise software gig that obfuscated their code. Problem was the obfuscator was breaking stuff and I stepped in and said:
Me: Why are we even obfuscating the code?
Lead: To avoid people hacking our code.
Me: Well have you even read our code? Honestly it’s obfuscated already due to the number of morons you’ve let write their gibberish in it.
And I will tell you, I can select any random block of code, and your mind would reel trying to read that spaghetti mess.
Class, function, and variable names are stored in the file global-metadata.dat when using IL2CPP to make builds.
There are programs out there to generate scripts for popular debuggers e.g. IDA to name subroutines from this file.
IL2CPP will do little to prevent hacking. With these tools and some rudimentary assembly knowledge, it is in fact very easy to modify IL2CPP games.
Concentrate on server side security. Every single message received from the internet should be considered to have been potentially sent by a hacker. If your back-end has vulnerabilities, hackers probably wouldn’t need to be able to modify your game to mess things up.
Thx Bob, that’s the answer I was looking for. I’m already building to IL2CPP, but now I know that it won’t be a substitution for obfuscation. If I do decide that obfuscation is desired, I’ll use BeeByte’s Obfuscator as long as it does not introduce any significant issues, and abort if it does. The developer seems very responsive and helpful, so if I need support he’ll be there. I don’t want to spend a lot of time on this, which is why I decided to ask questions first before implementation.
I will indeed be focusing on server side security, so no worries there. I’ll take a note of the various suggestions and do my best to follow best practices.
I know all these things, when I was talking about modding I meant changing the C++ code to modify existing features or add new features to the game. Obviously it’s easier to change C# code compared to C++ but it doesn’t change the fact that there are already tools which make it easy to decompile and change C++ code of games which use IL2CPP. Trust me if someone wants to hack or steal your game they will not be stopped by C++. I don’t try to sound rude but did you actually try to “mod” (change C++ code) of a IL2CPP game yet? Because then you would see that it isn’t that hard and im sure you will quickly find these tools and there are actually some nice guides how to get started.