Cifrado de código
La función de cifrado de código de Babel Obfuscator permite cifrar todo el código para protegerlo de la ingeniería inversa.
Babel Obfuscator transforma las instrucciones IL originales del método en un nuevo conjunto de instrucciones personalizadas, que puede incluir varias técnicas de ofuscación que dificultan la ingeniería inversa o la modificación del código. Una vez construida la nueva representación del código, se cifra con el algoritmo de cifrado elegido (por ejemplo, AES) para que sea aún más difícil de entender o de modificar.
En tiempo de ejecución, cuando el ensamblado protegido se carga en memoria, la máquina virtual de Babel (BVM) descifra y ejecuta el código protegido. La BVM es un motor de ejecución ligero que se incluye en el ensamblado ofuscado y aporta la funcionalidad necesaria para descifrar el código, ejecutarlo y garantizar que funcione correctamente.
¿Despliega en hosts en modo FIPS? Como el cifrado de código descifra los cuerpos de los métodos en tiempo de ejecución mediante el proveedor criptográfico de la plataforma, un contenedor que se ejecuta en un host en modo FIPS sin un proveedor FIPS de OpenSSL certificado puede no arrancar. Consulte Conformidad con FIPS para conocer la causa y la solución.
El cifrado de código ofrece un nivel alto de protección contra la ingeniería inversa y el robo de propiedad intelectual, pero tiene contrapartidas en el rendimiento y la complejidad de la aplicación, por lo que no se recomienda aplicarlo a todo el código. Es preferible aplicar esta técnica de ofuscación de forma selectiva a las partes más sensibles del código que requieren protección y usar otras técnicas de ofuscación para el resto. Así se logra un equilibrio entre seguridad y rendimiento.
Antes del cifrado de código
Después del cifrado de código
El cifrado de código de Babel es una solución totalmente administrada. Esto significa que los métodos cifrados no se sustituyen por código nativo destinado a una plataforma concreta. Esta solución basada en métodos administrados garantiza que no se comprometa el carácter multiplataforma de .NET Framework y permite que el compilador JIT (Just-In-Time) optimice el código para la CPU de destino.
Limitaciones del cifrado de código
La función de cifrado de código de Babel no se admite en los ensamblados de .NET MAUI y Blazor por las razones principales siguientes:
Restricciones de la plataforma
• Compilación AOT (Ahead-of-Time): las plataformas como iOS, que son fundamentales para .NET MAUI, dependen de la compilación AOT. Este proceso compila el código en binarios nativos por adelantado, lo que elimina la posibilidad de generar o modificar código dinámicamente en tiempo de ejecución, algo imprescindible para el cifrado y el descifrado de código.
Compatibilidad limitada con el espacio de nombres System.Reflection.Emit
• Generación de IL restringida: el espacio de nombres System.Reflection.Emit, que se usa para generar IL dinámicamente, tiene una compatibilidad limitada o nula en plataformas clave como iOS y WebAssembly (usado por Blazor). Como el cifrado de código suele basarse en la manipulación dinámica del IL, esta limitación dificulta aún más la implementación de funciones de cifrado en MAUI y Blazor.
Restricciones de seguridad
• Entornos aislados: tanto MAUI como Blazor funcionan en entornos donde las aplicaciones se aíslan (sandbox) por seguridad. Plataformas como iOS imponen medidas de seguridad estrictas que impiden cualquier forma de modificación del código en tiempo de ejecución para proteger contra la ejecución de código malicioso, lo que hace inviable el cifrado dinámico.
• Riesgo de inyección de código: la generación dinámica de código que requieren el cifrado y el descifrado plantea riesgos de seguridad importantes, entre ellos posibles vulnerabilidades de inyección de código. Los frameworks MAUI y Blazor dan prioridad a la seguridad y evitan esos riesgos.
Por estas restricciones, .NET MAUI y Blazor no admiten la función de cifrado de código y se centran en cambio en prácticas de desarrollo seguras, portables y coherentes entre plataformas.
Métodos que no se pueden cifrar
Dentro de un destino admitido, el cifrado de código se aplica método a método, y unos pocos métodos se dejan automáticamente sin cifrar:
• Los constructores de instancia (.ctor) nunca se cifran. Es así por diseño: una instancia debe inicializarse por completo a través de la cadena de constructores base antes de usarse, y esa garantía no puede mantenerse si el cifrado de código reubica el cuerpo del constructor. Tenga en cuenta que esto se aplica solo a los constructores de instancia: los constructores estáticos (.cctor) no se ven afectados y se cifran como cualquier otro método.
• Los métodos con una firma no admitida se omiten automáticamente. El cifrado de código reubica el cuerpo del método en la máquina virtual de Babel (BVM) y lo llama de vuelta mediante un serializador de argumentos uniforme, por lo que algunas formas de firma no pueden expresarse y se dejan sin cifrar:
- Métodos genéricos: se notifican como
EM0003. - Tipos de valor devuelto no admitidos: un valor devuelto
ref(por referencia), un puntero, un puntero a función o unref struct(de tipo similar a una referencia), comoSpan<T>/ReadOnlySpan<T>. Se notifican comoEM0001. - Parámetros de puntero o de puntero a función: se notifican como
EM0002. - Parámetros
ref struct(de tipo similar a una referencia), comoSpan<T>/ReadOnlySpan<T>: se notifican comoEM0013.
Estas limitaciones son inherentes al entorno de ejecución de .NET: a un puntero o a un ref struct no se les puede aplicar la conversión boxing, por lo que el serializador de argumentos de la BVM no puede transportarlos. Todo lo demás se admite, incluidos los parámetros ref/out/in (de cualquier tipo de elemento, incluidos las estructuras, las enumeraciones y los tipos de referencia pasados por referencia), los valores devueltos de tipo de valor y de instancia genérica, el control de excepciones, los iteradores y los métodos async. Consulte el apéndice Códigos de advertencia para ver la lista completa de códigos EM.
Los métodos excluidos por estas razones se notifican durante la compilación y no hacen que la ofuscación falle: simplemente se emiten sin cifrar.
Consideraciones de rendimiento
El cuerpo de un método cifrado sigue ejecutándose como código compilado por el JIT, de modo que su trabajo interno se ejecuta a una velocidad cercana a la nativa. Lo que añade el cifrado de código es un pequeño coste de despacho por llamada cada vez que se entra en un método cifrado (los argumentos se serializan hacia la BVM y el resultado se serializa de vuelta). Ese coste adicional es insignificante en los métodos que hacen un trabajo real, pero puede ser el factor dominante en métodos muy pequeños a los que se llama en bucles de uso muy intensivo (millones de llamadas). Por esta razón, y para que el ensamblado siga siendo pequeño y rápido, aplique el cifrado de código de forma selectiva a los métodos sensibles que necesitan protección y no a todo el código, y evite cifrar métodos auxiliares diminutos y de uso intensivo en una ruta crítica.
Activar el cifrado de código
Babel Obfuscator permite aplicar el cifrado de código de forma selectiva a métodos concretos en lugar de cifrar todo el código. Así se evitan los problemas de rendimiento que puede causar el cifrado de grandes cantidades de código.
Para indicar qué métodos deben cifrarse, puede usar una regla XML o el atributo Obfuscation. Con una regla XML se especifican los nombres de los métodos que deben cifrarse.
<Rule name="encrypt code" feature="msil encryption" exclude="false">
<Target>Methods</Target>
<Pattern>ACME.Algorithms::*</Pattern>
<Description>Encrypt all methods of Algorithm class.</Description>
</Rule>Como alternativa, el atributo Obfuscation permite marcar métodos individuales para su cifrado asignando el valor «msil encryption» al parámetro «Feature».
[Obfuscation(Feature = "msil encryption", Exclude = false)]
public void ProcessData()
{
// Encrypted code
}Al activar el cifrado de código, los métodos seleccionados se cifran durante el proceso de ofuscación.
Línea de comandos
babel myapp.exe --msilencryptionEn lugar de usar reglas XML o atributos, la opción --msilencryption puede aceptar opcionalmente expresiones regulares para seleccionar los métodos o los tipos en los que debe activarse el cifrado de código. Por ejemplo, el comando siguiente:
babel myapp.exe --msilencryption ACME.LicenseManager::.*configura Babel para cifrar todos los métodos de la clase LicenseManager del espacio de nombres ACME.
Tarea Babel de MSBuild
<PropertyGroup>
<MsilEncryption>true</MsilEncryption>
</PropertyGroup>
<Babel MsilEncryption="$(MsilEncryption)" />Babel Desktop
Seleccione el ensamblado en el diagrama del proyecto y, en el panel de propiedades, active MsilEncryption en el grupo Cifrado de código. Si lo desea, añada una lista de expresiones regulares para filtrar los espacios de nombres o las clases en los que debe aplicarse el cifrado de código. Deje la lista vacía si ha seleccionado los métodos que se van a cifrar mediante reglas XML o con el atributo Obfuscation. Consulte Proyectos de ofuscación para saber cómo trabajar con proyectos en Babel Desktop.
Después de la ejecución, la vista Resultados de ejecución enumera los métodos cifrados, para que pueda comprobar que se ha aplicado el cifrado de código.
Las estadísticas del cifrado de código aparecen también en el registro de salida (el panel Actividad de Babel Desktop), que recoge cada fase de la ofuscación, incluido el cifrado de código.
Encrypt Msil phase, elapsed time 00.579s
Embedded resource: omJYi
size : 13170 bytes
Method statistics:
194/[ 252] resources: 76.98 %
194/[ 252] overall: 76.98 %
Number of encrypted methods: 194Al examinar las estadísticas del cifrado de código en el registro de salida, puede verificar fácilmente si el cifrado de código se ha aplicado a su código y hacerse una idea del nivel de protección que ofrece. Así puede confirmar que su código sensible se ha cifrado correctamente y que permanece seguro y resistente al acceso no autorizado.