Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Hack resistance?

Discussion in 'Editor & General Support' started by Hanford, Jan 24, 2009.

  1. djoscar


    Jul 31, 2009
    Wow... seriously it's so damn easy to decompile the Windows standalone type of games made with Unity3d, never realized that.

    I just googled a simple free decompiler, opened up a .dll from one unity3d windows standalone game and tadah! All the scriptsand stuff are there, the only problem is that it creates some errors in script by converting it to C# or just by decompiling.

    I'm a total newbie when it comes to this kind of stuff and even I found it extremely easy :D

    Here's a simple Radar script decompiled with that decompiler in C Sharp:
    Code (csharp):
    1. public class Radar : MonoBehaviour
    2. {
    3.     // Fields
    4.     public Texture blip;
    5.     public Texture blipChasing;
    6.     public Transform centerObject;
    7.     public bool checkAIscript = true;
    8.     public string enemyTag = "Enemy";
    9.     private Vector2 mapCenter;
    10.     public Vector2 mapCenterCustom;
    11.     private float mapHeight;
    12.     public float mapScale = 0.3f;
    13.     public int mapSizePercent = 15;
    14.     private float mapWidth;
    15.     public Camera radarBG;
    16.     public radarLocationValues radarLocation = radarLocationValues.bottomLeft;
    18.     // Methods
    19.     public void drawBlip(object go, object aTexture)
    20.     {
    21.         Vector3 position = this.centerObject.position;
    22.         object property = RuntimeServices.GetProperty(RuntimeServices.GetProperty(go, "transform"), "position");
    23.         float num = Vector3.Distance(position, (Vector3) property);
    24.         object obj3 = RuntimeServices.InvokeBinaryOperator("op_Subtraction", position.x, RuntimeServices.GetProperty(property, "x"));
    25.         object obj4 = RuntimeServices.InvokeBinaryOperator("op_Subtraction", position.z, RuntimeServices.GetProperty(property, "z"));
    26.         float num2 = ((Mathf.Atan2(RuntimeServices.UnboxSingle(obj3), RuntimeServices.UnboxSingle(obj4)) * 57.29578f) - 270) - this.centerObject.eulerAngles.y;
    27.         float num3 = num * Mathf.Cos(num2 * 0.01745329f);
    28.         float num4 = num * Mathf.Sin(num2 * 0.01745329f);
    29.         num3 *= this.mapScale;
    30.         num4 *= this.mapScale;
    31.         if (num <= ((this.mapWidth * 0.5f) / this.mapScale))
    32.         {
    33.             GUI.DrawTexture(new Rect(this.mapCenter.x + num3, this.mapCenter.y + num4, (float) 4, (float) 4), (Texture) RuntimeServices.Coerce(aTexture, typeof(Texture)));
    34.         }
    35.     }
    37.     public void DrawBlipsForEnemies()
    38.     {
    39.         GameObject[] objArray = null;
    40.         objArray = GameObject.FindGameObjectsWithTag(this.enemyTag);
    41.         Vector3 position = this.transform.position;
    42.         int index = 0;
    43.         GameObject[] objArray2 = objArray;
    44.         int length = objArray2.Length;
    45.         while (index < length)
    46.         {
    47.             Texture blip = this.blip;
    48.             if (this.checkAIscript)
    49.             {
    50.                 EnemyAI component = (EnemyAI) objArray2[index].GetComponent("EnemyAI");
    51.             }
    52.             this.drawBlip(objArray2[index], blip);
    53.             index++;
    54.         }
    55.     }
    57.     public void Main()
    58.     {
    59.     }
    61.     public void OnGUI()
    62.     {
    63.         float num = this.centerObject.transform.position.x * this.mapScale;
    64.         float num2 = this.centerObject.transform.position.z * this.mapScale;
    65.         this.DrawBlipsForEnemies();
    66.     }
    68.     public void setMapLocation()
    69.     {
    70.         this.mapWidth = ((float) (Screen.width * this.mapSizePercent)) / 100f;
    71.         this.mapHeight = this.mapWidth;
    72.         if (this.radarLocation == radarLocationValues.topLeft)
    73.         {
    74.             this.mapCenter = new Vector2(this.mapWidth / ((float) 2), this.mapHeight / ((float) 2));
    75.         }
    76.         else if (this.radarLocation == radarLocationValues.topCenter)
    77.         {
    78.             this.mapCenter = new Vector2((float) (Screen.width / 2), this.mapHeight / ((float) 2));
    79.         }
    80.         else if (this.radarLocation == radarLocationValues.topRight)
    81.         {
    82.             this.mapCenter = new Vector2(Screen.width - (this.mapWidth / ((float) 2)), this.mapHeight / ((float) 2));
    83.         }
    84.         else if (this.radarLocation == radarLocationValues.middleLeft)
    85.         {
    86.             this.mapCenter = new Vector2(this.mapWidth / ((float) 2), (float) (Screen.height / 2));
    87.         }
    88.         else if (this.radarLocation == radarLocationValues.middleCenter)
    89.         {
    90.             this.mapCenter = new Vector2((float) (Screen.width / 2), (float) (Screen.height / 2));
    91.         }
    92.         else if (this.radarLocation == radarLocationValues.middleRight)
    93.         {
    94.             this.mapCenter = new Vector2(Screen.width - (this.mapWidth / ((float) 2)), (float) (Screen.height / 2));
    95.         }
    96.         else if (this.radarLocation == radarLocationValues.bottomLeft)
    97.         {
    98.             this.mapCenter = new Vector2(this.mapWidth / ((float) 2), Screen.height - (this.mapHeight / ((float) 2)));
    99.         }
    100.         else if (this.radarLocation == radarLocationValues.bottomCenter)
    101.         {
    102.             this.mapCenter = new Vector2((float) (Screen.width / 2), Screen.height - (this.mapHeight / ((float) 2)));
    103.         }
    104.         else if (this.radarLocation == radarLocationValues.bottomRight)
    105.         {
    106.             this.mapCenter = new Vector2(Screen.width - (this.mapWidth / ((float) 2)), Screen.height - (this.mapHeight / ((float) 2)));
    107.         }
    108.         else if (this.radarLocation == radarLocationValues.custom)
    109.         {
    110.             this.mapCenter = this.mapCenterCustom;
    111.         }
    112.     }
    114.     public void Start()
    115.     {
    116.         this.setMapLocation();
    117.     }
    118. }
    And here's original Radar script in Javascript:
    Code (csharp):
    1. var blip : Texture; // texture to use when the enemy isn't chasing
    2. var blipChasing : Texture; //When Chasing
    3. var radarBG : Texture;
    5. var centerObject : Transform;
    6. var mapScale = 0.3;
    7. var mapSizePercent = 15;
    9. var checkAIscript : boolean = true;
    10. var enemyTag = "Enemy";
    12. enum radarLocationValues {topLeft, topCenter, topRight, middleLeft, middleCenter, middleRight, bottomLeft, bottomCenter, bottomRight, custom}
    13. var radarLocation : radarLocationValues = radarLocationValues.bottomLeft;
    15. private var mapWidth : float;
    16. private var mapHeight : float;
    17. private var mapCenter : Vector2;
    18. var mapCenterCustom : Vector2;
    20. function Start () {
    21.     setMapLocation();  
    22. }
    24. function OnGUI () {
    25. //  GUI.matrix = Matrix4x4.TRS (, Quaternion.identity, Vector3(Screen.width / 600.0, Screen.height / 450.0, 1));
    27.     // Draw player blip (centerObject)
    28.     bX=centerObject.transform.position.x * mapScale;
    29.     bY=centerObject.transform.position.z * mapScale;  
    30.     GUI.DrawTexture(Rect(mapCenter.x - mapWidth/2,mapCenter.y-mapHeight/2,mapWidth,mapHeight),radarBG);
    32.     // Draw blips for Enemies
    33.     DrawBlipsForEnemies();
    35. }
    37. function drawBlip(go,aTexture){
    39.     centerPos=centerObject.position;
    40.     extPos=go.transform.position;
    42.     // first we need to get the distance of the enemy from the player
    43.     dist=Vector3.Distance(centerPos,extPos);
    45.     dx=centerPos.x-extPos.x; // how far to the side of the player is the enemy?
    46.     dz=centerPos.z-extPos.z; // how far in front or behind the player is the enemy?
    48.     // what's the angle to turn to face the enemy - compensating for the player's turning?
    49.     deltay=Mathf.Atan2(dx,dz)*Mathf.Rad2Deg - 270 - centerObject.eulerAngles.y;
    51.     // just basic trigonometry to find the point x,y (enemy's location) given the angle deltay
    52.     bX=dist*Mathf.Cos(deltay * Mathf.Deg2Rad);
    53.     bY=dist*Mathf.Sin(deltay * Mathf.Deg2Rad);
    55.     bX=bX*mapScale; // scales down the x-coordinate so that the plot stays within our radar
    56.     bY=bY*mapScale; // scales down the y-coordinate so that the plot stays within our radar
    58.     if(dist<=mapWidth*.5/mapScale){
    59.         // this is the diameter of our largest radar circle
    60.        GUI.DrawTexture(Rect(mapCenter.x+bX,mapCenter.y+bY,4,4),aTexture);
    62.     }
    64. }
    66. function DrawBlipsForEnemies(){
    67.     //You will need to replace isChasing with a variable from your AI script that is true when     the enemy is chasing the player, or doing watever you want it to be doing when it is red on    the radar.
    69.     //You will need to replace "EnemyAINew with the name of your AI script
    71.     // Find all game objects tagged Enemy
    72.     var gos : GameObject[];
    73.     gos = GameObject.FindGameObjectsWithTag(enemyTag);
    75.     var distance = Mathf.Infinity;
    76.     var position = transform.position;
    78.     // Iterate through them and call drawBlip function
    79.     for (var go : GameObject in gos)  {
    80.           var blipChoice : Texture = blip;
    81.           if(checkAIscript){
    82.                 var aiScript : EnemyAI = go.GetComponent("EnemyAI");
    83.             if(aiScript.isChasing)
    84.                     blipChoice = blipChasing;
    85.         }
    86.         drawBlip(go,blipChoice);
    87.     }
    89. }
    91. function setMapLocation () {
    92.     mapWidth = Screen.width*mapSizePercent/100.0;
    93.     mapHeight = mapWidth;
    95.     //sets mapCenter based on enum selection
    96.     if(radarLocation == radarLocationValues.topLeft){
    97.         mapCenter = Vector2(mapWidth/2, mapHeight/2);
    98.     } else if(radarLocation == radarLocationValues.topCenter){
    99.         mapCenter = Vector2(Screen.width/2, mapHeight/2);
    100.     } else if(radarLocation == radarLocationValues.topRight){
    101.         mapCenter = Vector2(Screen.width-mapWidth/2, mapHeight/2);
    102.     } else if(radarLocation == radarLocationValues.middleLeft){
    103.         mapCenter = Vector2(mapWidth/2, Screen.height/2);
    104.     } else if(radarLocation == radarLocationValues.middleCenter){
    105.         mapCenter = Vector2(Screen.width/2, Screen.height/2);
    106.     } else if(radarLocation == radarLocationValues.middleRight){
    107.         mapCenter = Vector2(Screen.width-mapWidth/2, Screen.height/2);
    108.     } else if(radarLocation == radarLocationValues.bottomLeft){
    109.         mapCenter = Vector2(mapWidth/2, Screen.height - mapHeight/2);
    110.     } else if(radarLocation == radarLocationValues.bottomCenter){
    111.         mapCenter = Vector2(Screen.width/2, Screen.height - mapHeight/2);
    112.     } else if(radarLocation == radarLocationValues.bottomRight){
    113.         mapCenter = Vector2(Screen.width-mapWidth/2, Screen.height - mapHeight/2);
    114.     } else if(radarLocation == radarLocationValues.custom){
    115.         mapCenter = mapCenterCustom;
    116.     }
    118. }
    There's probably quite a lot mistakes because the decompiler converted the js to cs but they are very easy to fix.

    That's how easy it is :S
  2. Quietus2


    Mar 28, 2008
    Welcome to the club, glad you decided to join!
  3. Wolf Dreamer

    Wolf Dreamer

    Sep 2, 2009
    What decompiler did you use? I'd like to test things out to see just how vulnerable things are, if I decide to buy this engine.

    There is software that allows you to alter your flash games to prevent them from being decompiled.

    Googling the name of the decompiler with "protects against" sometimes finds something.
  4. benblo


    Aug 14, 2007
    It's not even a decompiler per say, it's how the .NET framework is made and intended: to be reflected. Yes, it is a feature, and a damn awesome one!
    In the battle between shield and cannon, the cannon always wins... don't waste your time trying to prevent decompiling.
  5. Jason_DB


    Nov 30, 2008
    Ha! I recognize that radar script :)
  6. the_gnoblin


    Jan 10, 2009
    Last time I asked about protecting the actual game, but now I am interested in protecting the _code_.

    What are the best options? We all gonna get decompiled and protecting the code is useless? :(
  7. jeremyace


    Oct 12, 2005
    Trying to protect 100% is always useless, but there are ways to make it harder to *prevent* casual cracking.

    The problem is we can't implement any of them because of the way Unity currently does things.

    We need UT to build a system, as they have control of how the final assemblies are packaged, loaded and used.

  8. the_gnoblin


    Jan 10, 2009
    Is it possible to (at least) obfuscate the code, if so - what tool would you recommend?
  9. 3dever


    Mar 2, 2009
    Well.. This is really serious problem and i hope that it will be fixed soon. But really - what to do now to protect the code?
  10. bm


    Sep 15, 2009
    I was shocked how easy it is to decompile the code! I connect to the petition and also would like some protection.
  11. the_gnoblin


    Jan 10, 2009


    Sep 15, 2009
    Very sadly to hear about it. It can be refusal for creation of the big and serious projects on Unity. I hope for the fast decision of the given situation.
  13. Dreamora


    Apr 5, 2008
    boxedapp sounds like Molebox Pro / Molebox Ultra, just more expensive.
  14. the_gnoblin


    Jan 10, 2009
    What can you say about Molebox?
  15. Tutanhomon


    Sep 15, 2009
    That's a great topic against using unity in big COMMERCIAL projects!!! How can I create code when knowing that someone can use it for free by decompiling my projects??? That's not fair!!
  16. Quietus2


    Mar 28, 2008
    Well Jeremy I agree the ball is certainly in Unity's court. This thread has given them 9+ months worth of lead time to do something in 2.6. Whether it's AOT or just packing assemblies web player style, something. If there's nothing in the release to address it, I'll be very disappointed.

    Gnoblin, obfuscation and similar packages were discussed in detail in the last 2-3 pages of this thread.

    You 'big commercial project' is never safe, even when it's native binary. I don't think that's really the concern. It's more about currently the assemblies being a very low hanging fruit, sitting there by themselves and needing only one click to extract the game's source with Reflector.
  17. Lostlogic


    Sep 6, 2009
    You can never secure the client or assume it's telling the truth. If you want the truth you must rely on the server to handle it.

    In your example, the health should only come from the server. The client should do nothing more than display it. They can hack it all day long but it will only give them visual health. They will still die when their server health hits zero.

    When it comes down to it, secure MMOs treat the clients as nothing more than mechanisms to stream graphics to and accept controller (keyboard, mouse, gamepad) inputs from. Everything else must be done server side.
  18. benblo


    Aug 14, 2007
    Jesus people, get over it! No program has ever been safe from hacking! Haven't you heard about modding? There are tons of programs similar to Reflector that can deconstruct any Valve game (I'm not talking only code, but assets as well), or even sniffers that can "record" any mesh that's streamed to the graphic card. What about games made with LUA that ship scripts in plain ASCII sight?
    So what? You don't see Valve stop making game, but yeah they NEVER trust clients, and make sure the logic is on the server. And if you want to hack your single player experience, who cares? Hacks like noclip are even builtin.

    I'm not trying to protect UT here, and if they someday add obfuscation, why not, I won't complain. But I just don't see it as such a big deal, certainly not a priority over features that actually empower us to make better games, do things that we couldn't before!
    Overall, I think all this concern is misdirected: who would actually WANT to steal your code? is it worth the trouble for them? and even if they actually succeed... so what? would it have been worth the trouble for you to protect it?

    There was a great topic some time ago about protection, where Matthew made an excellent point:
    This topic also diverged on plagiarism, and again the concern is very often misdirected: who would actually want to steal your ideas? more importantly, can you do something about it? I'm not talking about GameX-clones and -likes here, the industry strives on it, always has, always will, make your peace with it. But the whole paranoia about protecting ideas before release is often quite ridiculous... here's a very interesting post by the clever kids at Wolfire, bottom line: game developers much prefer to work on their own idea than go through the trouble of stealing yours!!

    The lesson in all this is, beware of hypothetical scenarios, and be sure you spend your time on things that actually matter to your game and your business.
    IMO the argument that big studios/projects won't use Unity because the scripts are very poorly protected is completely unrealistic and hypothetical: does anyone here actually work for one of the big guns? if you do, give us your take on that.
    So far, I know that Fusion Fall was quite a big project, and that EA also has Unity projects in the oven...
  19. Quietus2


    Mar 28, 2008
    I know of one instance where this occurred, that was the release of most of the Eve Online client source code last year. They were none too happy about it. I would hazard to guess that if the Fusion Fall source code were to suddenly appear on bittorrent, they would feel the same way.

    I believe you're missing the point though, benblo. We all agree that no game is secure, even if it's written in assembler as long as OLYDBG exists. The concern is relative to how low the fruit is hanging with a Unity stand-alone.

    Just because you don't feel that one-click here's your source is a justifiable concern, doesn't mean that everyone feels the same way. The mere existence of the legion of apps such as molebox and varied obfuscators shows in fact that quite a lot of developers are concerned about it.

    For some it's intellectual property of the source itself, for others it might be the futility of securing your server back end using an MD5 hash that might as well be displayed on the splash screen for how hidden it is. I doubt many are writing an MMO, but they might be working on a multiuser game and be concerned about this. Whatever the reason, for you to summarily dismiss a developers concern as illegitimate, I feel, is the wrong perspective.

    You also suggest that it would 'be a lot of work' for Unity to address. Well, if we're talking a final and all encompassing solution such as AOT that would be true. However, they already have the code built into unity to pack assemblies for webplayers. Do you really think that in 9+ months they couldn't account for the time it would take to back-fill stand-alones using the same framework? That alone would satisfy a lot of folks, moving the required skill level for bad behavior up one notch, requiring familiarity with OLYDBG instead of one-click.
  20. Lucas Meijer_old

    Lucas Meijer_old

    Apr 5, 2008

    Just a heads up that we're aware of this issue at Unity HQ. We realize this is problematic for some people.

    In the short term, options at your disposal (might apply more or less to your situation, depending on the reason why decompilation is problematic for you)

    - Make sure that your code does not contain any secrets. As mentioned before in the thread, if you're making an MMO client, make the client totally non authorative.

    - Obfusciate your code.

    We're looking at ways to ease the pain in the future, but without doing full Ahead of Time compilation (only an option for standalones, not for webplayers), you will continue to face a situation where individuals motivated enough will be able to pry open your code.
    Yes, we can make it a bit harder. However, that would not change the fundemental problem.

    I'm sorry I don't have anything more substantial to offer at this point in time. Closing off, I'd like to mention that this decompilability is not unique to Unity, but in fact goes for any .Net program. That includes all C# programs in the world. It's the same story for actionscript code in flash movies.


    Lucas Meijer
    Unity Developer
  21. jeremyace


    Oct 12, 2005
    I just want to add a point about something that has been bothering me for a while.

    When anyone says "all .NET apps have this problem", they seem to forget that Unity is not a .NET application. Unity uses Mono as a glorified scripting engine. They could be using LUA, Squirrel, pen and paper with OCR or anything else. They still have final control over the files and how they are stored and loaded. It's just more difficult for them due to the scale of Mono/.NET.

    Quietus voiced the other points very well, so I won't try to re-hash those.

    I will add one final request though, please don't reply to a thread until you have read it. There have been many posts stating things that have already been stated and addressed fully, but the constant re-hashing obfuscates the points for both sides (pun intended). ;-)

  22. benblo


    Aug 14, 2007
    I'm not missing the point (I heard you), I'm just saying that for 95% of Unity users, it is a non-point and their time would be better spent on other matters. The failures of lots of DRM-heavy games, when at the same time some indies go DRM-free with lots of success, proves that you should beware of your priorities.

    Yes, too bad for Eve Online... but IMO you should worry about this when you're as successfull, and thus prone to attack, as them. Trivia: they said at GDC Cologne that they're responsible for 40% of Iceland's exports (or something ;) I'm no banker)...

    That's not the point: the list of features that they could implement in a few days is as long as the arm (or even the arms of the whole team combined). They can only take on so many at a time though, so they prioritize.
    Uservoice is there to separate the vocal minorities from the real concerns (even though in the end they make the design choices, and believe me I've seen some choices not taken that pain me: life is life, naaa-naaa-na-na)... I've haven't able to find a request by searching for "obfuscator" or "protection" or "Reflector", yet this thread has been going on for months, and most of the posts are "OMG I never knew you could decompile stuff (Unity or otherwise)".
    Yes it's the same for every program, no it hasn't stopped the Flash market from exploding (even though it's as easy to decompile and get code AND assets).

    In the end, as Lucas said, making it harder will only push the problem one step further, which is a euphemism to say: hide it under the carpet.
  23. AngryAnt


    Keyboard Operator

    Oct 25, 2005
    I think I've mentioned this before, but everything can be decompiled. Yes we could make sure that no-one would be able to decompile your stuff, but that would then include our webplayer/runtime - rendering the operation sort of pointless :p

    In short: Yes stuff can be made harder to decompile, but that just makes it exactly that - *harder* to decompile - not impossible. AOT does make things more complex to decompile, but its not the holy grail in this context - far from it (though some would say that it is the holy grail on iPhone :D).

    I suppose we could dedicate half the Unity development team to continuously releasing new encryption modules and have all end users upgrade their plugin every second week (probably meaning every time they play a Unity authored game), but somehow I don't think anyone would want that ;)
  24. benblo


    Aug 14, 2007
    Love your sig, way to go! ;)
  25. siliwangi


    Sep 25, 2009
    have anyone tried enigma protector? ,it has specific sdk for c# net,iam newbie in unity3d dunno if it's different,
    full list of available api :
    sample implementation :
    Code (csharp):
    2. public class Enigma_IDE
    3. {
    4.     [DllImport("enigma_ide.dll", CallingConvention = CallingConvention.StdCall)]
    5.     public static extern int EP_CryptHashBuffer(int Hash, IntPtr Buffer, int Size, IntPtr Digest);
    6. }
    as you see above,it's has internal check for file hashing,etc,serial number based on rsa mostly from 512 bits into 4096 bits,hardware lock,virtual box,antidebugger,from my observation into cracker community this one really one of hardest to crack list,not impossible but still better than armadillo which lost it's power.
    anyone can try this?really new implementing dll into unity3d :(
  26. Noname


    Nov 9, 2009
  27. n0mad


    Jan 27, 2009
    Thank you for giving us your full name, so that we will never hire you for any kind of work.

    Sucking other people's work won't work very long, you know it.

  28. n0mad


    Jan 27, 2009
    Yep, but actually, making a problem harder to exist will always be better than letting it coming in ;)

    Hack protection is a just an eternal fight.

    The more we fight, the more hackers it will repulse.
  29. brazilian


    Dec 11, 2009
    Thanks a lot.
    We hate that kind of person.

  30. gunder


    Jun 23, 2009
    but , unity could not put the compiled dll into a packed file, encrypted with the same system that resources? Only for standalone.
  31. luizgpa


    Aug 20, 2009
    Did anyone manage to get a obfuscator working with Unity for a web build?

    All the obfuscators I tested use a compiled file (.exe or .dll), but I don't have access to the binaries since Unity packs them with the assets in the .unity3d file.

    One way that might work is to build a .net dll, obfuscate it and them import in Unity, but I would have to use the obfuscated names and that would be painful.

    Do anyone knows a better way to do this?
  32. Reapazor


    Jun 19, 2008
    We've used dotFuscator (
    for a project where the client required it. *They were paranoid of .NET libraries*

    It gave us the option to let externally facing names stay the same, thus was easy to work with in Unity. It's pricey though.
  33. craigerz


    Mar 30, 2009
    I agree with AngryAnt. I could spend a lifetime protecting my game to no avail, hackers will always be one step ahead.

    As QF pointed out on the 2nd page, having some simple common sense "sanity" checks server-side before it alters the database should suffice. Gee the guy was level 1 5 minutes ago and now he's level 3. Is that possible? No? Account banned, record ip. He was at loc XYZ and now at loc XYZx10. Does he have wings? No? Account banned, record ip.

    It should also be noted that, despite Blizzard's bottomless pockets, their GMs are actively ingame monitoring activity. They also rely heavily on the community. A "report user" button clear as day can go a long way.

    The hacker will crack the game, get banned, lose his data, change his ip, get banned all over again. If that's how he wants to spend his day, just so long as you failsafe every var and catch him at every corner so as not to disturb the legit users.
  34. Quietus2


    Mar 28, 2008
    This is true. However with the current configuration the fruit hangs low enough that OllyDBG isn't required anymore. One button decompile + free unity lowers the bar quite a bit.

    You likely think different, but I don't see the fact a thief might use a crowbar to pry open my screen door as a reason not to lock my house at all.

    That's nice if your are developing an MMO with an external socket server. It is in fact, standard advice. However that fails miserably for all other scenarios.

    It's somewhat difficult to hide those sanity checks, when you have to distribute both the client and the server in the same project when using Unity's networking.

    Hello source code, goodbye sanity check.

    See the earlier discussion in this thread on naked hash strings and PHP security. It's the exact same weakness.

    As an indie, I would be very afraid of that button. You really think that people would only press it to report a hacker? That button would last around a day I figure, before you released a new version without it and had to change your email address to stop the spam.
  35. craigerz


    Mar 30, 2009
    Aha a good laugh, thanks for the corrections Quietus. I was thinking specifically of socket servers, Photon or SFS in particular. And uh...good point I'll remove the button now..
  36. Nexic


    Jan 10, 2010
    Would someone knowledgeable on the subject on obfuscation be able to take a look at my thread:

    A moderator told me that this was impossible but honestly I don't really believe that. As far as I can tell when making the web player unity does:

    1. Compile scripts to DLLs
    2. Pack DLLs into asset package.

    So there should be a way to protect those DLLs before they end up in the web player package. Anyone have any ideas on how to achieve this?
  37. Quietus2


    Mar 28, 2008
    When scripts are compiled they are thrown into a Untyscript.dll assembly for standalones and webplayers. I imagine if you look about in your project files you will find it. Assuming this is done on the fly as scripts change in the editor and is separate from the webplayer build process, you should be able to obfuscate this assembly and then have it included in the webplayer.

    Have you tried it?

    Another option for you as I see it, it to build an obfuscated DLL separately with all of your important logic and use that in your Unity webplayer project. Look at the perlin noise generator in the resources section of the Unity website, for an example of including an external .NET dll in your project.

    I remember reading over the last year, how some people actually drive their unity games with core logic in a C++ plugin. To me it sort of defeats the purpose of .NET scripting, but if it makes them feel safe. That's not an option with webplayers however.

    To get any movement on this topic, I think we'll have to wait until EA or someone similar decides to release a standalone project. Then we'll curiously see something in the patch notes akin to 'Security: Webplayer type packing for standalones, to make it harder for people to replace and interject code in your projects!"
  38. Nexic


    Jan 10, 2010
    Ah yes! You're probably correct. I forgot that unity compiles scripts as soon as they're edited. So really the build process is probably just packing what's already there into the asset bundle.

    Excellent, thanks a ton :)

    EDIT: Yep, just tested and the DLL files only change after editing your scripts - nothing happens to them after making a build. Therefore you can protect the DLLs before they get into the web player package.
  39. Quietus2


    Mar 28, 2008
    That's great news and glad I could be of help.

    As long as it's just light obfuscation, I don't foresee any issues with streaming and such. Please keep us abreast of how it all turns out for you. This topic is of interest for a good number of us.

    It raises the level of entry to an acceptable level in my eyes. Yes you can still save off the unencrypted assemblies with OlyDBG but then you're left with guessing which obfuscation package was used.

    In this scenario, I'd probably have a preference for some obfuscation package written a guy in a college dorm that nobody's every heard of. The more obscure the obfuscater the better.
  40. Nexic


    Jan 10, 2010
    Just thought I'd post a small update. After testing trials for just about every obfuscation product out there I eventually found one that worked, had a good level of protection and wasn't insanely expensive.

    I don't want to say the exact name of this product publicly as I don't want someone digging up this post and using that info to deobfuscate my program in the future. However it would only take a few hours to go through each of the trials and do the same testing I did. Also, if a well known member of this forum would like to know which software was used I'll be happy to tell them via PM.

    Here are some notes on doing it:
    - You need to place protection on Library/ScriptAssembelies/Assembly - UnityScript.dll before building the webplayer. If you modify any of your scripts then you'll need to redo the protection.

    - There are three dependant DLLs inside the main editor installation folder that you'll need to point the protection software to in order to generate a working DLL. They are:


    - I found that any kind of 'Control Flow' protection caused Unity to crash. However all other popular methods seemed to work fine with this particular piece of software.

    - You can test the protection simply by hitting play in the unity editor after overwriting the old DLL. If something is wrong it will either crash or just not do anything.

    - If you want to do it for the standalone app (instead of the web player) you should place the protection on the assemblies in your data folder and do it after building, rather than before.

    This particular piece of software does have a few deobfuscation tools floating around which I tested. Although most of them allowed Reflector to open the DLL (usually it would crash or just fail to open the assembly), the code inside was still heavily obfuscated and near impossible to follow. Therefore it seems that it's a solid choice (for now at least).

    I hope this helps.
  41. the_gnoblin


    Jan 10, 2009
    After examining my own code in reflector I noticed that coroutines do not reflect correctly!

    And that's good, because it spoils some fun for a person browsing your game sources :D.

    For example,

    Code (csharp):
    1. public class ACorObfuscate : MonoBehaviour
    2. {
    4.     IEnumerator Start()
    5.     {
    6.         yield return 0;
    7.         Debug.Log("hahaha");
    8.         yield return 0;
    9.         Debug.Log("hahaha");
    10.         yield return 0;
    11.         Debug.Log("hahaha");
    12.         yield return 0;
    14.         StartCoroutine("UpdateCoroutine");
    15.     }
    17.     IEnumerator UpdateCoroutine()
    18.     {
    19.         transform.Rotate(1f*Time.deltaTime,0,0);
    20.         yield return 0;
    21.     }
    22. }

    Code (csharp):
    1. public class ACorObfuscate : MonoBehaviour
    2. {
    3.     // Methods
    4.     private IEnumerator Start()
    5.     {
    6.         <>c__CompilerGenerated4 generated = new <>c__CompilerGenerated4(0, this);
    7.         return (IEnumerator) generated;
    8.     }
    10.     private IEnumerator UpdateCoroutine()
    11.     {
    12.         <>c__CompilerGenerated5 generated = new <>c__CompilerGenerated5(0, this);
    13.         return (IEnumerator) generated;
    14.     }
    16.     // Nested Types
    17.     [CompilerGenerated]
    18.     private class <>c__CompilerGenerated4 : IEnumerator, IDisposable, IEnumerator<object>
    19.     {
    20.         // Fields
    21.         internal object $current;
    22.         internal int $PC;
    23.         internal ACorObfuscate <5:<>THIS>;
    25.         // Methods
    26.         public <>c__CompilerGenerated4(int $PC, ACorObfuscate parent)
    27.         {
    28.             this.$PC = $PC;
    29.             this.<5:<>THIS> = parent;
    30.         }
    32.         public void Dispose()
    33.         {
    34.             switch (this.$PC)
    35.             {
    36.                 case 0:
    37.                 case 1:
    38.                 case 2:
    39.                 case 3:
    40.                 case 4:
    41.                     break;
    43.                 default:
    44.                     this.$PC = -1;
    45.                     break;
    46.             }
    47.         }
    49.         [CompilerGenerated]
    50.         public bool MoveNext()
    51.         {
    52.             int num;
    53.             try
    54.             {
    55.                 goto Label_00A0;
    56.             Label_0005:
    57.                 this.$current = 0;
    58.                 this.$PC = 1;
    59.                 goto Label_00C6;
    60.             Label_001D:
    61.                 Debug.Log("hahaha");
    62.                 this.$current = 0;
    63.                 this.$PC = 2;
    64.                 goto Label_00C6;
    65.             Label_003F:
    66.                 Debug.Log("hahaha");
    67.                 this.$current = 0;
    68.                 this.$PC = 3;
    69.                 goto Label_00C6;
    70.             Label_0061:
    71.                 Debug.Log("hahaha");
    72.                 this.$current = 0;
    73.                 this.$PC = 4;
    74.                 goto Label_00C6;
    75.             Label_0083:
    76.                 this.<5:<>THIS>.StartCoroutine("UpdateCoroutine");
    77.                 this.$PC = -1;
    78.                 goto Label_00BF;
    79.             Label_00A0:
    80.                 switch (this.$PC)
    81.                 {
    82.                     case 0:
    83.                         goto Label_0005;
    85.                     case 1:
    86.                         goto Label_001D;
    88.                     case 2:
    89.                         goto Label_003F;
    91.                     case 3:
    92.                         goto Label_0061;
    94.                     case 4:
    95.                         goto Label_0083;
    96.                 }
    97.             Label_00BF:
    98.                 num = 0;
    99.                 goto Label_00D9;
    100.             Label_00C6:
    101.                 num = 1;
    102.             }
    103.             fault
    104.             {
    105.                 this.Dispose();
    106.             }
    107.         Label_00D9:
    108.             return (bool) num;
    109.         }
    111.         public void Reset()
    112.         {
    113.             throw new NotSupportedException();
    114.         }
    116.         // Properties
    117.         object IEnumerator<object>.Current
    118.         {
    119.             get
    120.             {
    121.                 if (this.$PC <= 0)
    122.                 {
    123.                     throw new InvalidOperationException();
    124.                 }
    125.                 return this.$current;
    126.             }
    127.         }
    129.         object IEnumerator.Current
    130.         {
    131.             get
    132.             {
    133.                 if (this.$PC <= 0)
    134.                 {
    135.                     throw new InvalidOperationException();
    136.                 }
    137.                 return this.$current;
    138.             }
    139.         }
    140.     }
    142.     [CompilerGenerated]
    143.     private class <>c__CompilerGenerated5 : IEnumerator, IDisposable, IEnumerator<object>
    144.     {
    145.         // Fields
    146.         internal object $current;
    147.         internal int $PC;
    148.         internal ACorObfuscate <6:<>THIS>;
    150.         // Methods
    151.         public <>c__CompilerGenerated5(int $PC, ACorObfuscate parent)
    152.         {
    153.             this.$PC = $PC;
    154.             this.<6:<>THIS> = parent;
    155.         }
    157.         public void Dispose()
    158.         {
    159.             switch (this.$PC)
    160.             {
    161.                 case 0:
    162.                 case 1:
    163.                     break;
    165.                 default:
    166.                     this.$PC = -1;
    167.                     break;
    168.             }
    169.         }
    171.         [CompilerGenerated]
    172.         public bool MoveNext()
    173.         {
    174.             int num;
    175.             try
    176.             {
    177.                 goto Label_004E;
    178.             Label_0005:
    179.                 this.<6:<>THIS>.transform.Rotate((float) (1f * Time.deltaTime), 0f, (float) 0f);
    180.                 this.$current = 0;
    181.                 this.$PC = 1;
    182.                 goto Label_0068;
    183.             Label_0042:
    184.                 this.$PC = -1;
    185.                 goto Label_0061;
    186.             Label_004E:
    187.                 switch (this.$PC)
    188.                 {
    189.                     case 0:
    190.                         goto Label_0005;
    192.                     case 1:
    193.                         goto Label_0042;
    194.                 }
    195.             Label_0061:
    196.                 num = 0;
    197.                 goto Label_007B;
    198.             Label_0068:
    199.                 num = 1;
    200.             }
    201.             fault
    202.             {
    203.                 this.Dispose();
    204.             }
    205.         Label_007B:
    206.             return (bool) num;
    207.         }
    209.         public void Reset()
    210.         {
    211.             throw new NotSupportedException();
    212.         }
    214.         // Properties
    215.         object IEnumerator<object>.Current
    216.         {
    217.             get
    218.             {
    219.                 if (this.$PC <= 0)
    220.                 {
    221.                     throw new InvalidOperationException();
    222.                 }
    223.                 return this.$current;
    224.             }
    225.         }
    227.         object IEnumerator.Current
    228.         {
    229.             get
    230.             {
    231.                 if (this.$PC <= 0)
    232.                 {
    233.                     throw new InvalidOperationException();
    234.                 }
    235.                 return this.$current;
    236.             }
    237.         }
    238.     }
    239. }
    Which is, of course, a synthetic example - but still makes me happy. :roll:
  42. MadMax


    Aug 5, 2009
    Except now, everyone knows. :roll:
  43. loki70x7


    Apr 19, 2010
    Using the high score example of inserting the high score into a database via a PHP script.

    If I have this correct a hacker can alter the variable that stores the highscore in memory and change it before its passed to the PHP script.

    I'm not sure if anyone has mentioned a potential solution to that problem. Obfuscation seems to just raise the bar a bit.

    Is there anything that can be done to eliminate or at least raise the bar higher?
  44. Quietus2


    Mar 28, 2008

    Memory editor hacks can be circumvented by separating the displayed value from the real value which you have obfuscated through any number of forms of bit twiddling.

    They can search all day for 20340, yet while the change may show in the gui the game will merrily ignore it.

    Of course that's just one attack and says nothing about your MD5 key or your PHP transaction itself being safe. Anyone determined enough with mediocre skills will have at your md5 key in short order.

    For example, what if I were to steal your webplayer from the browser cache and embed it in my own devious javascript. One that uses sendmessage to call the function post_score("Quietus", 9999999999)? Forget MD5 keys, let the programmer do all the dirty work for me. That's efficiency!
  45. loki70x7


    Apr 19, 2010
    Ok so is there anything that can be done to combat using my function against me?
  46. Quietus2


    Mar 28, 2008
    - Don't call the post_score function post_score. CalcVectorDelta() works. Copying it verbatim from the wiki function name/parms and all is asking for trouble.

    - Make sure you have your application aware of where it's running. Take whatever safeguards you see fit if it's not running from the host it's supposed to. If you let then continue playing, at the very least exit out of your posting routines.

    That's discussed in detail by Higgy in the following video.

    -Change the way you store and access the hash string. Assemble it from pieces declared elsewhere, hide it in the middle of the inspector variable text file, bitshift it at the last moment. Just don't declare it in the function as a string for all to see.

    -If what the user a few posts above says is true, move the md5 calculations and the posting routines to be run as a coroutine.

    -Separate the logic for the md5 calculations and actual posting routines. Don't have a single logic entry point that as a function call can execute both.

    I'd say that's good set of best practices for what you're trying to do. I'm sure there's more you could come up with, but if you start thinking along the lines of plugging obvious holes you can at least get rid of most first and second attempts.

    Then you're just left with the determined folk. You can raise the bar a certain degree more on the webplayer than you can a standalone. It's just a matter of how much effort you want to put into it. Would it really matter if someone got a score of 999999999 on a dig-dug clone?
  47. siliwangi


    Sep 25, 2009
    now anyone has tested mono aot on unity 3 f1?how to do it?
  48. the_gnoblin


    Jan 10, 2009
    Now it turns out that the same "openness" applies to all assets in the game.
    Super easy in 2.6.1 (standalone webplayer), and in webplayer 3.0.

    Anyone can take anyone's built project and use shaders, materials, models, textures, sounds, guiskins in Unity Editor like his own.
  49. the_gnoblin


    Jan 10, 2009
    And I am not sure how to "obfuscate" a character in my game (like can be done with the code) =D.
  50. hippocoder


    Digital Ape

    Apr 11, 2010
    If you don't have too many people on one server, you can just move it all server side. Thats unhackable.