From 21c2a58f3c5b44f1d4455885ca2ebf9722095428 Mon Sep 17 00:00:00 2001 From: Jean-Marie 'Histausse' Mineau Date: Thu, 27 Nov 2025 05:31:22 +0100 Subject: [PATCH] include feedbacks --- slides.typ | 468 +++++++++++++++++++++++++++----------------- slides/outlines.typ | 61 +++++- 2 files changed, 348 insertions(+), 181 deletions(-) diff --git a/slides.typ b/slides.typ index 6cdfc7c..aa55cde 100644 --- a/slides.typ +++ b/slides.typ @@ -2,7 +2,9 @@ #import "slides/lib.typ": * #import "slides/icons.typ" as ico -#import "@preview/codly:1.3.0": * +#import "@local/codly:1.3.1": * +// Require local install, fix needed for highlight-inset +// TMP="$(mktemp -d)" && curl -L https://github.com/Dherse/codly/archive/refs/tags/v1.3.1.zip -o "${TMP}/c.zip" && unzip -d "${TMP}" "${TMP}/c.zip" && mkdir -p ~/.local/share/typst/packages/local/codly && mv "${TMP}/codly-1.3.1" ~/.local/share/typst/packages/local/codly/1.3.1 && rm -rf "${TMP}" #import "@preview/codly-languages:0.1.1": * #show: codly-init.with() #let default-codly = ( @@ -14,6 +16,8 @@ inset: (y: 0.15em), highlighted-default-color: highlight-color, highlight-fill: it => it.lighten(40%), //highlight-color, + highlight-inset: (x: 0pt, y: 0pt), + highlight-outset: (x: 0.15pt, y: 0.15em), ) #codly-disable() @@ -84,8 +88,12 @@ )[ #set align(center+horizon) #grid( - columns: (1fr, 1fr), - image("slides/imgs/google.png", width: 200pt), + columns: (1fr, 1fr), [ + #image("slides/imgs/google.png", width: 200pt) + - Smartphones are computers + - Android = linux + Android Runtime + - APK = computer program + ], //image("slides/imgs/phone.png", height: 350pt) ico.phone( height: 350pt, @@ -274,7 +282,7 @@ foreground: eye-3(x: 3%, y: 5%) )[ #set align(center+horizon) - #analyse-apk + #ico.analyse() ] #counter("logical-slide").update( n => n - 1 ) @@ -289,8 +297,8 @@ == Dynamic Analysis #item-by-item[ - Run the application - - _See_ dynamically loaded bytecode - - _See_ reflection calls + - *See* dynamically loaded bytecode + - *See* reflection calls - Limited by code coverage ] ], @@ -311,20 +319,18 @@ ] #slide( - title: [Which Tools?], + title: [Which Tools are _really_ Working?], foreground: eye-3(x: 3%, y: 5%) )[ #set align(center+horizon) - #analyse-apk -] - -#counter("logical-slide").update( n => n - 1 ) -#slide( - title: [Which Tools are Working?], - foreground: eye-3(x: 3%, y: 5%) -)[ - #set align(center+horizon) - #analyse-apk + #ico.analyse() + #h(10em) + #ico.machinery() + #place( + center+horizon, + dy: 120pt, + text(size: 400pt, fill: pirat-color.red)[?] + ) ] #slide( @@ -338,15 +344,6 @@ #highlight-block(pb1-text) ] -#slide( - title: [How does Class Loading works?], - foreground: eye-3(x: 3%, y: 5%) -)[ - #set align(center+horizon) - #analyse-apk -] -#counter("logical-slide").update( n => n - 1 ) - #slide( title: [Class Loading], )[ @@ -396,7 +393,7 @@ )[ #item-by-item[ - Used to select classes implementation - - More complexe than it looks + - More complex than it looks - Doubious documentation - Not studied in the context of Android Static Analysis ] @@ -498,8 +495,8 @@ Li #etal (2017): #v(0pt) #item-by-item[ - - Systematic literature review for Android static analysis - - Lists open-sourced tools + - Systematic literature review for Android static analysis (38 tools) + - Lists 31 open-sourced tools - Does not test the tools ] #uncover("4-")[Reaves #etal (2016):] @@ -530,7 +527,7 @@ ] #slide( - title: [Methodology], + title: [Methodology: RASTA], foreground: place( bottom + left, dx: 88%, @@ -683,16 +680,17 @@ */ #slide( - title: [Conclusion], + title: [PB1: Conclusion], )[ #v(1fr) //#set align(center) #item-by-item[ - - Over 22 tools, 10 are usable + - Over *22* tools, *10 are usable* - Newer applications are harder to analyse - - Applications with more bytecode are harder to analyse + - Applications with *more bytecode* are *harder* to analyse - Applications targetting more recent versions of Android are harder to analyse - - Confirms and extends Reaves #etal + - Confirms and *extends Reaves #etal* + - Docker containers for tool *released* ] #v(1fr) #align(center, text(fill: pirat-color.blue.darken(30%))[International Conference on Software and Systems Reuse (ICSR 2024)]) @@ -1262,8 +1260,10 @@ ) #place(right+top)[ #set align(left) + #align(center)[Pull Requests:] + #v(-1em) #link("https://github.com/androguard/androguard/pull/1149")[androguard/pull/1149] \ - #link("https://github.com/soot-oss/soot/pull/2211")[soot/pull/2211] \ + #link("https://github.com/soot-oss/soot/pull/2211")[soot/pull/2211] (#text(fill: green)[merged])\ #link("https://github.com/skylot/jadx/pull/2702")[jadx/pull/2702] ] ] @@ -1355,15 +1355,15 @@ } #slide( - title: [Conclusion] + title: [PB2: Conclusion] )[ #v(1fr) #item-by-item[ - We modeled the class loading algorithm - - Static Analysis Tools did not + - #h(2em) 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 + - #h(2em) Ambiguous cases exists in the wild ] #v(1fr) #align(center, text(fill: pirat-color.blue.darken(30%))[Digital Threats: Research and Practice, vol. 6 (3), 2025]) @@ -1396,8 +1396,8 @@ title: [Theseus: Overview], )[ #set align(center+horizon) - #only(1, theseus-outline(stage: "theseus-no-static")) - #only(2, theseus-outline(stage: "theseus")) + #only(1, theseus-outline(stage: "theseus-no-static", labels: true)) + #only(2, theseus-outline(stage: "theseus", labels: true)) ] #slide( @@ -1438,6 +1438,7 @@ #get_figure() ] +/* #for i in range(4) { // TODO: plutot barrer les lignes au lieux de les remplacer if i != 0 { counter("logical-slide").update( n => n - 1 ) } @@ -1487,9 +1488,9 @@ ``` } ] -} +}*/ -#for i in range(5) { +#for i in range(7) { if i != 0 { counter("logical-slide").update( n => n - 1 ) } slide( @@ -1498,152 +1499,248 @@ #show: yes-codly #set align(center+horizon) - #if i == 1 { - codly( - offset: 5, - highlighted-lines: (6,), - ..default-codly - ) - } else if i == 4 { - codly( - offset: 5, - highlights: ( - (line: 6, start: 0, end: 25, fill: pirat-color.blue), - ), - ..default-codly - ) - } else { - codly( - offset: 5, - ..default-codly - ) - } - ```java - String retData = (String) mth.invoke(obj, args); - ``` - - #v(-0.5em) - #align(center+horizon, sym.arrow.b.stroked) - #v(-0.5em) - - #if i == 1 { - codly( - offset: 5, - highlighted-lines: (10,), - ..default-codly - ) - } else if i == 3 { - 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+9, end: 32, fill: pirat-color.blue), - ), - ..default-codly - ) - } else if i == 4 { - codly( - offset: 5, - highlights: ( - (line: 8, start: 12, end: 19, fill: pirat-color.blue), - (line: 12, start: 0, end: 25, fill: pirat-color.blue), - ), - ..default-codly - ) - } else if i == 2 { - 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; - ``` + #scale(90%, reflow: true, grid( + rows: (3em, 2em, 12em), + { + if i == 1 { + codly( + offset: 4, + highlighted-lines: (5,), + ..default-codly + ) + } else if i == 3 { + codly( + offset: 4, + highlights: ( + (line: 6, start: 27, end: 48, fill: pirat-color.blue), + ), + ..default-codly + ) + } else { + codly( + offset: 4, + ..default-codly + ) + } + ```java + Method mth = getMethod(); + String retData = (String) mth.invoke(obj, args); + ``` + }, + { + v(1fr) + align(center+horizon, sym.arrow.b.stroked) + v(1fr) + }, + { + if i == 3 { + codly( + offset: 4, + highlights: ( + (line: 10, start: 12, end: 33, fill: pirat-color.blue), + ), + ..default-codly + ) + } else if i == 4 { + codly( + offset: 4, + 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 if i == 5 { + codly( + offset: 4, + 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+9, end: 32, fill: pirat-color.blue), + ), + ..default-codly + ) + } else if i == 6 { + codly( + offset: 4, + highlights: ( + (line: 8, start: 12, end: 19, fill: pirat-color.blue), + (line: 12, start: 0, end: 25, fill: pirat-color.blue), + ), + ..default-codly + ) + } else if i < 2 { + codly( + offset: 5, + ..default-codly + ) + } else { + codly( + offset: 4, + ..default-codly + ) + } + v(1fr) + if i in (0, 1) { + ```java + String retData = obj.myMethod((String)args[0]); + ``` + } else { + ```java + Method mth = getMethod(); + 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; + ``` + } + v(1fr) + } + )) ] } + #slide( foreground: { - arrow( - stroke: 10pt + pirat-color.red, - ( - 250pt, - -400pt, - ), - ( - 250pt, - -350pt, + } +)[ + #set align(center+horizon) + #theseus-outline() + #place( + bottom+left, + dx: -20pt, + dy: -360pt, + box[ + #for i in range(3) { + place( + dx: i*10pt, + dy: i*10pt, + ico.apk(height: 60pt, fill: red) + ) + } + #place(dy: 85pt)[*RASTA*] + ] + ) + #arrow( + stroke: 6pt + black, + ( + 4pt, + -245pt + ), + ( + 4pt, + -195pt, + ), + ) +] +#counter("logical-slide").update( n => n - 1 ) +#slide( + foreground: { + place( + bottom+left, + dx: 200pt, + dy: -50pt, + ellipse( + width: 100pt, + height: 300pt, + stroke: 10pt + pirat-color.red, ) ) - arrow( - stroke: 10pt + pirat-color.red, - ( - 130pt, - -400pt, - ), - ( - 180pt, - -320pt, - ) - ) - arrow( - stroke: 10pt + pirat-color.red, - ( - 370pt, - -400pt, - ), - ( - 320pt, - -320pt, - ) + place( + bottom+left, + dx: 140pt, + dy: -370pt, + text(weight: "bold", fill: pirat-color.red, size: 30pt)[Dynamic Results] ) } )[ #set align(center+horizon) #theseus-outline() + #place( + bottom+left, + dx: -20pt, + dy: -360pt, + box[ + #for i in range(3) { + place( + dx: i*10pt, + dy: i*10pt, + ico.apk(height: 60pt, fill: red) + ) + } + #place(dy: 85pt)[*RASTA*] + ] + ) + #arrow( + stroke: 6pt + black, + ( + 4pt, + -245pt + ), + ( + 4pt, + -195pt, + ), + ) ] -#counter("logical-slide").update( n => n - 1 ) -#slide( - title: [Dynamic Analysis], - foreground: ghost-1(x: 97%, y: 10%, height: 70pt) -)[ - #set align(center+horizon) - #show figure.caption: none - // TODO: enlever 1er 6iem pass, garder nb failed, remplacer vide par '-' sous '209' - // enlever nb activity - #set table( - fill: (x, y) => { - if ( - x == 4 and y > 2 - ) { - highlight-color - } else { - none - } - } - ) - #scale(100%, reflow: true, get_figure()) -] +#for i in range(3) { + let hide-if-not-2(i, body) = if i == 2 { body } else { hide(body) } + slide( + title: [Dynamic Analysis], + foreground: ghost-1(x: 97%, y: 10%, height: 70pt) + )[ + #import "5_theseus/X_var.typ": * + #import "lib.typ": num, mypercent + #set align(center+horizon) + #let nb_col = 4 + #stack(dir: ltr, table( + columns: nb_col, + stroke: none, + fill: (x, y) => { + if ( + (x == 2 and y > 2 and i == 1) or + ((x, y) in ((1, 6), (1, 7)) and i == 2) + ) { + highlight-color + } else { + none + } + }, + inset: 7pt, + align: center+horizon, + table.header( + table.hline(), + table.cell(colspan: nb_col, inset: 2pt)[], + table.cell(rowspan: 2)[], + table.cell(rowspan: 2)[nb apk], + table.vline(end: 3), + table.vline(start: 4), + table.cell(colspan: 2, inset: (bottom: 0pt))[activities visited], + [0], [$>= 1$], + ), + 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.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: nb_col, inset: 2pt)[], + table.hline(), + ), h(3em), hide-if-not-2(i)[ + #set list(spacing: 2em) + - *Reflection* detected on \ #strong(mypercent(dyn_res.reflection.nb, dyn_res.all.nb)) of APKs tested + - *DLC* detected on \ #strong(mypercent(dyn_res.code_loading.nb, dyn_res.all.nb)) of APKs tested + ]) + ] +} #slide( title: [Collected Bytecode], @@ -1696,6 +1793,12 @@ -250pt, ) ) + place( + bottom+left, + dx: 680pt, + dy: -350pt, + text(weight: "bold", fill: pirat-color.red, size: 30pt)[#set align(center); Improved \ Results?] + ) } )[ #set align(center+horizon) @@ -1703,7 +1806,7 @@ ] #for i in range(3) { - counter("logical-slide").update( n => n - 1 ) + if i != 0 {counter("logical-slide").update( n => n - 1 )} slide( title: [Added Method Calls], @@ -1734,7 +1837,7 @@ table.header( //[SHA 256], [Original CG edges], [New CG edges], [Edges added], [Reflection edges added], table.cell(rowspan: 2)[APK SHA 256], - table.cell(colspan: nb_col - 1)[Number of Call Graph edges], [Diff (Total After - Before)], [Added Reflection], + table.cell(colspan: nb_col - 1)[Number of Call Graph edges], [Diff (Total After - Before)], [Replaced Reflection], ), table.hline(), ..compared_callgraph.map( @@ -1788,13 +1891,18 @@ ] #slide( - title: [Conclusion], + title: [PB3: Conclusion], )[ + #set list(indent: 1em) #item-by-item[ - - We can statically analyse APKs with reflection and dynamic code loading with our method + - 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 + - The dynamically loaded bytecode we intercepted is *mainly telemetry and advertisement* related + ] + #only("4-", underline[Software Contributions:]) + #item-by-item(start: 5)[ + - *Androscalpel*: rust crate to *parse, modify and generate bytecode* + - *Theseus*: tool implementing the method presented here ] ] diff --git a/slides/outlines.typ b/slides/outlines.typ index 69a15f9..eb3376e 100644 --- a/slides/outlines.typ +++ b/slides/outlines.typ @@ -230,7 +230,8 @@ let height = big_icon_size + small_icon_size /2 + arrow_gap let width = 6.5 * small_icon_size + 2 * big_icon_size let app1 = apk( - height: small_icon_size + height: small_icon_size, + fill: red, ) let app2 = apk( height: small_icon_size, @@ -305,6 +306,24 @@ dy: patcher_pos.at(1), patcher, ) + { + set text(weight: "semibold", fill: luma(30%)) + place( + left+top, + dx: analyser_pos.at(0) + 0.5em, + dy: analyser_pos.at(1) - 1.5em, + )[Static Analysis] + place( + left+top, + dx: rprt_pos.at(0) - 1em, + dy: rprt_pos.at(1) - 2.5em, + )[#set align(center); Reflection \ Data] + place( + left+top, + dx: patcher_pos.at(0) + 0.5em, + dy: patcher_pos.at(1) - 1.5em, + )[Patching] + } arrow( stroke: arrow_width + black, ( @@ -376,6 +395,8 @@ small_icon_size: 60pt, big_icon_size: 90pt, stage: "theseus", + labels: false, + rasta: false, ) = context { let stages = ( "static-only", @@ -515,6 +536,43 @@ height: height, //stroke: black, { + if labels { + set text(weight: "semibold", fill: luma(30%)) + if stage != "static-only" { + place( + left+bottom, + dx: rprt_pos2.at(0) - 1.4em, + dy: rprt_pos2.at(1) - rprt_size.height - 0.5em, + )[#set align(center); Reflection \ Data] + place( + left+bottom, + dx: dex_pos0.at(0) - 4em, + dy: dex_pos0.at(1) + 0.8em, + )[Dyn Loaded Code] + place( + left+bottom, + dx: patcher_pos.at(0) - 0.5em, + dy: patcher_pos.at(1) - patcher_size.height - 0.5em, + )[Patching] + place( + left+bottom, + dx: phone_pos.at(0) - 1em, + dy: phone_pos.at(1) - phone_size.height - 0.5em, + )[#set align(center); Dynamic \ Analysis] + } + if stage in ( + "theseus", + "static-vs-dyn", + "theseus-vs-static", + "static-only" + ) { + place( + left+bottom, + dx: analyser_pos.at(0) - 1.5em, + dy: analyser_pos.at(1) + 1em, + )[Static Analysis] + } + } if stage == "static-only" { place( left+bottom, @@ -725,6 +783,7 @@ if stage in ( "theseus", "theseus-vs-static", + "theseus-no-static" ) { arrow( stroke: arrow_width + black,