diff --git a/5_theseus/3_dynamic_data_collection.typ b/5_theseus/3_dynamic_data_collection.typ index 0888681..d41685d 100644 --- a/5_theseus/3_dynamic_data_collection.typ +++ b/5_theseus/3_dynamic_data_collection.typ @@ -110,11 +110,11 @@ We spawned multiple emulators, installed Frida on it, took a snapshot of the emu Then we run the application for a five minutes 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 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 requiee improvement. #todo(strike(stroke: green)[Comment on dit proprement que c'est tout pété?]) +As we will see in @sec:th-dyn-failure, our experimental setup is quite naive and still requires improvement. #todo(strike(stroke: green)[Comment on dit proprement que c'est tout pété?]) +For example, it does not implement any anti-evasion techniques, which can be a significant issue when analysing malware. 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. +It would 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] +#todo[Futur work: Droiddonjon like, GroddDroid (or other) improved exploration, potentiellement faire de l'execution forcé avec frida] diff --git a/5_theseus/4_results.typ b/5_theseus/4_results.typ index f0e2f3b..f767507 100644 --- a/5_theseus/4_results.typ +++ b/5_theseus/4_results.typ @@ -1,4 +1,4 @@ -#import "../lib.typ": todo, SDK, num, mypercent, ART, ie, jfl-note +#import "../lib.typ": todo, SDK, num, mypercent, ART, ie, APKs, jfl-note #import "X_var.typ": * #import "../3_rasta/X_var.typ": NBTOTALSTRING @@ -24,11 +24,13 @@ Infortunatly, although we managed to start the applications, we can see from the 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 the application might be trying to load a native library that simply do not exists in the application. +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. #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. #figure({ @@ -59,9 +61,9 @@ As shown in the table, even if the application fails to start an activity, some table.cell(colspan: nb_col, inset: 2pt)[], table.hline(), 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[], + [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), num(dyn_res.all.avg_nz_act), + [With Reflection], num(dyn_res.reflection.nb), [], [], num(dyn_res.reflection.z_act_visited), num(dyn_res.reflection.nz_act_visited), num(dyn_res.reflection.avg_nz_act), + [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), num(dyn_res.code_loading.avg_nz_act), table.cell(colspan: nb_col, inset: 2pt)[], table.hline(), )}, @@ -103,8 +105,99 @@ The remaining #num(nb_bytecode_collected - nb_google - nb_appsflyer - nb_faceboo caption: [Most common dynamically loaded files] ) -=== Impact on Analysis Tools +=== Impact on Analysis Tools Finishing Rate + +#todo[alt text @fig:th-status-npatched-vs-patched] +#todo[Check SAAF and IC3 results on patched] +#figure({ + image( + "figs/comparision-of-exit-status.svg", + width: 100%, + alt: "", + ) + place(center + horizon, rotate(24deg, text(red.transparentize(0%), size: 20pt, "PRELIMINARY RESULTS"))) + }, + caption: [Exist status of static analysis tools on original #APKs (left) and patched #APKs (right)] +) #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?] + +=== Example + +We use on our approach on a small #APK. +We handcrafted this application for the purpose of demonstrating how this can improve help a reverse engineer in its work. +Accordingly, this application is quite small and contains boff 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. + +#figure( + ```java +package com.example.theseus; + +public class Main { + private static final String DEX = "ZGV4CjA [...] EAAABEAwAA"; + Activity ac; + private Key key = new SecretKeySpec("_-_Secret Key_-_".getBytes(), "AES"); + ClassLoader cl = new InMemoryDexClassLoader(ByteBuffer.wrap(Base64.decode(DEX, 2)), Main.class.getClassLoader()); + + public void main() throws Exception { + String[] strArr = {"n6WGYJzjDrUvR9cYljlNlw==", "dapES0wl/iFIPuMnH3fh7g=="}; + Class loadClass = this.cl.loadClass(decrypt("W5f3xRf3wCSYcYG7ckYGR5xuuESDZ2NcDUzGxsq3sls=")); + Object obj = "imei"; + for (int i = 0; i < 2; i++) { + obj = loadClass.getMethod(decrypt(strArr[i]), String.class, Activity.class).invoke(null, obj, this.ac); + } + } + public String decrypt(String str) throws Exception { + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(2, this.key); + return new String(cipher.doFinal(Base64.decode(str, 2))); + } + + ... +} + ``` + caption: [Code of the main class of the application showed by Jadx, before patching], +) + +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()`. +@fig:th-demo-before shows the 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. +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. + +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. +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 @figth-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 confirm that `get_data()` calls `Utils.source()` and `send_data()` calls `Utils.sink()`. + +#figure( + ```java + public void main() throws Exception { + String[] strArr = {"n6WGYJzjDrUvR9cYljlNlw==", "dapES0wl/iFIPuMnH3fh7g=="}; + Class loadClass = this.cl.loadClass(decrypt("W5f3xRf3wCSYcYG7ckYGR5xuuESDZ2NcDUzGxsq3sls=")); + Object obj = "imei"; + for (int i = 0; i < 2; i++) { + Method method = loadClass.getMethod(decrypt(strArr[i]), String.class, Activity.class); + Object[] objArr = {obj, this.ac}; + obj = T.check_is_Malicious_get_data_fe2fa96eab371e46(method) ? + Malicious.get_data((String) objArr[0], (Activity) objArr[1]) : + T.check_is_Malicious_send_data_ca50fd7916476073(method) ? + Malicious.send_data((String) objArr[0], (Activity) objArr[1]) : + method.invoke(null, objArr); + } + } + ``` + caption: [Code of `Main.main()` showed by Jadx, after patching], +) + + +#todo[androgard call graph] diff --git a/5_theseus/X_var.typ b/5_theseus/X_var.typ index bf48fc4..840908f 100644 --- a/5_theseus/X_var.typ +++ b/5_theseus/X_var.typ @@ -5,18 +5,21 @@ nb: 4957, nb_failed_first_run: 2136, nb_failed: 209, - z_act_visited: 3860, - nz_act_visited: 888, + z_act_visited: 4025, + nz_act_visited: 723, + avg_nz_act: 1.3, ), reflection: ( nb: 3948, - z_act_visited: 3152, - nz_act_visited: 796, + z_act_visited: 3298, + nz_act_visited: 650, + avg_nz_act: 1.3, ), code_loading: ( nb: 598, - z_act_visited: 434, - nz_act_visited: 164, + z_act_visited: 453, + nz_act_visited: 145, + avg_nz_act: 1.2, ) ) diff --git a/5_theseus/figs/comparision-of-exit-status.svg b/5_theseus/figs/comparision-of-exit-status.svg new file mode 100644 index 0000000..6991b0d --- /dev/null +++ b/5_theseus/figs/comparision-of-exit-status.svg @@ -0,0 +1,2968 @@ + + + + + + + + 2025-09-06T21:39:01.904334 + image/svg+xml + + + Matplotlib v3.10.6, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +