Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to hide core logic from the users in a custom plugin

Discussion in 'Scripting' started by sudarshans, Apr 11, 2022.

  1. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Hello,
    Im creating a new plugin in C# and I have converted it to dll as well, but the issue is I'm able to decouple the core logic by tracing the public methods that I have exposed in my dll plugin, what I have noticed is the visual studio isn't able to decouple the core logic properly but the JetBrains Rider editor is able to do it completely.

    So how can I hide my core logic? I already know that we will not be able to achieve it up to 100% but at least I don't want users to go through the core logic just by tracing back my public APIs easily.

    I went one step forward and checked how the unity engine is hiding its core logic, all the unity engine core logic is written in C++ which is being kept in a different dll, and calls to those C++ methods were done through extern call, so in JetBrains rider, I was able to track the flow until the extern call's being made but not after that so I wasn't able to see the core logic there.
    upload_2022-4-11_12-20-2.png

    Even I wanted a similar architecture but the only change is I won't be writing my core logic in C++ it will be their C# itself but I can go for a different dll's option.

    Can someone please help here to find out the architecture? the small example code snippet would be really helpful.
     

    Attached Files:

  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,848
    Not really an answer to your question, but in Visual Studio 2022 I've been able to peek at the code of any of the pre-compiled .dll's for the packages I'm using, which I believe is new over the previous Visual Studio.

    Is it that big a deal if users can peek at the code?
     
  3. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Yes, it would be a concern in my case.
    And were you able to see the implementation of the C++ part as well in your visual studio 2022? the packages which you were mentioning might have followed a similar approach that what I have done, they just have converted into dll but as you said we will be easily able to trace back to core logic through the public functions exposed in the dll using the latest editors but I don't think with the similar approach you will be able to see unity engine core logic, they have used extern method call's for that particular reason itself I guess.

    Basically, I wanted to mimic unity architecture in my C# plugin.
     
    Last edited: Apr 11, 2022
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    That doesn't work at all. C# or to be more precise .NET as a whole was designed to be compatible with all .NET languages and transparent so the code can actually be validated. So being able to easily decompile the intermediate code is actually a feature of .NET. You can also decompile C++ code, however it's much harder because C++ doesn't have much metadata about the original code. The best thing you can do on the C# / .NET side is to obfuscate your code. It can still be decompiled, but class, variable and method names could be replaced by gibberish so it makes it harder to make sense of the code. However it will never prevent someone from decompiling or analysing the code.

    As I said, you can also disassemble and analyse C++ code. However looking through a large code base and making sense of anything is really hard, but still possible. With C# that's much easier, especially a lot of the metadata can not be changed when we talk about Unity.
     
    lordofduct likes this.
  5. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Okay, thanks for the detailed explanation.
    But does that mean is it impossible to make it hard for the user to trace the core logic in C#/.NET environment (except obfuscation)?

    Can't we do something like this?
    write all the core logic in C# and keep it in a separate dll and make C# script that will act as a bridge between the customer code and the core logic.
    the bridge script should call the core logic dll methods through [DllImport("__Internal")] calls or any similar approach?
    so here the external users will trace the code until the bridge but not to our core logic dll as we don't have any hard references to core logic dll.

    or else can't we use similar approach between 2 C# dll's?
    Code (CSharp):
    1.   [MethodImpl(MethodImplOptions.InternalCall)]
    2.     internal static extern void Internal_LogException(Exception exception, Object obj);
    I tried to do both of them but there were issues so just wanted to know if I'm going in the right way or not.
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,848
    Care to elaborate? Are you concern someone is going to copy your package?

    No I can't, but as I understand most Unity packages are distributed as source anyway.
     
  7. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    yes, Im developing an SDK so doesn't want someone to copy my code.
     
  8. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    760
    The disassembled code is not the exact same code as the original, e.g. syntactic sugar will lost. But everything is there that the compiler creates for you in the background.
    async/await: SharpLab
     
  9. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Didn't get you, In jet brains rider the decoupled code was almost similar to the source code itself.
     
  10. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    you should worry more about making something people want, then worrying about the code being decompiled
     
  11. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Hey,
    I don't see any issue in the question I have asked, it is purely technical.
    If you have any idea on the same then you can please help here or please keep your nonsense suggestions with yourself.
     
  12. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Well, the main issue with the question is, it has been asked countless of times.
    and as I said, there's not much you can do about it. You can obfuscate your code, but that's it if you want it to be IL (intermediate language) code.

    The real question is against what threats do you want to be protected? Copyright violations have to be prosecuted by the copyright holder, in this caes that's you. You can'r effectively protect your C# code, or any code in this regards, if the product is shipped to the user. There are different levels of "obfuscation". However, as I already said IL code, even when obfuscated, can still be decompiled but now has funny and meaningless names which makes it a bit harder to make sense of the code but it's just a matter of time. If it's a highly advanced algorithm you want to protect, the person can even use your compiled code verbatim without the need to decompile.

    Compiled C++ code is much harder to decompile and therefore "safer", though still not really protected in any way. The code has to run on the user's side and therefore must be readable and understandable by the CPU. It requires much more work, knowledge and determination to reverse engineer C++ code from a binary (especially since C++ has tons of optimisations which makes it almost impossible to reconstruct the exact source code, though some kind of similar code can always be reconstructed).

    Note there are many tools out there that help with reverse engineering and decompilation like Ghidra or IDA for example. Professional security hackers usually have specialized plugins and tools which would help them depending on the field they're working in.

    Well, an SDK needs to have well defined interfaces anyways which you can not even obfuscate. So it makes things a lot easier to track down a certain function anyways. You generally "protect" yourself by applying a license and go after people who violate it. Yes, that is practically quite difficult, that's why there are still many people using a cracked windows version. Microsoft of course tried to make it harder by having license checks woven into the code everywhere and that you require an online activation so it becomes much harder to circumvent. However effectively there's nothing you can do besides: not publishing it at all. That's why most companies have critical secret code on their own servers and just sell the service. That's the only real secure way to protect your code.
     
    passerbycmc likes this.
  13. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    The question wasn't about decompiling or obfuscation, it was just about whether we can achieve the same or any similar unity engine architecture that they have implemented for communicating between C# <=> C++, in C# <=> C# itself or not? is it possible to remove hard reference by using [DllImport("__Internal")] or any such similar implementation or not?
    It was just a yes or no question, if it is yes then we can discuss more on that, if it is no then it is done, but in these threads, people always take the discussion somewhere else.
    If we ask how to add 2 numbers, people start questing why you want to add 2 numbers, and why they want to know the reason! the question was simple either answer what is being asked or move on.

    Im already aware that hiding code up to 100% is not possible which I have already mentioned in my question itself!
     
    Last edited: Apr 12, 2022
  14. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Anyway thanks for the explanation.
     
  15. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    This just makes no sense at all. What architecture are you talking about? Native code is native code, no matter how it was produced, it doesn't have to be C++. However C# does not compile to native code but to IL code. That's how C# and .NET works.
     
  16. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Dude, I have a C# plugin which is converted to dll so now instead of making direct calls to the public methods in the DLL, is there any way to do it in the same or similar approach?

    Code (CSharp):
    1. [DllImport("__Internal")]
    2. private static extern void SendEvent(string eventName, string eventProperties);
    3. or
    4. [MethodImpl(MethodImplOptions.InternalCall)]
    5. internal static extern void Internal_LogException(Exception exception, Object obj);
    using any of the following services?
    Code (CSharp):
    1. using System.Runtime.CompilerServices;
    2. or
    3. using UnityEngine.Bindings;
    4. or
    5. using System.Runtime.InteropServices;
    Note: Im already aware of the Reflection concept and not expecting any answers on the same.

    What does it have to do with my question? won't you be able to answer the above question without knowing this? if not then you can just ignore it.
     
    Last edited: Apr 12, 2022
  17. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    Well, as I said this makes no sense :). What you just showed ARE direct calls of public methods. Those are exported native methods which you can call directly.

    Let me try to explain it with a bad analogy: Imagine an electric car and a petrol car. Both convert some form of energy into kinetic energy. An electric car can tow a petrol car and a petrol car can tow an electric car as this is just exchange of kinetic energy. However you can not use petrol to make an electric car move, nor can you use electricity to move a petrol car.

    C# compiles to IL code. That's what is inside a compiled assembly / DLL. C# code can not run on its own. It relies on the .NET or Mono framework which provides a virtual machine for the target platform. This virtual machine will JIT(just in time) compile the IL code, right when it needs to be executed, to native code. That's why from an outside perspective it's actually easier to call native methods directly compared to call C# code from native code. Managed code lives and works inside of the context of the virtual machine. The memory is managed by the runtime and any C# / IL code lives inside that VM. So having two pieces of C# / IL code which lives in the same context, you're now somehow asking if you can call a part of it differently. IL code is IL code, no matter in which assembly it lives in. The assembly needs to be loaded / included in your project and you are free to access any public class / type defined inside that assembly. Keep in mind that your "scripts" in Unity are also compiled into a .NET assembly (a seperate dll file called Assembly-CSharp.dll). So a "managed plugin" is not really anything different from any regular C# code. Assemblies need to define which other assemblies they want to reference in order to compile them. This is handled by Unity when Untiy compiles your scripts.

    "True" managed C# plugins which may be added after the application was build can be loaded through reflection from the main code and it would be possible to use reflection to interact with the classes / methods / fields / properties of those classes. However when you actually implement plugin support, you would use a seperate shared DLL that defines some interfaces which the plugin classes implement. That way you only need to use a bit of reflection to create an instance of a single class from the plugin and everything else is just normal C# / .NET interaction through interfaces.

    I'm also not sure how you end up talking about how to call or reference ordinary managed code when your title was about hiding code. As we have said a couple of times, C# code can not really be protected. No code can, but C# / IL is specifically designed for great interoperability between different .NET languages and therefore can easily be decompiled. If you want better protection of your code, you need to use a different language, like C++ for example. Yes, it would make certain things a lot more complicated which is a bit like trying to make communism, democracy and anarchy working together :)

    Note: Unity has the IL2CPP backend as a scripting environment. This will actually crosscompile / convert all your IL code (after it is compiled to usual .NET assemblies) to C++ and then compile a native application out of your whole project. Though this is not just a fancy cross compiler that turns managed code into native code. It also provides a runtime environment that of course has to mimic / replace the usual .NET runtime. So you can not turn a single managed dll into a native dll that way. Also, even though IL2CPP finally compiles to native code, it's still based on the original IL code which contains tons of metadata. I've seen decompilers which can even decompile IL2CPP builds. It's again a bit harder and some more information is lost, but it's still possible. Though as I said IL2CPP is a solution for building your application / game. Since you talk about a managed SDK, that plays no role here.

    I really have no idea what you want to hear.
     
  18. runner78

    runner78

    Joined:
    Mar 14, 2015
    Posts:
    760
    It should be noted here that unity communication between native and managed code is more than DDLImport. Unity runtime hosts the .NET runtime, you can see that in your screenshot in the first post. Many classes have a native counterpart. But this only works because Unity hosts the runtime itself and can use the internal runtime API.

    In a video about the current work on the transition to modern .NET, something was also said about the communication between .NET and the native engine, but the video is currently no longer available due to audio problems.
     
    Bunny83 likes this.
  19. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,739
    if you compiled a dll from C# you can just toss it in your plugins folder and just use it directly, nothing else special has to be done. No need to overcomplicate it
     
  20. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    Dude, I know that. It's not about complicating, please don't message just for sake of the messaging.
     
  21. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,848
    Lets sum this up so you can stop being so aggro.

    Can you structure things the way Unity does? I mean, they have, so there's no reason why you can't.

    Will it be complicated and take lots of extra time? Most likely.

    Is it worth your actual time? Probably not.

    I asked the Sirenix (Odin Inspector) devs if they care about end users being able to peek at their code, and they couldn't care less. I'm sure most devs with successful products feel the same.
     
    passerbycmc likes this.
  22. sudarshans

    sudarshans

    Joined:
    Nov 13, 2021
    Posts:
    26
    hmm... I thought these forums are for answering the question that is being asked, something like in schools how the lectures used to answer each and every question, the good teachers never asks the purpose of the question or instruct the student to do only the things that teacher knows.
    yeah, it might be not optimized but fine I will profile and find out some alternative for the same.
    It might consume extra time but fine what if I want to invest time in the same.
    Here everyone expects me to tell the reason for asking this question, I mean what does it have to do with the answer? why should I narrate the story of why I have asked the question.

    And then these suggestions!
    - "you should worry more about making something people want, than worrying about the code being decompiled"
    - "No need to overcomplicate it"
    - "I'm sure most devs with successful products feel the same"

    etc etc...

    I was just expecting some code examples (if there were any solutions), it doesn't matter if it is an optimal solution or not.

    There are a lot of Unity engine inbuilt API which most of the devs suggest not to use because of the performance issues, yeah that might be right but does that mean Unity engineers are dumb? why did they waste time to make that API and sent it to production whereas even the noob devs suggest not to use that!