wip
Some checks failed
/ test_checkout (push) Failing after 22s

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-08-29 00:24:31 +02:00
parent eb35d092ac
commit 067bedd9d1
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
6 changed files with 105 additions and 59 deletions

View file

@ -0,0 +1,13 @@
#import "../lib.typ": todo
== Introduction <sec:th-intro>
#todo[
Expliquer qu'a la lumiere de @sec:rasta et @sec:cl, le reverser a des pbs:
les outils plantent parfois, et a cause de l'obfuscation il a envie de pouvoir modifier le code
Mofigier ke cide sugnifie obtenir une nouvelle application dont on avait patché la partie qui pose pb.
Par example on voudait patcher les applis reflexifs (citer la concurence), les chargement dynamique, etc.
L'interet de patcher une app est qu'ensuite on peut donner cette APK a un outils d'analyse, cf @sec:rasta.
Dans ce chapitre, on va donc presenter...
]

View file

@ -1,4 +1,4 @@
#import "../lib.typ": todo, APK, DEX, JAR, OAT, eg, ART, paragraph, jm-note
#import "../lib.typ": todo, APK, DEX, JAR, OAT, eg, ART, paragraph, jm-note, jfl-note
/*
* Parler de dex lego et du papier qui encode les resultats d'anger en jimple
@ -12,17 +12,17 @@
#todo[This is a draft, clean this up]
#todo[Reflectif call? Reflection call?]
In this section, we will see how we can transform the application code to make dynamic codeloading and reflexif calls analysable by static analysis tools.
In this section, we will see how we can transform the application code to make dynamic codeloading and reflexive calls more analysable by static analysis tools.
=== Reflection <sec:th-trans-ref>
=== Transforming Reflection <sec:th-trans-ref>
In Android, reflection can be used to do two things: instanciate a class, or call a method.
Either way, reflection starts by retreiving the `Class` object representing the class to use.
This class is usually retrived using a `ClassLoader` object, but can also be retrieved directly from the classloader of the class defining the calling method.
Either way, reflection starts by retrieving the `Class` object representing the class to use.
This class is usually retrieved using a `ClassLoader` object, but can also be retrieved directly from the classloader of the class defining the calling method.
// elaborate? const-class dalvik instruction / MyClass.class in java?
One the class is retrieve, it can be instanciated using the deprecated method `Class.newInstance()`, like shown in @lst:-th-expl-cl-new-instance, or a specific method can be retrieved.
The current approche to instanciate a class is to retrieve the specific `Constructor` object, then calling `Constructor.newInstance(..)` like in @lst:-th-expl-cl-cnstr.
Similarly, to call a method, the `Method` object must be retrieved, then called using `Method.invoke(..)`, like shown in @lst:-th-expl-cl-call.
Once the class is retrieved, it can be instanciated using the deprecated method `Class.newInstance()`, as shown in @lst:-th-expl-cl-new-instance, or a specific method can be retrieved.
The current approach to instanciate a class is to retrieve the specific `Constructor` object, then calling `Constructor.newInstance(..)` like in @lst:-th-expl-cl-cnstr.
Similarly, to call a method, the `Method` object must be retrieved, then called using `Method.invoke(..)`, as shown in @lst:-th-expl-cl-call.
Although the process seems to differ between class instanciation and method call from the Java stand point, the runtime opperations are very similar.
When instanciating an object with `Object obj = cst.newInstance("Hello Void")`, the constructor method `<init>(Ljava/lang/String;)V`, represented by the `Constructor` `cst`, is called on the object `obj`.
@ -53,16 +53,18 @@ When instanciating an object with `Object obj = cst.newInstance("Hello Void")`,
caption: [Calling a method using reflection]
) <lst:-th-expl-cl-call>
To allow static analysis tools to analyse an application that use reflection, we want to replace the reflection call by the bytecode that does the actual calls.
To allow static analysis tools to analyse an application that use reflection, we want to replace the reflection call by the bytecode that actually call the method.
#jfl-note[
One of the main reason to use reflection is to access classes not from the application.
Although allows the use classes that do not exist in the application in bytecode, at runtime, if the classes are not found in the current classloader, the application will crash.
Similarly, some analysis tools might have trouble analysis application calling non existing classes.
@sec:th-trans-cl deals with the issue of adding dynamically loaded bytecode to the application.
][#underline[pas clair]]
A notable issue is that a specific reflection call can call different methods.
@lst:th-worst-case-ref illustrate a worst case scenario where any method can be call at the same reflection call.
In those situation, we cannot garanty that we know all the methods that can be called (#eg the name of the method called could be retrieved from a remote server).
@lst:th-worst-case-ref illustrates a worst case scenario where any method can be called at the same reflection call.
In those situation, #jfl-note[we cannot garanty that we know all the methods][expliquer (on va collecter les noms en best efforts?) Expliquer ce qu'on veut dire "acceder a une classe qui n'est pas dans l'APK, si l'appli crash a quoi ca sert?] that can be called (#eg the name of the method called could be retrieved from a remote server).
#figure(
```java
@ -74,9 +76,13 @@ In those situation, we cannot garanty that we know all the methods that can be c
) <lst:th-worst-case-ref>
To handle those situation, instead of entirely removing the reflection call, we can modify the application code to test if the `Method` (or `Constructor`) object match any expected method, and if yes, directly call the method.
If the object does not match any expected method, the code can fallback to the original reflection call.
@lst:-th-expl-cl-call-trans demonstrate this transformation on @lst:-th-expl-cl-call.
It should be noted that we do the transformation at the bytecode level, the code in the listing correspond to the output of JADX #todo[Ref to list of common tools?] reformated for readability.
If the object does not match any expected method, #jfl-note[the code can fallback to the original reflection call.][comme DroidRA? \ hheuuu, a verifier]
#jfl-note[@lst:-th-expl-cl-call-trans demonstrate this transformation on @lst:-th-expl-cl-call.][Expliquer @lst:-th-expl-cl-call-trans ligne importante]
#jfl-note[It should be noted that we do the transformation at the bytecode level, the code in the listing correspond to the output of JADX][
J'aurais bien fait une section a part sur "comment on fait ces transformation concretement;
plus pedagique de décrire les transformation sans bytecode, ensuite, sous section qui discute
les facon de modifier le bytecode, soot, apktool, ect et qui explique les limites, puis dire comment tu fait mes modifications
] #todo[Ref to list of common tools?] reformated for readability.
The method check is done in a separate method injected inside the application to avoid clutering the application too much.
Because Java (and thus Android) uses polymorphic methods, we cannot just check the method name and its class, but also the whole method signature.
We chose to limit the transformation to the specific instruction that call `Method.invoke(..)`.
@ -92,7 +98,7 @@ And finally, because the instruction following the reflection call expect an `Ob
This back and forth between types might confuse some analysis tools.
This could be improved in futur works by analysing the code around the reflection call.
For example, if the result of the reflection call is imediatly cast into the expected type (#eg in @lst:-th-expl-cl-call, the result is cast to a `String`), they should not be any need to cast it to Object in between.
For example, if the result of the reflection call is immediatly cast into the expected type (#eg in @lst:-th-expl-cl-call, the result is cast to a `String`), they should not be any need to cast it to Object in between.
Similarly, it is common to have the method parameter arrays generated just before the reflection call never be used again (This is due to `Method.invoke(..)` beeing a varargs method: the array can be generated by the compiler at compile time).
In those cases, the parameters could be used directly whithout the detour inside an array.
@ -128,19 +134,21 @@ In those cases, the parameters could be used directly whithout the detour inside
objRet = mth.invoke(obj, args);
}
String retData = (String) objRet;
```,
``` + todo[Ajouter lignes],
caption: [@lst:-th-expl-cl-call after the de-reflection transformation]
) <lst:-th-expl-cl-call-trans>
=== Code Loading <sec:th-trans-cl>
=== Transforming Code Loading <sec:th-trans-cl>
#jfl-note[Ici je pensais lire comment on tranforme le code qui load du code, mais on me parle de multi dex]
An application can dynamically import code from several format like #DEX, #APK, #JAR or #OAT, either stored in memory or in a file.
Because it is an internal, platform dependant format, we elected to ignore the #OAT format.
Practically, #JAR and #APK files are zip files containing #DEX files.
This means that we only need to find a way to integrate #DEX files to the application.
We elected to simply add the dex files to the application, using the multi-dex feature introduced by the SDK 21 now used by all applications as shown in @fig:th-inserting-dex.
We elected to simply add the dex files to the application, using the multi-dex feature introduced by the SDK 21 now used by all applications as shown in @fig:th-inserting-dex. #jfl-note[aleady discussed in @sec:cl]
This gives access to the dynamically loaded code to static analysis tool.
#figure(
@ -159,9 +167,9 @@ Specifically, to call dynamically loaded code, an application needs to use refle
=== Class Collisions <sec:th-class-collision>
We saw in @sec:cl-obfuscation that having several classes with the same name in the same application can be problematic.
In @sec:th-trans-cl, we are adding code from another source.
By doing so, we augment the probability of having class collisions.
We saw in @sec:cl/*-obfuscation*/ that having several classes with the same name in the same application can be problematic.
In @sec:th-trans-cl, we are adding new code.
#jfl-note[By doing so, we increase the probability of having class collisions.][Un mini exemple de collision serait utilse: on a du mal a comprendre d'ou vient la collision car c'est nous qui ajoutons des classes]
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.
We decided to restrain our scope to the use of class loader from the Android SDK.
In the abscence of class collision, those class loader behave seamlessly and adding the classes to application maintains the behavior.
@ -214,7 +222,7 @@ The pseudo-code in @lst:renaming-algo show the three steps of this algorithm:
=== Limitations
#paragraph()[Custom Classloaders][
The first obvious limitation is that we do not know what custom classloaders do, so we cannot accuratly emulate their behavior.
#jfl-note[The first obvious limitation is that we do not know what custom classloaders do, so we cannot accuratly reproduce statically their behavior.][est ce que c'est une limite des 2 transformations proposées? j'ai l'impression que tu veux faire une 3ieme transformation]
We elected to fallback to the behavior of the `BaseDexClassLoader`, which is the highest Android specific classloader in the inheritance hierarchy, and whose behavior is shared by all classloaders safe `DelegateLastClassLoader`.
The current implementation of the #ART enforce some restrictions on the classloaders behavior to optimize the runtime performance by caching classes.
This gives us some garanties that custom classesloaders will keep a some coherences will the classic classloaders.
@ -240,3 +248,5 @@ This would greatly increase the risk of breaking the application during its tran
Instead, we elected to ignore the classloaders when selecting the method to invoque.
This leads to potential invalid runtime behaviore, as the first method that matching the class name will be called, but the alternative methods from other classloader still appears in the new application, albeit in a block that might be flagged as dead-code by a sufficiently advenced static analyser.
]
#jfl-note[Deporter ces limites en fin de chapitre? je lirai tout ca + tard]

View file

@ -1,7 +1,17 @@
#import "../lib.typ": todo, SDK, API, ART, DEX, APK, JAR, ADB
#import "../lib.typ": todo, SDK, API, ART, DEX, APK, JAR, ADB, jfl-note
== Collecting Runtime Information <sec:th-dyn>
#jfl-note[Figure dcrivant le process a mettre en avant?]
/*
* APK -> DYN -> RUNTIME INFO
* APK -> TRANSFO
* RUNTIME INFO -> TRANSFO
* TRANSFO -> APK'
*/
In order to perform the transformations described in @sec:th-trans, we need information like the name and signature of the method called with reflection, or the actual bytecode loaded dynamically.
We are doing those transformation specifically because those information are difficult to extract statically.
Hence, we are using dynamic analysis to collect the runtime information we need.
@ -11,29 +21,33 @@ We use Frida(see @sec:bg-frida) to instrument the application and intercept call
Initially, we considered instrumenting the constructor methods of the classloaders of the Android #SDK.
However, this is a significant number of methods to instrument, and looking at older application, we realized that we missed the `DexFile` class.
`DexFile` is a now depreciated class but still usable class that can be used to load bytecode dynamically.
Instead of looking for all possible methods to load bytecode, we decided to look at the #ART source code an list all the places where the internal function used to parse bytecode is called.
`DexFile` is a now deprecated but still usable class that can be used to load bytecode dynamically.
We initially missed this class because it is neither a `ClassLoader` class nor an #SDK class (anymore).
To avoid running into this kind of oversight again, we decided to look at the #ART source code an list all the places where the internal function used to parse bytecode are called.
We found that all those calls are from under either `DexFile.openInMemoryDexFilesNative(..)` or `DexFile.openDexFileNative(..)`, two hidden #API methods.
As a reference, in 2015, DexHunter~@zhang2015dexhunter already noticed `DexFile.openDexFileNative(..)` (although in the end DexHunter intrument another function, `DefineClass(..)`).
`DefineClass(..)` is still a good function to instrument, but it is a C++ native method that does not have a Java interface, making it harder to work with using Frida, and we want to avoid patching the source code of the #ART like DexHunter did.
For this reason, we decided to hook `DexFile.openInMemoryDexFilesNative(..)` and `DexFile.openDexFileNative(..)` instead.
Those methods takes as argument a list of Androis code files, either in the form of in memory byte arrays or file path, and a reference to the classloader associated to the code.
The code files can have many format, usually #DEX files, or #APK / #JAR files containing #DEX files, but it can also be internal format like `.aot` #todo[check, aot explain somewhere?]. #todo[cf later to explain that only #DEX / #APK / #JAR are found?]
Instrumenting those methods allows us to collect all the #DEX files loaded by the #ART and associate them to their classloaders.
=== Collecting Reflection Data
Like describe in @sec:th-trans-ref, they are 3 methods that we need to instrument to capture reflection calls: `Class.newInstance()`, `Constructor.newInstance(..)` and `Method.invoke(..)`.
As described in @sec:th-trans-ref, they are 3 methods that we need to instrument to capture reflection calls: `Class.newInstance()`, `Constructor.newInstance(..)` and `Method.invoke(..)`.
Because Java has polymorphism, we need not only the method name and defining class, but also the whole signature of the method.
In addition to that, in case they are several classes with the same name as the defining class, we also need the classloader of the defining class to distinguish it from the other classes.
In addition to that, in case there are several classes with the same name as the defining class, we also need the classloader of the defining class to distinguish it from the other classes.
A more challenging information to collect is the from where the reflection method is called.
_Where_ the reflection method is called is more difficult to find.
In order to correctly modify the application, we need to know which specific call to a reflection method we intercepted.
Specifically, we need to known the caller method (once again, we need the method name, full signature, defining class and its classloader), and the speficic instruction that called the reflection method (in case the caller method call a reflection method several times).
This information is more difficult to collect than one would expect.
Those information are stored in the stack, but before the #SDK 34, the stack was not directly accessible programmatically.
Historically, when a reverse engineer needed to access the stack, they would trigger and catch an exception, get the stack from the exception.
The issue with this approche is that the data stored in the exception are meant for debbuging.
The issue with this approach is that the data stored in the exception are meant for debbuging.
In particullar, the location of the call in the bytecode has a different meaning depending on the debug information encoded in the bytecode.
It can either be the address of the bytecode instruction invoking the callee method in the instruction array of the caller method, or the line number of original source code that call the callee method.
In the #SDK 34, Android introduced the `StackWalker` #API.
Fortunatelly, in the #SDK 34, Android introduced the `StackWalker` #API.
This #API allow to programatically travel the current stack and retrieve informations from it, including the bytecode address of the instruction calling the callee methods.
Considering that the line number is not a reliable information, we chose to use the new #API, despite the restriction that come with chosing such a recent Android version (it was released in october 2023, arround 2 years ago, and less than 50% of the current Android market share support this #API today #todo[archive ref https://gs.statcounter.com/android-version-market-share]).
@ -42,28 +56,29 @@ Considering that the line number is not a reliable information, we chose to use
Dynamic analysis requires actually running the application.
In order to test automatically multiple applications, we needed to simulate human interractions with the applications.
We found a few tools available, #todo[ref les outils testé, peut etre mettre dans state of the art?].
After some tests, the most suitable one we found were the Monkey, a standard Android tool from Google that generate random event, and GroddDroid #todo[ref].
We choose to avoir the Monkey because we noticed that it will often trigger event that will close the application (events likes pressing the 'home' button, or openning the general setting drop-down menu at the top of the screen).
After some tests, the #jfl-note[most suitable][donne un peu des details des pbs des autres et que comme vu en @sec:bg les outils sont peu reutilisable] one we found were the Monkey, a standard Android tool from Google that generate random event, and GroddDroid #todo[ref].
We chose to avoid the Monkey because we noticed that it will often trigger event that will close the application (events likes pressing the 'home' button, or openning the general setting drop-down menu at the top of the screen).
GroddDroid different execution modes.
We choosed to use the most simple one, that explore the application following a depth-first search algorithm.
GroddDroid can do more advance explorations targetting suspicious section of the application en priority, but this require to perform heavy static analysis.
We elected to avoid this option to keep the exploiration lightwight and limit the chance to encontering a fatal issue.
We elected to avoid this option to keep the exploiration lightwight and limit the chance of crashing the analysis (we saw in @sec:rasta the issues brought by complexe analysis).
Behind the scene, GroddDroid uses UI Automator to interact with the application, an standar Android API used intended for automatic testing.
Because we a using Frida, we do not need to use a custom version of Android with a modified #ART or kernel like some dynamica analysis framework. #todo[references]
However, we decided to not inject Frida in the original application, so we need to have root access to directly run Frida in Android, wich is not a normal thing to have on Android.
Because we are using Frida, we do not need to use a custom version of Android with a modified #ART or kernel like DexHunter of AppSpear.
However, we decided to not inject Frida in the original application, so we need to have root access to directly run Frida in Android which is not a normal thing to have on Android.
Because dynamic analysis can be slow, we also decided to run the applications on emulators.
This makes its easier to run several analysis in parallel.
The alternative would have been to run the application on actual smartphones, and would have required multiple phones to run the analysis in parallel.
For simplicity, we choosed to use Google Android emulator for our experiment.
We spawned multiple emulators, installed Frida on it, took a snapshot of the emulator before installing the application to analyse.
Then we run the application for a minute #todo[check la valeur exacte] with GroddRunner, and at the end of the analysis, we reload the snapshot in case the application modified the system in some unforseen way.
If at some point the emulator start responding for too long, we force kill it and restart it.
If at some point the emulator start responding for too long, we terminate it and restart it.
#todo[Droid donjon, dire qu'on est au niveau -1 de l'anti-evation]
As we will see in @sec:th-res #todo[donner la bonne subsection], our experimental setup is quite naive and still requiere improvement. #todo[Comment on dit proprement que c'est tout pété?]
Nonetheless, our analysis tool itself only require a #ADB connection to a phone with a rooted Android system to work.
To analyse a specific application, using an actual smartphone and exploring the application manually is still possible and a lot more stable than our automated batch analysis setup.
As we will see in @sec:th-res #todo[donner la bonne subsection], our experimental setup is quite naive and still requiee improvement. #todo(strike(stroke: green)[Comment on dit proprement que c'est tout pété?])
Nonetheless, the benefit of our implementation is that it only requires a #ADB connection to a phone with a rooted Android system to work.
Of course, to analyse a specific application, a reverse engineer could use an actual smartphone and explore the application manually.
It wiykd be a lot more stable than our automated batch analysis setup.
#todo[Futur work: Droiddonjon like, GroddDroid improved exploration, potentiellement faire de l'execution forcé avec frida]

View file

@ -1,22 +1,23 @@
#import "../lib.typ": todo, SDK, num, mypercent, ART, ie
#import "X_var.typ": *
#import "../3_rasta/X_var.typ": NBTOTALSTRING
== Result <sec:th-res>
== Results <sec:th-res>
#todo[better section name for @sec:th-res]
To studdy the impact of our transformation on analysis tools, we took reused application from the dataset we sampled in @sec:rasta-dataset.
To studdy the impact of our transformation on analysis tools, we reused applications from the dataset we sampled in @sec:rasta/*-dataset*/.
Because we are running the application on a rescent version of Android (#SDK 34), we only took the most recent applications: the one collected in 2023.
This represent #num(5000) applications.
This represent #num(5000) applications over the #NBTOTALSTRING total of the initial dataset.
Among them, we could not retrieve 43 from Androzoo, leaving us with #num(dyn_res.all.nb) applications to test.
=== The Limits of Our Dynamic Analysis <sec:th-dyn-failure>
=== Dynamic Analysis Results <sec:th-dyn-failure>
After running the dynamic analysis on our dataset the first time we realised our dynamic setup was quite fragile.
We found that #mypercent(dyn_res.all.nb_failed_first_run, dyn_res.all.nb) of the execution failed with various errors.
The majority of those errors were related to faillures to connect to the Frida agent or start the activity from Frida.
Some of those errors seamed to come from Frida, while other seamed related to the emulator failing to start the application.
We found the simply relauching the analysis for the applications that failled was the most simple way to fix those issues, and after 6 passes we went from #num(dyn_res.all.nb_failed_first_run) to #num(dyn_res.all.nb_failed) application that could not be analysed.
We found that relaunching the analysis for the applications that failled was the most simple way to fix those issues, and after 6 passes we went from #num(dyn_res.all.nb_failed_first_run) to #num(dyn_res.all.nb_failed) application that could not be analysed.
The remaining errors look more related to the application itself or Android, with #num(96) errors being a failure to install the application, and #num(110) other beeing a null pointer exception from Frida.
Infortunatly, although we managed to start the applications, we can see from the list of activity visited by GroddDroid that a majority (#mypercent(dyn_res.all.z_act_visited, dyn_res.all.nb - dyn_res.all.nb_failed)) of the application stopped before even starting one activity.
@ -30,15 +31,16 @@ In other case, Frida is to blame: we found some cases where calling a method fro
@tab:th-dyn-visited shows the number of applications that we analysed, if we managed to start at least one activity and if we intercepted code loading or reflection.
As shown in the table, even if the application fails to start an activity, some times it will still load external code or use reflection.
#figure(
#figure({
let nb_col = 7
table(
columns: 6,
columns: nb_col,
stroke: none,
inset: 7pt,
align: center+horizon,
table.header(
table.hline(),
table.cell(colspan: 6, inset: 2pt)[],
table.cell(colspan: nb_col, inset: 2pt)[],
table.cell(rowspan: 2)[],
table.cell(rowspan: 2)[nb apk],
table.vline(end: 3),
@ -47,19 +49,22 @@ As shown in the table, even if the application fails to start an activity, some
table.vline(end: 3),
table.vline(start: 4),
table.cell(colspan: 2, inset: (bottom: 0pt))[activities visited],
table.vline(end: 3),
table.vline(start: 4),
table.cell(rowspan: 2)[average nb \ activities when > 0],
[1#super[st] pass], [6#super[th] pass],
[0], [$>= 1$],
),
table.cell(colspan: 6, inset: 2pt)[],
table.cell(colspan: nb_col, inset: 2pt)[],
table.hline(),
table.cell(colspan: 6, inset: 2pt)[],
[All], num(dyn_res.all.nb), num(dyn_res.all.nb_failed_first_run), num(dyn_res.all.nb_failed), num(dyn_res.all.z_act_visited), num(dyn_res.all.nz_act_visited),
[With Reflection], num(dyn_res.reflection.nb), [], [], num(dyn_res.reflection.z_act_visited), num(dyn_res.reflection.nz_act_visited),
[With Code Loading], num(dyn_res.code_loading.nb), [], [], num(dyn_res.code_loading.z_act_visited), num(dyn_res.code_loading.nz_act_visited),
table.cell(colspan: 3, inset: 2pt)[],
table.cell(colspan: nb_col, inset: 2pt)[],
[All], num(dyn_res.all.nb), num(dyn_res.all.nb_failed_first_run), num(dyn_res.all.nb_failed), num(dyn_res.all.z_act_visited), num(dyn_res.all.nz_act_visited), todo[],
[With Reflection], num(dyn_res.reflection.nb), [], [], num(dyn_res.reflection.z_act_visited), num(dyn_res.reflection.nz_act_visited), todo[],
[With Code Loading], num(dyn_res.code_loading.nb), [], [], num(dyn_res.code_loading.z_act_visited), num(dyn_res.code_loading.nz_act_visited), todo[],
table.cell(colspan: nb_col, inset: 2pt)[],
table.hline(),
),
)},
caption: [Summary of the dynamic exploration of the applications from the RASTA dataset collected by Androzoo in 2023]
) <tab:th-dyn-visited>
@ -72,7 +77,7 @@ This biaised must be kept in mind when reading the next subsection that studdy t
We collected a total of #nb_bytecode_collected files for #dyn_res.code_loading.nb application that we detected loading bytecode dynamicatlly.
#num(92) of them were loaded by a `DexClassLoader`, #num(547) were loaded by a `InMemoryDexClassLoader` and #num(1) was loaded by a `PathClassLoader`.
Interressingly, once we compared the files, we found that we only collected #num(bytecode_hashes.len()) distinct files, and that #num(bytecode_hashes.at(0).at(0)) where identicals.
Once we compared the files, we found that we only collected #num(bytecode_hashes.len()) distinct files, and that #num(bytecode_hashes.at(0).at(0)) where identicals.
Once we looked more in details, we found that most of those files are advertisement libraries.
In total, we collected #num(nb_google) files containing Google ads librairies and #num(nb_facebook) files containing Facebook ads librairies.
In addition, we found #num(nb_appsflyer) files containing code that we believe to be AppsFlyer, and company that provides "measurement, analytics, engagement, and fraud protection technologies".
@ -101,3 +106,5 @@ The remaining #num(nb_bytecode_collected - nb_google - nb_appsflyer - nb_faceboo
=== Impact on Analysis Tools
#todo[Check if flowdroid improve, compare sucess rate of RASTA, show result for demo app]
#jfl-note[Combien d'app tranforme? on parle des 888? on fait les 2 tranformation sur chaque apk? ca reussit tout le temps?]

View file

@ -11,7 +11,8 @@
#todo[better title for theseus chapter title for @sec:th]
#include("1_static_transformation.typ")
#include("2_dynamic_data_collection.typ")
#include("3_results.typ")
#include("4_ttv.typ")
#include("1_introduction.typ")
#include("2_static_transformation.typ")
#include("3_dynamic_data_collection.typ")
#include("4_results.typ")
#include("5_ttv.typ")