• Aucun résultat trouvé

Récapitulatif

Les opérations d’édition et obfuscation présentées dans ce chapitre sont résumées par la figure 4.3. Nous les classons selon trois critères principaux que sont la facilité de l’automati- sation des opérations d’obfuscation, les représentations ainsi que les méthodes adaptées pour la recherche de similitude en présence de ces obfuscation. Enfin, nous exposons pour chaque opération d’obfuscation sa classification selon Bellon [86] et Roy [87]. Bellon, pour son com- paratif quantitatif de différents outils de recherche de clones classe les clones en trois grandes catégories : les clones de type 1 comportant pour seules opérations d’édition des modifica- tions de formatage insensibles par analyse lexicale, les clones de type 2 avec des modifications d’identificateurs et de type et les clones de type 3 avec des modifications syntaxiques. Roy et al. quant à eux introduisent pour leur comparatif qualitatif un classement à quatre niveaux dont les deux premiers correspondent à ceux de Bellon. Le troisième niveau concerne les clones avec opérations d’ajout, suppression et modification de code ; le quatrième niveau introduit des

clones algorithmiquement équivalents avec transposition d’instructions, ajout de structures de contrôle ainsi que d’autres opérations syntaxiquement non-neutres.

4.11. Ré capitulatif 70 Opération Automatisation de l’obfuscation

Représentations adaptées Méthodes adaptées Taxonomie

[86]/[87]

Modifications de formatage Possible Toutes sauf brute 1/1c

Édition de commentaires Possible Toutes sauf brute 1/1b

Renommage d’identificateurs Possible (syntaxe) Toutes sauf brute, lexicale

non abstraite

2/2a

Transposition de code Possible (PDG) PDG PDG homomorphes, aligne-

ment local de séquences, ex- tension sur germes exacts

3/4abc Ins./supp. de code triviale-

ment inutile

Possible syntaxique normalisée, PDG PDG homomorphes, aligne-

ment de séquences, extension sur germes exacts

3/3d

Ins./supp. de code non-

trivialement inutile

Difficile Trace d’exécution PDG homomorphes, aligne-

ment de séquences, extension sur germes exacts

3 Réécriture triviale d’expres-

sions Possible (syntaxe) Syntaxique normalisée alignement de séquences, ha-chage dégradé de sous-arbres 3/2bd,3ab

Réécriture non-triviale d’ex- pressions

Difficile Aucune Alignement de séquences, ha-

chage dégradé de sous-arbre 3

Changements de types Possible (syn-

taxe+sémantique)

Syntaxique abstraite 2/2c

Modifications de structures de contrôle

Possible (syntaxe) Syntaxique normalisée Alignement de séquences, ex-

tension sur germes exacts

3/3ce,4d Factorisation/développement

de fonctions

Possible Graphe d’appel Factorisation, extension sur

graphe d’appel

3/4 Traduction vers un autre lan-

gage

Possible Traduction inverse, arbres de

syntaxe avec abstraction de langage

3

Obfuscation dynamique Possible Trace d’exécution NA

Modifications sémantiques

non-triviales

Impossible (manuel) Aucune Aucune NA

Fonction originale Obfuscation syntaxiquement neutre

1 intfib(int n) {

/∗ Initialisation des variables ∗/ intk = 1; int l = 1; int m = 0; 5 /∗ Traitement des cas où n <= 2 ∗/

if(n == 1) return k; if(n == 2) return l;

/∗ m contient la somme des deux termes précédents ∗/

for(int i = 3; i < n; i++) 10 { m = k + l; k = l; l = m; }

returnm; }

1 intfib(n) {

3 inta = 1; /∗ Renommage de variables ∗/ intb = 1; int c = 0;

if(n == 1) return k; if(n == 2) return l;

for(int i = 3; i < n; i++) { c = a + b; a = b; b = c; }

8 returnm; }

Transposition de code Insertion de code inutile

1 intfib(int n) {

intl = 1; int k = 1; intm = 0;

for(int i = 3; i < n; i++) 6 { m = k + l; k = l; l = m; } if(n == 2) return l; if(n == 1) return k; returnm; } 1 intfib(int n) {

intk = 1; int l = 1; int m = 0; intinutile = 1; /∗ Déclaration inutile ∗/ 5 if(n == 1) return k;

if(n == 2) return l; for(int i = 3; i < n; i++)

{ m = k + l; k = l; l = m; } for(int i = 0; i < n; i++)

10 inutile++; /∗ Boucle inutile insérée ∗/ returnm;

}

Réécriture d’expression Changement de types

1 intfib(int n) {

3 intk = 1; int l = 1; int m = 0; if(n == 1) return k∗1; if(n == 2) return l+0; for(int i = 3; i < n; i += 1) { m = k + 2∗l − l; 8 k = l/1 + 0; l = m ∗ m / (m ∗ 1); } returnpgcd(m,m); } 1 intfib(short n) {

3 longk = 1; long l = 1; long m = 0; if(n == 1) return k;

if(n == 2) return l;

for(unsigned int i = 3; i < n; i++) { m = k + l; k = l; l = m; } 8 return(int)m;

}

Fig. 4.4 – Fonction de calcul de nombres de Fibonacci en Java obfusquée par différentes méthodes

4.11. Récapitulatif 72

Modification de structures de contrôle Externalisation de fonction

1 intfib(int n) {

intk = 1; int l = 1; int m = 0; if(! (n == 1 || n == 2)) {

6 for(int j = 0; j < 1; j++) for(int i = 3; i < n; i++)

{ m = k + l; k = l; l = m; } returnm; } else if (n == 1) return k; 11 else if(n == 2) return l; } 1 intfib(int n) {

3 intk = 1; int l = 1; int m = 0; intm = fib12(n);

if(m != −1) return m; else

{

8 for(int i = 3; i < n; i++) { m = k + l; k = l; l = m; } returnm; } } 13 intfib12(int n) { if(n == 1) return 1; if(n == 2) return 2; 18 return−1; }

Traduction en JavaScript Modification sémantique non-triviale et obfuscation dynamique

1 /∗ Fonction JavaScript avec chargement dynamique ∗/

function fib(n) {

var k = 1; var l = 1; var m = 0; if(n == 1) return k;

6 if(n == 2) return l; for(var i = 3; i < n; i++)

eval("c␣=␣a␣+␣b;␣a␣=␣b;␣b␣=␣c;". replace("a","k").replace("b","l"). replace("c","m")); returnm; } 1 intfib(int n) { /∗ Réécriture récursive ∗/ if(n == 1) return 1; 5 else if(n == 2) return 2; else returnfib(n−1) + fib(n−2); }

Recherche de correspondances sur des

chaînes de lexèmes

74

Considérer le code source sous la forme d’une chaîne de lexèmes permet l’utilisation d’al- gorithmes de recherche de motifs classiquement utilisés sur les séquences. Nous pouvons dans un premier temps réaliser des alignements entre chaînes de lexèmes afin de déduire des sous- séquences similaires avec l’existence éventuelle de fossés : des correspondances approchées peuvent alors être mises en évidence. À cet effet, nous employons des techniques de program- mation dynamique. Une digression est alors réalisée pour étendre ces méthodes aux structures bidimensionnelles que sont les arbres de syntaxe : il est alors possible de quantifier la distance entre deux sous-arbres.

Après avoir rappelé les méthodes d’alignement dans le chapitre 5, nous nous intéressons à des approches plus dimensionnables mais se limitant à la recherche de facteurs répétés exactement similaires. Les structures d’indexation de suffixes que sont la table et l’arbre de suffixes sont introduites avec d’autres structures utiles annexes. Elles seront utilisées afin de proposer la méthode de factorisation exposée dans le chapitre 7. Celle-ci permet de modéliser les similarités sur un jeu fixe de projets par le calcul d’un graphe d’appel synthétique commun à ces projets, le code dupliqué commun étant représenté par des fonctions créées.

Ensuite nous nous concentrons sur les techniques de méta-lexémisation travaillant sur des k-uplets de lexèmes consécutifs (k-grams) plutôt que sur des lexèmes uniques. L’alphabet manipulé est donc de cardinalité plus importante mais la fréquence d’apparition de chacun de ses membres est plus faible. Ces k-grams peuvent être représentés par des empreintes et indexés dans une base pour un ensemble incrémental de projets.

La figure 4.4 présente une vue synthétique des différents processus décrits au cours de cette partie avec leur rôle et coopération pour la recherche de similarité dans du code source.

de lexèmes Graphe des farmax exacts (chapitre 6) Séquence de k-grams Empreintes de k-grams Méta-lexèmes partagés Séquences candidates de lexèmes Graphe de fonctions de séquences de lexèmes Alignement local des fonctions (chapitre 5)

Présélection des paires de fonctions proches

Graphe factorisé final de fonctions de séquences Lexémisation IndexationInterrogation Unités candidates Analyse du graphe Comparaison des paires d'unités (chapitre 5)

Graphe des farmax des séquences

(chapitre 6)

Empreintes de (2i k)-grams sur paire

de séquences Jeu de correspondances exactes non-chevauchantes

Tuilage glouton

Factorisation

(chapitre 7)

Méta-lexémisation

(chapitre 8) Correspondances approchées Métrique de similarité et correspondances fonctionnelles

5

Comparaison de paires par alignement de séquences

Sommaire

5.1 Introduction à l’alignement de séquences de lexèmes . . . 78 5.2 Programmation dynamique . . . 79 5.3 Alignement global . . . 80 5.3.1 Alignement global avec code non-transposable . . . 80 5.3.2 Alignement global avec code transposable . . . 82

La plus longue sous-séquence commune est un facteur (ou un quasi- facteur) . . . 82 La plus longue sous-séquence commune n’est pas un facteur . . . 82 5.4 Alignement local . . . 83 5.4.1 Algorithme de Smith-Waterman . . . 83 5.4.2 Algorithme amélioré avec coupure . . . 84 5.4.3 Raccordement de facteurs par matrice dotplot . . . 84 5.5 Extension aux alignements sur les arbres . . . 85 5.5.1 Approches algorithmiques . . . 85 5.5.2 Applications . . . 87

Insérer ou supprimer du code entre la version originale et la copie d’un morceau de code source induit un impact sur la représentation de celui-ci, que ce soit sous une forme lexemisée ou par un arbre de syntaxe. Ainsi la recherche de facteurs exacts ou sous-arbres exacts, que nous allons aborder ultérieurement, se révèle inadéquate car découpant des correspondances pour cause de fossés non correspondants. Nous rappelons ainsi dans ce chapitre des techniques d’alignement de chaînes et d’arbres par programmation dynamique permettant de récupérer de telles correspondances approchées. Si celles-ci sont de complexité générale prohibitive pour la comparaison globale de projets, elles peuvent s’avérer intéressantes afin de raffiner la nature de zones de forte similarité pressenties par d’autres méthodes de filtrage de complexité temporelle plus avantageuse.

5.1. Introduction à l’alignement de séquences de lexèmes 78

5.1 Introduction à l’alignement de séquences de lexèmes

Présentation La recherche de similarité entre deux projets exprimés sous la forme de chaînes de lexèmes peut bénéficier de méthodes d’alignement de séquences. Ces méthodes utilisant des techniques de programmation dynamique sont couramment utilisées en bioinfor- matique afin de déterminer des zones de similarité entre séquences biologiques (nucléotides d’ADN, ARN ou acides aminés) et quantifier leur niveau de différence ou de ressemblance afin de déterminer des phylogénies. Il s’agit ainsi schématiquement de déterminer les chaînes ajou- tées, supprimées ou modifiées entre les deux séquences comparées. Dans cet objectif soit une métrique de similarité, soit une métrique de distance entre deux séquences t1 et t2 d’éléments

d’un alphabet Σ est calculée à l’aide d’opérations élémentaires que sont : 1. La correspondance entre deux éléments (match).

2. La suppression d’un élément (del). 3. L’ajout d’un élément (add).

4. Le remplacement d’un élément (sub).

À chacune de ces opérations est associée un coût C : l’objectif étant de trouver la séquence d’opérations d’édition maximisant la métrique de similarité ou minimisant la métrique de dis- tance. Dans le premier cas les coûts des opérations d’édition (Cdel, Cadd, Csub) sont strictement

inférieurs au coût de correspondance (Cmatch), dans le second cas il s’agit du contraire. On

note que le jeu d’opérations élémentaires présenté n’est pas minimal (dans la mesure où une substitution peut être remplacée par une suppression suivie d’un ajout) ; il pourrait également être étendu avec de nouvelles opérations si cela présente un intérêt pratique (substitutions d’une chaîne de longueur non-unitaire d’éléments, coûts personnalisés utilisant des matrices de coût...).

Plus longue sous-séquence commune (LCS : Longest Common Subsequence) et suite d’opérations d’édition à deux chaînes d’éléments En cherchant à minimiser la distance d’édition avec Cmatch = 0 < Cdel = Cadd = Csub et en collectant la suite d’opérations

élémentaires match, nous pouvons déterminer une plus longue sous-séquence commune à deux séquences de lexèmes u et v. Nous rappelons (voir page 11) qu’une sous-séquence u′ de la

chaîne u est une suite extraite de u et se présente sous la forme u′ = u

1u′2· · · u′n′ avec pour

tout i ∈ [1..n′− 1] l’existence d’indices α < β tels que u

i = uαet u′i+1= uβ. La détermination

de la LCS de deux suites finies de lexèmes u et v dérivés d’unités de code source nous permet de relever des chaînes discontinues de lexèmes similaires entre u et v liées à la présence de lexèmes insérés, supprimés ou modifiés par des opérations d’édition de code. Alors que la connaissance des opérations de match permet l’établissement de la LCS, celle complémentaire des opérations d’édition permet d’établir la séquence d’opérations élémentaires d’édition de coût minimal transformant u en v. Ainsi par exemple, le couple de chaînes u = abc et v = acd possède pour LCS la sous-séquence ac (obtenue par les opérations de match sur les lexèmes concordants a et c) et pour séquence d’opérations d’édition la suppression de b puis l’ajout de d.

Facteurs correspondants L’objectif d’une méthode d’alignement de lexèmes ne consiste pas à déterminer la LCS de deux unités lexemisées mais plutôt un ensemble de couples de facteurs exactement ou approximativement correspondants. Un couple de facteurs exactement

correspondants présente une égalité lexème par lexème contrairement aux couples approxi- mativement correspondants possédant une distance d’édition non nulle. Le seul critère d’une distance d’édition seuil apparaît néanmoins limitatif pour juger de la pertinence d’un couple de facteurs correspondants. Celle-ci doit être mise en relation avec la longueur (voire le volume) des facteurs. La présence d’une zone de non-correspondance (fossé) importante peut égale- ment motiver la réductibilité d’un couple de correspondances, i.e. son découpage en plusieurs couples de plus petites longueurs n’intégrant pas le fossé.