normalization and move french to the end
All checks were successful
/ test_checkout (push) Successful in 1m51s

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-09-29 20:15:36 +02:00
parent 4e38131df5
commit c3a27f8711
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
7 changed files with 57 additions and 53 deletions

View file

@ -45,7 +45,7 @@ When instantiating an object with `Object obj = cst.newInstance("Hello Void")`,
) <lst:-th-expl-cl-call>
One of the main reasons to use reflection is to access classes that are not present in the application bytecode, nor are platform classes.
Indeed, the application will crash if the #ART encounters references to a class that cannot be found by the current classloader.
Indeed, the application will crash if the #ART encounters references to a class that cannot be found by the current class loader.
This is often the case when dealing with classes from bytecode loaded dynamically.
To allow static analysis tools to analyse an application that uses reflection, we want to replace the reflection call with the bytecode that actually calls the method.
@ -149,9 +149,9 @@ This means that we only need to find a way to integrate #DEX files into the appl
We saw in @sec:cl the class loading model of Android.
When doing dynamic code loading, an application defines a new `ClassLoader` that handles the new bytecode, and starts accessing its classes using reflection.
We also saw in @sec:cl that Android now use the multi-dex format, allowing it to handle any number of #DEX files in one classloader.
We also saw in @sec:cl that Android now use the multi-dex format, allowing it to handle any number of #DEX files in one class loader.
Therefore, the simpler way to give access to the dynamically loaded code to static analysis tools is to add the dex files to the application.
This should not impact the classloading model as long as there is no class collision (we will explore this in @sec:th-class-collision) and as long as the original application did not try to access inaccessible classes (we will develop this issue in @sec:th-limits).
This should not impact the class loading model as long as there is no class collision (we will explore this in @sec:th-class-collision) and as long as the original application did not try to access inaccessible classes (we will develop this issue in @sec:th-limits).
#figure(
image(
@ -166,7 +166,7 @@ In the end, we decided to *not* modify the original code that loads the bytecode
We already added the bytecode loaded dynamically, and most tools already ignore dynamic code loading.
At runtime, although the bytecode is already present in the application, the application will still dynamically load the code.
This ensures that the application keeps working as intended, even if the transformation we applied is incomplete.
Specifically, to call dynamically loaded code, an application needs to use reflection, and we saw in @sec:th-trans-ref that we need to keep reflection calls, and in order to keep reflection calls, we need the classloader created when loading bytecode.
Specifically, to call dynamically loaded code, an application needs to use reflection, and we saw in @sec:th-trans-ref that we need to keep reflection calls, and in order to keep reflection calls, we need the class loader created when loading bytecode.
=== Class Collisions <sec:th-class-collision>
@ -174,17 +174,17 @@ We saw in @sec:cl/*-obfuscation*/ that having several classes with the same name
In @sec:th-trans-cl, we are adding new code.
By doing so, we increase the probability of having class collisions:
The developer may have reused a helper class in both the dynamically loaded bytecode and the application, or an obfuscation process may have renamed classes without checking for intersection between the two sources of bytecode.
When loaded dynamically, the classes are in a different classloader, and the class resolution is resolved at runtime, like we saw in @sec:cl-loading.
When loaded dynamically, the classes are in a different class loader, and the class resolution is resolved at runtime, like we saw in @sec:cl-loading.
We decided to restrain our scope to the use of class loaders from the Android #SDK.
In the absence of class collision, those class loaders behave seamlessly and adding the classes to the application maintains the behaviour.
When we detect a collision, we rename one of the colliding classes in order to be able to differentiate between classes.
To avoid breaking the application, we then need to rename all references to this specific class and be careful not to modify references to the other class.
To do so, we regroup each class by the classloaders defining them.
Then, for each colliding class name and each classloader, we check the actual class used by the classloader.
If the class has been renamed, we rename all references to this class in the classes defined by this classloader.
To find the class used by a classloader, we reproduce the behaviour of the different classloaders of the Android #SDK.
This is an important step: remember that the delegation process can lead to situations where the class defined by a classloader is not the class that will be loaded when querying the classloader.
To do so, we regroup each class by the class loaders defining them.
Then, for each colliding class name and each class loader, we check the actual class used by the class loader.
If the class has been renamed, we rename all references to this class in the classes defined by this class loader.
To find the class used by a class loader, we reproduce the behaviour of the different class loaders of the Android #SDK.
This is an important step: remember that the delegation process can lead to situations where the class defined by a class loader is not the class that will be loaded when querying the class loader.
The pseudo-code in @lst:renaming-algo shows the three steps of this algorithm:
- First, we detect collisions and rename class definitions to remove the collisions.
- Then we rename the reference to the colliding classes to make sure the right classes are called.
@ -208,7 +208,7 @@ The pseudo-code in @lst:renaming-algo shows the three steps of this algorithm:
defining_cl = cl.resolve_class(clz).class_loader
cl.rename_reference(clz, defining_cl.new_name(clz))
# Merge the classloader into a flat APK
# Merge the class loader into a flat APK
new_apk = Apk()
for cl in class_loaders:
for dex in cl.get_dex():