diff --git a/.gitignore b/.gitignore index 1ca8ae5..a88827b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,4 @@ public/ *.bin *.so -.direnv/ -.envrc -include.mk -themes/ .hugo_build.lock diff --git a/config.toml b/config.toml index 25e0f9c..4d794e4 100644 --- a/config.toml +++ b/config.toml @@ -23,7 +23,6 @@ enableRobotsTXT = false toc = true post_navigation = true mainSections = [ - "ia", "secu_systeme", "conception_formelle", "secu_logicielle", diff --git a/content/ia/1_introduction/index.md b/content/ia/1_introduction/index.md deleted file mode 100644 index 7c1ccd3..0000000 --- a/content/ia/1_introduction/index.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "IA : introduction" -date: 2023-10-10 -tags: ["IA"] -categories: ["Intelligence artiticielle", "Cours"] -mathjax: true ---- - -En terme de prévisions autour de l'intelligence artificielle, on entend tout et -n'importe quoi et pas depuis hier! Beaucoup de prévision se sont révélée fausse -néanmoins des progrès existent dans la génération d'image ou encore les voitures -autonomes. - -Nous pouvons citer [Herbert Simon][h_simon] (1965) : - -> Machines will be capable, within twenty years, of doing any work a man can do - -Ou encore [Marvin Minsky][m_minsky] (1970) - -> We solved the venerable mind/body problem, explaining how a system composed -> of matter can have the properties of mind - -Mais plusieurs limitation existants encore: - - * Si dans certains domaines spécialisés elle surpasse les capacité humaines, on - est encore loin **d'une véritable IA générale**; - * Des attaques contre les modèles d'apprentissage existent comme l'ajout de - bruit, des **erreurs dans la détection** entrainent pourtant **une détection - confiante**; - * Les limites fondamentales, comme **morales** par exemple : que doit faire - une voiture autonome dans une situation critique. Nous avons aussi citer les - biais. - -[h_simon]: https://fr.wikipedia.org/wiki/Herbert_Simon -[m_minsky]: https://fr.wikipedia.org/wiki/Marvin_Minsky - -## Un peu d'historique - -Tout commence en 1950 par une publication [d'Alan Turing][a_turing] : *Can -machine think?* - -Le terme *intelligence artificielle* est utilise pour la première fois en 1956 -par [John McCarthy][j_mccarthy]. - -En 1959, le MIT lance le projet *Intelligence Artificielle*. En 1997, *Deep -Blue* bat Gary Kasparov aux échecs. À partir de là les choses s'accélèrent : en -2011 *Watson* bat les champions de *Jéopardy!*, en 2015 un programme -informatique jour à des jeux vidéo *Atari*, en 2016 *AlphaGo* bat les campions -de jeu de Go, en 2017 *Libratus* bat des champions de poker. - -Les progrès récents commencent à ouvrir la porte au prévisions faites dès les -années 60 par Minsky et Simon. - -[a_turing]: https://fr.wikipedia.org/wiki/Alan_Turing -[j_mccarthy]: https://fr.wikipedia.org/wiki/John_McCarthy - -## Définir une IA - -Une définissions possible est "*formalisation et une analyse de causalités -d'évènements*". Elle entraine beaucoup de fantasmes. - -Une première approche pourrait être "*discipline de l'informatique dont le but -est de construire des programmes intelligents*". Dans ce cas qu'est-ce qu'un -programme? - -Marvin Minsky de son côté la définie comme: *ce que l'homme ferait moyennant une -certaine intelligence*. Donc relatif à ce que savent faire les hommes. - -Plus pragmatique et pratique, nous pourrions dire "*tous problèmes pour lesquels -il n'existent pas d'algorithme connu ou avec un coût raisonnable*". Cette -définition peut être résumée en "*problème que l'on ne sait pas résoudre -efficacement*", elle est donc à relier avec **la théorie de la complexité** - -Plus adapté, "*l'intelligence artificielle doit proposer des -solutions logicielles permettant aux programmes de raisonner logiquement*". Cette -définition est basée sur les formalismes logiques : - - * Démonstration automatique des théorèmes; - * Utilisation des règles précises d'inférence; - * Qualités issues des mathématiques (preuves, explications). diff --git a/content/secu_systeme/3_compilation_obfiscation_llvm/index.md b/content/secu_systeme/3_compilation_obfiscation_llvm/index.md deleted file mode 100644 index 324ff76..0000000 --- a/content/secu_systeme/3_compilation_obfiscation_llvm/index.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -title: "Sécurité système : Introduction à la compilation et obfuscation avec llvm" -date: 2023-10-12 -tags: ["LLVM", "assembleur"] -categories: ["Sécurité système", "Cours", "TD"] -mathjax: true ---- - -Le but de ce cours est de comprendre ce qui se passe lors de la création de -binaire. Nous nous concentrerons sur clang / LLVM dans le cadre de la sécurité, -que se soit en défense ou en attaque. - -## compilation en trois étapes - -Il est possible de découper la compilation en 3 étapes avec LLVM: - - 1. Le code source passe par un *frontend* avec l'analyse syntaxique et - sémantique; - 2. Le résultat passe par un *middle-end* qui va procéder aux optimisations; - 3. Et enfin par un *backend* qui va transformer le code optimisé en code - assembleur et réaliser des optimisations spécifique à l'architecture cible. - -L'obfuscation du code se fait au niveau du *middle-end*. - -Dans le cadre de la suite d'outils autour de LLVM nous avons plusieurs -*backends* (clang, flang, RetDec) qui transforme le code en entrée en code -LLVM (*middle-end*) sur lequel seront réalisées les optimisations. Et enfin -le résultat passe dans un compilateur qui le transforme en code binaire (*ARM. -x86_64, Webassembly,...*). - -Nous avons déjà vu cette partie lors -[de l'introcution]({{}}) - -### Vie d'un hello world - -Voyons maintenant ce qu'il se passe dans le cadre du code suivant: - -```c -#include -int main(int argc, char** argv) { - puts("Hello, world!\n"); - return 0; -} -``` -#### l'AST (Frontend) - -Voyons l'AST produit avec `clang` grâce à la commande suivante avec la commande: - -```shell -clang -Xclang -ast-dump -fsyntax-only main.c -``` - -le voici donc: - -```text -`-FunctionDecl 0x56028bd93cc0 line:3:5 main 'int (int, char **)' - |-ParmVarDecl 0x56028bd93b68 col:14 argc 'int' - |-ParmVarDecl 0x56028bd93be8 col:27 argv 'char **' - `-CompoundStmt 0x56028bd93e88 - |-CallExpr 0x56028bd93e00 'int' - | |-ImplicitCastExpr 0x56028bd93de8 'int (*)(const char *)' - | | `-DeclRefExpr 0x56028bd93d70 'int (const char *)' Function 0x56028bd8f730 'puts' 'int (const char *)' - | `-ImplicitCastExpr 0x56028bd93e40 'const char *' - | `-ImplicitCastExpr 0x56028bd93e28 'char *' - | `-StringLiteral 0x56028bd93d90 'char[15]' lvalue "Hello, world!\n" - `-ReturnStmt 0x56028bd93e78 - `-IntegerLiteral 0x56028bd93e58 'int' 0 -``` - -#### Langage intermédiaire (Middle-end) - -Maintenant, regardons le code LLVM produit par clang: - -```shell -clang -S -emit-llvm -Xclang -disable-O0-optnone main.c -o -``` - -À ce niveau, le code obtenu n'est pas optimisé: - -```llvm -; ModuleID = '/home/user/code/main.c' -source_filename = "/home/user/code/main.c" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" -@.str = private unnamed_addr constant [15 x i8] c"Hello, world!\0A\00", align 1 -; Function Attrs: noinline nounwind uwtable -define dso_local i32 @main(i32 noundef %0, ptr noundef %1) #0 { - %3 = alloca i32, align 4 - %4 = alloca i32, align 4 - %5 = alloca ptr, align 8 - store i32 0, ptr %3, align 4 - store i32 %0, ptr %4, align 4 - store ptr %1, ptr %5, align 8 - %6 = call i32 @puts(ptr noundef @.str) - ret i32 0 -} -declare i32 @puts(ptr noundef) #1 -``` -#### Optimisation (Middle-end) - -Voici la commande permettant de lancer les optimisations sur le code -intermediaire LLVM: - - -```shell -opt -S -O2 main.ll -``` - -Le code produit contient plus les élements jugés inutiles comme ici `argv` et -`argc` : - -```text -; ModuleID = '' -source_filename = "/home/user/code/main.c" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" -@.str = private unnamed_addr constant [15 x i8] c"Hello, world!\0A\00", align 1 -; Function Attrs: nofree noinline nounwind uwtable -define dso_local i32 @main(i32 noundef %0, ptr nocapture noundef readnone %1) local_unnamed_addr #0 { - %3 = tail call i32 @puts(ptr noundef nonnull @.str) - ret i32 0 -} -; Function Attrs: nofree nounwind -declare noundef i32 @puts(ptr nocapture noundef readonly) local_unnamed_addr #1 -``` - -#### Transformation en assembleur (Backend) - -Voici la commande qui permet de transformer le code intermédiaire optimisé en -code assembleur: - -```shell -llc main.ll -o main.s -``` - -Le code obtenu sera cette-fois dépendant de l'architecture cible. Une fois -trasformé en code machine, il se sera pas directement exécutable, il manque -l'édition de lien. - -## LLVM - -LLVM signifie *Low Level Virtual Machine* est une spécification d'une -**représentation intermédiaire** (LLVM-IR) accompagnée d'un ensemble d'outils -qui communiquent autour de rette réprésentation. - -LLVM se compose de **modules**, son langage est de type RISC fortement typé et -non signé - certaine opération le sont par contre comme `div` et `sdiv`. - -Prenons comme exemple le code *C* suivant : - - -```c -double polynome(float x) { - return 2*x*x*x + 7*x*x + 9*x + 1234; -} -``` - -Voici la représenation LLVM : - -```llvm -; Function Attrs: mustprogress nofree nosync nounwind readnone willreturn uwtable -define dso_local double @polynome(float noundef %0) local_unnamed_addr #0 { - %2 = fmul float %0, 2.000000e+00 - %3 = fmul float %2, %0 - %4 = fmul float %0, 7.000000e+00 - %5 = fmul float %4, %0 - %6 = tail call float @llvm.fmuladd.f32(float %3, float %0, float %5) - %7 = tail call float @llvm.fmuladd.f32(float %0, float 9.000000e+00, float %6) - %8 = fadd float %7, 1.234000e+03 - %9 = fpext float %8 to double - ret double %9 -``` -### Branchements - -Il n'y a pas de structure de contrôle comme dans les langages de plus haut -niveau, mais certaines instructions permettent des branchements (conditionnels -ou non) comme `br`, `ret`, `switch`, `invoke`, `resume` ... - -Prenons comme exemple : - -```c -void then_(int); -void else_(int); -void if_then_else(int a, int b, int c) { - if(a) then_(b); - else else_(c); -} -``` - le code correspondant en représentation intermediaire: - -```llvm - %4 = icmp eq i32 %0, 0 - br i1 %4, label %6, label %5 - -5: ; preds = %3 - tail call void @then_(i32 noundef %1) #2 - br label %7 - -6: ; preds = %3 - tail call void @else_(i32 noundef %2) #2 -``` - -### Static Single Assignement et PHI-Node - -Les instructions LLVM prennent la forme de *SSA* ce qui signifie principalement -qu'une variable peut être **assignée une seule fois**. C'est ici que les -*PHI-Nodes* entrent en jeu. - -Prenons le code *C* suivant: - -```c -a = 1; -if (v < 10) - a = 2; -b = a; -``` - -Le code *LLVM-IR* coorespondant est le suivant: - -```llvm -a1 = 1; -if (v < 10) - a2 = 2; -b = PHI(a1, a2); -``` - -L'instruction `b = PHI(a1, a2)` permet de faire une *affectation conditionnelle* -de `b`. Le fonctionement de phi est le suivant: - -```llvm -%10 = PHI i32 [valeur, label] [valeur, label] -``` -`PHI` peut faire référence à des variables non déclarées. - -### Memoire - -LLVM-IR dispose de quelques instructions pour l'accès à la mémoire comme `load`, -`store`, `cmpxchg`, - -### Types complexe - -*LLVM-IR* dispose de plusieurs rypes complexe comme: - - * **les vecteurs** sour la forme `<4 x i32>` représentant 4 entiers de 32 bits; - * **les tableaux** sous la forme `i32[10]` - * **les structures** sous la forme `my_struct = type { i32, i32}` - -### Les exceptions - -*LLVM-IR* permet la gestion des exceptions, mais nous n;utiliserons pas ces -mécanismes das le cadre de ce cours. LLVM dispose de fonction intrinsèques pour -la gestion des exceptions préfixée par `llvm.eh`. Toutes les fonctions -disponibles sont référencées [sur cette page][l_eh_exception] - -[l_eh_exception]: https://llvm.org/docs/ExceptionHandling.html#exception-handling-intrinsics - -## L'obfuscation - -L'obfuscation a pour but principal de ralentir au maximum l'opération de reverse -engineering. Il est souvent question de protéger les parties les plus sensibles, -celle contenant des clés de chiffrement, des algorithmes etc. - -Cette protection sera de toutes manières éphemère et elle a un prix, voire même -plusieurs: - - * exécution plus lente - * consommation mémoire alourdie - * binaire plus volumineux - -Il faut alors trouver un compromis. nous allons voir les techniques utilisées. - -### Obfusquer les instructions - -Il est question de remplacer les instructions élémentaires par des équivalent -par exemple: - -```text -A + B == (A & B)<<1 + (A ^ B) -A - B == (A & -B)<<1 + (A ^ -B) -A ^ B == A + B - (A & B)<<1 -``` - -### Prédicat opaques - -Il existe plusiers façon d'opacifier certaines partie du code. Il est pas -exemple possible d'ajouter du **code mort** : une condition toujours vérifiée -mène au code "correct" : - -```c -int predicat = F(x); // F(x) est toujours vrai -if ( predicat ) { - // donc on exécutera toujours le bon code - good_code(); -} -else { - // et ça c'est pour perdre le 'reverser' - useless_insanely_complex_code(); -} -``` - -Il est aussi possible de remplacer certaines fonctions mathematiques par -certaines autres par exemple: - -``` -log2(x) == log10(x) - log10(2) -``` - -Il est aussi possible des constantes opaque, trouver par exemple pi avec la -formule suivante: - -```c -pi = 4 * (1 - 1/3 + 1/5 - 1/7 + ... + 1/N); -``` - -Après suffisement d'itération, la marge d'erreur est en dessous de 0,2. - -### Tester ses obfuscations - -Il est très important de **tester les obfuscations** déjà pour ne pas introduire de -bugs, mais aussi pour vérifier qu'elles survivent aux optimisations. Il est -possible de faires des tests unitaires, des tests par *fuzzing*, test de -reproductibilité. - -Il faut savoir que certaines optimisations effectuées par les compilateurs -peuvent faire penser à des obfuscations. La division etant très couteuses, elle -est remplacée par une série d'opérations plus rapide. - -## Premiers TP - -Il est question ici de modifier le code intermédiaire LLVM en utilisant son API. -Nous allons manipuler l'IR. LLVM est capable de faire de l'instrospection par -exemple: - -```c -// est ce que I est une fonction -isa(I); -```