Leistung und Tuning
Die Kontrollflussverschleierung tauscht etwas Laufzeit gegen Schutz. Die leichtgewichtigen Algorithmen kosten praktisch nichts. Die abflachenden Algorithmen fügen einen Dispatcher hinzu, der bei jedem Aufruf läuft, und kosten deshalb mehr. Diese Seite vergleicht die Algorithmen, erklärt die Einstellung Iterationen und zeigt, wie Sie häufig aufgerufene Methoden schnell halten.
Die Algorithmen vergleichen
| Algorithmus | Familie | Relative Laufzeitkosten | Verifizierbarer IL-Code | Edition |
|---|---|---|---|---|
if | Leichtgewichtig | Vernachlässigbar | Ja | Alle |
goto | Leichtgewichtig | Vernachlässigbar | Ja | Alle |
switch | Abflachung | Hoch | Ja | Alle |
case | Abflachung (mit switch) | Hoch | Ja | Alle |
call | Abflachung (mit switch) | Hoch | Ja | Alle |
value | Abflachung (mit switch) | Hoch | Ja | Alle |
chain (Chained State) | Abflachung | Hoch (≈ switch) | Ja | Ultimate |
token | — | Niedrig | Nein | Alle |
underflow | — | Niedrig | Nein | Alle |
So lesen Sie die Tabelle.
ifundgotofügen Verzweigungen und Bedingungen hinzu, aber keinen Dispatcher. Ihre Laufzeitkosten sind deshalb vernachlässigbar, und Sie können sie breit anwenden.- Die Abflachungsfamilie (
switch,case,call,value) undchainbauen eine Methode um einen Dispatcher herum neu auf. Bei einer kleinen Methode, die in einer engen Schleife mehrere zehn Millionen Mal aufgerufen wird, erhöht ein einziger Abflachungsdurchgang die Zeit pro Aufruf dieser Methode um etwa eine Größenordnung. Chained State weicht nur um wenige Prozent vonswitchab, obwohl es sich viel schwerer deobfuskieren lässt: Der zusätzliche Schutz ist gegenüber der Abflachung selbst praktisch kostenlos. tokenundunderflowkosten zur Laufzeit wenig, erzeugen aber nicht verifizierbaren IL-Code. Babel schaltet sie deshalb unter .NET und .NET Core aus.
Diese Zahlen beschreiben den ungünstigsten Fall. Sie stammen aus einem Mikro-Benchmark, der nichts anderes tut, als eine winzige abgeflachte Methode in einer Schleife aufzurufen. Die meisten Methoden werden nicht in so intensiv durchlaufenen Schleifen aufgerufen, sodass sich die Abflachung in der Praxis meist wenig auswirkt. Es lohnt sich dennoch, die Abflachung den wichtigen Methoden vorzubehalten und sie von häufig durchlaufenen Pfaden fernzuhalten.
Iterationen des Kontrollflusses
Wie stark ein Kontrollflussalgorithmus verschleiert, hängt vor allem von der Anzahl der Iterationen ab, die auf eine Methode angewendet werden: Je höher die Anzahl, desto komplexer das Ergebnis und, bei den abflachenden Algorithmen, desto höher die Laufzeitkosten, weil jeder Durchgang eine weitere Dispatch-Ebene hinzufügt.
In Babel legen Sie die Anzahl der Iterationen mit --iterations <n> fest (oder mit ILIterations in MSBuild). Der Wert 0 schaltet die Kontrollflussverschleierung aus. Es gibt eine Obergrenze, ab der sich der Ablauf einer Methode nicht weiter verändern lässt. Sie hängt von der Struktur der Methode ab. Eine höhere Anzahl fügt deshalb nicht immer weitere Anweisungen hinzu.
Kontrollflussverschleierung begrenzen
Weil die Abflachung jeder Methode, die sie bearbeitet, Verzweigungen hinzufügt, kann eine wahllose Anwendung leistungskritischen Code verlangsamen. Begrenzen Sie die Kontrollflussverschleierung mit XML-Regeln auf die Methoden, die sie brauchen, und schließen Sie die übrigen aus, zum Beispiel vom Compiler generierte Methoden, die selten schützenswertes geistiges Eigentum enthalten:
<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>Diese Regel gilt für die Funktion control flow mit exclude="true", und applyToMembers="true" dehnt sie auf alle Member der passenden Klassen aus. Das Element <Target> wählt die Art des Elements (hier Classes), <Pattern> gleicht deren Namen ab (* = alle), und <HasAttribute> beschränkt die Übereinstimmung auf Klassen, die CompilerGeneratedAttribute tragen.
Auf dieselbe Weise schließen Sie jede Methode aus, von der Sie wissen, dass sie leistungskritisch ist. So wägen Sie Schutz und Geschwindigkeit gegeneinander ab: Flachen Sie den sensiblen Code ab und lassen Sie die häufig durchlaufenen Pfade unberührt.