diff --git a/main.typ b/main.typ index 2371373..e35515d 100644 --- a/main.typ +++ b/main.typ @@ -71,7 +71,7 @@ year: 2025, month: 12, day: 9, - ).display("[year]-[month]-XX"), //[day]"), + ).display("[year]-[month]-[day]"), jury-content: jury-content, university: "CS", keywords-en: keywords-en, diff --git a/slides.typ b/slides.typ index 70913c7..217a10b 100644 --- a/slides.typ +++ b/slides.typ @@ -1,19 +1,24 @@ #import "@preview/polylux:0.4.0": * #import "slides/lib.typ": * -#show link: it => if type(it.dest) == label { - context { - if query(it.dest).len() == 0 { - it.body - } else { - it - } - } -} else { - it -} +#import "@preview/codly:1.3.0": * +#import "@preview/codly-languages:0.1.1": * +#show: codly-init.with() +#let default-codly = ( + display-name: false, + display-icon: false, + zebra-fill: none, + fill: luma(240), + radius: 1em, + inset: (y: 0.15em), + highlighted-default-color: highlight-color, + highlight-fill: it => it.lighten(40%), //highlight-color, +) +#codly-disable() #set text(lang: "en") +#set list(marker: none) + #show: sns-polylux-template.with( txt-font: "New Computer Modern", title-font: "TeX Gyre Heros", @@ -24,6 +29,8 @@ short-title : [], //[PhD Defense], //short-event : [Rennes, 2025/12/9], title-size : 32pt, + section-size : 18pt, + size : 22pt, //logo-1 : image("slides/imgs/logo_irisa.png"), //logo-2 : image("slides/imgs/logo_pirat.png"), // @@ -40,6 +47,14 @@ date : datetime(year: 2025, month: 12, day: 9), ) + +/* +* Intro: +* Dear jury, gentle people of the audience, here and online, thank you for your presence. +* I am Jean-Marie Mineau, and today I will be defending my thesis about Android Application reverse engineering and the many difficulties a reverse engineer might encounter. +* This thesis was suppervised by Jean-François Lalande and Valerie Viet Triem Tong, within the PIRAT research team at IRISA. +*/ + #title-slide( logo: grid(columns: 2, image("slides/imgs/logo_pirat.png"), @@ -53,18 +68,279 @@ new-sec: true, title: [Introduction], hide-title: true, - foreground: { - ghost-5(dx: 10%, dy: 30pt) - ghost-4(dx: 95%, dy: 80%) - //ghost-4(dx: 45%, dy: 43%) - } + /*foreground: { + ghost-5(x: 10%, y: 30pt) + ghost-4(x: 95%, y: 80%) + //ghost-4(x: 45%, y: 43%) + }*/ )[ - Intro 1 + #set align(center+horizon) + #grid( + columns: (1fr, 1fr), + image("slides/imgs/google.png", width: 200pt), + image("slides/imgs/phone.png", height: 350pt) + ) + #v(2em) +] + +#counter("logical-slide").update( n => n - 1 ) + +#slide( + foreground: ghost-4(x: 60%, y: 25%, rot: 45deg) +)[ + #set align(center+horizon) + #grid( + columns: (3fr, 2fr), stack(dir: ltr, + item-by-item[ + - Personal Data and PII + - Computing Power + - Phone + - Mic, Camera, \ Geolocalisation + ], + [ $ => $ ], + item-by-item()[ + - Ransomware/Spyware + - Cryptojacker + - Expander (phone billing) + - Stalkerware + ] + ), + { + move(dx: 20pt, image("slides/imgs/phone.png", height: 350pt)) + } + ) +] + +#slide( + title: [Analysing Applications: Which Tools?], + foreground: eye-3(x: 3%, y: 5%) +)[ + #set align(center+horizon) + #move(dx: -50pt, image("slides/imgs/apk-analysis.svg", width: 300pt)) +] + +// something is broken, so hack to keep the page number at the same +#counter("logical-slide").update( n => n - 1 ) + +#slide( + title: [Analysing Applications: Which Tools?], +)[ + #set list(spacing: 3em) + #item-by-item[ + - #cite(, form: "prose"): systematic literature review for Android static analysis, lists open-sourced tools + - #cite(, form: "prose"): tests analysis tools, raises concerns about reusability and analysis of + real-world applications + ] +] + +// something is broken, so hack to keep the page number at the same +#counter("logical-slide").update( n => n - 1 ) +#slide( + title: [Analysing Applications: Which Tools?], +)[ + #highlight-block(pb1-text) +] + +#slide( + title: [Obfuscation], + //foreground: eye-1(x: 95%, y: 85%, mirror: true) +)[ + #set list(marker: [-]) + + Applications might use *obfuscation* to either: + + - protect their IP + - hide malicious behaviour + + #v(1em)#uncover(2)[ + + We will focus on two techniques: + + - *Dynamic Code Loading* + - *Reflection* + ] +] + +#for i in range(4) { + if i != 0 { + counter("logical-slide").update( n => n - 1 ) + } + show: yes-codly + + slide( + title: [Obfuscation], + subtitle: if i == 0 [Example] else if i == 1 [Dynamic Code Loading] else if i in (2, 3) [Reflection] else { none }, + foreground: eye-1(x: 95%, y: 85%, mirror: true) + )[ + #if i == 0 { + codly(..default-codly) + } else if i == 1 { + codly( + highlighted-lines: (1, 5, 6, 7, 8), + ..default-codly + ) + } else if i == 2 { + codly( + highlighted-lines: (2, 3), + highlights: ( + (line: 10, start: 42, end: 59, fill: pirat-color.blue), + (line: 13, start: 3, end: 21, fill: pirat-color.blue), + ), + ..default-codly + ) + } else if i == 3 { + codly( + highlighted-lines: (10,), + highlights: ( + (line: 12, start: 14, end: 34, fill: pirat-color.blue), + (line: 15, start: 2, end: 19, fill: pirat-color.blue), + ), + ..default-codly + ) + } + #scale(70%, reflow: true)[ + ```java + String DEX = "ZGV4CjA [...] EAAABEAwAA"; + String className = "W5f3 [...] 3sls="; + String methodName = "n6WGYJzjDrUvR9cYljlNlw=="; + + ClassLoader cl = new InMemoryDexClassLoader( + ByteBuffer.wrap(Base64.decode(DEX, 2)), + Main.class.getClassLoader() + ); + + Class loadedClass = this.cl.loadClass(decrypt(className)); + Object obj = "FooBar"; + Object ret = loadedClass.getMethod( + decrypt(methodName), + String.class + ).invoke(null, obj); + ```] + ] +} + +#counter("logical-slide").update( n => n - 1 ) +#slide( + title: [Obfuscation], + subtitle: [Deobfuscated], +)[ + #show: yes-codly + #codly( + skips: ((3, 10), (5, 10), (6, 10)), + ..default-codly + ) + #scale(100%)[ + ```java + public class Foo { + public static String bar(String arg) { + } + } + String ret = Foo.bar("FooBar"); + + ```] +] + +#slide( + title: [Class Loading], +)[ + #set align(center) + #show: yes-codly + #grid( + columns: (2fr, 1em, 1fr), + scale(70%, reflow: true)[ + #codly( + highlights: ( + (line: 1, start: 0, end: 11, fill: pirat-color.blue), + (line: 1, start: 22, end: 43, fill: pirat-color.blue), + (line: 3, start: 14, end: 27, fill: pirat-color.blue), + (line: 6, start: 32, end: 40, fill: pirat-color.blue), + ), + ..default-codly + ) + ```java + ClassLoader cl = new InMemoryDexClassLoader( + ByteBuffer.wrap(Base64.decode(DEX, 2)), + Main.class.getClassLoader() + ); + + Class loadedClass = this.cl.loadClass(decrypt(className)); + ``` + ], [], uncover(2, scale(70%, reflow: true)[ + #codly( + ..default-codly + ) + ```java + class A { + public static void foo() { + B b = new B(); + b.bar(); + } + } + ``` + + Where is the class loader? + ]) + ) +] + +#counter("logical-slide").update( n => n - 1 ) +#slide( + title: [Class Loading], +)[ + #item-by-item[ + - Used to select classes implementation + - More complexe than it looks + - Doubious documentation + - Not studied in the context of Android Static Analysis + ] +] + +#counter("logical-slide").update( n => n - 1 ) +#slide( + title: [Class Loading], +)[ + #highlight-block(pb2-text) +] + +#slide( + foreground: ghost-5(x: 10%, y: 7%) +)[ + #set align(center+horizon) + #grid( + columns: (1fr, 1fr), + gutter: 2em, + [ + == Dynamic Analysis + #item-by-item[ + - Run the application + - _See_ dynamically loaded bytecode + - _See_ reflection calls + - Limited by code coverage + ] + ], + [ + == Static Analysis + #item-by-item(start: 5)[ + - Do *not* run the application + - *Not* limited by code coverage + - Some values cannot be computed + ] + + ], + grid.cell(colspan: 2, uncover(7)[ + #text(size: 30pt)[Can we combine both?] + ]), + ) ] #slide[ - Intro 2 + #highlight-block(pb3-text) +] +#slide[ + #highlight-block(pb1-text) + #highlight-block(pb2-text) + #highlight-block(pb3-text) ] #new-section-slide([Tool Reusability]) @@ -120,13 +396,11 @@ /* - #slide()[ #get_figure()) ] #slide()[ -/* #pl.toolbox.slide-number #context({ pl.toolbox.all-sections((sections, current) => { @@ -135,67 +409,12 @@ } }) }) - */ - #sections() -] - -// #toc-slide( title: [Outline] ) - - - -#slide( - title: [A slide without subtitle], -)[ - This slide does not have a subtitle, but belongs to the first section. -] - -#new-section-slide([Second section]) - -#slide( - title: [Title], - subtitle: [Subtitle], -)[ - plop -] - -#slide( - subtitle: [Hidden subtitle], -)[ - This slide however does not have a title. It belongs to the second section. -] - -#new-section-slide([Third section]) - -#focus-slide()[ - This is a _focus-slide_. -] - -#slide(new-sec: true, title: [Fourth section],)[ - A slide can also open a new section... -] - -#focus-slide(new-sec: [Fifth section],)[ - ... and also a focus-slide can do it! -] - -#empty-slide()[ - Ending slide ] */ -/* -* Notes: -* -* Intro: -* Dear jury, gentle people of the audience, here and online, thank you for your presence. -* I am Jean-Marie Mineau, and today I will be defending my thesis about Android Application reverse engineering and the many difficulties a reverse engineer might encounter. -* This thesis was suppervised by Jean-François Lalande and Valerie Viet Triem Tong, within the PIRAT research team at IRISA. -* -*/ - #pagebreak() #set page(height: auto, margin: 25mm) #bibliography("bibliography.bib") diff --git a/slides/imgs/apk-analysis.svg b/slides/imgs/apk-analysis.svg new file mode 100644 index 0000000..6ee3b9f --- /dev/null +++ b/slides/imgs/apk-analysis.svg @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + .APK + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/slides/imgs/dex_insertion.svg b/slides/imgs/dex_insertion.svg new file mode 100644 index 0000000..96efa89 --- /dev/null +++ b/slides/imgs/dex_insertion.svg @@ -0,0 +1,1062 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .DEX + + + + + + + + + + + + .DEX + + + + + + + + + app.apk + classes.dex + + + .DEX + + + + + + + + + classes2.dex + + + .DEX + + + + + + + + + classes.dex + lib.dex + + + .XML + + + + + + + AndroidManifest.xml + + + .XML + + + + + + + resources.arsc + lib/ + + + .DEX + + + + + + + + + classes2.dex + + + .DEX + + + + + + + + + classes5.dex + + + .DEX + + + + + + + + + classes6.dex + + + .DEX + + + + + + + + + classes3.dex + + + .DEX + + + + + + + + + classes4.dex + + + lib.jar + + + res/ + + + assets/ + + + + + Original Files + Added Files + + + + + diff --git a/slides/imgs/google.png b/slides/imgs/google.png new file mode 100644 index 0000000..a0135df Binary files /dev/null and b/slides/imgs/google.png differ diff --git a/slides/imgs/phone.png b/slides/imgs/phone.png new file mode 100644 index 0000000..8c31937 Binary files /dev/null and b/slides/imgs/phone.png differ diff --git a/slides/imgs/phone.png~ b/slides/imgs/phone.png~ new file mode 100644 index 0000000..d866f2f Binary files /dev/null and b/slides/imgs/phone.png~ differ diff --git a/slides/lib.typ b/slides/lib.typ index 39066fa..cd7e603 100644 --- a/slides/lib.typ +++ b/slides/lib.typ @@ -1,6 +1,8 @@ #import "sns_polylux_template.typ": * #import "figures.typ": figures, get_figure +#import "../lib.typ": pb1-text, pb2-text, pb3-text + #let pirat-color = ( black: rgb("#000000"), white: rgb("#FFFFFF"), @@ -17,6 +19,12 @@ rgb("#E69426"), pirat-color.red, ) +#let highlight-block = block.with( + fill: pirat-color.blue, + width: 100%, + inset: 8pt, + radius: 4pt, +) #let colortest = [ #for th in ( @@ -30,17 +38,60 @@ pirat-color.red, } ] -#let ghost(img, dx: 0pt, dy: 0pt, height: 100pt, ..args,) = context { +#let highlight-color = pirat-color.blue.lighten(40%) + +/* don't work ? at least for raw block? +#let scale-down-to-page(body) = { + layout(size => { + let size_body = measure(body) + let ratio = if size_body.width == 0pt and size_body.height == 0pt { + none + } else if size_body.width == 0pt { + size.height / size_body.height + } else if size_body.height == 0pt { + size.width / size_body.width + } else { + let r_x = size.width / size_body.width + let r_y = size.height / size_body.height + calc.max(r_x, r_y) + } + if ratio == none or ratio >= 1 { + body + } else { + scale(ratio * 100%, body) + } + repr(size) + linebreak() + repr(size_body) + linebreak() + repr(ratio*100%) + }) +} +*/ + +#let ghost( + img, x: 0pt, + y: 0pt, + mirror: false, + rot: 0deg, + height: 100pt +) = context { let img = image(img, height: height) + if mirror { + img = scale(x: -100%, img) + } + if rot != 0deg { + img = rotate(rot, img) + } + let s = measure(img) let hx = s.width / 2 let hy = s.height / 2 place( bottom + left, - dx: dx - hx, - dy: -dy + hy, - ..args, + dx: x - hx, + dy: -y + hy, img ) } diff --git a/slides/sns_polylux_template.typ b/slides/sns_polylux_template.typ index 3ae4ff7..2e5a121 100644 --- a/slides/sns_polylux_template.typ +++ b/slides/sns_polylux_template.typ @@ -38,6 +38,7 @@ cmyk(25%,7%,0%,0%), #let sns-polylux-template_second-text-color = state("txt_color2", none) #let sns-polylux-template_title-text-color = state("title_color", none) #let sns-polylux-template_size = state("size", none) +#let sns-polylux-template_section-size = state("section-size",none) #let sns-polylux-template_title-size = state("title-size", none) // Data @@ -67,6 +68,7 @@ cmyk(25%,7%,0%,0%), txt-color2 : white, title-color : rgb("#444444"), size : 20pt, + section-size : 20pt, title-size : 64pt, bkgnd-color : white, @@ -93,6 +95,7 @@ cmyk(25%,7%,0%,0%), sns-polylux-template_title-text-color.update(title-color) sns-polylux-template_size.update(size) sns-polylux-template_title-size.update(title-size) + sns-polylux-template_section-size.update(section-size) sns-polylux-template_colormap.update(colormap) @@ -127,7 +130,7 @@ cmyk(25%,7%,0%,0%), context { //let section-pages = sns-polylux-template_section-pages.final() if not "END" in sns-polylux-template_section-pages.at(here()).keys() { - let page_num = counter("logical-slide").at(here()) + let page_num = counter("logical-slide").get() sns-polylux-template_section-pages.update(secpages => { secpages.insert("END", page_num) secpages @@ -138,7 +141,7 @@ cmyk(25%,7%,0%,0%), #let register-section(title, no_dedicated_slide: false) = context { // YUCKKKKK, but polylux don't allows access to integers soooooo - let page_num = counter("logical-slide").at(here()) //sns-polylux-template_slide-pages.at(here()) + let page_num = counter("logical-slide").get() //sns-polylux-template_slide-pages.at(here()) if no_dedicated_slide { page_num.at(0) += 1 } @@ -282,9 +285,11 @@ cmyk(25%,7%,0%,0%), counter("logical-slide").update( n => n - 1 ) } ) -#let sections() = context { +#let sections(pos_before_slide) = context { let section-pages = sns-polylux-template_section-pages.final() - let pnum = counter("logical-slide").at(here()).first() + 1 + let section-size = sns-polylux-template_section-size.at(pos_before_slide) + set text(size: section-size) + let pnum = counter("logical-slide").at(pos_before_slide).first() + 1 pl.toolbox.all-sections((sections, current) => { let beg-end-section = (:) let prev-sec = none @@ -383,6 +388,8 @@ cmyk(25%,7%,0%,0%), let progress_bar_height = 3pt let title_bar_height = 2cm + let pos_before_slide = here() + // HEADER let header = align(top, context( { let logo = sns-polylux-template_logo-2.at(here()) @@ -412,7 +419,7 @@ cmyk(25%,7%,0%,0%), align(horizon + center,text(fill: sns-polylux-template_second-text-color.at(here()), { v(-1em) - if hide-section { hide(sections()) } else { sections() } + if hide-section { hide(sections(pos_before_slide)) } else { sections(pos_before_slide) } })) ), grid.cell(colspan: columns.len(), progress-bar()),