Skip to Content
Nueva versión 12 disponible 🎉

Ocultar el grafo de llamadas con el proxy dinámico

Un descompilador reconstruye mucho a partir de las llamadas a métodos: qué API del framework usa, cómo se encadenan sus propios métodos e incluso los nombres originales de las variables locales (deducidos de los tipos a los que llama). Este ejemplo usa el paquete NuGet de Babel Obfuscator para activar el proxy dinámico, que sustituye las llamadas directas por llamadas encaminadas a través de delegados proxy generados, de modo que el grafo de llamadas ya no puede leerse en los metadatos.

Para usar este ejemplo necesita una licencia de sitio de Babel Obfuscator (ediciones Ultimate, Server o Data Center). El código fuente está disponible en GitHub:

git clone https://github.com/babelfornet/dynamic-proxy-nuget-example.git

La lógica de negocio que se quiere ocultar es un conjunto corriente de métodos que se llaman entre sí y llaman a la biblioteca de .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 … }

Tras la ofuscación, ninguna de estas llamadas es ya directa: cada una pasa por un proxy generado.

El proyecto de consola PricingEngine activa el proxy dinámico en Release con una sola propiedad:

<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 se ocultan las llamadas a la biblioteca de clases base de .NET y a los ensamblados a los que se hace referencia; esto basta para anular el truco con el que un descompilador recupera el nombre original de una variable a partir del tipo al que llama. Con internal=true se ocultan también las llamadas entre sus propios tipos, lo que oscurece la lógica de la aplicación. El proxy dinámico cubre las llamadas estáticas, de instancia, virtuales, de interfaz y genéricas (incluido LINQ), y deja como directas un pequeño conjunto de llamadas sensibles al llamador para que el comportamiento no cambie nunca.

Al compilar el ejemplo en Release se obtiene una salida idéntica, pero las llamadas directas originales del motor de precios se sustituyen por tipos de delegado proxy generados: el flujo de llamadas que reconstruye un descompilador ya no es el que usted escribió. El proxy dinámico también resiste la desofuscación automática.

Last updated on