Compare commits
No commits in common. "adf3897621a4cb9541b8d2668adb7f997c938e29" and "9b629692ea3b3e345267e34fd1c164a9ac0772e9" have entirely different histories.
adf3897621
...
9b629692ea
4 changed files with 0 additions and 422 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,8 +1,4 @@
|
||||||
public/
|
public/
|
||||||
*.bin
|
*.bin
|
||||||
*.so
|
*.so
|
||||||
.direnv/
|
|
||||||
.envrc
|
|
||||||
include.mk
|
|
||||||
themes/
|
|
||||||
.hugo_build.lock
|
.hugo_build.lock
|
||||||
|
|
|
@ -23,7 +23,6 @@ enableRobotsTXT = false
|
||||||
toc = true
|
toc = true
|
||||||
post_navigation = true
|
post_navigation = true
|
||||||
mainSections = [
|
mainSections = [
|
||||||
"ia",
|
|
||||||
"secu_systeme",
|
"secu_systeme",
|
||||||
"conception_formelle",
|
"conception_formelle",
|
||||||
"secu_logicielle",
|
"secu_logicielle",
|
||||||
|
|
|
@ -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).
|
|
|
@ -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]({{<ref "secu_systeme/1_introduction/index.md#la-compilation">}})
|
|
||||||
|
|
||||||
### Vie d'un hello world
|
|
||||||
|
|
||||||
Voyons maintenant ce qu'il se passe dans le cadre du code suivant:
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include <stdio.h>
|
|
||||||
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 <main.c:3:1, line:6:1> line:3:5 main 'int (int, char **)'
|
|
||||||
|-ParmVarDecl 0x56028bd93b68 <col:10, col:14> col:14 argc 'int'
|
|
||||||
|-ParmVarDecl 0x56028bd93be8 <col:20, col:27> col:27 argv 'char **'
|
|
||||||
`-CompoundStmt 0x56028bd93e88 <col:33, line:6:1>
|
|
||||||
|-CallExpr 0x56028bd93e00 <line:4:3, col:25> 'int'
|
|
||||||
| |-ImplicitCastExpr 0x56028bd93de8 <col:3> 'int (*)(const char *)' <FunctionToPointerDecay>
|
|
||||||
| | `-DeclRefExpr 0x56028bd93d70 <col:3> 'int (const char *)' Function 0x56028bd8f730 'puts' 'int (const char *)'
|
|
||||||
| `-ImplicitCastExpr 0x56028bd93e40 <col:8> 'const char *' <NoOp>
|
|
||||||
| `-ImplicitCastExpr 0x56028bd93e28 <col:8> 'char *' <ArrayToPointerDecay>
|
|
||||||
| `-StringLiteral 0x56028bd93d90 <col:8> 'char[15]' lvalue "Hello, world!\n"
|
|
||||||
`-ReturnStmt 0x56028bd93e78 <line:5:3, col:10>
|
|
||||||
`-IntegerLiteral 0x56028bd93e58 <col:10> '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 = '<stdin>'
|
|
||||||
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<CAllInst>(I);
|
|
||||||
```
|
|
Loading…
Add table
Add a link
Reference in a new issue