In recent years, there has been a growing concern regarding the escalating security threats targeting mobile applications. Reports indicate that mobile app threats increased by more than 30% between the first half of 2022 and the first half of 2023. Gibler et al.’s research has unveiled a substantial number of mobile applications being mere “copies” of existing legitimate apps. Beyond the challenges of intellectual property theft, modification, or bypassing paid features to name a few, the industry also grapples with emerging security risks, particularly with the increasing reliance of businesses on mobile devices. The proliferation of fraudulent and malicious codes leveraging automated manipulation by attackers poses significant threats to mobile ecosystems.
From a technical standpoint, the process of reverse engineering mobile apps has comparatively become simpler than before with the availability of sophisticated tools that are easier to use to accomplish some of the traditionally tedious parts of reverse engineering. The unprecedented surge in security challenges, coupled with the absence of built-in defenses, has prompted mobile app developers and security professionals to actively seek additional protective measures.
Among the key technologies addressing these concerns, code obfuscation is a crucial solution. This technique involves modifying an application while retaining the same behavior, with the primary goal of rendering software code more difficult for both automated tools and human analysts to decipher. While extensive research has been devoted to in-lab technical analyses on the effectiveness of new obfuscation techniques or countermeasures against misuse by actors developing malware, there has been a noticeable gap in understanding how non-malicious software developers integrate obfuscation into their realworld development processes.
Our objective in this piece is to address key questions that shed light on the practical aspects of software obfuscation in the context of mobile app development and protection:
By exploring these questions, we aim to contribute valuable insights that can inform and enhance the practical implementation of software obfuscation techniques in the mobile app development landscape.
Obfuscation stands out as a crucial technique in software protection, acting as a deterrent against malicious reverse engineering. The original and obfuscated version produces the same output when executed but they are much more difficult for attackers to understand.
To address these threats and strengthen iOS and Android app security, reputable software security solution providers have introduced app obfuscation. Understanding obfuscation techniques in Android and iOS app development is vital to enhance security and protect mobile apps from evolving threats. The benefits of such understanding mean app publishers and security researchers can refine their development strategies based on the existing and expected future threat landscape.
Obfuscation is necessary for both the major mobile platforms, Android and iOS. While the assumption might be that obfuscation on iOS is less necessary due to its perceived heightened security, recent security incidents challenge this notion. Sophisticated tools, such as Ghidra, have demonstrated that iOS reverse engineering is not as challenging as commonly believed. iOS developers experience similar challenges as their Android counterparts, grappling with severe software piracy issues.
Through obfuscation, one can modify applications in such a way that it makes it more difficult to understand for both automated tools and human attackers. This results in code that is sufficiently complex to discourage analysis and unauthorized modifications. Decompiling the obfuscated code becomes a more challenging task, even with dedicated time and effort. However, with enough effort it is always possible to recover the algorithms and data structures protected by obfuscation. Therefore, the primary objective is to increase the time and effort required, rendering it practically infeasible for a malicious actor to reverse engineer the application. Applying the obfuscation in slightly different ways with each build or release can act as a deterrent and prevent scalable or repeatable deobfuscation techniques.
At a high level, obfuscation aims to make reverse engineering impractical by making static and dynamic analysis tricky and challenging through various means:
Research has shown that software obfuscation is not widely prioritized in mobile development. Developers in security-sensitive sectors acknowledge malicious reverse engineering as a significant threat infact global research on mobile application security reported that 28% of respondents claim an increase in attempts to reverse engineer or modify apps is driving their organization to consider or purchase mobile app security products.
Often with organizations that do use obfuscation, the implementation is primarily geared towards protecting the information contained within the apps rather than the overall design and implementation of the software.
Before we dive further into the different obfuscation techniques let us understand the structures of both Android APK and iOS IPA. This foundational understanding allows us to thoroughly explore how the obfuscation techniques discussed later are intricately woven into the code.
An Android Application Package (APK) file is a compressed zip file that contains the entirety of an Android app, including four main directories ( res , assets , lib , and META-INF ) and three essential files ( AndroidManifest.xml , classes.dex , and resources.arsc ). Let’s outline the functions and contents of these directories and files below:
res: This directory stores Android resource files, which are subsequently mapped into the R file in Android, acquiring corresponding IDs.
assets: Similar to the res directory, the assets directory stores static files in the APK. In contrast to the res directory, developers can create subdirectories at any depth with an arbitrary file structure.
lib: This directory stores platform-specific compiled code (typically library files like .so ). Subdirectories can be created based on processor types, such as armeabi , x86 , x86_64 , etc.
META-INF: Responsible for preserving the signature information of a specific app, this directory validates the integrity of an APK file.
AndroidManifest.xml: An XML configuration file for the APK, declaring fundamental information such as name, version, required permissions, and components. Each APK has only one AndroidManifest file.
classes.dex: This file (or files - classes.dex , classes1.dex , classes2.dex …) contains comprehensive information about the classes in an app. The data is organized in a manner that the Dalvik virtual machine can comprehend and execute.
resources.arsc: Used to document the relationship between resource files and their corresponding resource IDs, this file facilitates the location of specific resources.
iOS applications are packaged in a file format known as IPA—it’s an archive of an application that can be released to the Apple Store. They can be decompressed by altering the extension to zip. IPA files have a well-defined internal structure, typically comprising a primary folder with a nested subfolder named .app .
Upon exploration to see package contents, you’ll encounter the following components:
Info.plist file: This file describes the application to the iOS operating system, featuring properties such as the icon files, application display name, version, unique identifier, and the filename of the main executable. The structure of an IPA file is outlined in the Info.plist .
Application’s main executable: The main executable file contains the application’s code, encapsulating all the Objective-C and Swift code contributed by developers. Its filename is determined by the Info.plist . Additionally, the application file includes entitlement information, denoting Apple’s permissions.
External executable libraries: Libraries utilized by the main executable.
Frameworks: A folder housing frameworks employed by the application. Each framework resides in its folder under Frameworks/.framework and contains native code and resources. For Swift applications, the Swift runtime libraries are directly under the Frameworks folder.
Plugins: This section handles application extensions, enabling the execution of external functionalities.
Resources: Documents, images, icons, video, and audio files essential for the application’s functionality.
www: For web apps developed using frameworks like Cordova or React Native, the web data is stored in a folder named www , encompassing web pages, resources, JavaScript, CSS files, and more.
Nib or storyboard files: Files describing the application’s user interface (UI) and its interaction with logic.
Signature information: iOS validates the integrity of native code elements by ensuring they haven’t been modified since being signed by the developer/distributor.
Provisioning information: This file dictates the deployment permissions of the application. Developers can only install their application on a limited set of devices, necessitating a provisioning profile obtained from Apple’s developer console. The provisioning information includes a copy of the application’s entitlements, ensuring that permissions are authorized by Apple.
Now that you are familiar with the APK and IPA structures, let us take APK as our example and understand how a malicious actor would approach reverse engineering to exploit this structure.
Attackers can easily inject malicious code and ads into unprotected Android apps due to the straightforward process of unpacking, disassembling, and repacking. Android’s lack of a key-pair certification requirement by a Certificate Authority (CA) allows modified apps to be signed with the attacker’s key, making them appear as new and legitimate. To further deceive security products relying on APK file hash values, attackers may use different keys for the same repacked app.
Unpacking and disassembling play a crucial role in reverse engineering Android apps. These processes enable attackers to extract hard-coded evidence like cryptokeys and user credentials. Before the prevalence of obfuscation techniques in Android development, reverse engineering was considerably less complicated.
To unpack the APK content, the initial step involves decompressing the APK file, achievable with standard ZIP file handling software or more advanced tools like apktool. Decompressed APKs can be modified and repackaged, resulting in a valid new/modified app that must be re-signed, typically using apksigner.
Determining whether an app is tampered with or malicious can be accomplished through:
Analyzing the certificate located in the META-INF folder, often with the file extension RSA, using Java keytool. Although frequently self-signed, searching for the certificate fingerprint may provide additional information.
To analyze the functionality, reverse engineers analyse the DEX files and native libraries. For dex files, there are two approaches: either decompile it into Java source code or disassemble it into a format such as smali. Native libraries, often written in C and compiled for multiple CPU architectures can be reverse engineered by tools like IDA Pro and Ghidra.
However, the effectiveness of these tools can be significantly hampered if obfuscation techniques have been successfully applied. In such cases, reconstructing the original state of an app becomes a challenging task.
Now that we have established the need for obfuscation in mobile apps, let us explore a few different obfuscation techniques.
Code obfuscation techniques can be classified based on the transformation subject into various categories.
1. Name obfuscation
The first, most obvious, category involves identifier name transformations which involves replacing names with meaning with meaningless names.
2. Control flow transformations
The second category encompasses control-flow transformations, where the program’s control-flow is changed while maintaining the same computational functionality. Typical transformations include aggregation, reordering, and the inclusion of redundant computations.
3. Data abstraction
Code obfuscation also employs data abstraction techniques. This can be achieved through name modifications, updating inheritance relations, or changing the structure of data arrays.
4. Obfuscating procedural abstractions
This section describes altering the original code structure to remove procedural instructions could involve the following:
5. Preventative transformations
Preventive transformation techniques are implemented to evade debugging and decompilation tools.
6. API call hiding
Reflection stands as a potent Java programming technique employed for extending functionalities, including the verification of backward compatibility or dynamic loading of methods. Widely used in debugging and testing tools, the Java Reflection application programming interface (API) enables a program to access class information during execution, facilitating actions such as creating new objects, invoking methods. Additionally, Reflection can serve as an alternative approach for data obfuscation because it inhibits the automated analysis of method calls or field access in an application.
7. Code virtualization
Code virtualization involves transforming your method body into a sequence of instructions for a randomized virtual machine, which is then injected into your app during the build process. Code virtualization begins by analyzing the behavior of your method code and generating compact and efficient virtual machines, each with its unique instruction set. After generating these virtual machines, your method is re-implemented based on the new instruction set. When your method is invoked, the new implementation is loaded, and an interpreter on the native virtual machine executes the instructions. This result is the original method code being completely concealed within the application.
Unlike code encryption, the original code is never reconstructed at runtime.
Code virtualization synergizes effectively with other techniques such as API call hiding, string encryption, or control flow obfuscation. For maximum protection, it can even be combined with code encryption.
Employing these diverse layers of protection ensures that unobfuscated method calls are challenging to detect, and direct correspondence with previously released versions of your app cannot be easily established.
Code obfuscation offers significant advantages, primarily driven by its adaptability. This technique allows for tailoring the level of security based on specific requirements, balancing computational overhead with performance considerations. The key advantages of code obfuscation include:
Protection
Code obfuscation serves as a robust defense against both static and dynamic analysis attacks. By introducing complexity and making it challenging for attackers, it necessitates more time and resources for them to achieve their objectives.
Diversity
The ability to generate various instances of the original program enhances resilience against global attacks. This diversity hinders attackers from developing standardized approaches, adding an extra layer of defense.
Cost-effectiveness
Code obfuscation incurs minimal maintenance costs, thanks to the automated transformation process and compatibility with existing systems. This results in an efficient and economical approach to enhancing security.
Platform independence
The application of code obfuscation transformations on high-level code ensures the preservation of platform independence. This feature contributes to the versatility of the obfuscated code across different environments.
You can try these examples out in the JavaScript Obfuscator Tool, though these techniques are, of course, applicable to other languages.
Assessing the effectiveness of code obfuscation techniques typically involves evaluating their potency, resilience, cost, and stealth in the app.
Potency
Potency measures the degree of obscurity introduced to the code to enhance its complexity, making it more challenging for an attacker to comprehend. This involves assessing the increased nesting complexity, control-flow complexity, variables complexity, and program length resulting from obfuscation techniques.
Resilience
Resilience gauges the robustness of the obfuscation technique against automated deobfuscators. Evaluation criteria include:
Cost
Cost evaluates the additional resources consumed by obfuscated code during runtime, including:
Stealth
The effectiveness of obfuscation is determined by its ability to resist automated removal while maintaining resistance to human analysis. Key considerations include:
In summary, a comprehensive evaluation of code obfuscation techniques involves considering their impact on potency, resilience against deobfuscation, resource cost, and effectiveness in maintaining stealth against both automated tools and human analysis.
The adoption of obfuscation techniques in mobile app development emerges as a crucial strategy to mitigate security risks. Beyond the immediate need to counteract escalating security threats targeting mobile applications, the benefits of obfuscation extend to broader concerns such as preventing unauthorized cloning and modifications. By obscuring the underlying code, obfuscation not only enhances the resilience of apps against malicious reverse engineering but also protects the integrity of the app market, enabling innovation and fair competition.
Moreover, the implementation of obfuscation serves as a proactive measure to protect valuable intellectual property and proprietary algorithms, ensuring that developers can confidently bring their creations to market without fear of exploitation or unauthorized replication. As the mobile app ecosystem continues to evolve amidst growing security challenges, using obfuscation techniques is the right step to take.
DexGuard and iXGuard are the protection products offered by Guardsquare, which feature the different mentioned obfuscation techniques along with Runtime Application Self-Protection (RASP) to provide multilayered, polymorphic protection.
DexGuard offers mobile app protection with extensive Android app obfuscation and security protocols strengthening your app and SDK security by implementing multiple layers of code-hardening measures.
iXGuard extends similar security features to iOS apps.
Guardsquare offers the most complete approach to mobile application security on the market. Guardsquare’s products integrates seamlessly across the development cycle: from app security testing to code hardening to real-time visibility into the threat landscape. Guardsquare solutions provide enhanced mobile application security from early in the development process through publication.
More than 900 customers worldwide across all major industries rely on Guardsquare to help them identify security risks and protect their mobile applications against reverse engineering and tampering.