Symbol Renaming: App Security’s Maginot Line?

If you don’t follow application security closely, you might think of application obfuscation and symbol renaming as being synonymous – and with good reason. Many platforms and languages, like .NET, Java, and JavaScript have popular obfuscators that do little else–our own Dotfuscator Community Edition for .NET and ProGuard for Java are good examples. However, obfuscation is far more than symbol renaming – and in-app protection is far more than obfuscation. Much of this expansion has been driven by new security requirements, shifting attack vectors, the rise of mobile and IoT computing and, lastly, the growing recognition inside regulations and legislation of the exposure that can result from inadequately protected software. 

Is your application security built to defend against yesterday’s threats? 

One of the most widely used metaphors for expensive efforts that offer a false sense of security, the Maginot Line consists of concrete fortifications and weapons installations built by France in the 1930s to prevent a potential invasion by Germany. It was expertly constructed, reflecting the hard-won lessons from “the war to end all wars” (World War I). Unfortunately for France, Germany applied their own lessons-learned to their tactics, and were quick to adapt the latest technologies to warfare. They made short work of this supposed impenetrable defense, mostly by driving around it and flying over it. The reliance on a single (and dated) line of defense led to a quick and humiliating rout, which came as a shock to the French public, who had been led to believe that it was “impregnable”.

RenameMaginot

Renaming in context

While certainly still relevant, renaming plays a smaller role in today’s notion of in-app protection. Figure 1 illustrates both the relative positioning of renaming inside the broader category of effective in-app security as well as some of the variants of renaming that may (or may not) be available inside a typical renaming transform.

Renaming Limitations

Renaming takes meaningful method names like “create_user_account” or “post_transaction” and variables like “account_num” and turns them into meaningless names like “a,” “b,” and “c.” This can make reverse engineering an application confusing for a hacker, but not necessarily for other programs. Non-sensical names don’t confuse debuggers, monitoring tools, or even basic source-code generators.

As such, effective obfuscation needs to offer a layered approach, including:

  • Control Flow: changing the instruction flow executed at runtime in such a way as to break reverse engineering tools, while not altering the application’s behavior.
  • String Encryption: mangling string resources so that it is difficult to search for and read them directly from the binaries. 
  • Instruction Pattern Transformation: replacing common instruction patterns generated by the compiler with other, less obvious constructs that may not map cleanly to high-level languages such as Java or C#.
  • Dummy Code Insertion: inserting code into the executable that does not affect the logic of the program, but breaks decompilers or makes reverse engineered code much more difficult to analyze.
  • Unused Code and Metadata Removal: removing debugging information, non-essential metadata, and unused code from applications. This makes them smaller and reduces the information available to an attacker. 

Renaming Variants 

Renaming is only safe when the transform can identify and update all references to a symbol with “the new name.” There are some scenarios where this is not possible. For instance, renamers can have difficulty identifying symbols referenced dynamically at runtime (e.g. used via reflection APIs). Another scenario is the case where the component exposes symbols for outside use by other (unprotected) applications. In both these scenarios, the renamer needs exclude these symbols from the renaming process in order for the application to work correctly.

These scenarios give rise to more sophisticated implementations of renaming that can 

  • automatically detect publicly visible symbols and preserve their names (library-mode)
  • share renaming across separate components (allowing consistent public symbol renaming across dependencies )
  • recognize popular patterns and frameworks (like XAML) and extend renaming in ways that static analysis alone could not

In short, even within the microcosm of renaming, there are meaningful distinctions.

Prevention is more than obfuscation

Just as obfuscation is more than just renaming, prevention is more than obfuscation. Obfuscation is a collection of transforms designed to prevent reverse engineering and tampering. The operative word in that last sentence is “prevent.” Obfuscation is a “Preventative Control” and there are now a number of other accepted and recognized alternatives (or compliments) to obfuscation that also help to prevent unauthorized access and control over applications. A sampling of these include anti-tamper, anti-emulator, anti-root, and anti-debug controls. Sometimes, prior to an actual attack, a bad actor will use these tools (in combination with others) to poke, prod and otherwise compromise the software and systems they are hoping to ultimately hack. By denying the hacker access to these tools – even in their own sandbox – these controls are recognized as “preventative.”

In-app protection is more than prevention

Just as prevention was more than obfuscation, in-app protection is more than prevention. There are a number of similar models out there and they all generally converge around three to five pillars.

  • Preventative Controls try to keep bad things from ever happening. If these controls were 100% effective, we could all stop here. If electrical insulation was all that we needed to prevent home fires, we could save a lot of time and money wasted on smoke detectors. 
  • Detective Controls are the next line of defense. These controls detect when one of the events that we have been trying to prevent has somehow managed to occur anyhow. 
  • Responsive Controls are the activities triggered when an event has been detected. These can fall into defensive measures, corrective measures, and reporting or notification responses. 

Today’s in-app protection solutions must offer controls from each of these categories to ensure an effective level of security and to address what are now considered standard practices by security auditors, regulators, and enforcement agencies. 

Depth and Breadth

Just as symbol renaming comes in a variety of forms with varying levels of sophistication, anti-debugger, anti-tamper, and all of the other categories of controls are (at least) as varied and specialized. Runtimes (Android versus .NET versus JavaScript…), platform (cloud, mobile, desktop, IoT…), and compliance obligations (PCI DSS, GDPR, …) each bring unique requirements that cannot be ignored. 

Whenever possible, learn from the mistakes of others (to avoid unnecessary mistakes of your own)

Keeping up with evolving threats as well as evolving compliance obligations is – to continue with the military analogy – a two-front war that is typically well outside of scope for companies in any other line of business. 

And that’s an important pillar of PreEmptive’s mission – to track and counter the tool developments of common adversaries, understand the implications of the latest breaches, and reconcile all of this against the backdrop of evolving regulations – all to help ensure that our clients aren’t building last year’s defenses for next year’s attacks.