Hunting for readable strings in the binary (anti-piracy saga)

Alright, so when I thought I was done with encoding string variables in compile time…

  • I discovered that unless you disable RTTI and give up on “dynamic_cast” and “typeid”, the MSVC linker discloses in the final release binary the name of every class with virtual methods (along with its parent classes and namespaces). This includes any classes inheriting from another class with virtual methods !!
    Example: “.?AULookAndFeelMethods@TextEditor@juce@@” because of virtual void juce::TextEditor::LookAndFeelMethods::fillTextEditorBackground(…)

  • I also found that whenever a lambda is defined, the MSVC linker also discloses the name of the method (along with its parent classes and namespaces).
    Example: “.?AV<lambda_2>@?L@??updateOutputsComboBox@AudioDeviceSettingsPanel@juce@@AEAAXXZ@” because of “outputDeviceDropDown->onChange = [this] { updateConfig (true, false, false, false); };” in the juce::AudioDeviceSettingsPanel::updateOutputsComboBox() method.

However you need enable RTTI to use even the juce_core module of JUCE . See juce_core\native\juce_BasicNativeHeaders.h:76:

#error “You’re compiling without RTTI enabled! This is needed for a lot of JUCE classes, please update your compiler settings!”

I haven’t tried with XCode yet, do you know if compilers for mac are doing the same?

I’m terribly disappointed. For some reason I had always wrongly assumed that the name of classes, methods and variables could be kept private.

Is anyone using any tools to mitigate this?

EDITS: Added info about RTTI, how to disable it in MSVC and info about juce_core requiring it.

One mitigation method I’ve seen is simply to have some defines where you have something like…

#if JUCE_DEBUG
 #define TheNameIReallyWant TheNameIReallyWant
#else
 #define TheNameIReallyWant mGgzNunpmbPf7lIOuKRk9qpPmmFcKvsq
#endif

Creating all these is a bit of a pain, but if you script it you could have the definitions randomly generated for every build. If you make the definitions part of the project you could also probably change JUCE names too, but I’m not going to recommend you do that. You would probably have to be more careful about naming collisions than usual.

1 Like

That sounds like a balanced way to tackle this problem. I had resorted to manually renaming some critical functions and classes because I couldn’t justify the time I would have had to invest in doing this for every library that I am using (even with the help of scripts).

I wish though that compilers randomized those names automatically if they can’t strip them. I’m struggling to understand why they aren’t doing it already.

I suppose it boils down to the fact that they have to implement typeinfo::name() and make it return something intelligible.

But since the format of the returned string it’s not specified (for now), it would be nice if compilers supported an option to return (and hence store in the binary) a hash of the name rather than the name in plaintext.

2 Likes

Alas, RTTI depends on the identity for the objects being available at runtime, and this is what those strings are for … if you can use the -fno-rtti flag in your release builds (don’t forget to rigorously test!), then these strings won’t be present, but be sure that you use code-coverage style testing to make sure you’re not shipping exceptions!

I dont see why RTTI is in the way of scrabbling the names. While developing Java, this was one of the first things you enable for release builds and it is a feature of pretty much every build tool out there.
Not sure why c++ is making a fuss about it.

RTTI isn’t “in the way of scrabbling the names”, RTTI is why the names are there in the first place. Read the docs. Its pretty clear why this is happening - RTTI depends on the strings to identify virtual methods at runtime. If you disable RTTI: no strings. But! You probably cannot disable RTTI easily - or if you can, please report back and let us know how it went in the rest of the JUCE codebase …

If you want to obfuscate the RTTI type information, thats up to you. Seems like its an interesting subject, though, to others as well:

I was clarifying, that RTTI or the disablement of it, has nothing todo with the topic at hand and isn’t solving the situation. You can’t disable it simply since JUCE is depending on dynamic casts.

So I don’t quite understand why bringing up this flag, that is difficult to use correctly and will for sure not solve the problem.

False. The strings discussed here:

This includes any classes inheriting from another class with virtual methods!! Example: .?AULookAndFeelMethods@TextEditor@juce@@”

… are present in the binary because they are used at runttime to infer type inheritance. If you could (i.e. you don’t depend on RTTI), you may turn off RTTI with -fno-rtti - but NO, we cannot do that, because of the wide use of virtual methods within JUCE itself.

So the only solution is to obfuscate the strings in the binary, and I gave an example of how that is done above. This problem is not JUCE-specific, it is widely discussed all over the C++ eco-sphere …

Which I said “You can’t disable it simply since JUCE is depending on dynamic casts.”

which I also said “I don’t see why RTTI is in the way of scrabbling the names”

Thanks!

You can disable RTTI, but you have to be sure that the side effects of doing so won’t crash your project. Maybe someone has found a way to do it in JUCE, maybe not. There may indeed be a subset of JUCE classes for which this won’t be an issue - someone interested in the subject might know more.

Either way if you want to get rid of those RTTI strings, leaking information about your app, the only really viable option is obfuscation, as per the repo above.

“Currently, the tool only applies to Linux/Android and ELF files.” :frowning:

Thanks for the reference anyway.

Bad news :frowning: It is necessary even for juce_core:

See in juce_core\native\juce_BasicNativeHeaders.h:74

#if JUCE_MSVC
#ifndef _CPPRTTI
#error “You’re compiling without RTTI enabled! This is needed for a lot of JUCE classes, please update your compiler settings!”
#endif

Are there any examples of software that used obfuscation or symbol-stripping to prevent piracy?
Because I’m pretty sure that software pirates have been cracking programs like that for decades.

1 Like

Quick (obvious) note for those who try selective obfuscation in the C++ code. Replacing a few selected class “normal” names by “.?AUhjdhsfdjsf23” stands out as much as “.?AURegistrationCheck”. To be able to use cryptic names you would have to obfuscate all the JUCE classes shown in plaintext too, which is a non-trivial task.

1 Like

Obfuscating will only make it difficult, it won’t make it impossible to crack.

This is a battle better spent pushing more valuable features into your products, imho. Users don’t care about your obfuscated strings. Stay focused on your users.

5 Likes

I’d even say that obfuscating will only make it more fun to crack.

3 Likes

Yeah, I’ve seen some successes in the past with putting messages in there, instead of obfuscating the strings and names of things, but rather inviting hackers to make contact with the core developers for potential work packages, instead of wasting their time … :wink:

1 Like