|
@ -1,12 +1,15 @@
|
|||
#import "../lib.typ": todo
|
||||
#import "../lib.typ": todo, epigraph
|
||||
|
||||
= Résumé en Français
|
||||
|
||||
#epigraph("Ellana Caldin, Le Pacte des Marchombres, Tome 1: Ellana, de Pierre Bottero")[Il y a deux réponses à cette question, comme à toutes les questions : celle du savant et celle du poète.]
|
||||
|
||||
|
||||
#todo[
|
||||
Write a "Substantial Summary" in french, at least 4 pages: https://ed-matisse.doctorat-bretagne.fr/fr/soutenance-de-these#p-151
|
||||
]
|
||||
|
||||
#lorem(2000)
|
||||
#lorem(200)
|
||||
|
||||
/*
|
||||
* Vocabulaire:
|
||||
|
|
6
1_introduction/main.typ
Normal file
|
@ -0,0 +1,6 @@
|
|||
#import "../lib.typ": todo
|
||||
|
||||
= Introduction <sec:intro>
|
||||
|
||||
#todo[Write an introduction]
|
||||
|
7
2_background/main.typ
Normal file
|
@ -0,0 +1,7 @@
|
|||
#import "../lib.typ": todo
|
||||
|
||||
= Background <sec:bg>
|
||||
|
||||
#todo[Present your field background]
|
||||
|
||||
#lorem(200)
|
7
3_related_work/main.typ
Normal file
|
@ -0,0 +1,7 @@
|
|||
#import "../lib.typ": todo
|
||||
|
||||
= Related Work
|
||||
|
||||
#todo[Do the State of the Art]
|
||||
|
||||
#lorem(200)
|
|
@ -1,11 +1,11 @@
|
|||
#import "../lib.typ": todo, highlight, num
|
||||
#import "../lib.typ": todo, highlight, num, paragraph
|
||||
#import "X_var.typ": *
|
||||
#import "X_lib.typ": *
|
||||
|
||||
== Experiments <sec:rasta-xp>
|
||||
|
||||
|
||||
=== *RQ1*: Re-Usability Evaluation
|
||||
=== RQ1: Re-Usability Evaluation
|
||||
|
||||
|
||||
#todo[alt text for figure rasta-exit / rasta-exit-drebin]
|
||||
|
@ -155,10 +155,11 @@ For the sake of clarity, we separated Java based / non Java based tools.
|
|||
), caption: [Finishing rate by bytecode size for APK detected in 2022]
|
||||
) <fig:rasta-decorelation-size>
|
||||
|
||||
_Fixed application year. (5000 APKs)_
|
||||
#paragraph([Fixed application year. (#num(5000) APKs)])[
|
||||
We selected the year 2022 which has a good amount of representatives for each decile of size in our application dataset.
|
||||
@fig:rasta-rate-evolution-java-2022} (resp. @fig:rasta-rate-evolution-non-java-2022) shows the finishing rate of the tools in function of the size of the bytecode for Java based tools (resp. non Java based tools) analyzing applications of 2022.
|
||||
We can observe that all Java based tools have a finishing rate decreasing over years. 50% of non Java based tools have the same behavior.
|
||||
]
|
||||
|
||||
#todo[Alt text for fig rasta-decorelation-size]
|
||||
#figure(stack(dir: ltr,
|
||||
|
@ -183,9 +184,10 @@ We can observe that all Java based tools have a finishing rate decreasing over y
|
|||
), caption: [Finishing rate by discovery year with a bytecode size $in$ [4.08, 5.2] MB]
|
||||
) <fig:rasta-decorelation-size>
|
||||
|
||||
_Fixed application bytecode size. (6252 APKs)_ We selected the sixth decile (between 4.08 and 5.20 MB), which is well represented in a wide number of years.
|
||||
#paragraph([Fixed application bytecode size. (#num(6252) APKs)])[We selected the sixth decile (between 4.08 and 5.20 MB), which is well represented in a wide number of years.
|
||||
@fig:rasta-rate-evolution-java-decile-year (resp. @fig:rasta-rate-evolution-non-java-decile-year) represents the finishing rate depending of the year at a fixed bytecode size.
|
||||
We observe that 9 tools over 12 have a finishing rate dropping below 20% for Java based tools, which is not the case for non Java based tools.
|
||||
]
|
||||
|
||||
#todo[Alt text for fig rasta-decorelation-min-sdk]
|
||||
#figure(stack(dir: ltr,
|
|
@ -1,4 +1,4 @@
|
|||
#import "../lib.typ": todo, etal
|
||||
#import "../lib.typ": todo, etal, paragraph
|
||||
#import "X_var.typ": *
|
||||
#import "X_lib.typ": *
|
||||
|
||||
|
@ -254,11 +254,12 @@ Anadroid: DONE
|
|||
SELECT AVG(cnt), MAX(cnt) FROM (SELECT COUNT(*) AS cnt FROM error WHERE tool_name = 'anadroid' AND msg='Could not decode arsc file' GROUP BY sha256);
|
||||
*/
|
||||
|
||||
_Androguard and Androguard_dad_
|
||||
#paragraph[Androguard and Androguard_dad])[
|
||||
Surprisingly, while Androguard almost never fails to analyze an APK, the internal decompiler of Androguard (DAD) fails more than half of the time.
|
||||
The analysis of the logs shows that the issue comes from the way the decompiled methods are stored: each method is stored in a file named after the method name and signature, and this file name can quickly exceed the size limit (255 characters on most file systems).
|
||||
It should be noticed that Androguard_dad rarely fails on the Drebin dataset.
|
||||
This illustrate the importance to test tools on real and up-to-date APKs: even a bad handling of filenames can influence an analysis.
|
||||
]
|
||||
|
||||
/*
|
||||
Androguard: Done
|
||||
|
@ -271,7 +272,7 @@ NullPointerException
|
|||
dad: SError
|
||||
*/
|
||||
|
||||
_Mallodroid and Apparecium_
|
||||
#paragraph([Mallodroid and Apparecium])[
|
||||
Mallodroid and Apparecium stand out as the tools that raised the most errors in one run.
|
||||
They can raise more than #num(10000) error by analysis.
|
||||
However, it happened only for a few dozen of APKs, and conspicuously, the same APKs raised the same hight number of errors for both tools.
|
||||
|
@ -282,6 +283,7 @@ Still, the tools claim to return a result, so, from our perspective, we consider
|
|||
For other numerous errors, we could not identify the reason why those specific applications raise so many exceptions.
|
||||
However we noticed that Mallodroid and Apparecium use outdated version of Androguard (respectively the version 3.0 and 2.0), and neither Androguard v3.3.5 nor DAD with Androguard v3.3.5 raise those exceptions.
|
||||
This suggest the issue has been fixed by Androguard and that Mallodroid and Apparecium could benefit from a dependency upgrade.
|
||||
]
|
||||
|
||||
/*
|
||||
Apparecium: DONE
|
||||
|
@ -294,9 +296,10 @@ mallodroid: Done
|
|||
Instruction10x%
|
||||
*/
|
||||
|
||||
_Blueseal_
|
||||
#paragraph([Blueseal])[
|
||||
Because Blueseal rarely log more than one error when crashing, it is easy to identify the relevant error.
|
||||
The majority of crashes comes from unsupported Android versions (due to the magic number of the DEX files not being supported by the version of back smali used by Blueseal) and methods whose implementation are not found (like native methods).
|
||||
]
|
||||
|
||||
/*
|
||||
Blueseal: Done
|
||||
|
@ -304,9 +307,10 @@ Blueseal: Done
|
|||
No idea how to fix. Update soot? version unclear ('trunk'...), but copyright up to 2010 so 2010?
|
||||
*/
|
||||
|
||||
_Droidsafe and SAAF_
|
||||
#paragraph([Droidsafe and SAAF])[
|
||||
Our investigation of the most common errors raised by Droidsafe and SAAF showed that they are often preceded by an error from apktool.
|
||||
Indeed, #num(28654) runs of Droidsafe and #num(38635) runs of SAAF failed after raising at least one of `brut.androlib.AndrolibException` or `brut.androlib.err.UndefinedResObject`, suggesting that those tools would benefit from an upgrade of apktool.
|
||||
]
|
||||
|
||||
/*
|
||||
Droidsafe:
|
||||
|
@ -322,10 +326,11 @@ Droidsafe:
|
|||
CannotFindMethodException
|
||||
*/
|
||||
|
||||
_Ic3 and Ic3_fork_
|
||||
#paragraph([Ic3 and Ic3_fork])[
|
||||
We compared the number of errors between Ic3 and Ic3_fork.
|
||||
Ic3_fork reports less errors for all types of analysis which suggests that the author of the fork have removed the outputed errors from the original code: the thrown errors are captured in a generic `RuntimeException` which removes the semantic, making it harder our investigations.
|
||||
Nevertheless, Ic3_fork has more failures than Ic3: the number of errors reported by a tool is not correlated to the final success of its analysis.
|
||||
]
|
||||
|
||||
/*
|
||||
ic3: DONE
|
||||
|
@ -343,7 +348,7 @@ IccTa: Done
|
|||
jasError
|
||||
*/
|
||||
|
||||
_Flowdroid_
|
||||
#paragraph([Flowdroid])[
|
||||
Our exchanges with the authors of Flowdroid led us to expect more timeouts from too long executions than failed run.
|
||||
#todo[Deja dit? : Surprisingly we only got #mypercent(37,NBTOTAL) of timeout, and a hight number of failures.]
|
||||
We tried to detect recurring causes of failures, but the complexity of Flowdroid make the investigation difficult.
|
||||
|
@ -352,6 +357,7 @@ Other errors that came up regularly are `java.nio.channels.ClosedChannelExceptio
|
|||
We randomly selected 20 APKs that generated stack overflows in Flowdroid and retried the analysis with 500G of RAM allocated to the JVM.
|
||||
18 of those runs still failed with a stack overflow without using all the allocated memory, the other two failed after raising null pointer exceptions from `getClassFlows`.
|
||||
This shows that the lack of memory is not the primary cause of those failures.
|
||||
]
|
||||
|
||||
/*
|
||||
Flowdroid: TODO java.nio.channels.ClosedChannelException cause or consequence?
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
Before Width: | Height: | Size: 262 KiB After Width: | Height: | Size: 262 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
Before Width: | Height: | Size: 192 KiB After Width: | Height: | Size: 192 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 266 KiB |
Before Width: | Height: | Size: 568 KiB After Width: | Height: | Size: 568 KiB |
Before Width: | Height: | Size: 355 KiB After Width: | Height: | Size: 355 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 207 KiB After Width: | Height: | Size: 207 KiB |
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
|
@ -1,9 +1,9 @@
|
|||
#import "../lib.typ": etal
|
||||
#import "../lib.typ": etal, paragraph
|
||||
#import "X_var.typ": *
|
||||
|
||||
== State of the art <sec:cl-soa>
|
||||
|
||||
_Class loading_
|
||||
#paragraph([Class loading])[
|
||||
Class loading mechanisms have been studied in the general context of the Java language.
|
||||
Gong@gong_secure_1998 describes the JDK 1.2 class loading architecture and capabilities.
|
||||
One of the main advantages of class loading is the type safety property that prevents type spoofing.
|
||||
|
@ -23,16 +23,18 @@ Dynamic hook mechanisms should be used to intercept the bytecode at load time.
|
|||
These techniques can be of some help for the reverser, but they require to instrument the source code of AOSP or the application itself.
|
||||
The engineering cost is high and anti-debugging techniques can slow down the process.
|
||||
Thus, a reverser always starts by studying statically an application using static analysis tools@Li2017, and will eventually go to dynamic analysis@Egele2012 if further costly extra analysis is needed (for example, if they spot the use of a custom class loader).
|
||||
In the first phase of an analysis where the used methods are static, the reverser can have the feeling that what he sees in the bytecode is what is loaded at runtime.
|
||||
Our goal is to show that tools mentioned in the literature@Li2017 can suffer from attacks exploiting confusion inside regular class loading mechanisms of Android.
|
||||
In the first phase of an analysis where the used methods are static, the reverser can have the feeling that what he sees in the bytecode is what is loaded at runtime.
|
||||
Our goal is to show that tools mentioned in the literature@Li2017 can suffer from attacks exploiting confusion inside regular class loading mechanisms of Android.
|
||||
]
|
||||
|
||||
_Hidden APIs_
|
||||
#paragraph([Hidden APIs])[
|
||||
Li #etal did an empirical study of the usage and evolution of hidden APIs@li_accessing_2016.
|
||||
They found that hidden APIs are added and removed in every release of Android, and that they are used both by benign and malicious applications.
|
||||
More recently, He #etal @he_systematic_2023 did a systematic study of hidden service API related to security.
|
||||
They studied how the hidden API can be used to bypass Android security restrictions and found that although Google countermeasures are effective, they need to be implemented inside the system services and not the hidden API due to the lack of in-app privilege isolation: the framework code is in the same process as the user code, meaning any restriction in the framework can be bypassed by the user.
|
||||
]
|
||||
|
||||
_Static analysis tools_
|
||||
#paragraph([Static analysis tools])[
|
||||
Static analysis tools are used to perform operations on an APK file, for example extracting its bytecode or information from the Manifest file.
|
||||
Because of the complexity of Android, few tools have followed all the evolutions of the file format and are robust enough to analyze all applications without crashing@mineau_evaluating_2024.
|
||||
The tools can share the backend used to manipulate the code.
|
||||
|
@ -42,3 +44,4 @@ This framework enables advanced features such as inserting or removing bytecode
|
|||
The most known tool built on top of Soot is FlowDroid@Arzt2014a, which enables to compute information flows statically into the code.
|
||||
|
||||
Because these tools are used by reversers, we will evaluate the accuracy of the provided results in the case of an application developer exploits the possible confusions that brings the class loading mechanisms of Android.
|
||||
]
|
|
@ -1,4 +1,4 @@
|
|||
#import "../lib.typ": eg, todo
|
||||
#import "../lib.typ": eg, todo, paragraph
|
||||
#import "X_var.typ": *
|
||||
|
||||
== Obfuscation Techniques <sec:cl-obfuscation>
|
||||
|
@ -26,10 +26,10 @@ on peut shadow une classe hidden
|
|||
From the results presented in @sec:cl-loading, three approaches can be designed to hide the behavior of an application.
|
||||
|
||||
/*
|
||||
_Hidden classes_
|
||||
#paragraph([Hidden classes])[
|
||||
Applications both malicious and benign have been known to use hidden API to access advance features #todo[ref ?].
|
||||
Using #hidec can have an impact on the accuracy of analysis tools because they may not have access to the code of these classes.
|
||||
|
||||
]
|
||||
#todo[Google blacklist/greylist/ect, ref to paper that says this can be bypass]
|
||||
|
||||
#todo[Compare classes in android.jar, framework.jar and other, are they hidden whitelisted classes?]
|
||||
|
@ -39,24 +39,26 @@ Basic shadowing imply to have several class with the same name in the applicatio
|
|||
On the other hand, using #hidec leave classes without implementation in the application, which can also be detected.
|
||||
*/
|
||||
|
||||
*Self shadow*_: shadowing a class with another from APK_
|
||||
#paragraph([*Self shadow*: shadowing a class with another from APK])[
|
||||
This method consists in hiding the implementation of a class with another one by exploiting the possible collision of class names, as described in @sec:cl-collision with multiple #dexfiles.
|
||||
If reversers or tools ignore the priority order of a multi-dex file, they can take into account the wrong version of a class.
|
||||
|
||||
]
|
||||
|
||||
//priorité aux classes SDK meme si une shadow classe est définie dans l'APK (tout ca a cause de Boot)
|
||||
*SDK shadow*_: shadowing a SDK class_
|
||||
#paragraph([*SDK shadow*: shadowing a SDK class])[
|
||||
This method consists in presenting to the reverser a fake implementation of a class of the SDK.
|
||||
This class is embedded in the APK file and has the same name as the one of the SDK.
|
||||
Because `BootClassLoader` will give priority to the #Asdk at runtime, the reverser or tool should ignore any version of a class that is contained in the APK.
|
||||
The only constraint when shadowing an SDK class is that the shadowing implementation must respect the signature of real classes.
|
||||
Note that, by introducing a custom class loader, the attacker could inverse the priority, but this case is out of the scope of this paper.
|
||||
]
|
||||
|
||||
// priorité aux classes hidden (car du SDK) meme si une shadow classe est définie dans l'APK
|
||||
*Hidden shadow*_: shadowing an hidden class_
|
||||
#paragraph([*Hidden shadow*: shadowing an hidden class])[
|
||||
This method is similar to the previous one, except the class that is shadowed is a #hidecsingular.
|
||||
Because ART will give priority to the internal version of the class, the version provided in the APK file will be ignored.
|
||||
Such shadow attacks are more difficult to detect by a reverser, that may not know the existence of this specific hidden class in Android.
|
||||
]
|
||||
|
||||
=== Impact on static analysis tools <sec:cl-evaltools>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#import "../lib.typ": num, todo
|
||||
#import "../lib.typ": num, todo, paragraph
|
||||
#import "X_var.typ": *
|
||||
|
||||
== Shadow attacks in the wild <sec:cl-wild>
|
||||
|
@ -95,11 +95,12 @@ For those pairs of shadow classes, we disassembled them using Apktool to perform
|
|||
For self-shadow, we compare the pair.
|
||||
For the shadowing of the SDK or Hidden class, we compare the code found in the APK with implementations found in the emulator and `android.jar` of SDK 32, 33, and 34.
|
||||
|
||||
_Self-shadowing_
|
||||
#paragraph([Self-shadowing])[
|
||||
We observe a low number of applications doing self-shadow attacks.
|
||||
For each class that is shadowed, we compared its bytecode with the shadowed one.
|
||||
We observe that 74.8% are identical which suggests that the compilation process embeds the same class multiple times but makes variations in headers or metadata values.
|
||||
We investigate later in @sec:cl-malware the case of malicious applications.
|
||||
]
|
||||
|
||||
#figure(
|
||||
image(
|
||||
|
@ -110,7 +111,7 @@ We investigate later in @sec:cl-malware the case of malicious applications.
|
|||
caption: [Redefined SDK classes, sorted by the first SDK they appeared in.]
|
||||
)<fig:cl-classes_by_first_sdk>
|
||||
|
||||
_SDK shadowing_
|
||||
#paragraph([SDK shadowing])[
|
||||
For the shadowing of SDK classes, we observe a low ratio of identical classes.
|
||||
This result could lead to the wrong conclusion that developers embed malicious versions of the SDK classes, but our manual investigation shows that the difference is slight and probably due to compiler optimization.
|
||||
To go further in the investigation, in @fig:cl-classes_by_first_sdk we represent these redefined classes with the following rules:
|
||||
|
@ -136,8 +137,8 @@ Nevertheless, we decided to count them as identical, because `android.jar` is th
|
|||
|
||||
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.
|
||||
]
|
||||
|
||||
#todo[cl-topsdk]
|
||||
#figure({
|
||||
show table: set text(size: 0.80em)
|
||||
table(
|
||||
|
@ -188,53 +189,8 @@ When substantial differences appear it is mainly because different versions of t
|
|||
)},
|
||||
caption: [Shadow classes compared to SDK 34 for a dataset of #nbapk applications]
|
||||
) <tab:cl-topsdk>
|
||||
/*
|
||||
\catcode`\$=12% deactivate $ sign // WORKAROUND
|
||||
\begin{table}[tb]
|
||||
\caption{Top 10 of shadow classes of the SDK}
|
||||
\label{tab:topsdk}
|
||||
\footnotesize
|
||||
\begin{tabular}{lrr}
|
||||
\toprule
|
||||
\bf Class & \bf occurrences & \bf Identical ratio \\
|
||||
\midrule
|
||||
\footnotesize redefined for SDK <= 7 & & \\
|
||||
\midrule
|
||||
\csvreader[
|
||||
late after line = \\,
|
||||
%separator=semicolon,
|
||||
head to column names,
|
||||
]{redef_sdk_7minus.csv}{}{%
|
||||
\class & \mynums{\occ} & \idper \%
|
||||
}%
|
||||
\midrule
|
||||
\footnotesize redefined for SDK = 8 & & \\
|
||||
\midrule
|
||||
\csvreader[
|
||||
late after line = \\,
|
||||
%separator=semicolon,
|
||||
head to column names,
|
||||
]{redef_sdk_8.csv}{}{%
|
||||
\class & \mynums{\occ} & \idper \%
|
||||
}%
|
||||
\midrule
|
||||
\footnotesize redefined for SDK = 16 & & \\
|
||||
\midrule
|
||||
\csvreader[
|
||||
late after line = \\,
|
||||
%separator=semicolon,
|
||||
head to column names,
|
||||
respect dollar = false, % NOT WORKING :-(((((((((( https://tex.stackexchange.com/questions/486250/csvsimple-respect-dollar-not-working
|
||||
]{redef_sdk_16.csv}{}{%
|
||||
\class & \mynums{\occ} & \idper \%
|
||||
}%
|
||||
\bottomrule
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
\catcode`\$=3% reactivate $ sign // WORKAROUND
|
||||
*/
|
||||
|
||||
_Hidden shadowing_
|
||||
#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`:
|
||||
|
||||
|
@ -244,8 +200,8 @@ The top 3 packages whose code actually differs from the ones found in Android ar
|
|||
`org.json` is a package in Android SDK, not a hidden one.
|
||||
However, `JSONObject$1` is an anonymous class not provided by `android.jar` because its class `JSONObject` is an empty stub, and thus, does not use `JSONObject$1`.
|
||||
Thus, this class falls in the category of hidden #platc.
|
||||
|
||||
All these hidden shadow classes are libraries included by the developers who probably did not know that they were already embedded in Android.
|
||||
]
|
||||
|
||||
=== Shadowing in malware applications <sec:cl-malware>
|
||||
|
Before Width: | Height: | Size: 398 KiB After Width: | Height: | Size: 398 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
63
main.typ
|
@ -69,64 +69,11 @@
|
|||
|
||||
#counter(page).update(1)
|
||||
|
||||
= Introduction <sec:intro>
|
||||
|
||||
#todo[Write an introduction]
|
||||
|
||||
#lorem(200)
|
||||
|
||||
#figure(
|
||||
circle(radius: 50pt),
|
||||
caption: [A circle],
|
||||
) <fig:intro>
|
||||
|
||||
#lorem(200)
|
||||
|
||||
= Background <sec:bg>
|
||||
|
||||
#todo[Present your field background]
|
||||
|
||||
#lorem(200)
|
||||
|
||||
#figure(
|
||||
table(
|
||||
columns: (20pt, 20pt, 20pt),
|
||||
align: center+horizon,
|
||||
table.header(
|
||||
table.cell(colspan:3)[Play]
|
||||
),
|
||||
emoji.crossmark, [], emoji.circle.stroked,
|
||||
[], emoji.circle.stroked, [],
|
||||
emoji.crossmark, [], emoji.crossmark,
|
||||
),
|
||||
caption: [A tic tac toe game],
|
||||
) <tab:tic-tac-toe>
|
||||
|
||||
== Something
|
||||
|
||||
#lorem(200)
|
||||
|
||||
== Something Else
|
||||
|
||||
#lorem(200)
|
||||
|
||||
= Related Work
|
||||
|
||||
#todo[Do the State of the Art]
|
||||
|
||||
#lorem(200)
|
||||
|
||||
#figure([
|
||||
```python
|
||||
for _ in range(10):
|
||||
print("Hello Void")
|
||||
```
|
||||
], caption: [Some code],
|
||||
) <alg:hello-void>
|
||||
|
||||
|
||||
#include("3_rasta/main.typ")
|
||||
#include("4_class_loader/main.typ")
|
||||
#include("1_introduction/main.typ")
|
||||
#include("2_background/main.typ")
|
||||
#include("3_related_work/main.typ")
|
||||
#include("4_rasta/main.typ")
|
||||
#include("5_class_loader/main.typ")
|
||||
|
||||
= Contribution n
|
||||
|
||||
|
|