Configurazione dell’offuscamento
Una volta installato il pacchetto, ogni funzionalità di Babel Obfuscator si configura con proprietà ed elementi MSBuild nel file di progetto. Questa pagina mostra le impostazioni che userai più spesso, una funzionalità alla volta.
Come le impostazioni arrivano a Babel
Il pacchetto definisce una proprietà o un elemento MSBuild per ogni attributo del task Babel e li passa al task quando viene eseguito il target CoreObfuscate. La maggior parte delle proprietà ha lo stesso nome dell’attributo del task e accetta gli stessi valori dell’opzione corrispondente della riga di comando. Quelle che differiscono, come BabelLicense, MergeInternalize o BabelRules, sono elencate nella tabella Corrispondenza delle proprietà .
Le impostazioni vanno in un PropertyGroup, di solito condizionato alla configurazione di build, così le build Debug restano leggibili:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<StringEncryption>stream</StringEncryption>
<ControlFlowObfuscation>if=on;switch=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
<ValueEncryption>true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DebuggingProtection>true</DebuggingProtection>
<VerboseLevel>2</VerboseLevel>
</PropertyGroup>Gli elenchi di file sono espressi come elementi in un ItemGroup: BabelRules, MergeAssembly, EmbedAssembly, MapInFile e BabelPlugin.
Le proprietà vengono valutate prima dell’esecuzione di qualsiasi target, quindi un valore impostato nel corpo del progetto vale per l’intera build. Per calcolare un’impostazione a partire da qualcosa che è noto solo durante la build, come il percorso risolto di un riferimento al pacchetto, impostala invece all’interno del target BeforeObfuscate. Vedi Sovrascrivere la configurazione del task.
Valori predefiniti applicati dal pacchetto
Senza alcuna impostazione, un progetto che referenzia il pacchetto viene offuscato come segue:
- La ridenominazione dei simboli è attiva per tipi, metodi, proprietà , campi, eventi e parametri, membri virtuali compresi, e i namespace vengono appiattiti. La proprietÃ
SymbolsRenamingli attiva o disattiva tutti insieme; le singole proprietÃObfuscateTypes,ObfuscateMethods,ObfuscateProperties,ObfuscateFields,ObfuscateEvents,ObfuscateParameters,VirtualFunctionseFlattenNamespacesprevalgono su di essa una per una. - Tutte le altre funzionalità sono disattivate finché non le abiliti.
- I simboli di debug vengono rigenerati quando il compilatore ha prodotto un PDB accanto all’assembly (
GenerateDebugviene impostato automaticamente), così le build offuscate si possono ancora sottoporre a debug e gli stack trace possono essere decodificati. - Le cartelle di ricerca degli assembly vengono ricavate dai riferimenti del progetto, così Babel risolve ogni dipendenza vista dal compilatore.
- Il file di configurazione di Babel viene ignorato (
NoConfigurationètrue), così la build dipende solo da ciò che il progetto dichiara. - Un file
babelRules.xmlnella cartella del progetto viene rilevato automaticamente. - La chiave con nome sicuro risolta da MSBuild per il progetto (
KeyOriginatorFile, oppureKeyContainerName) viene passata a Babel, così l’assembly offuscato viene firmato come l’originale.
Per disattivare la ridenominazione, per un progetto di test o per un passaggio di sola cifratura delle stringhe:
<PropertyGroup>
<SymbolsRenaming>false</SymbolsRenaming>
</PropertyGroup>Regole di offuscamento
Le regole escludono dei simboli da una funzionalità oppure applicano una funzionalità a un insieme di simboli. Ci sono tre modi per fornirle:
- Aggiungi un file
babelRules.xmlalla cartella del progetto: viene usato automaticamente. - Referenzia un numero qualsiasi di file di regole con elementi
BabelRules:
<ItemGroup>
<BabelRules Include="rules\renaming.xml" />
<BabelRules Include="rules\encryption.xml" />
</ItemGroup>- Scrivi le regole inline nella proprietÃ
XmlRules, comoda per un piccolo insieme di regole che appartiene al progetto:
<PropertyGroup>
<XmlRules>
<Rules>
<Rule name="rename public types" feature="renaming" exclude="false">
<Access>Public</Access>
<Pattern>*</Pattern>
</Rule>
</Rules>
</XmlRules>
</PropertyGroup>L’esempio Applicazione Android usa una regola inline per rinominare i tipi pubblici, e l’esempio Blazor Web App usa un file di regole per offuscare le classi dietro le pagine Razor. Le regole si possono anche associare al codice con gli attributi personalizzati.
Cifratura delle stringhe
StringEncryption abilita la cifratura delle stringhe e seleziona l’algoritmo, esattamente come l’opzione --stringencryption: true usa l’algoritmo predefinito, mentre xor, hash, stream o custom ne selezionano uno. Vedi Algoritmi standard per un confronto.
<PropertyGroup>
<StringEncryption>stream</StringEncryption>
</PropertyGroup>STREAM Ultimate è l’algoritmo da preferire per i target moderni: è interamente gestito, quindi funziona con il trimming, con NativeAOT e sugli host soggetti a vincoli FIPS, ed è verificato da .NET Framework fino a .NET 10, compresi Android, iOS e .NET MAUI. Nei progetti Android, iOS e MAUI il pacchetto esegue già Babel dopo la compilazione e prima del confezionamento, che è il punto in cui la cifratura delle stringhe deve avvenire perché l’app confezionata contenga le stringhe cifrate. Le stringhe dichiarate const non possono essere cifrate; vedi Cifratura delle stringhe const.
Offuscamento del flusso di controllo
ControlFlowObfuscation accetta lo stesso elenco chiave-valore di --controlflow, e ControlFlowIterations il numero di passaggi (ILIterations è accettato come alias):
<PropertyGroup>
<ControlFlowObfuscation>goto=on;if=on;switch=on;case=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
</PropertyGroup>La versione 12 aggiunge l’algoritmo Chained State Ultimate, una trasformazione di appiattimento (flattening) rafforzata contro i deoffuscatori automatici. Abilitalo con chain=on, da solo o insieme agli altri algoritmi:
<PropertyGroup>
<ControlFlowObfuscation>switch=on;case=on;chain=on</ControlFlowObfuscation>
</PropertyGroup>L’appiattimento ha un costo in fase di esecuzione. Usa le regole per non applicarlo ai metodi eseguiti più di frequente, come spiegato in Prestazioni e tuning.
Cifratura del codice
MsilEncryption cifra i corpi dei metodi. true cifra ogni metodo idoneo, mentre un’espressione regolare limita la cifratura ai metodi il cui nome completo corrisponde all’espressione:
<PropertyGroup>
<MsilEncryption>MyApp\.Licensing\..*</MsilEncryption>
</PropertyGroup>Vedi Cifratura del codice per il comportamento in fase di esecuzione, il codice protetto da password e i metodi che non possono essere cifrati.
Protezione di valori, risorse e chiamate
<PropertyGroup>
<ValueEncryption>array=true;true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DynamicProxy>all</DynamicProxy>
<SuppressIldasm>true</SuppressIldasm>
<SuppressReflection>true</SuppressReflection>
</PropertyGroup>Ogni proprietà accetta i valori dell’opzione corrispondente: vedi Cifratura di valori e array, Cifratura delle risorse e Proxy dinamico.
Rilevamento delle manomissioni e protezione dal debug
<PropertyGroup>
<TamperingDetection>true</TamperingDetection>
<DebuggingProtection>true</DebuggingProtection>
</PropertyGroup>Sui target desktop il controllo delle manomissioni calcola l’hash dell’immagine caricata in memoria, quindi è inattivo nelle applicazioni pubblicate a file singolo, con trimming o con AOT. Il pacchetto passa a Babel l’impostazione PublishSingleFile del progetto, e Babel emette un avviso quando il rilevamento delle manomissioni viene richiesto per una pubblicazione di questo tipo. Vedi Rilevamento delle manomissioni e Protezione dal debug.
Integrità del pacchetto Android e iOS Ultimate
A partire dalla versione 12, sui target .NET per Android e iOS (.NET MAUI compreso) il rilevamento delle manomissioni verifica l’integrità del pacchetto invece dell’immagine: il certificato di firma dell’APK su Android, l’identificatore del bundle e l’identificatore del team Apple su iOS. Fissa i valori attesi con le proprietà aggiunte in questa versione:
<!-- Android: SHA-256 fingerprint of the release signing certificate -->
<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
<TamperingDetection>true</TamperingDetection>
<TrustedSigner>2924C53EE9C511E9F26E0720FD8151064F7621681667D7AB1899E551CDB25104</TrustedSigner>
</PropertyGroup>
<!-- iOS: bundle identifier and Apple Team ID -->
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<TamperingDetection>true</TamperingDetection>
<TrustedBundle>com.mycompany.myapp</TrustedBundle>
<TrustedTeam>ABCDE12345</TrustedTeam>
</PropertyGroup>TrustedSigner accetta più impronte separate da punto e virgola, per esempio quella di una chiave di caricamento e quella della chiave di firma dell’app di Google Play. Come ottenere ciascun valore, e che cosa succede quando un valore fissato manca, è spiegato in Integrità del pacchetto su Android (MAUI) e Integrità del pacchetto su iOS (MAUI). L’esempio Applicazione Android mostra un file di progetto Android completo.
AES gestito per gli host FIPS
I decifratori di runtime inseriti per la cifratura del codice, delle stringhe (XOR e HASH), dei valori e delle risorse usano il provider crittografico della piattaforma, e un host con una configurazione FIPS di OpenSSL non funzionante non riesce ad avviare un assembly di questo tipo. Dalla versione 11.8, encryption=aesmanaged seleziona invece un decifratore gestito autonomo:
<PropertyGroup>
<MsilEncryption>true</MsilEncryption>
<Use>encryption=aesmanaged</Use>
</PropertyGroup>Use accetta qualsiasi coppia chiave-valore accettata dall’opzione --use; più coppie si separano con il punto e virgola, per esempio tagassembly=on. Vedi Conformità FIPS per i compromessi del decifratore gestito.
Unire e incorporare assembly
Gli elementi MergeAssembly ed EmbedAssembly elencano gli assembly da unire all’assembly target o da incorporarvi. MergeInternalize rende interni i tipi pubblici uniti, e MergeCopyAttributes controlla se i loro attributi a livello di assembly vengono copiati:
<ItemGroup>
<MergeAssembly Include="$(TargetDir)Acme.View.dll" />
<MergeAssembly Include="$(TargetDir)Acme.ViewModel.dll" />
<EmbedAssembly Include="$(TargetDir)Framework.Mvvm.dll" />
</ItemGroup>
<PropertyGroup>
<MergeInternalize>true</MergeInternalize>
</PropertyGroup>Babel viene eseguito subito dopo la compilazione, prima che la build copi le dipendenze nella cartella di output, quindi in una build pulita un percorso sotto $(TargetDir) potrebbe non esistere ancora. L’origine affidabile è l’elenco dei riferimenti risolti per il compilatore, che puoi filtrare all’interno del target BeforeObfuscate:
<Target Name="BeforeObfuscate">
<ItemGroup>
<MergeAssembly Include="@(ReferencePathWithRefAssemblies)"
Condition="'%(Filename)' == 'Acme.View' Or '%(Filename)' == 'Acme.ViewModel'" />
</ItemGroup>
</Target>Il pacchetto rimuove inoltre gli assembly uniti e incorporati dal file .deps.json e dall’insieme dei file da pubblicare, così dotnet publish non li distribuisce due volte. Vedi Unione e incorporamento per la funzionalità e Pubblicazione per i dettagli che riguardano la build.
File map e ridenominazione cross-assembly
GenerateMapOutFile scrive il file map XML dell’assembly offuscato, usato per decodificare gli stack trace e per rinominare in modo coerente tra più assembly l’interfaccia pubblica di una libreria. Gli elementi MapInFile passano a Babel i file map degli assembly già offuscati:
<PropertyGroup>
<GenerateMapOutFile>true</GenerateMapOutFile>
<BabelMapOutFile>$(SolutionDir)MapOut\$(TargetFileName).map.xml</BabelMapOutFile>
</PropertyGroup>
<ItemGroup>
<MapInFile Include="$(SolutionDir)MapOut\Acme.Core.dll.map.xml" />
</ItemGroup>Vedi File map XML e Ridenominazione cross-assembly. L’esempio Test unitari usa un file map per eseguire i test su una libreria offuscata, e l’esempio Pubblicare un’app .NET offusca l’interfaccia pubblica di una dipendenza NuGet e passa il suo file map all’applicazione.
Ottimizzazioni
<PropertyGroup>
<DeadCodeElimination>true</DeadCodeElimination>
<SealClasses>true</SealClasses>
<EnumRemoval>true</EnumRemoval>
<ConstRemoval>true</ConstRemoval>
<DisgregateRemoval>true</DisgregateRemoval>
<InlineExpansion>true</InlineExpansion>
<CleanAttributes>true</CleanAttributes>
</PropertyGroup>Ogni ottimizzazione è descritta in Ottimizzazioni.
Plugin
Gli elementi BabelPlugin referenziano gli assembly dei plugin da caricare, e PluginsArguments passa loro gli argomenti come coppie chiave-valore. Il plugin BabelEncrypt è incluso nel pacchetto, nella stessa cartella degli strumenti di build:
<ItemGroup>
<BabelPlugin Include="$(BabelTaskDir)BabelEncrypt.dll" />
</ItemGroup>
<PropertyGroup>
<PluginsArguments>dictionary=exclusionlist.txt</PluginsArguments>
</PropertyGroup>Vedi Plugin di Babel Obfuscator e l’Encrypt Plugin.
Log e diagnostica
<PropertyGroup>
<VerboseLevel>3</VerboseLevel>
<GenerateLogFile>true</GenerateLogFile>
<BabelLogFile>$(IntermediateOutputPath)babel.log</BabelLogFile>
<BabelProvideCommandLineArgs>true</BabelProvideCommandLineArgs>
<ShowStatistics>true</ShowStatistics>
<Trace>MyApp\.Services\..*</Trace>
<BabelWarningsToIgnore>W00008</BabelWarningsToIgnore>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>VerboseLevelcontrolla quanta parte del log di Babel arriva nell’output della build;GenerateLogFileeBabelLogFilesalvano il log completo in un file.BabelProvideCommandLineArgsstampa la riga di comando che il pacchetto passa a Babel e la memorizza nell’elemento@(BabelCommandLineArgs).Traceaccetta un’espressione regolare e spiega, simbolo per simbolo, perché una corrispondenza è stata offuscata oppure no: è il modo più rapido per eseguire il debug delle regole.BabelWarningsToIgnore,BabelWarningsAsErrorseBabelWarningsAsInfosaccettano elenchi di codici di avviso e cambiano il modo in cui vengono segnalati.MakeBabelProjectFilescrive un progetto MSBuild equivalente alla configurazione corrente, utile per riprodurre una build con il task Babel o con lo strumento da riga di comando.
Tutte le proprietà accettate dal pacchetto sono elencate nel Riferimento del pacchetto.