I declare this manuscript finished
All checks were successful
/ test_checkout (push) Successful in 1m48s

This commit is contained in:
Jean-Marie Mineau 2025-10-07 17:16:32 +02:00
parent 9f39ded209
commit 5c3a6955bd
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
14 changed files with 162 additions and 131 deletions

View file

@ -73,6 +73,15 @@ This behaviour implements a priority and avoids redefining by error a core class
This behaviour is useful for overriding specific classes of a class loader while keeping the other classes.
A normal class loader would prioritise the classes of its delegate over its own.
At runtime, Android instantiates for each application three instances of class loaders described previously: `bootClassLoader`, the unique instance of `BootClassLoader`, and two instances of `PathClassLoader`: `systemClassLoader` and `appClassLoader`.
`bootClassLoader` is responsible for loading Android *#platc*.
It is the direct delegate of the two other class loaders instantiated by Android.
`appClassLoader` points to the application `.apk` file, and is used to load the classes inside the application
`systemClassLoader` is a `PathClassLoader` pointing to `'.'`, the working directory of the application, which is `'/'` by default.
The documentation of `ClassLoader.getSystemClassLoader` reports that this class loader is the default context class loader for the main application thread.
In reality, the #platc are loaded by `bootClassLoader` and the classes from the application are loaded from `appClassLoader`.
`systemClassLoader` is never used in production according to our careful reading of the #AOSP sources.
#figure(
```python
def get_mutli_dex_classses_dex_name(index: int):
@ -98,15 +107,6 @@ A normal class loader would prioritise the classes of its delegate over its own.
caption: [Default Class Loading Algorithm for Android Applications],
) <lst:cl-loading-alg>
At runtime, Android instantiates for each application three instances of class loaders described previously: `bootClassLoader`, the unique instance of `BootClassLoader`, and two instances of `PathClassLoader`: `systemClassLoader` and `appClassLoader`.
`bootClassLoader` is responsible for loading Android *#platc*.
It is the direct delegate of the two other class loaders instantiated by Android.
`appClassLoader` points to the application `.apk` file, and is used to load the classes inside the application
`systemClassLoader` is a `PathClassLoader` pointing to `'.'`, the working directory of the application, which is `'/'` by default.
The documentation of `ClassLoader.getSystemClassLoader` reports that this class loader is the default context class loader for the main application thread.
In reality, the #platc are loaded by `bootClassLoader` and the classes from the application are loaded from `appClassLoader`.
`systemClassLoader` is never used in production according to our careful reading of the #AOSP sources.
In addition to the class loaders instantiated by ART when starting an application, the developer of an application can use class loaders explicitly by calling ones from the #Asdk, or by recoding custom class loaders that inherit from the `ClassLoader` class.
At this point, accurately modelling the complete class loading algorithm becomes impossible: the developer can program any algorithm of their choice.
For this reason, this case is excluded from this chapter, and we focus on the default behaviour where the context class loader is the one pointing to the `.apk` file and where its delegate is `BootClassLoader`.

View file

@ -107,14 +107,6 @@ We used 4 versions of this application:
Like for the third one, we similarly store data in `com.android.okhttp.Request` and then retrieve it.
Again, the shadowing implementation discards the data.
We used the 4 selected tools on the 4 versions of the application and compared the results on the control application to the results on the other application implementing the different obfuscation techniques.
We found that these static analysis tools do not consider the class loading mechanism, either because the tools only look at the content of the application file (#eg a disassembler) or because they consider class loading to be a dynamic feature and thus out of their scope.
In @tab:cl-results, we report on the types of shadowing that can trick each tool.
A plain circle is a shadow attack that leads to a wrong result.
A white circle indicates a tool emitting warnings or that displays the two versions of the class.
A cross is a tool not impacted by a shadow attack.
//We explain in more detail in the following the results for each considered tool.
#figure({
table(
columns: 5,
@ -147,6 +139,14 @@ A cross is a tool not impacted by a shadow attack.
caption: [Working attacks against static analysis tools]
) <tab:cl-results>
We used the 4 selected tools on the 4 versions of the application and compared the results on the control application to the results on the other application implementing the different obfuscation techniques.
We found that these static analysis tools do not consider the class loading mechanism, either because the tools only look at the content of the application file (#eg a disassembler) or because they consider class loading to be a dynamic feature and thus out of their scope.
In @tab:cl-results, we report on the types of shadowing that can trick each tool.
A plain circle is a shadow attack that leads to a wrong result.
A white circle indicates a tool emitting warnings or that displays the two versions of the class.
A cross is a tool not impacted by a shadow attack.
//We explain in more detail in the following the results for each considered tool.
==== Jadx
//Jadx is a reverse engineering tool that regenerates the Java source code of an application.

View file

@ -143,14 +143,6 @@ Manual inspection of some applications revealed that the two main reasons are:
- Instead of checking if the method's attributes are null inline, like Android does, applications use the method `org.apache.http.util.Args.notNull()`. According to comments in the source code of Android#footnote[https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/org/apache/http/params/HttpConnectionParams.java;drc=3bdd327f8532a79b83f575cc62e8eb09a1f93f3d?], the class was forked in 2007 from the Apache 'httpcomponents' project. Looking at the history of the project, the use of `Args.notNull()` was introduced in 2012#footnote[https://github.com/apache/httpcomponents-core/commit/9104a92ea79e338d876b1b60f5cd2b243ba7069f?]. This shows that applications are embedding code from more recent versions of this library without realising their version will not be the one used.
- Very small changes that we found can be attributed to the compilation process (e.g. swapping registers: `v0` is used instead of `v1` and `v1` instead of `v0`), but even if we consider them different, they are very similar.
The remaining 4.99% of classes that are identical to the Android version are classes where the body of the methods is replaced by stubs that throw `RuntimeException("Stub!")`.
This code corresponds to what we found in `android.jar`, but not the code we found in the emulator, which is surprising.
Nevertheless, we decided to count them as identical, because `android.jar` is the official jar file for developers, and stubs are replaced in the emulator: it is intended by Google developers.
Other results of @tab:cl-topsdk can be similarly discussed: either they are identical with a high ratio, or they are different because of small variations.
When substantial differences appear, it is mainly because different versions of the same library have been used or an #SDK class is embedded for retro-compatibility.
]
#figure({
show table: set text(size: 0.80em)
table(
@ -202,6 +194,14 @@ When substantial differences appear, it is mainly because different versions of
caption: [Shadow classes compared to #SDK 34 for a dataset of #nbapk applications]
) <tab:cl-topsdk>
The remaining 4.99% of classes that are identical to the Android version are classes where the body of the methods is replaced by stubs that throw `RuntimeException("Stub!")`.
This code corresponds to what we found in `android.jar`, but not the code we found in the emulator, which is surprising.
Nevertheless, we decided to count them as identical, because `android.jar` is the official jar file for developers, and stubs are replaced in the emulator: it is intended by Google developers.
Other results of @tab:cl-topsdk can be similarly discussed: either they are identical with a high ratio, or they are different because of small variations.
When substantial differences appear, it is mainly because different versions of the same library have been used or an #SDK class is embedded for retro-compatibility.
]
#paragraph([Hidden shadowing])[
For applications redefining hidden classes, on average, 16.1 classes are redefined (cf bottom part of @tab:cl-shadow).
The top 3 packages whose code actually differs from the ones found in Android are `java.util.stream`, `org.ccil.cowan.tagsoup` and `org.json`: