cd ../projets/
$ cat secure-ic-obfuscation.md

Étude d'Obfuscation LLVM — Stage Secure-IC

Analyse comparative de 4 obfuscateurs LLVM (O-LLVM, Hikari, Pluto, Tigress) sur du code AES, suivie d'une intégration PoC de Hikari dans la chaîne de compilation du firmware Securyzr iSE de Secure-IC.

Jul 2024
llvmobfuscationreverse-engineeringembarquéaesrisc-vsecure-ic

Stage de fin d’études chez Secure-IC (Rennes, fév.–juil. 2024). Équipe : HOST SW Agent Program, département Chip to Cloud. Encadrant : Sofiane TAKARABT.


Contexte

Le Securyzr iSE de Secure-IC est un module de sécurité matériel (HSM) basé sur un processeur RISC-V. Il gère les opérations cryptographiques, le démarrage sécurisé, le provisionnement de clés et l’identité des appareils pour les marchés IoT/automobile/défense. Le firmware qui y tourne contient de la propriété intellectuelle sensible.

Objectif : évaluer des outils d’obfuscation, en sélectionner un, et l’intégrer dans le système de build de Securyzr pour protéger le code firmware sensible contre le reverse engineering.


Analyse Comparative

Benchmark obfuscateurs — overhead vs similarité CFG

Environnement

Quatre VMs Ubuntu Server 22.04.4 dans VirtualBox 7.0 sur un hôte Windows 10 (Dell, i5-1135G7, 16 Go RAM). Chaque VM avait 3072 Mo de RAM et 4 vCPUs — une par obfuscateur, pour éviter les conflits de dépendances.

ObfuscateurTypeVersion LLVM
O-LLVMPlugin LLVM4.0.1
HikariPlugin LLVM15.0.2
PlutoPlugin LLVM14.0.6
TigressSource-à-source

Code de test : prod_mat.c (multiplication matricielle) et aes.c (implémentation TinyAES).

Outils de mesure : clock() pour le temps d’exécution, du pour la taille des binaires, heaptrack + /bin/time pour la mémoire, BinDiff + Ghidra (BinExport) pour la similarité binaire.

Transformations Testées

TransformationDescription
BCFBogus Control Flow — insère des blocs factices gardés par des prédicats opaques
FLAAplatissement du flot de contrôle — route tous les blocs via un dispatcher
SUBSubstitution d’instructions — remplace les opérateurs par des séquences équivalentes
MBAArithmétique Booléenne Mixte
IBBranchement Indirect
FWFonction Wrapper
BBSDécoupage de Blocs de Base
GLEChiffrement des Variables Globales
VRTLZVirtualisation (Tigress uniquement) — convertit une fonction en interpréteur bytecode
TWOBCF + FLA combinés

Impact sur la Taille des Binaires

BCF et FLA introduisent le plus de surcoût. Tigress VRTLZ est extrême :

ObfuscateurTransformationaes.c originalaes.c obfusquéRatio
TigressVRTLZ18 672 o2 646 922 o61,2×
TigressBCF47 960 o161 986 o3,75×
PlutoTWO25 344 o89 848 o3,5×
O-LLVMTWO25 376 o47 584 o1,88×
HikariSE25 440 o62 504 o2,46×

SUB, FW, MBA et SE ont un impact minimal sur la taille pour tous les outils.

Temps d’Exécution

O-LLVM avait le pire surcoût d’exécution. Hikari était le plus efficace :

  • O-LLVM FLA sur prod_mat.c : ratio ~5×
  • O-LLVM TWO sur prod_mat.c : ratio ~22×
  • Tigress TWO sur aes.c : 0,9013 s (exclu comme valeur aberrante — 50× la baseline)
  • Tigress BCF sur prod_mat.c : segmentation fault — le binaire crashait
  • Hikari : constamment sous 4× de surcoût sur toutes les transformations

Scores de Similarité (BinDiff)

Score plus bas = plus grande divergence par rapport à l’original = obfuscation plus forte :

ObfuscateurTransformationSimilarité
PlutoBCF31,4 %
TigressBCF34,3 %
O-LLVMBCF55,4 %
HikariBCF77,9 %
TousSUB~47–48 %
HikariIB49,2 %

BCF est la transformation la plus impactante pour tous les outils. Pluto produisait les scores les plus bas (plus obfusqué), mais avec un surcoût d’exécution plus élevé qu’Hikari.

Comparaison Compilateurs : LLVM (Clang 14.0) vs GCC 11.4

  • Clang prend légèrement plus de temps à compiler à tous les niveaux d’optimisation
  • Les deux compilateurs améliorent le temps d’exécution avec des niveaux -O plus élevés
  • Les binaires compilés par Clang consomment significativement moins de mémoire que GCC à partir de -O1

Analyse d’Impact

Un pattern clair : un score de similarité plus bas corrèle avec un coût en ressources plus élevé (taille + temps d’exécution). Les transformations comme SUB, FW, MBA, SE produisent peu de changements sur le CFG et affectent à peine les performances. BCF et FLA modifient significativement la structure binaire mais à un coût en ressources.

Hikari offre le meilleur compromis : obfuscation significative avec un surcoût acceptable, basé sur la version LLVM la plus récente, et sans les crashs observés avec Tigress.


PoC — Intégration dans Securyzr iSE

Intégration Hikari dans la chaîne de build Securyzr iSE

Obfuscateur Retenu : Hikari

Choisi pour : le plus large ensemble de transformations parmi les plugins LLVM, base LLVM 15.0.2 (la plus récente), meilleure performance en temps d’exécution, et aucun problème de stabilité (contrairement à Tigress qui crashait sur plusieurs cas de test).

Serveur de Build

SpécificationValeur
CPUIntel Xeon Gold 6242R @ 3,10 GHz
RAM64 Go
Stockage>2,5 To
OSRed Hat Enterprise Linux 8.5

Compilation de Hikari

1
2
3
4
5
cmake -G "Ninja" -DCMAKE_BUILD_TYPE=Release \
  -DLLVM_ENABLE_PROJECTS=clang \
  -DLLVM_TARGETS_TO_BUILD="X86;ARM;RISCV;AArch64" \
  ./llvm -B ./build
ninja

Build multi-architectures ciblant X86, ARM, RISC-V et AArch64 — pour correspondre à la cible RISC-V de Securyzr et à l’environnement de développement x86.

Code Cible

Le Securyzr iSE tourne sur RISC-V et gère la génération de la NVM (Non-Volatile Memory) au pré-boot. La NVM contient le firmware qui doit être chiffré avant d’être envoyé au Secure Element pour authentification. Le chiffrement utilise une implémentation propriétaire d’AES GCM en C — c’est ce code qui est ciblé par l’obfuscation.

Le périmètre a été limité au sous-ensemble AES (compilable x86, sans cross-compilation) compte tenu des 6 mois du stage.

Intégration dans le Système de Build

L’intégration a nécessité trois modifications à la hiérarchie de Makefiles existante :

  1. Remplacement du compilateur : passer de gcc au clang avec Hikari intégré
  2. Flags compilateur : ajouter les flags -mllvm -<transformation> pour chaque technique d’obfuscation
  3. Sortie IR : configurer le build pour émettre des fichiers .ll/.bc en plus des .o, permettant une recompilation multi-architecture ultérieure
1
2
CC = /path/to/hikari/bin/clang
CFLAGS_IR = -emit-llvm -c -mllvm -bcf  # exemple : Bogus Control Flow

Le build produit un répertoire build/ avec :

  • obj/ : fichiers objets obfusqués
  • ir/ : fichiers LLVM IR (indépendants de la plateforme, réutilisables)
  • bin/ : exécutable final

Validation

Deux tests sur le matériel FPGA cible (CPUs ARM + RISC-V) :

  • Test prog — lance le flux de démarrage sécurisé, qui nécessite que la NVM soit correctement générée et chargée. Résultat : NVM générée, transférée, firmware lancé avec succès.
  • CRYPTO_TESTS — teste les fonctions cryptographiques du SE. Résultat : le code AES obfusqué a produit les sorties correctes.

Le code obfusqué n’a eu aucun impact fonctionnel sur le processus de build ou la séquence de boot Securyzr.


Difficultés Rencontrées

  • Compatibilité toolchain : changer de compilateur dans un projet C avec une hiérarchie de makefiles a nécessité de résoudre des problèmes de compatibilité de linker et de bibliothèques
  • Cross-compilation : Securyzr cible RISC-V ; le serveur de développement est x86. L’approche IR-first (émettre .ll, recompiler pour la cible) était la voie à suivre mais impliquait une complexité du système de build
  • Stabilité : Tigress a généré des crashs lors de l’analyse — ce qui l’a exclu pour la production quelle que soit sa force d’obfuscation
  • Limites de mesure : heaptrack montrait 76,8 Ko uniforme pour toutes les transformations (probablement dû à l’allocation statique), nécessitant /bin/time comme solution de secours

Ce que j’en retiens

  • Comment comparer des outils d’obfuscation de façon systématique — taille, temps d’exécution, mémoire, similarité CFG (BinDiff)
  • Pourquoi BCF et FLA ont significativement plus d’impact que SUB ou MBA au niveau binaire
  • Comment intégrer un obfuscateur LLVM dans un système de build C multi-makefile existant
  • La différence entre la force d’obfuscation et l’adéquation pour la production — Tigress produisait les binaires les plus obfusqués mais crashait sur du code réel
  • Les contraintes de cross-compilation dans un contexte de sécurité embarquée RISC-V

Ressources