C# shouldn't necessarily be avoided. I work for PreEmptive Solutions on their Dotfuscator team, though this is a fairly unbiased answer. (without a single plug :) )
C# is inherently easier to decompile into source code because it contains meta-data and most C# code must comply with a set of rules called "verifiable code". Decompilers take advantage of the adherence to these rules along with attached metadata to make decompilation much better than equivalent C++ decompilers. It's hard to get into with a thorough understanding of IL, but basically each class in your code will end up having metadata. It's how .Net prevents you from having to ship header or symbol files for someone to use an assembly/API you write. And for the verifiable code bit, it basically means that you can only use code that the .Net runtime can verify is "safe". The runtime is limited in it's static analysis capabilities, so there is a lot of code that can be verified is "safe and correct", but won't be verifiable by the runtime. Decompilers take advantage of this set of rules.
If you're shipping a product in C# and have IP you want to protect, you basically have to use an obfuscation product. It's the same in similar languages/runtimes such as Java. Obfuscation products for .Net do a variety of things:
- Obfuscate control flow (turn if statements into loops with
goto
and reorder everything)
- Inject garbage no-operation instructions to "hide" the real code
- Rename metadata so it doesn't really help. (turning class names like
FooBar
into a
)
- Change how classes are structured (moving 5 methods into a single method taking all the arguments from the 5 methods)
- Inject unverifiable code or metadata (though this is usually easily defeated by deobfuscators)
- Encrypt strings so that they aren't just plain text
- Encrypt resources such as attached configuration files, XAML pages, etc
- Add tamper protection (detection that the application has been changed)
Depending on which obfuscator you use, these methods can make it so for all intents and purposes the code is a huge "black box" without diving into the IL and hand deobfuscating it(very time consuming)
C++ has the plus that obfuscation isn't nearly as important. There is no metadata so decompilers have a much harder time. However, there are still a great deal of hackers that can read x86 assembly. And C++ applications very commonly get cracked. (good) C++ obfuscators are also much harder to come by. This is because they are about as difficult to obfuscate as they are to decompile. .Net has a set of rules that obfuscators use to both read your code, and obfuscate it. C++ doesn't have these rules.
So, in summary
- C# basically requires obfuscation
- C++ is much harder to decompile, but obfuscation support is lacking
- No matter which one you choose, it is always possible to crack. All you can do is slow it down or make it undesirable to crack due to the amount of time required
Also, as one more note. C# obfuscators which produce verifiable code basically give you an application which will run everywhere, as long as they have a new enough version of .Net. C# obfuscators which don't produce verifiable code will usually work everywhere, but they aren't guaranteed to. It's possible Microsoft would come out with some JIT optimization in the next version of .Net that will break your unverifiable obfuscated code. C++ code inherently does not usually run "everywhere", but it usually does. Obfuscated C++ code will probably work everywhere you intend to, but it's not guaranteed. Writing correct and thread-safe C++ is hard enough, much less worrying about an obfuscator screwing that up.