slides
All checks were successful
/ test_checkout (push) Successful in 1m48s

This commit is contained in:
Jean-Marie 'Histausse' Mineau 2025-10-20 01:02:40 +02:00
parent 071a82ea56
commit 37f8298cb7
Signed by: histausse
GPG key ID: B66AEEDA9B645AD2
2 changed files with 508 additions and 20 deletions

View file

@ -99,6 +99,7 @@ In reality, the #platc are loaded by `bootClassLoader` and the classes from the
while file_exists_in_apk(dex_file) and \
not class_found_in_dex_file(class_name, dex_file):
index += 1
dex_file = get_mutli_dex_classses_dex_name(index)
if file_exists_in_apk(dex_file):
return load_from_file(dex_file, class_name)
else:

View file

@ -18,6 +18,8 @@
#set text(lang: "en")
#set list(marker: none)
#set par(leading: 0.2em)
#set list(spacing: 1em)
#show: sns-polylux-template.with(
txt-font: "New Computer Modern",
@ -598,13 +600,15 @@
title: [MultiDex]
)[
#set align(center + horizon)
#only(1)[
#block(
fill: green.lighten(50%),
#let apk-block = block.with(
//fill: green.lighten(50%),
stroke: black,
inset: 10pt,
radius: 12pt,
)[
)
#only(1)[
#apk-block[
#set align(left+top)
=== `app.apk`
@ -619,11 +623,7 @@
]
]
#only(2)[
#block(
fill: green.lighten(50%),
inset: 8pt,
radius: 8pt,
)[
#apk-block[
#set align(left+top)
=== `app.apk`
#line(length: 50%)
@ -645,11 +645,7 @@
]
]
#only(3)[
#block(
fill: green.lighten(50%),
inset: 8pt,
radius: 8pt,
)[
#apk-block[
#set align(left+top)
=== `app.apk`
#line(length: 75%)
@ -686,6 +682,224 @@
#ghost-4(x: 2%, y: 2%, mirror: true)
]
#only(4)[
#grid(
columns: (1fr, 1fr),
apk-block[
#set align(left+top)
=== `app.apk`
#line(length: 30%)
```
AndroidManifest.xml
resources.arsc
META-INF/
res/
classes.dex
classes2.dex
classes3.dex
```
],
yes-codly[
#scale(100%, reflow: true,
```python
def get_dex_name(index: int):
if index == 0:
return "classes.dex"
else:
return f"classes{index+1}.dex"
```)
]
)
]
#only(5)[
#grid(
columns: (1fr, 1fr),
apk-block[
#set align(left+top)
=== `app.apk`
#line(length: 30%)
```
AndroidManifest.xml
resources.arsc
META-INF/
res/
classes.dex
classes2.dex
classes3.dex
```
],
yes-codly[
#set list(spacing: 0.5em)
#scale(100%, reflow: true,
```python
def get_dex_name(index: int):
if index == 0:
return "classes.dex"
else:
return f"classes{index+1}.dex"
```)
- `classes0.dex` ?
- `classes1.dex` ?
- `classes02.dex` ?
- `classes10.dex` ?
]
)
#ghost-4(x: 2%, y: 2%, mirror: true)
]
]
#for i in range(4) {
if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide(
title: [Shadow Attacks]
)[
#if i in (0, 4) {
codly(
..default-codly
)
} else if i == 1 {
codly(
highlighted-lines: (7, 8),
..default-codly
)
} else if i == 2 {
codly(
highlighted-lines: (1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15),
..default-codly
)
}
#let partial-hide(
i,
hidden: (),
partial: (),
body
) = {
if i in hidden {
hide(body)
} else if i in partial {
set text(fill: luma(200))
body
} else {
body
}
}
#set align(center+horizon)
#grid(
columns: (1fr, 1fr),
column-gutter: 2em,
yes-codly[
#scale(60%, reflow: true,
```python
def get_dex_name(index: int):
if index == 0:
return "classes.dex"
else:
return f"classes{index+1}.dex"
def load_class(class_name: str):
if is_platform_class(class_name):
return boot_cl.load(class_name)
else:
index = 0
dex_file = get_dex_name(index)
while exists_in_apk(dex_file) and \
class_name not in classes_of(dex_file):
index += 1
dex_file = get_dex_name(index)
if file_exists_in_apk(dex_file):
return load_from_file(dex_file, class_name)
else:
raise ClassNotFoundError()
```)
], [
#set align(left)
#set text(size: 18pt)
#partial-hide(i, hidden: (0,), partial: (2,))[
=== SDK shadowing
Trick the tool into using an APK class instead of an SDK class
]
#partial-hide(i, hidden: (0,), partial: (2,))[
=== Hidden API shadowing
Trick the tool into using an APK class instead of an hidden API class
]
#partial-hide(i, hidden: (0,1))[
=== Self Shadowing
Trick the tool into using an APK class instead of another APK class
]
]
)
]
}
#slide(
title: [Impact on Tools],
)[
#set align(center+horizon)
#show figure.caption: none
#show link.where(dest: <acr-sdk>): it => it.body
#show figure: it => {
set align(left)
show table: set align(center+horizon)
it
}
#scale(100%, reflow: true, get_figure(<tab:cl-results>))
]
#slide(
title: [Example: Androguard],
foreground: eye-4(x: 97%, y: 85%, mirror: true)
)[
#set align(center+horizon)
#show image: scale.with(250%)
#set figure(gap: 4em)
#v(3em)
#grid(
columns: (1fr, 1fr),
get_figure(<fig:cl-andro_non_obf_cg>),
get_figure(<fig:cl-andro_obf_cg>)
)
]
#for i in range(3) {
if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide(
title: [In the Wild],
foreground: eye-2(x: 8%, y: 67%, height: 70pt)
)[
#set align(center+horizon)
#show figure.caption: none
#show link.where(dest: <acr-sdk>): it => it.body
#set table(
fill: (x, y) => {
if (
i == 1 and (x, y) in ((1, 13), (2, 13), (4, 13), (8, 13))
) or (
i == 2 and (x, y) in ((1, 14), (2, 14))
) {
highlight-color
} else {
none
}
}
)
#scale(90%, reflow: true, get_figure(<tab:cl-shadow>))
]
}
#slide(
title: [Conclusion]
)[
#item-by-item[
- We modeled the class loading algorithm
- Static Analysis Tools did not
- We introduced obfuscation techniques based on this model
- Ambiguous cases exists in the wild
- We did not find deliberate shadow attacks
]
]
#slide[
@ -701,20 +915,293 @@
#new-section-slide([The Application of Theseus])
#slide[Th 1
#slide(
title: [Overview],
)[
#set align(center+horizon)
#show figure.caption: none
#scale(100%, reflow: true, get_figure(<fig:th-process>))
]
#slide[Th 2
#slide(
title: [Dynamic Analysis],
)[
- Frida: intercepts method calls
#v(2em)
#uncover("2-")[
- Android Emulator: runs on computer/server
- Grodd Runner: clicks buttons
]
#v(2em)
#uncover(3)[
- Phone with adb enable: actuall hardware
- Human: intelligent button clicker
]
]
#slide(
title: [Dynamic Code Loading],
foreground: ghost-6(x: 80%, y: 15%, mirror: true)
)[
#set align(center+horizon)
#show figure.caption: none
#show image: box.with(width: 58%)
#get_figure(<fig:th-inserting-dex>)
]
#for i in range(4) {
if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide(
title: [Reflection],
//foreground: ghost-6(x: 80%, y: 15%, mirror: true)
)[
#show: yes-codly
#set align(center+horizon)
#if i == 1 {
codly(
highlighted-lines: (6,),
..default-codly
)
} else if i == 3 {
codly(
offset: 5,
..default-codly
)
} else {
codly(..default-codly)
}
#if i in (0, 1) {
```java
ClassLoader cl = MainActivity.class.getClassLoader();
Class clz = cl.loadClass("Reflectee");
Object obj = clz.newInstance();
Method mth = clz.getMethod("myMethod", String.class);
Object[] args = {(Object)"an argument"};
String retData = (String) mth.invoke(obj, args);
```
} else if i == 2{
```java
ClassLoader cl = MainActivity.class.getClassLoader();
Class clz = cl.loadClass(getFromInternet());
Object obj = clz.newInstance();
Method mth = clz.getMethod(getFromInternet(), String.class);
Object[] args = {(Object)getFromInternet()};
String retData = (String) mth.invoke(obj, args);
```
} else {
```java
String retData = (String) mth.invoke(obj, args);
```
}
]
}
#for i in range(5) {
if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide(
title: [Reflection Transformation]
)[
#show: yes-codly
#set align(center+horizon)
#if i == 1 {
codly(
offset: 5,
highlighted-lines: (10,),
..default-codly
)
} else if i == 2 {
codly(
offset: 5,
highlights: (
(line: 6, start: 0, end: 13, fill: pirat-color.blue),
(line: 8, start: 3, end: 8, fill: pirat-color.blue),
(line: 10, start: 3, end: 8, fill: pirat-color.blue),
(line: 12, start: 18, end: 32, fill: pirat-color.blue),
),
..default-codly
)
} else if i == 3 {
codly(
offset: 5,
highlights: (
(line: 8, start: 12, end: 19, fill: pirat-color.blue),
),
..default-codly
)
} else if i == 4 {
codly(
offset: 5,
highlights: (
(line: 7, start: 5, end: 43, fill: pirat-color.blue),
(line: 8, start: 21, end: 31, fill: pirat-color.blue),
(line: 8, start: 38, end: 45, fill: pirat-color.blue),
(line: 8, start: 47, end: 54, fill: pirat-color.blue),
),
..default-codly
)
} else {
codly(
offset: 5,
..default-codly
)
}
```java
Object objRet;
if (T.check_is_reflectee_mymethod_XXXX(mth)) {
objRet = (Object)((Reflectee) obj).myMethod((String)args[0]);
} else {
objRet = mth.invoke(obj, args);
}
String retData = (String) objRet;
```
]
}
#slide(
title: [Dynamic Analysis],
foreground: ghost-1(x: 97%, y: 10%, height: 70pt)
)[
#set align(center+horizon)
#show figure.caption: none
#set table(
fill: (x, y) => {
if (
x == 4 and y > 2
) {
highlight-color
} else {
none
}
}
)
#scale(100%, reflow: true, get_figure(<tab:th-dyn-visited>))
]
#slide(
title: [Collected Bytecode],
foreground: eye-1(x: 5%, y: 70%, height: 70pt)
)[
#set align(center+horizon)
#show link.where(dest: <acr-apk>): it => it.body
#show link.where(dest: <acr-dex>): it => it.body
#show figure.caption: none
#set table(
fill: (x, y) => {
if (
x == 4 and y > 2
) {
highlight-color
} else {
none
}
}
)
#scale(90%, reflow: true, get_figure(<tab:th-bytecode-hashes>))
]
#for i in range(3) {
if i != 0 { counter("logical-slide").update( n => n - 1 ) }
slide(
title: [Added Calls],
)[
#set align(center+horizon)
#show link.where(dest: <acr-apk>): it => it.body
#show link.where(dest: <acr-dex>): it => it.body
#show figure.caption: none
#set table(
fill: (x, y) => {
if (
i == 1 and (x == 3 and y > 1)
) or (
i == 2 and (x == 4 and y > 1)
){
highlight-color
} else {
none
}
}
)
#scale(90%, reflow: true, get_figure(<tab:th-compare-cg>))
]
}
#slide(
title: [Added Calls],
foreground: ghost-3(x: 93%, y: 10%)
)[
#import "@preview/diagraph:0.3.5": render
#set align(center+horizon)
#scale(47%, box(render(
read("5_theseus/figs/patched_main_main.dot"),
//width: 100%,
labels: (name) => { move(dy: -7pt, scale(140%, text(size: 10pt, weight: "bold", name))) }
)))
]
#slide(
title: [Impact on Finishing Rate],
)[
#set align(center+horizon)
#show figure.caption: none
#scale(90%, reflow: true, get_figure(<fig:th-status-npatched-vs-patched>))
]
#slide(
title: [Conclusion],
)[
#item-by-item[
- We can statically analyse APKs with reflection and dynamic code loading with our method
- Our dynamic analysis is questionable
- The dynamically loaded bytecode we intercepted is mainly telemetry and advertisement related
- We released a tool to instrument APKs
]
]
#new-section-slide([Conclusion])
#slide[Conclusion 1
#slide[
We showed that:
#item-by-item[
- After five years, more than half the static analysis tools are no longer usable.
The size of the application seems to be the most significant factor.
- Android behaviour is complex and well known.
In the specific case of class loading, we showed that state-of-the-art tools do not match Android, leading to invalid analyses.
- APKs can be augmented with instrumentation to improve further analyses with any other tools.
- Also, dynamic analysis is still very much not trivial.
]
]
#slide[Conclusion 2
#slide(
title: [Futur Work],
foreground: {
ghost-3(x: 80%, y: 70%)
ghost-6(x: 20%, y: 30%)
ghost-7(x: 70%, y: 20%)
}
)[
#set align(center+horizon)
A lot of engineering, preferably spearheaded by Google.
]
#counter("logical-slide").update( n => n - 1 )
#slide(
title: [Futur Work]
)[
#item-by-item[
- Benchmark to evaluate finishing rate
- Make tools reusing sources from Android Open Source Project
- Require developpers to provide high coverage tests inputs with the APKs
]
]