Configuration of Shielding Tool Rules on Android
  • 30 Apr 2025
  • 13 Minutes to read
  • Dark
    Light
  • PDF

Configuration of Shielding Tool Rules on Android

  • Dark
    Light
  • PDF

Article summary

You can specify a rules list in the App Shielding configuration. This list determines how App Shielding will modify the Android application, especially with regards to shielding and obfuscation.

Find below information about how the rules list can be used to match classes, fields, and methods to resolve issues with shielding or obfuscation.

Syntax

class_op+ [[!]@annotation] [[!]access] [[!]flag]* [[!]class_spec] class_name [[!]extension class_name] (; | { 
  [member_op]* [[!]@annotation] [[!]access] [[!]flag]* [member_type] 
  [return_type] member_name [method_arguments];
})

You can specify as many rules as needed, and the rules are matched from top to bottom.

When configuring rules, you can use one line per rule, or, for better readability, you can use multiple lines for one rule.

# Comment
class_operation [[!]access] [!]class_spec class_name [[!]extension class_name] (; | {
[[!]access] [[!]class_mod] member type name;
})
set_operation name value;
firstdex class_name;
verify regex;

Definitions

The following tables list the commands to be used in a rules list.

List of operation options

Operation Name

Description

include

Include the specified rule file.

frameworkjar

Path to a.jar file that contains some Android framework code.

verify

Specify file paths to do consistency check on startup.

skipVerifyPath

Specify file paths to skip paths from consistency check on startup.

allowScreenshotsForActivity

Exclude an activity from screenshot blocking, where the activity is referenced by its fully qualified class name. For more information, see the entry for Block screenshots in Configuration Options for Android.

bindingCache

Specify the percentage of pull bindings to keep in a Least Recently Used cache. For more information, see Least Recently Used Cache.

List of class_operation options

class_operation (class_op)

opposite of

Description

match

-

Select classes or members for some operation.

bind

untouchable, unbind

Select classes or members to bind or exclude from binding.

scrambleStrings

keepStrings

Select classes or members in which to scramble constant string values.

obfuscate

preserve

Select classes or members to obfuscate or exclude from obfuscation.

removeAnnotation

keepAnnotation

Select classes or members from which to remove annotations.

removeDebug

keepDebug

Select classes or members from which to remove debug information.

removeLines

keepLines

Select classes or members from which to remove line numbers.

removeSourceFile

keepSourceFile

Select classes from which to remove the source file name.

firstdex

secondarydex

Select classes to include in the first (or secondary) classes.dex.

List of member_operation options

member_operation (member_op)

opposite of

Description

match

-

Select members for some operation.

bind

untouchable, unbind

Select members to bind or exclude from binding.

scrambleStrings

keepStrings

Select classes or members in which to scramble constant string values.

obfuscate

preserve

Select members to obfuscate or exclude from obfuscation.

removeAnnotation

keepAnnotation

Select classes or members from which to remove annotations.

removeDebug

keepDebug

Select members from which to remove debug information.

removeLines

keepLines

Select members from which to remove line numbers.

List of class and member definitions

Element

Description

Supports wildcards

Can be negated

@annotation

Optional. A qualified class name, e.g. @com.example.myannotation.

access

Optional. Possible values:

  • public

  • private

  • protected

flag

Optional. Possible values:

  • static

  • abstract

  • final

  • native

Multiple choices are allowed.

class_spec

Optional. Possible values:

  • class

  • enum

  • interface

class_name

Mandatory. The fully qualified name of the Java class.

extension

Optional. Possible values:

  • extends

  • implements

Must be followed by class_name.

member_type

Optional. Possible values:

  • method

  • field

return_value

Optional. Primitive type or fully-qualified name of a Java class.

member_name

Mandatory. The name of the class field(s) and/or method(s).

The following additional matchers are available:

  • <any>,<members> match any/all fields and methods.

  • <fields> matches all fields.

  • <methods> matches all methods.

value

Optional. A double-quote string, any integer or Boolean (true or false).

method_arguments

Optional. Comma-separated list of arguments in class_name format in brackets, e.g. (class_name, class_name)

To be used for method identification only. Use (...) to match methods with at least 1 argument (any type).

Specification

Negating

For the rules, most class specifiers can be negated by adding an exclamation mark in front of them.

Wildcards

Wildcards can be used as follows:

  • * matches zero or more characters.

  • + matches one or more characters.

  • ? matches exactly one character.

*Test matches MyTestOne and Test but not TestTwo.

Comments

Comments are prefixed with #.

# Verify a single file
verify "assets/my-asset.bin";
# Verify all files in the assets folder
verify "^assets/.*";
# Verify all HTML/JS in the assets folder
verify "^assets/(.+)\.(html|js)$";
# Verify all files inside the APK
verify ".*";

Binding

Using the untouchable or unbind operation will ensure that a matching class, method, or field will not be affected when values are removed during the binding operation. Optionally, the bind operation asks the Shielding Tool to include the specific value in the binding process (which would most likely bind the value regardless).

After shielding, the performance of the application can be impacted at startup. In this case, exclusion options can be defined to reduce the impact of shielding on the application.

The security provided by App Shielding is reduced by excluding classes from the shielding process. The number of bindings must be high enough to ensure security requirements (more than 2,000 push/pull bindings).

Since certain parts of the app code need to run for App Shielding to be launched, the shielding process will automatically trace and mark the relevant parts as untouchable: they cannot be modified in any way.

By default, the <clinit> class initializers, all variables, and method calls called from these are marked as untouchable. In some cases, however, tracing is not possible because the method call flow cannot be determined. Issues may occur especially with code reflection or provider classes in the Android manifest.

For these cases, a generic way to exclude classes and methods in a declarative manner is available so that the shielding process will not modify them in any way by marking them as untouchable.

String scrambling

Using the keepStrings operation will ensure that a matching class, method, or field will not be affected by the string scrambling operation. Optionally, the scrambleStrings operation asks the Shielding Tool to scramble string values.If both bind and scrambleStrings are enabled for a class or method, some constants are used for push and pull binding, and the remaining constants are used for string scambling.

Obfuscation

Using the preserve operation will ensure that a matching class, method, or field will not be affected when renamed as part of the obfuscation process.

Enabling obfuscation is not a mere ON / OFF feature. Rules must be configured manually. The effort to fine-tune the configuration is comparable to the one required to fine-tune similar commercially available obfuscation tools.

App obfuscation

App Shielding provides app obfuscation mechanisms where the Java bytecode of an application is obfuscated, and e.g. class names, function names, and field names are modified. Parts of an application code are rewritten in a purposefully unintelligible way, while the application functionalities are left unchanged. The purpose of obfuscation is to discourage static analysis attempts.

If an attacker attempts a static analysis of the code by decompiling the app, the previously sequential instructions with telling variable names and significant values, should, after obfuscation, look like a seemingly random set of instructions. This can in turn prevent or at least slow down any attack whose success requires some knowledge of the underlying code – some forms of repackaging, code hooking, code injections etc. App Shielding offers the possibility to obfuscate the Java bytecode part of an Android APK or AAB application format.

You can obfuscate the application either with App Shielding alone, or use it in combination with the Android built-in minification and resource shrinking options. These options can be enabled in the app's gradle file. For more information about Android options for shrinking and obfuscation, refer to the Android Studio documentation.

Enabling the App Shielding obfuscation feature in combination with Android obfuscation options results in a greater obfuscation depth of your code.

Obfuscation of App Shielding

The design of the native App Shielding libraries makes it difficult to analyze, as it uses several layers of obfuscation, employing multiple techniques. This is done primarily to make it harder to partially or completely remove or disable App Shielding.

Features such as shielding, where parts of the application data are removed from the code, depend on App Shielding being hard to disassemble and analyze to obtain encryption keys or other information. The shielding mechanism tries to force the attacker to break App Shielding (or the cryptography).

Enable obfuscation with default rules

The Shielding Tool comes with a number of default rule files. These are loaded automatically, according to the enabled options. For instance, to enable obfuscation, you can pass a rules file to the Shielding Tool with the command line option --rules <file> with the following contents:

include "builtin:obfuscate-on.cfg"

This loads the built-in rules file builtin:obfuscate-on.cfg, which enables class name obfuscation for all classes, and then loads another built-in rules file, builtin:default-unobfuscate.cfg, which includes rules to not obfuscate some well-known exceptions.

To load the built-in rules file via the configuration settings of the OneSpan Customer Portal or OneSpan Mobile Portal, you have the following two options:

  1. Enable Rules.cfg, and copy the include "builtin:obfuscate-on.cfg" command into the input field.

  2. Enable Default Obfuscate.

Enable obfuscation with customized rules

You can customize the settings for obfuscation by defining rules in the Rules.cfg file. This determines how App Shielding will modify the Android application, especially in the context of shielding and obfuscation.

obfuscate class com.example.app.*; # Obfuscate all classes within com.example.app package.

Customize obfuscation rules is not a mere ON / OFF feature. The rules must be configured manually (see class_operation options). The effort to fine-tune the configuration is comparable to the one required to fine-tune similar commercially available obfuscation tools.

Obfuscation techniques

App Shielding offers three types of obfuscation techniques:

  • Default obfuscation techniques

  • Obfuscation on demand: debug information removal

  • Obfuscation as a side-effect

Default obfuscation techniques

App Shielding employs these obfuscation techniques with any Java bytecode that is not implicitly or explicitly excluded from obfuscation. By default, App Shielding provides the implicit obfuscation rule. The explicit obfuscation rule is the sum of all rule lines that have been explicitly set in the configuration file, using the preserve and obfuscate keywords.

You can either apply none of these by implicitly or explicitly excluding the relevant piece of code, or apply all of them by targeting the relevant piece of code (or by not excluding it, either implicitly or explicitly).

The default obfuscation techniques are:

  • Class, method, and field name randomization

    Classes, methods, and field names are replaced with random, meaningless, and often shorter values. All references and resources are also updated accordingly.

  • Namespace flattening

    All classes will be moved into one big flat namespace which removes information about logical class grouping.

  • Code shuffling

    Code that belongs in one class can be moved to another class, and all references to this code can be updated accordingly to the relocated piece of code. This creates new application-internal code dependencies which are confusing to an attacker but also act as an internal binding mechanism that makes it harder to remove certain parts of the code tree.

Obfuscation on demand: debug information removal

If you use this technique, obfuscation is only applied if a specific keyword is added as an rule line in the App Shielding configuration.

Debug information such as parameter type information, source file and line number information can be removed.

  • removeDebug / keepDebug

  • removeSourceFile / keepSourceFile

  • removeLines / keepLines

Java annotations can be removed with the removeAnnotation / keepAnnotation keywords.

This may break some uses of Java Generics.

Obfuscation as a side-effect

To enable your app to protect itself, App Shielding decompiles, modifies, and recompiles the app. Some of the modifications performed on the app also obfuscate the app code: the mandatory obfuscation and the binding and string scrambling mechanisms.

  • Mandatory obfuscation

    Java bytecode related to App Shielding will be obfuscated, whether obfuscation is enabled or disabled on the configuration level. This is why every shielded app is retrieved with a mapping.txt file. This file contains the mapping of original names to obfuscated names. Also, it is useful for de-obfuscating the traces from the app and should therefore be preserved.

  • Binding and string scrambling

    The main purpose of the binding and string scrambling mechanisms is to prevent an attacker from removing the App Shielding libraries and configurations from the shielded app. A side-effect of these mechanisms happens to be code obfuscation: where previously there was a plain text right-hand string value, there is now a piece of code. See the following table for a comparison of the string value and the pieces of code after binding and string scrambling.

Comparison of code after applying obfuscation as a side-effect

Original code

After Binding

After String Scrambling

const-string v0, "ABC"

const-string v1, "DEF"

const-string v2, "GHI"

const-class v0, Lcom/onespan/shieldenum/ObfTest;

const v1, 0x15a

invoke-static {v0, v1}, Ljt/d;->a(Ljava/lang/Class;I)V

const v0, 0x7fb

invoke-static {v0}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v0

const v1, 0x7fc

invoke-static {v1}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v1

const v2, 0x7fd

invoke-static {v2}, Ljt/d;->a(I)Ljava/lang/String;

move-result-object v2

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5, 0x38ff

xor-int/lit16 v5, v5, 0x38bd

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x3

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String; ->intern()Ljava/lang/String;

move-result-object v0

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5,0x3867

xor-int/lit16 v5, v5, 0x3822

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

const v4, 0x0

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x2

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String;->intern()Ljava/lang/String;

move-result-object v1

const/16 v4, 0x3

new-array v3, v4, [C

const/16 v5, 0x7f17

xor-int/lit16 v5, v5, 0x7f5f

int-to-char v5, v5

const v4, 0x1

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0x1

int-to-char v5, v5

const v4, 0x2

aput-char v5, v3, v4

const v4, 0x1

aget-char v5, v3, v4

xor-int/lit16 v5, v5, 0xf

int-to-char v5, v5

const v4, 0x0

aput-char v5, v3, v4

new-instance v5, Ljava/lang/String;

invoke-direct {v5, v3}, Ljava/lang/String;-><init>([C)V

invoke-virtual {v5}, Ljava/lang/String;->intern()Ljava/lang/String;

move-result-object v2

The size of the obfuscated code does not necessarily mean it is safer or slower to execute. The String Scrambling mechanism leaves a longer code but is actually less safe and more performant than the Binding mechanism. Notice also the difference between the Binding and String Scrambling mechanisms and the default obfuscation techniques described above. The Binding mechanism affects the right-hand side of a string value while the String Scrambling essentially affects labels (classes, methods, fields etc.). Both are useful and contribute to strengthen the app against static analysis.

Repackaging checks

App Shielding checks at the start of the application if the application is repackaged. The repackaging check consists of three parts:

  • Check that all expected files are present, that is, no file was removed.

  • Check that no unexpected file was added to the apk.

  • Verify the file integrity of a few selected files, that is, verify that the content of the file was not modified.

If the repackaging check fails, App Shielding reports the app as repackaged.

By default, all files that are present in the application when integrating App Shielding are expected, although there are a few well known exceptions. For example, Google Play inserts some files on converting an app bundle to an apk before the apk is installed.

By default, App Shielding verifies the file integrity of:

  • AndroidManifest.xml

  • classesX.dex

  • the App Shielding library.

For more information about repackaging, see Repackaging detection.

# Verify a single file
verify "assets/my-asset.bin";
# Verify all files in the assets folder
verify "^assets/.*";
# Verify all HTML/JS in the assets folder
verify "^assets/(.+)\.(html|js)$";
# Verify all files inside the APK
verify ".*";

By default, not all application files are specifically checked at startup for possible application launch time performance considerations. Integrity checks of large files may cause noticeably longer launch times.

Amazon App Store and repackaging checks

Amazon App Store will modify your application’s files. For all applications, Amazon App Store will also inject some code and files into the application, which for most applications also shuffles the classes in the apps' classesX.dex files. Since this modification would trigger the default repackaging checks of App Shielding, the file integrity checking must be tuned for apps published on Amazon App Store.

Due to the changes, the file integrity checking for Amazon App Store must be tuned for Amazon App Store.

The Shielding Tool has a built-in rules file, builtin:amazon-app-store-support.cfg, for the Amazon App Store. To use the file, you must use the following in your rules file:

include "builtin:amazon-app-store-support.cfg";

These rules will skip the classesX.dex files and the files that are known to be injected by Amazon App Store. The content of builtin:amazon-app-store-support.cfg:

# Amazon App Store modifies classes.dex, all the classesX.dex files must be skipped
skipVerifyPath "classes*.dex";

# Amazon App Store injects these files
skipVerifyPath "com.amazon.*";
skipVerifyPath "kiwi";

Rule examples

Class-level rule

  • No values from MyClass will be removed:

    untouchable class com.example.MyClass;
  • No values from any class inside the com.example.* package will be removed:

    untouchable class com.example.*;
  • No class that implements MyInterface will be modified:

    untouchable class * implements com.example.MyInterface;
  • No class that (immediately) derives from MyBaseClass will be modified:

    untouchable class * extends com.example.MyBaseClass;

Method-level rule

  • This rule only matches the public static method getValue that returns int within MyClass:

    untouchable class com.example.MyClass
    {
      public static method int getValue;
    }
  • This method matches any public method called getValue and setValue in any class that implements SomeInterface. All other methods in these classes can still be modified:

    untouchable class * implements SomeInterface
    {
      public method * getValue;
      public method void setValue;
    }
  • To match a constructor, it must be added as a regular method <init> with a void return value:

    untouchable class * implements SomeInterface
    {
      public method * getValue;
      public method void setValue;
    }

Troubleshooting

For detailed information about troubleshooting, see the Troubleshooting Guide.

Troubleshooting the shielding of an app

If you experience issues (e.g. the application does not start properly), you may need to exclude certain classes from the binding process. For detailed information, see Binding.


Was this article helpful?

Changing your password will log you out immediately. Use the new password to log back in.
First name must have atleast 2 characters. Numbers and special characters are not allowed.
Last name must have atleast 1 characters. Numbers and special characters are not allowed.
Enter a valid email
Enter a valid password
Your profile has been successfully updated.
ESC

Ozzy, our interactive help assistant