Nascondere il grafo delle chiamate con il proxy dinamico
Un decompilatore ricostruisce molto dalle chiamate ai metodi: quali API del framework usi, come i tuoi metodi si concatenano e perfino i nomi originali delle variabili locali (dedotti dai tipi che chiami). Questo esempio usa il pacchetto NuGet di Babel Obfuscator per abilitare il proxy dinamico, che sostituisce le chiamate dirette con chiamate instradate attraverso delegate proxy generati, così il grafo delle chiamate non è più leggibile dai metadati.
Per usare questo esempio ti serve una licenza per sede di Babel Obfuscator (edizioni Ultimate, Server o Data Center). Il codice sorgente è disponibile su GitHub:
git clone https://github.com/babelfornet/dynamic-proxy-nuget-example.gitLa logica di business da nascondere è un normale insieme di metodi che si chiamano tra loro e
chiamano la libreria .NET (src/PricingEngine/Pricing.cs):
internal static class Pricing
{
public static decimal Quote(string sku, int quantity, string tier)
{
decimal unit = BasePrice(sku); // internal call
decimal discounted = ApplyTierDiscount(unit, tier);
decimal volume = ApplyVolumeBreak(discounted, quantity);
return Math.Round(volume * quantity, 2); // external (BCL) call
}
// BasePrice / ApplyTierDiscount / ApplyVolumeBreak …
}Dopo l’offuscamento nessuna di queste chiamate è più diretta: ognuna passa attraverso un proxy generato.
Il progetto console PricingEngine abilita il proxy dinamico in Release con una sola proprietà :
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Babel.Obfuscator" Version="12.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DynamicProxy>external=true;internal=true;true</DynamicProxy>
<FlattenNamespaces>true</FlattenNamespaces>
<OverloadedRenaming>true</OverloadedRenaming>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>Con external=true vengono nascoste le chiamate alla libreria di classi base di .NET e agli assembly
referenziati: basta questo a vanificare il trucco con cui un decompilatore ricava il nome originale
di una variabile dal tipo che chiama. Con internal=true vengono nascoste anche le chiamate tra i
tuoi tipi, oscurando la logica dell’applicazione. Il proxy dinamico
copre le chiamate statiche, di istanza, virtuali, di interfaccia e generiche (LINQ compreso), e
lascia dirette poche chiamate sensibili al chiamante, così il comportamento non cambia mai.
La build dell’esempio in Release produce un output identico e sostituisce le chiamate dirette originali del motore dei prezzi con tipi delegate proxy generati: il flusso delle chiamate che un decompilatore ricostruisce non è più quello che hai scritto. Il proxy dinamico inoltre resiste al deoffuscamento automatico.