0 x0, introduction

The confusion of Android code is a cliche, but most Android people are probably still at this stage (like me before this article) :

  • 1. Do not confuse the Debug package in daily development, and enable code confusion before the official Release of the Release package;
  • 2. Benefits of confusion β‘  β†’ Names of classes, methods, and variables become short and meaningless, increasing the reading cost of decompiled code;
  • 3, the benefits of confusion β‘‘ β†’ delete useless classes, methods and attributes, reduce the size of APK package;
  • 4. Confusion benefit β‘’ β†’ Optimize the bytecode to remove useless instructions and make the application run faster;
  • Set minifyEnabled to true for build.gradle and proguard-rules.pro.
  • 6, where to obfuscate rules β†’ online search common obfuscate template copy and paste, the project depends on the third party library official document copy and paste;

Most will stop there, but better still, ProGuard has heard of R8, understands obfuscation syntax, and will customize obfuscation rules.

Meeting above these, daily development has been very enough, but now IT industry so “volume”, interview, the interviewer asked:

What does the confusion actually do? Have you seen the obfuscation source? The underlying principle…

It makes sense (manual dog head to save life ~)

So this section dives a little deeper into code obfuscation in Android


0x1. Daily use

Go through the motions, as usual, by writing down simple examples to demonstrate how to use them in daily life. Skip this Part only if you are interested in how confusion works

1. APK comparison before and after the confusion

Create a new project and introduce kotlin-related dependencies, coroutines, etc. (app hierarchy build.gradle) :

dependencies {
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx. Core: the core - KTX: 1.3.0'
    implementation 'androidx. Appcompat: appcompat: 1.1.0'
    implementation 'androidx. Constraintlayout: constraintlayout: 1.1.3'
    implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.3.7'
    implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.7'
}
Copy the code

Create mainActivity. kt, request URL, and load content:

class MainActivity : AppCompatActivity(), CoroutineScope by MainScope()  {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        et_url.setText("https://www.baidu.com")
        bt_load.setOnClickListener {
            launch {
                tv_content.text = "Start loading request..."
                tv_content.text = "Loaded, the page reads: \n\n\n${loadUrl(et_url.text.toString())}"}}}private suspend fun loadUrl(url: String) = withContext(Dispatchers.IO) {
        var content = ""
        (URL(url).openConnection() as HttpURLConnection).apply {
            requestMethod = "GET"
            content = dealResponse(inputStream)
            disconnect()
        }
        return@withContext content
    }

    private fun dealResponse(inputStream: InputStream): String {
        val reader = BufferedReader(InputStreamReader(inputStream))
        return StringBuffer().apply {
            var str = reader.readLine()
            while (null! = str) { append(str) str = reader.readLine() } }.toString() }override fun onDestroy(a) {
        super.onDestroy()
        cancel()
    }
}
Copy the code

Under operation Kangkang:

App-level build.gradle with release signature and compile configuration:

signingConfigs { release { storeFile file('test.jks') storePassword '123456' keyAlias 'test' keyPassword '123456' } } BuildTypes {release {// enable code compression, optimization and obfuscation minifyEnabled true // Enable resource compression, With minifyEnabled=true, run shrinkResources true. // Specify the obfuscation retention rule proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // Package signingConfig SigningConfigs. release}}Copy the code

Gradle assemble assemble:

23333, streaking, drag APK directly into the decompile tool jadX-GUI, no pressure to see the code:

Kangkang Release package:

**Load Proguard mappings** / mapping.txt **Load Proguard mappings** / mapping.txt **Load Proguard mappings**

Also drag to jADX-GUI riconkang:

Readability is significantly reduced ~


2. The App Crash log is confused and the fault is located

Have you ever wondered what the log message would look like if the confused APK reported an error?

Instead of code validation, throw a null pointer exception at the click:

With the b.B.A.A.on click (unknown Srouce:2) here, it seems difficult to directly locate the true location of the error code.

An inefficient solution is to compare the generated mapping.txt file by yourself. For example, search for B.B.A.A above:

It’s not hard to see what the problem is, but it’s not recommended for everyday development. It’s easy to find here just because the sample code is simple. Another approach is recommended:

To get confused, if your app is published on Google Play, go to the official documentation:

The crash stack trace is obfuscated or symbolized

No uploaded to Google Play and it doesn’t matter, directly using the android SDK/tools/proguard/bin/proguardgui. The bat:

If you do not want to update the SDK, you can directly use the script package I provided to blur R8. Download the address (extract code: 1234) : r8-retrace.7z. The use process is as shown in the figure below:

To confuse before and after comparisons:

Yes, so much for everyday use and the detailed rules of obfuscation.


0x2, confusion rules detailed explanation

Tips: There’s no need to memorize, click “like” or “mark” and check back when you need to use it. If you missed anything, please leave it in the comments section

1. The parameters are confused

-optimizationpasses 5                       # Code obfogged compression ratio, values between 0 and 7, default 5
-verbose                                    Log when confusion occurs
-dontoptimize                               # Do not optimize input class files
-dontshrink                                 # Turn off compression
-dontpreverify                              # Turn off prevalidation (Java platform, Android does not need it, removing it will speed up obfuscations)
-dontoptimize                               Turn off code optimization
-dontobfuscate                              # Close confusion
-ignorewarnings                             # Ignore warnings
-dontwarn com.squareup.okhttp.**            Specifies that the class does not output warning messages
-dontusemixedcaseclassnames                 All types are lowercase after confusion
-dontskipnonpubliclibraryclasses            # Classes that do not skip non-public libraries
-printmapping mapping.txt                   Generate the mapping file mapping.txt between the original class name and the confused class name
-useuniqueclassmembernames                  # Confound the names of methods in the confound class
-allowaccessmodification                    Allows access to and modification of modifiers on classes and members of classes
-renamesourcefileattribute SourceFile       # Convert the meaningful class names in the source code to SourceFile to obfuscate the specific crashed code
-keepattributes SourceFile,LineNumberTable  # Reserve the line number
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod Avoid confusion between annotations, inner classes, generics, and anonymous classes-optimizations ! code/simplification/cast,! field/ ,! class/merging/# Specifies the algorithm to use when obfuscating
Copy the code

2. Keep the Settings unconfusing

Grammatical composition:

[hold command] [class] {[member]}Copy the code

Hold command:

-keep # Prevent classes and class members from being removed or confused; -keepnames # Prevents class and class members from being confused; -keepClassMembers # Prevent classmembers from being removed or confused; -keepClassMemberNames # Prevents class members from being confused; -keepClasseswithMembers # Prevent classeswithmembers from being removed or confused; - # keepclasseswithmembernames prevent has the members of the class and class members are confused;Copy the code

Class:

  • Concrete classes
  • Access modifier β†’ public, private, protected
  • Wildcard (*) β†’ Matches characters of any length, but does not include the package name separator (.)
  • Wildcard (**) β†’ Matches any character with package name delimiter (.)
  • Extends β†’ Matches implements a subclass of a parent class
  • Implements β†’ matches a class that implements an interface
  • $β†’ Inner class

Members:

  • Match all constructors β†’
  • Match all fields β†’
  • Match all methods β†’
  • Access modifier β†’ public, private, protected
  • In addition to* ε’Œ ζ”―ι‚£In addition to wildcard characters, also supports* * *Wildcard that matches any parameter type
  • . β†’ Match any type parameter of any length, such as void test(…) You can match test methods with different numbers of parameters

Common examples of custom obfuscation rules:

# does not confuse the class name and the contents of the class
-keep class cn.coderpig.myapp.example.Test { *; }

# Do not confuse class names under the specified package name, excluding class names under subpackages
-keep class cn.coderpig.myapp*

# Does not confuse the contents of a class with the specified package name-keep class cn.coderpig.myapp* {*; }Do not confuse class names under the specified package name, including class names under subpackages
-keep class cn.coderpig.myapp**

Do not confuse subclasses of a class
-keep public class * extends cn.coderpig.myapp.base.BaseFragment

Do not obfuscate classes that implement an interface
-keep class * implements cn.coderpig.myapp.dao.DaoImp

Do not confuse classes whose name contains "entity" and the contents of the class-keep class **.*entity*.** {*; }Do not confuse all public content in inner classes
-keep class cn.coderpig.myapp.widget.CustomView$OnClickInterface {
    public *;
}

Does not obfuscate all methods of the specified class
-keep cn.coderpig.myapp.example.Test {
    public <methods>;
}

Does not obfuscate all fields of the specified class
-keep cn.coderpig.myapp.example.Test {
    public <fields>;
}

Does not obfuscate all constructors of a specified class
-keep cn.coderpig.myapp.example.Test {
    public <init>;
}

Do not confuse methods that specify parameters as parameters
-keep cn.coderpig.myapp.example.Test {
    public <methods>(java.lang.String);
}

Do not obfuscate class-specific methods
-keep cn.coderpig.myapp.example.Test {
    public test(java.lang.String);
}

Do not confuse native methods
-keepclasseswithmembernames class * {
    native <methods>;
}

Do not obfuscate enumerated classes
-keepclassmembers enum * {
  public static **[] values();
  public static ** valueOf(java.lang.String);
}

Do not confuse resource classes
-keepclassmembers class **.R$* {
    public static <fields>;
}

Do not confuse custom controls
-keep public class * entends android.view.View {
    *** get*();
    void set* * * * (); public <init>; }* do not confuse class members that implement the Serializable interface.
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}

Do not obfuscate class members that implement the Parcelable interface
-keep class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator *;
}

# Precautions:
# 
# β‘  JNI method can not be confused, method name should be consistent with native method;
# β‘‘ Do not confuse the classes used in reflection, otherwise reflection may cause problems;
# 3 The four components, Application subclasses, Framework classes, and custom views are not confused by default.
# 4 WebView's JS call interface methods should not be confused;
# β‘€ Annotate related classes without confusion;
# β‘₯ Bean data class (GSON, Fastjson, etc.)
# 7 Values and valuesof in the enum class
# Serializable (Parceable, Serializable);
# ⑨ Third-party libraries or SDKS, please refer to the confusion rules provided by the third party. If not provided, it is recommended that all third-party packages be not confused.
Copy the code

3. Obfuscation rule superposition

Did you ever think that the code example created by the daily use above, proguard-rules.pro, does not configure obfuscation rules, but it does?

This is because obfuscation rules are superimposed, and the source of obfuscation rules is not just proguard-rules.pro in the main module, but also these:

  • (1) < module – dir > / proguard – rules. Pro

Not only can the main module have proguard-rules.pro, but submodules can also have proguard-rules.pro, because rules are superimposed, so the configuration of one module can affect other modules.

  • (2) the proguard – android – optimize. TXT

AGP is generated at compile time, contains rules that are useful for most Android projects, and enables the @keep * annotation.

AGP also provides proguard-defaults. TXT or proguard-android. TXT, which can be set via getDefaultProguardFile, but it is recommended to use this file (more optimized configuration).

  • (3) < module – dir > / build/intermediates/proguard – rules/debug/aapt_rules. TXT

Automatically generated, AAPT2 generates retention rules based on references to classes, layouts, and other application resources in the application list, such as not confusing each Activity.

  • AAR library β†’ /proguard.txt
  • / meta-INF /proguard/

If you want to see the obfuscation rule after all rules are added together, add the following configuration to proguard-rules.pro in your home directory:

# Output the obfuscation rule after all rules are added
-printconfiguration ./build/outputs/mapping/full-config.txt
Copy the code

4. Resource compression

Resource compression is actually divided into two steps: If shrinkResources is set to true or not, AGP performs resource merging when building APK. Resource merging is performed only when there are two or more resources with the same name. AGP selects the file with higher priority from the duplicate items and only passes this resource to AAPT2. For distribution in APK.

Cascade priority:

Dependency β†’ primary resource β†’ channel β†’ build type

For example, if duplicate resources exist in the main resource and channel, Gradle selects the resources in the channel. If duplicate resources are present in the same level, such as SRC /main/res/ and SRC /main/res2, Gradle will report a resource merge error.

Corresponding to processDebugResources in the package Task, The aAPT compiled flat product and merged manifest file are linked to generate._ap file (including resource file, manifest file, resource relational mapping table file resources.arsc) and R.java file (saving the mapping relationship of resource type, resource name and address).

When resource compression is enabled, all unused resources are removed by default. If you want to define which resources to keep, you can create an XML file, such as keep.xml, in the res/raw/ directory. There may be more than one such document) :


      
<resources xmlns:tools="http://schemas.android.com/tools"
    <!--Define which resources are to be retained-->
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    <! -- Define which resources need to be removed -->
    tools:discard="@layout/unused2"
    <! -- Enable strict mode, strict,safe, the former strictly according to keep and discard specified resources -->
    <! -- Delete unreferenced Resources conservatively, such as Resources referenced using resources.getidentifier () in your code -->
    tools:shrinkMode="strict"/>
Copy the code

In addition, you can add resConfigs to build.gradle to remove unwanted alternate resource files.

android { ... DefaultConfig {resConfigs "zh-rcn"}}Copy the code

0x3. Extract ProGuard, DX, D8 and R8 from desugar

Android CPU, Compilers, D8&R8 (Android CPU, Compilers, D8&R8

The same Java code can run on Windows, Linux, and Mac, depending on the JVM (Java Virtual Machine) on different platforms/versions. Java code is compiled to produce.class bytecode files. It is then translated by the JVM into platform-specific machine code and run.

The internal composition of the JVM is shown below:

  • Class loaders: load compiled.classes, link, detect corrupted bytecode, locate and initialize static variables and static code;
  • Runtime data: stack, heap, method variables, etc.
  • Execution engine: Executes loaded code and cleans up all generated garbage (GC);

When the program is run, Interpreter interprets the bytecode into machine code and then runs it, switching to the JIT compiler when repeated code is found. The JIT compiler compiles duplicate code into native machine code, which is run directly when the same method is called, thereby improving system performance.

JVMS are designed for unlimited power/storage devices, and Android devices are weak (power, memory size, storage, etc.).

Unable to use directly, Google designed its own Java virtual machine for the Android platform, Dalvik, to support the execution of Java applications that have been converted to.dex (Dalvik Executable) compression format.

Unlike JVM bytecode, which is stack-based, Dalvik is register-based (where variables are stored), which is more efficient and requires less space.

Java and. Kt code files are jointly compiled by the Java and Kotlin compilers into.class, which is then compiled into.dex files and packaged into.apk files.

When the APK is installed on the device, when the application icon is clicked, the system starts a new Dalvik process and loads the dex code that the application contains. At runtime, the dex code is compiled by Interpreter or JIT, and the application interface is visible:

In Dalvik, every run of the application requires a compilation operation, and this time is counted into the execution time of the program, so the startup speed of the program will be a little slow. Of course, there is also the advantage that the installation speed of the application is fast.

After Android 4.4.4, Google began to introduce alternatives to Dalvik — ART, from JIT(Just In Time) to AOT (Ahead-of-time), The application uses dex2OAT to compile dex into.oat binaries at the first installation.

When you click the app icon to start, ART directly loads the. Oat file and runs, which significantly improves the startup speed, avoids repeated compilation, reduces CPU usage frequency and power consumption. Of course, there are also some disadvantages: longer application installation time and larger storage space occupation.

In addition to application installation, dex2OAT compilation may be triggered by OTA upgrades, first/non-first system startup, and system idle, depending on the configuration of the corresponding system.

As mentioned above, the Android virtual machine uses a regist-based instruction set (Opcodes). The problem with this is that the new syntax features introduced in higher versions of Java cannot be used directly on it.

To make use of Java 8 features, Google uses Transformation to add desugaring, a one-step compilation process.

When using a higher-version JDK syntax that is not supported by the current Android version, it is converted at compile time to a lower-version JDK syntax that is supported by the current Android version.

The development of desugarization is as follows:

Now you have a basic understanding of the role of ProGuard, DX, D8, and R8 in the confusion process


Use ProGuard or R8?

A: If there is no history baggage, direct R8, after all, is compatible with most ProGuard rules, faster compile time, and more Kotlin friendly.

Let’s just describe them briefly:

  • ProGuard β†’ Free tool to compress, optimize, and obfuscate Java bytecode files, open source repository address: ProGuard
  • R8 β†’ Alternative to ProGuard, supports existing ProGuard rules, is faster and stronger, AGP 3.4.0 or higher, uses R8 by default to obviate the compiler.

If you do not want to use R8 and want to use ProGuard back (it is possible but not necessary), you can disable R8 by adding the following configuration to gradle.properties:

android.enableR8=false
android.enableR8.libraries=false
Copy the code

Error may be reported when compiling APK:

Add -ignorewarnings to the proguard-rules.pro file to resolve this.

In addition, building a project with ProGuard or R8 will output the following in build\outputs\mapping\release:

  • Mapping. TXT β†’ Conversion between original and confused class, method, field names;
  • Seeds. TXT β†’ no confusion between classes and members;
  • Usage. TXT β†’ APK remove code;
  • Resources. TXT β†’ Resource optimization record file, which resources refer to other resources, which resources are in use, which resources are removed;

R8 can add the following configuration output to the proguard-rules.pro file:

# Output mapping.txt file
-printmapping ./build/outputs/mapping/mapping.txt

# output the seeds.txt file
-printseeds ./build/outputs/mapping/seeds.txt

# output usage.txt file
-printusage ./build/outputs/mapping/usage.txt
Copy the code

What did D8 do?

Then follow the source code, kangkang D8 specific do what, in the last section “Complement Android skill tree — from the AGP build process to APK packaging process” we find out the APK packaging Task chain, packaging into Dex experience Task has three:

  • TransformClassesWithDexBuilderForDebug – class packaged into dex
  • Dex transformDexArchiveWithExternalLibsDexMergerForDebug – third library package
  • TransformDexArchiveWithDexMergerForDebug – packaged final dex

Three Task is eventually by DX or D8 dex, with the first Task: DexArchiveBuilderTransform. The transform ()

Here you get the list of files that need to be desweetened, and then you go down to the familiar: class in the process directory and class in the.jar.

The list of files to be desweetened is uploaded to the convertToDexArchive().

With launchProcessing () – > dexArchiveBuilder. Convert ()

An abstract class, with the specific implementation class D8DexArchiveBuilder. Convert (),

The D8Command class is the D8 command line configuration class, which translates the above configuration into D8 packaged commands:

Command line configuration parameters detailed can see the official document: D8, here do not go to plane D8 source code, d8 do know the role is: desugar + will. Class bytecode into dex.


What did R8 do now?

I don’t know if you have found D8 in R8’s bag.

Then the TaskManager. Java search D8, through a variety of jump, came to the source: createPostCompilationTasks (), you can see before creating the D8 related Transform also did some other operation

β‘  R8 – Before execution

As you can see from the comments, these Tasks are used to turn a.class into a dex file, with optional steps such as obfuscating, jacoco(code coverage tool), and creating a TransformManager instance to manage the various TransformManagers. Continue to:

This arouses my curiosity. Is it possible to create a desugar Task? Doesn’t desugar happen in D8? Follow the code:

DESUGAR is compatible with D8, which was introduced in AS 3.0, and now defaults to D8, so there is no way to create a DESUGAR Task. Continue to:

Access to external expansion, merger Java resource, interested in merging algorithm can point in MergeJavaResourcesTransform. The transform () look, here don’t speak ~

Keep going:


(2) R8-Java code compression

If you go further, you will come across the key word R8:

Here are three Maybe’s:

  • MaybeCreateJavaCodeShrinkerTransform – > Java code compression
  • MaybeCreateResourcesShrinkerTransform compression and resources
  • MaybeCreateDexSplitterTransform and dex segmentation

Let’s start with the first one:

Here to distinguish between PROGUARD and R8, create different obfuscated TransformTask, focus on createR8Transform() core code as follows:

}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
R8Transform transform =
        new R8Transform(
                variantScope,
                userMainDexListFiles,
                userMainDexListProguardRules,
                inputProguardMapping,
                variantScope.getOutputProguardMappingFile());

// Handles obfuscation rules. Callback is used to perform subsequent actions after obfuscation
return applyProguardRules(
        variantScope,
        inputProguardMapping,
        variantScope.getOutputProguardMappingFile(),
        testedVariantData,
        transform,
        callback);
Copy the code

Following applyProguardRules(), focus on the following core code (the previous one is related to testing) :

// This is a "normal" variant in an app/library.
applyProguardConfigForNonTest(transform, variantScope);
Copy the code

Follow this method:

Add other sources of obfuscation rules, such as the obfuscation files generated by AAPT, and if it is an AAR, just keep.

Follow r8transform.transform (), again parameter, and finally call the following method:

Finally, this method is located in r8tool. kt. The core code is as follows:

// Initialize an instance of r8CommandBuilder
valr8CommandBuilder = CompatProguardCommandBuilder(! useFullR8, D8DiagnosticsHandler(messageReceiver))// Then call a series of methods, such as obfuscate correlation
addMainDexRules()
setMainDexListConsumer()
addProguardConfigurationFiles()
addProguardConfiguration()
setProguardMapOutputPath()

// Configuration related: whether to disable zoom, tree shaking, desugar, and compile modes
setDisableMinification(toolConfig.disableMinification)
setDisableTreeShaking(toolConfig.disableTreeShaking)
setDisableDesugaring(toolConfig.disableDesugaring)
setMode(compilationMode)
setProgramConsumer(programConsumer)
...

/ / initialization r8ProgramResourceProvider instance, which is used to give the R8 provides all of the resources
val r8ProgramResourceProvider = R8ProgramResourceProvider()

// All parameters are passed

// Finally call r8.run ()
ClassFileProviderFactory(libraries).use { libClasspath ->
    r8CommandBuilder.addLibraryResourceProvider(libClasspath.orderedProvider)
    R8.run(r8CommandBuilder.build())
}
Copy the code

The specific logic, which can be traced back to r8.class β†’ run(), does these things:

  • Code deletion: Using syntax tree static analysis techniques, find and delete unused code, such as uninstantiated classes, etc.
  • Code optimization: optimize the runtime code, delete dead code, unused parameters, selective inlining, class merging, etc.
  • Code obfuscation: optimize the identifier name to reduce the amount of code, and determine whether to allow the modification of the identifier name in the obfuscation rule;
  • Line number remapping, etc.

Just 333 lines of processing code, logic complex, such as terror, interested in their own see, I am not chew…

β‘’ R8 – Resource compression

Then with the second maybeCreateResourcesShrinkerTransform () :

With the ShrinkResourcesTransform. The transform (),

Splitterrunnable.run () ΒΆ SplitterRunnable.run() ΒΆ

// β‘  Create a resource optimization record file
File reportFile = null;
if(params.mappingFile ! =null) {
    File logDir = params.mappingFile.getParentFile();
    if(logDir ! =null) {
        reportFile = new File(logDir, "resources.txt"); }}// β‘‘ Analyze resources and usage
analyzer.analyze();

// rewrite the.ap_ file (generated by AAPT2 above) and remove the resources that are not used.
analyzer.rewriteResourceZip(params.uncompressedResourceFile, params.compressedResourceFile);

// 3 Export statistics
Copy the code

Unzip the.ap_ file generated by AAPT and determine if it is an unused resource. If it is, remove it.


β‘£ R8 – Adjust D8 to remove Dex

Only if you use Multidex will you go here. Follow the code below:

With the DexSplitterTransform. The transform ()

With the DexSplitterTool. Builder

With the DexSplitter. The run ()

With the DexSplitterHelper. The run ()

So finally still use the D8 dozen Dex, finally finished the process of about ~


0x7. Custom confusion dictionary

When I decompile someone’s APP, I saw that the identifier was not ABcd, but Chinese and special characters. How did I do that? Create a file, such as dictionary, in the same directory as the app’s proguard-rules directory. For example:

+ - Γ—... Too long to omitCopy the code

Next, add the following configuration to proguard-rules:

-obfuscationdictionary ./dictionary
-classobfuscationdictionary ./dictionary
-packageobfuscationdictionary ./dictionary
Copy the code

Gradle Assemble assemble Release APK Kangkang:

23333, the effect is excellent, like to decompile other people’s APK classmates saw, won’t hit me!


0x8. Modularity confusion

Initiation of confusion is controlled by the APP module and is independent of submodules.

It is recommended to set common obfuscation rules in app module and exclusive obfuscation rules in submodule. Submodule can distinguish between project and AAR:

# Project type, BuildTypes {release {minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}} # AAR type android {defaultConfig {consumerProguardFiles 'lib-proguard-rules.txt'}... }Copy the code

Of course, if you want the obfuscation rules to be controlled by the app module, just delete the obfuscation when you remove the module


summary

Gan, look at the source code to spits blood, Gradle related stuff is really a bottomless pit, after learning one after another, there are also famous ASM bytecode pilings, various open source performance optimization/detection tools based on APK build process Task hooks, etc.

Don’t learn, learn not to move, rest, I am thinking of writing a dependency library automatic detection + obfuscating automatically generated plug-in will not be fun? Sure, just thinking about it.

However, the source code through the process to understand the bottom of the heart, and then look at others when these open source projects will not be the same as reading the book ~

References:

  • Confusing user manual for Android developers

  • Android CPU, Compilers, D8 & R8

  • About D8/R8: Desugaring, APK package volume optimization, etc

  • The principle analysis of Android compatibility with Java 8 syntax features

  • A library module can contain its own ProGuard profile

  • Reduce, obfuscate, and optimize applications

  • Android | code confuse what do?