Skip to Content
Nouvelle version 12 disponible 🎉

Performances et réglages

L’obfuscation du flux de contrôle échange un peu de temps d’exécution contre de la protection. Les algorithmes légers sont pratiquement gratuits ; les algorithmes d’aplatissement ajoutent un répartiteur qui s’exécute à chaque appel et coûtent donc davantage. Cette page compare les algorithmes, explique le paramètre itérations et montre comment préserver la rapidité des méthodes critiques pour les performances.

Comparer les algorithmes

AlgorithmeFamilleCoût relatif à l’exécutionIL vérifiableÉdition
ifLégèreNégligeableOuiToutes
gotoLégèreNégligeableOuiToutes
switchAplatissementÉlevéOuiToutes
caseAplatissement (avec switch)ÉlevéOuiToutes
callAplatissement (avec switch)ÉlevéOuiToutes
valueAplatissement (avec switch)ÉlevéOuiToutes
chain (état chaîné)AplatissementÉlevé (≈ switch)OuiUltimate
token—FaibleNonToutes
underflow—FaibleNonToutes

Lecture du tableau.

  • if et goto ajoutent des branchements et des conditions, mais pas de répartiteur : leur coût à l’exécution est négligeable et vous pouvez les appliquer largement.
  • La famille d’aplatissement (switch, case, call, value) et chain reconstruisent une méthode autour d’un répartiteur. Sur une petite méthode appelée des dizaines de millions de fois dans une boucle serrée, une seule passe d’aplatissement ajoute environ un ordre de grandeur au temps par appel de cette méthode. L’état chaîné reste à quelques pour cent de switch alors qu’il est beaucoup plus difficile à désobfusquer : la protection supplémentaire est pratiquement gratuite par rapport à l’aplatissement lui-même.
  • token et underflow sont peu coûteux à l’exécution mais produisent de l’IL non vérifiable ; Babel les désactive donc sur .NET et .NET Core.

Ces chiffres correspondent au pire cas : ils proviennent d’un micro-benchmark qui ne fait rien d’autre qu’appeler en boucle une minuscule méthode aplatie. La plupart des méthodes ne sont pas appelées dans des boucles aussi sollicitées, et l’effet réel de l’aplatissement est donc généralement faible. Il reste néanmoins préférable de réserver l’aplatissement aux méthodes qui comptent et de l’éviter sur les chemins critiques.

Itérations du flux de contrôle

L’effet d’obfuscation de tout algorithme de flux de contrôle dépend avant tout du nombre d’itérations appliquées à une méthode : plus ce nombre est élevé, plus le résultat est complexe et, pour les algorithmes d’aplatissement, plus le coût à l’exécution augmente, car chaque passe ajoute une couche de répartition.

Babel vous permet de définir le nombre d’itérations avec --iterations <n> (ou ILIterations depuis MSBuild). La valeur 0 désactive l’obfuscation du flux de contrôle. Il existe une limite supérieure, qui dépend de la structure de la méthode, au-delà de laquelle le flux d’une méthode donnée ne peut plus être modifié ; augmenter ce nombre n’ajoute donc pas toujours des instructions.

Limiter l’obfuscation du flux de contrôle

Comme l’aplatissement ajoute des branchements à chaque méthode qu’il touche, l’appliquer sans discernement peut ralentir le code critique pour les performances. Utilisez des règles XML pour limiter l’obfuscation du flux de contrôle aux méthodes qui en ont besoin et exclure les autres, par exemple les méthodes générées par le compilateur, qui contiennent rarement une propriété intellectuelle à protéger :

<Rules> <Rule name="rule" feature="control flow" exclude="true" applyToMembers="true"> <Target>Classes</Target> <Pattern>*</Pattern> <HasAttribute onEnclosingType="false">System.Runtime.CompilerServices.CompilerGeneratedAttribute</HasAttribute> </Rule> </Rules>

Cette règle vise la fonctionnalité control flow avec exclude="true", et applyToMembers="true" l’étend à tous les membres des classes correspondantes. L’élément <Target> sélectionne le genre d’élément (ici, Classes), <Pattern> correspond à leurs noms (* = tous) et <HasAttribute> restreint la correspondance aux classes qui portent CompilerGeneratedAttribute.

La même approche permet d’exclure toute méthode que vous savez critique pour les performances, afin d’équilibrer protection et vitesse : aplatissez le code sensible et laissez intacts les chemins critiques.

Last updated on