This commit is contained in:
parent
039970904e
commit
10df431972
9 changed files with 240 additions and 250 deletions
|
@ -9,35 +9,35 @@
|
|||
|
||||
#todo[better section name for @sec:th-res]
|
||||
|
||||
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 over the #NBTOTALSTRING total of the initial dataset.
|
||||
To study 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 recent version of Android (#SDK 34), we only took the most recent applications: the one collected in 2023.
|
||||
This represents #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.
|
||||
|
||||
We will first look at the results of the dynamic analysis and look closer at the bytecode we intercepted.
|
||||
Then, we will studdy the impact the instrumentation have on static analysis tools, notably on their success rate, and we will finish with the analysis of an handcrafted application to check the instrumentation does in fact improve the results of analysis tools.
|
||||
We will first look at the results of the dynamic analysis and look at the bytecode we intercepted.
|
||||
Then, we will study the impact the instrumentation has on static analysis tools, notably on their success rate, and we will finish with the analysis of a handcrafted application to check whether the instrumentation does, in fact, improve the results of analysis tools.
|
||||
|
||||
=== 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 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.
|
||||
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 executions failed with various errors.
|
||||
The majority of those errors were related to failures to connect to the Frida agent or start the activity from Frida.
|
||||
Some of those errors seemed to come from Frida, while others seemed related to the emulator failing to start the application.
|
||||
We found that relaunching the analysis for the applications that failed was the simplest 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) applications 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) others being 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.
|
||||
Some applications do not have an activity, and are not intended to interact with a user, but those are clearly a minority and do not explain such a high number.
|
||||
We expected some issue related to the use of an emulator, like the lack of x86_64 library in the applications, or contermesures aborting the application if the emulator is detected.
|
||||
We manually looked at some applications, but did not found a notable pattern.
|
||||
In some cases, the application was just broken -- for instance, an application was trying to load a native library that simply does not exists in the application.
|
||||
In other case, Frida is to blame: we found some cases where calling a method from Frida can confuse the #ART.
|
||||
`protected` methods needs to be called from the class that defined the method or one of its children calsses, but Frida might be considered by the #ART as an other class, leading to the #ART aborting the application.
|
||||
Unfortunately, although we managed to start the applications, we can see from the list of activities visited by GroddDroid that a majority (#mypercent(dyn_res.all.z_act_visited, dyn_res.all.nb - dyn_res.all.nb_failed)) of the applications stopped before even starting one activity.
|
||||
Some applications do not have any activities and are not intended to interact with a user, but those are clearly a minority and do not explain such a high number.
|
||||
We expected some issues related to the use of an emulator, like the lack of x86_64 library in the applications, or contermesures aborting the application if the emulator is detected.
|
||||
We manually looked at some applications, but did not find a notable pattern.
|
||||
In some cases, the application was just broken -- for instance, an application was trying to load a native library that simply does not exist in the application.
|
||||
In other cases, Frida is to blame: we found some cases where calling a method from Frida can confuse the #ART.
|
||||
`protected` methods need to be called from the class that defined the method or one of its child classes, but Frida might be considered by the #ART as another class, leading to the #ART aborting the application.
|
||||
#todo[jfl was suppose to test a few other app #emoji.eyes]
|
||||
@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.
|
||||
It also shows the average number of activities visited (when at least one activity was started).
|
||||
This average slightly higher than 1, which seems reasonable: a lot of applications do not need more than one activity, but some do and we did manage to explore at least some of those additionnal activities.
|
||||
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.
|
||||
This average is slightly higher than 1, which seems reasonable: a lot of applications do not need more than one activity, but some do, and we did manage to explore at least some of those additional activities.
|
||||
As shown in the table, even if the application fails to start an activity, sometimes it will still load external code or use reflection.
|
||||
|
||||
#figure({
|
||||
let nb_col = 7
|
||||
|
@ -76,21 +76,21 @@ As shown in the table, even if the application fails to start an activity, some
|
|||
caption: [Summary of the dynamic exploration of the applications from the RASTA dataset collected by Androzoo in 2023]
|
||||
) <tab:th-dyn-visited>
|
||||
|
||||
The high number of application that did not start an activity means that our result will be highly biaised.
|
||||
The code that might be loaded or method that might be called by reflection from inside activities is filtered out by the limit of or dynamic execution.
|
||||
This biaised must be kept in mind when reading the next subsection that studdy the bytecode that we intercepted.
|
||||
The high number of applications that did not start an activity means that our results will be highly biased.
|
||||
The code/method that might be loaded/called by reflection from inside activities is filtered out by the limit of or dynamic execution.
|
||||
This bias must be kept in mind while reading the next subsection that studies the bytecode that we intercepted.
|
||||
|
||||
=== The Bytecode Loaded by Application <sec:th-code-collected>
|
||||
|
||||
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`.
|
||||
We collected a total of #nb_bytecode_collected files for #dyn_res.code_loading.nb application that we detected loading bytecode dynamically.
|
||||
#num(92) of them were loaded by a `DexClassLoader`, #num(547) were loaded by a `InMemoryDexClassLoader`, and #num(1) was loaded by a `PathClassLoader`.
|
||||
|
||||
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".
|
||||
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)) were identical.
|
||||
Once we looked more in detail, we found that most of those files are advertisement libraries.
|
||||
In total, we collected #num(nb_google) files containing Google ads libraries and #num(nb_facebook) files containing Facebook ads libraries.
|
||||
In addition, we found #num(nb_appsflyer) files containing code that we believe to be AppsFlyer, a company that provides "measurement, analytics, engagement, and fraud protection technologies".
|
||||
The remaining #num(nb_bytecode_collected - nb_google - nb_appsflyer - nb_facebook) files were custom code from high security applications (#ie banking, social security)
|
||||
@tab:th-bytecode-hashes sumarize the information we collected about the most common bytecode files.
|
||||
@tab:th-bytecode-hashes summarises the information we collected about the most common bytecode files.
|
||||
|
||||
#figure(
|
||||
table(
|
||||
|
@ -115,18 +115,18 @@ The remaining #num(nb_bytecode_collected - nb_google - nb_appsflyer - nb_faceboo
|
|||
|
||||
We took the applications associated with the #num(nb_bytecode_collected - nb_google - nb_appsflyer - nb_facebook) unique #DEX files we found to see the impact of our transformation.
|
||||
|
||||
The applications where indeed obfuscated, making a manual analysis tedious.
|
||||
We did not found visible #DEX files or #APK files inside the applications, meaning the applications are either downloading or generating them from variables and assets at runtime.
|
||||
The applications were indeed obfuscated, making a manual analysis tedious.
|
||||
We did not find visible #DEX files or #APK files inside the applications, meaning the applications are either downloading or generating them from variables and assets at runtime.
|
||||
To estimate the scope of the code we made available, we use Androguard to generate the call graph of the applications, before and after the instrumentation.
|
||||
@tab:th-compare-cg shows the number of edges of those call graphs.
|
||||
The columns before and after shows the total number of edges of the graphs, and the diff columns is the number of new edges detected (#ie the number of edges after instrumentation minus the number of edges before).
|
||||
The columns before and after show the total number of edges of the graphs, and the diff column indicates the number of new edges detected (#ie the number of edges after instrumentation minus the number of edges before).
|
||||
This number include edges from the bytecode loaded dynamically, as well as the call added to reflect reflection calls, and calls to "glue" methods (method like `Integer.intValue()` used to convert objects to scalar values, or calls to `T.check_is_Xxx_xxx(Method)` used to check if a `Method` object represent a known method).
|
||||
The last column, "Added Reflection", is the list of non-glue method calls found in the call graph of the instrumented application but neither in call graph of the original #APK, nor in the call graphes of the added bytecode files that we computed separately.
|
||||
This correspond to the calls we added to represent reflection calls.
|
||||
This corresponds to the calls we added to represent reflection calls.
|
||||
|
||||
The first application, #lower(compared_callgraph.at(0).sha256), is noticable.
|
||||
The instrumented #APK has ten times more edges to its call graph than the original, and only one reflection call.
|
||||
This is consistant with the behaviour of a packer: the application load the main of its code at runtime and switch from the bootstrap code to the loaded code with a single reflection call.
|
||||
This is consistent with the behaviour of a packer: the application loads the main part of its code at runtime and switches from the bootstrap code to the loaded code with a single reflection call.
|
||||
|
||||
#figure({
|
||||
let nb_col = 5
|
||||
|
@ -152,27 +152,27 @@ This is consistant with the behaviour of a packer: the application load the main
|
|||
[#lower("5D2CD1D10ABE9B1E8D93C4C339A6B4E3D75895DE1FC49E248248B5F0B05EF1CE").slice(0, 10)...], table.cell(colspan: nb_col - 1)[_Instrumentation Crashed_],
|
||||
table.hline(),
|
||||
)},
|
||||
caption: [Edges added to the call graphes computed by Androguard by instrumenting the applications]
|
||||
caption: [Edges added to the call graphs computed by Androguard by instrumenting the applications]
|
||||
) <tab:th-compare-cg>
|
||||
|
||||
Unfortunately, our implementation of the transformation is imperfect and does fails some time, as illustrated by #lower("5D2CD1D10ABE9B1E8D93C4C339A6B4E3D75895DE1FC49E248248B5F0B05EF1CE") in @tab:th-compare-cg.
|
||||
Unfortunately, our implementation of the transformation is imperfect and does fails sometime, as illustrated by #lower("5D2CD1D10ABE9B1E8D93C4C339A6B4E3D75895DE1FC49E248248B5F0B05EF1CE") in @tab:th-compare-cg.
|
||||
However, over the #num(dyn_res.all.nb - dyn_res.all.nb_failed) applications whose dynamic analysis finished in our experiment, #num(nb_patched) were patched.
|
||||
The remaining #mypercent(dyn_res.all.nb - dyn_res.all.nb_failed - nb_patched, dyn_res.all.nb - dyn_res.all.nb_failed) failed either due to some quirk in the zip format of the #APK file, because of a bug in our implementation when exceeding the method reference limit in a single #DEX file, or in the case of #lower("5D2CD1D10ABE9B1E8D93C4C339A6B4E3D75895DE1FC49E248248B5F0B05EF1CE"), because the application reused the original application classloader to load new code instead of instanciated a new classes loader (a behavior we did not expected as not possible using only the #SDK, but enabled by hidden #APIs).
|
||||
Taking into accound the failure from both dynamic analysis and the instrumentation process, we have a #mypercent(dyn_res.all.nb - nb_patched, dyn_res.all.nb) failure rate.
|
||||
Taking into account the failure from both dynamic analysis and the instrumentation process, we have a #mypercent(dyn_res.all.nb - nb_patched, dyn_res.all.nb) failure rate.
|
||||
This is a reasonable failure rate, but we should keep in mind that it adds up to the failure rate of the other tools we want to use on the patched application.
|
||||
|
||||
To check the impact on the finishing rate of or instrumentation, we then run the same experiment we run in @sec:rasta.
|
||||
We run the tools on the #APK before and after instrumentation, and compared the finishing rates in @fig:th-status-npatched-vs-patched (without taking into account #APKs we failed to patch#footnote[Due to an handling error during the experiment, the figure show the results for #nb_patched_rasta #APKs instead of #nb_patched.]).
|
||||
To check the impact on the finishing rate of our instrumentation, we then run the same experiment we ran in @sec:rasta.
|
||||
We run the tools on the #APK before and after instrumentation, and compared the finishing rates in @fig:th-status-npatched-vs-patched (without taking into account #APKs we failed to patch#footnote[Due to a handling error during the experiment, the figure shows the results for #nb_patched_rasta #APKs instead of #nb_patched.]).
|
||||
|
||||
The finishing rate comparision is shown in @fig:th-status-npatched-vs-patched.
|
||||
We can see that in most cases, the finishing rate either the same, or slightly lower for the instrumented application.
|
||||
This is consistent with the fact that we add more bytecode to the application, hence adding more oportunities of failure during analysis.
|
||||
The finishing rate comparison is shown in @fig:th-status-npatched-vs-patched.
|
||||
We can see that in most cases, the finishing rate is either the same or slightly lower for the instrumented application.
|
||||
This is consistent with the fact that we add more bytecode to the application, hence adding more opportunities for failure during analysis.
|
||||
They are two notable exceptions: Saaf and IC3.
|
||||
The finishing rate of IC3 which was previously reasibabe drop to 0 after our instrumentation, while the finishing rate of Saaf jump to 100%, which is extremely suspicious.
|
||||
Analysing the logs of the analysis showed that both cases have the same origin: the bytecode generated by our instrumentation has version number of 37 (the version introduced by Android 7.0).
|
||||
Infortunately, neither the version of Apktool used by Saaf nor Dare (the tool used by IC3 to convert Dalvik bytecode to Java bytecode) recognize this version of bytecode, and thus failed to parse the #APK.
|
||||
In the case of Dare and IC3, our experiment correctly identify this a crash.
|
||||
On the other hand, Saaf do not detect the issue with Apktool and pursue the analysis with no bytecode to analyse and return a valid return file, but for an empty application.
|
||||
The finishing rate of IC3, which was previously reasonable, dropped to 0 after our instrumentation, while the finishing rate of Saaf jumped to 100%, which is extremely suspicious.
|
||||
Analysing the logs of the analysis showed that both cases have the same origin: the bytecode generated by our instrumentation has a version number of 37 (the version introduced by Android 7.0).
|
||||
Unfortunately, neither the version of Apktool used by Saaf nor Dare (the tool used by IC3 to convert Dalvik bytecode to Java bytecode) recognises this version of bytecode, and thus failed to parse the #APK.
|
||||
In the case of Dare and IC3, our experiment correctly identifies this as a crash.
|
||||
On the other hand, Saaf do not detect the issue with Apktool and pursues the analysis with no bytecode to analyse and returns a valid return file, but for an empty application.
|
||||
|
||||
#todo[alt text @fig:th-status-npatched-vs-patched]
|
||||
#figure({
|
||||
|
@ -190,11 +190,11 @@ On the other hand, Saaf do not detect the issue with Apktool and pursue the anal
|
|||
|
||||
=== Example
|
||||
|
||||
In this subsection, we use on our approach on a small #APK to look in more details into analysis of the transformed application.
|
||||
We handcrafted this application for the purpose of demonstrating how this can improve help a reverse engineer in its work.
|
||||
In this subsection, we use our approach on a small #APK to look in more detail into the analysis of the transformed application.
|
||||
We handcrafted this application for the purpose of demonstrating how this can help a reverse engineer in their work.
|
||||
Accordingly, this application is quite small and contains both dynamic code loading and reflection.
|
||||
We defined a method `Utils.source()` and `Utils.sink()` to model respectively a method that collect sensitive data and that exfiltrate data.
|
||||
Those methods are the one we will use with Flowdroid to track data flows.
|
||||
We defined a method `Utils.source()` and `Utils.sink()` to model a method that collects sensitive data and a method that exfiltrates data.
|
||||
Those methods are the ones we will use with Flowdroid to track data flows.
|
||||
|
||||
#figure(
|
||||
```java
|
||||
|
@ -223,25 +223,25 @@ public class Main {
|
|||
...
|
||||
}
|
||||
```,
|
||||
caption: [Code of the main class of the application showed by Jadx, before patching],
|
||||
caption: [Code of the main class of the application, as shown by Jadx, before patching],
|
||||
)<lst:th-demo-before>
|
||||
|
||||
A first analysis of the contant of the application shows that the application contains one `Activity` that instanciate the class `Main` and call `Main.main()`.
|
||||
@lst:th-demo-before shows the most of the code of `Main` as returned by Jadx.
|
||||
A first analysis of the content of the application shows that the application contains one `Activity` that instantiates the class `Main` and calls `Main.main()`.
|
||||
@lst:th-demo-before shows most of the code of `Main` as returned by Jadx.
|
||||
We can see that the class contains another #DEX file encoded in base 64 and loaded in the `InMemoryDexClassLoader` `cl`.
|
||||
A class is then loaded from this class loader, and two methods from this class loader are called.
|
||||
The names of this class and methods are not directly accessible as they have been chipĥered and are decoded just before beeing used at runtime.
|
||||
Here, the encryption key is available statically, and in theorie, a verry good static analyser implementing Android `Cipher` #API could compute the actual methods called.
|
||||
The names of this class and methods are not directly accessible as they have been chipĥered and are decoded just before being used at runtime.
|
||||
Here, the encryption key is available statically, and in theory, a very good static analyser implementing Android `Cipher` #API could compute the actual methods called.
|
||||
However, we could easily imagine an application that gets this key from a remote command and control server.
|
||||
In this case, it would be impossible to compute those methods with static analysis alone.
|
||||
When running Flowdroid on this application, it computed a callgraph of 43 edges on this application, an no data leaks.
|
||||
This is not particularly surprising considering the obfusctation methods used.
|
||||
When running Flowdroid on this application, it computed a call graph of 43 edges on this application, and no data leaks.
|
||||
This is not particularly surprising considering the obfuscation methods used.
|
||||
|
||||
Then we run the dynamic analysis we described in @sec:th-dyn on the application and apply the transformation described in @sec:th-trans to add the dynamic informations to it.
|
||||
This time, Flowdroid compute a larger callgraph of 76 edges, and does find a data leak.
|
||||
Then we run the dynamic analysis we described in @sec:th-dyn on the application and apply the transformation described in @sec:th-trans to add the dynamic information to it.
|
||||
This time, Flowdroid computes a larger call graph of 76 edges, and does find a data leak.
|
||||
Indeed, when looking at the new application with Jadx, we notice a new class `Malicious`, and the code of `Main.main()` is now as shown in @lst:th-demo-after:
|
||||
the method called in the loop is either `Malicious.get_data`, `Malicious.send_data()` or `Method.invoke()`.
|
||||
Although self explanatory, verifying the code of those methods indeed confirms that `get_data()` calls `Utils.source()` and `send_data()` calls `Utils.sink()`.
|
||||
Although self-explanatory, verifying the code of those methods indeed confirms that `get_data()` calls `Utils.source()` and `send_data()` calls `Utils.sink()`.
|
||||
|
||||
#figure(
|
||||
```java
|
||||
|
@ -260,14 +260,14 @@ Although self explanatory, verifying the code of those methods indeed confirms t
|
|||
}
|
||||
}
|
||||
```,
|
||||
caption: [Code of `Main.main()` showed by Jadx, after patching],
|
||||
caption: [Code of `Main.main()`, as shown by Jadx, after patching],
|
||||
)<lst:th-demo-after>
|
||||
|
||||
For an higher level view of the method, we can also look at its the call graph.
|
||||
We used Androguard to generate the call graphes in @fig:th-cg-before and @fig:th-cg-after#footnote[We manually edited the generated .dot files for readability.].
|
||||
@fig:th-cg-before show the original call graph, and gives a good idea of the obfuscation methods used: we can see calls to `Main.decrypt(String)` that it self calls cryptographic #APIs, as well as calls to `ClassLoader.loadClass(String)`, `Class.getMethod(String, Class[])` and `Method.invoke(Object, Object[])`.
|
||||
This indicate relflection calls base on ciphered strings, but does not reveal what the method actually does.
|
||||
In comparison, @fig:th-cg-after, the call graph after instrumentation, still shows the cryptographic and reflection calls, be also four new methods calls.
|
||||
For a higher-level view of the method, we can also look at its call graph.
|
||||
We used Androguard to generate the call graphs in @fig:th-cg-before and @fig:th-cg-after#footnote[We manually edited the generated .dot files for readability.].
|
||||
@fig:th-cg-before shows the original call graph, and gives a good idea of the obfuscation methods used: we can see calls to `Main.decrypt(String)` that itself calls cryptographic #APIs, as well as calls to `ClassLoader.loadClass(String)`, `Class.getMethod(String, Class[])` and `Method.invoke(Object, Object[])`.
|
||||
This indicates reflection calls based on ciphered strings, but does not reveal what the method actually does.
|
||||
In comparison, @fig:th-cg-after, the call graph after instrumentation, still shows the cryptographic and reflection calls, as well as four new method calls.
|
||||
In grey on the figure, we can see the glue methods (`T.check_is_Xxx_xxx(Method)`).
|
||||
Those methods are part of the instrumentation process presented in @sec:th-trans, but do not bring a lot to the analysis of the call graph.
|
||||
In red on the figure however, we have the calls that were hidded by reflection in the first call graph, and thank to the bytecode of the methods called being injected in the application, we can also see that they call `Utils.source(String)` and `Utils.sink(String)`, the methods we defined for this application as source of confidential data and exfiltration method.
|
||||
|
@ -281,7 +281,7 @@ In red on the figure however, we have the calls that were hidded by reflection i
|
|||
"",
|
||||
).join(),
|
||||
),
|
||||
caption: [Call Graph of `Main.main()` view by Androguard before patching],
|
||||
caption: [Call Graph of `Main.main()` generated by Androguard before patching],
|
||||
) <fig:th-cg-before>
|
||||
|
||||
#figure(
|
||||
|
@ -292,11 +292,11 @@ In red on the figure however, we have the calls that were hidded by reflection i
|
|||
"",
|
||||
).join(),
|
||||
),
|
||||
caption: [Call Graph of `Main.main()` view by Androguard after patching],
|
||||
caption: [Call Graph of `Main.main()` generated by Androguard after patching],
|
||||
) <fig:th-cg-after>
|
||||
|
||||
#v(2em)
|
||||
|
||||
To conclude, we showed that our approach indeed improves the results of analysis tools without impacting too much their finishing rate.
|
||||
Infortunately, we also noticed that our dynamic analysis is suboptimal, either due to our experimental setup or due to our solution to explore the applications.
|
||||
In the next section, we will present in more detail the limitation of our solution, as well as futur work that can be done to improve the contributions presented in this chapter.
|
||||
To conclude, we showed that our approach indeed improves the results of analysis tools without impacting their finishing rates too much.
|
||||
Unfortunately, we also noticed that our dynamic analysis is suboptimal, either due to our experimental setup or due to our solution to explore the applications.
|
||||
In the next section, we will present in more detail the limitations of our solution, as well as future work that can be done to improve the contributions presented in this chapter.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue