Skip to content

Referencia

Múltiples ensamblados y renombrado entre ellos

Ofusca juntos los ensamblados interdependientes para que las referencias entre ensamblados sigan siendo válidas — incluido cómo renombrar los miembros públicos compartidos entre tus propios ensamblados.

Si tu producto distribuye más de un ensamblado y estos se referencian entre sí — una app más sus bibliotecas, o un conjunto de bibliotecas — ofúscalos juntos en una sola ejecución. Nebula mantiene así consistente cada referencia entre ensamblados.

Por qué ofuscarlos juntos

Cuando el ensamblado A llama a un tipo o miembro público en el ensamblado B, renombrar ese miembro en B solo es seguro si la referencia en A se reescribe al nuevo nombre al mismo tiempo. Nebula solo puede reescribir referencias en ensamblados que formen parte de la misma ejecución — así que lista todos los ensamblados interdependientes en inputs:

{
  "inputs": [
    "bin/Release/MyApp.dll",
    "bin/Release/MyApp.Core.dll",
    "bin/Release/MyApp.Plugins.dll"
  ],
  "outputDirectory": "bin/Release/obf"
}

Qué se renombra de forma predeterminada

  • Los miembros internos / privados se renombran — no son visibles fuera del ensamblado, así que esto siempre es seguro.
  • La API pública se preserva (preservePublicApi tiene el valor predeterminado true), de modo que el código fuera de la ejecución que referencia a tus ensamblados sigue funcionando.
  • Incluso con preservePublicApi: false, Nebula autodetecta los miembros públicos que un input hermano referencia y mantiene esos nombres estables — a menos que habilites explícitamente el renombrado entre ensamblados (abajo).
  • autoDetect (activado de forma predeterminada) también preserva los nombres usados mediante reflexión, serialización, DI y XAML, y respeta [Obfuscation].

Renombrar miembros públicos compartidos entre tus ensamblados

Este es el caso importante para los proyectos multiensamblado: quieres renombrar un método o tipo público en B que A llama. La configuración del proyecto necesita tres cosas:

{
  "inputs": ["bin/Release/A.dll", "bin/Release/B.dll"],   // EVERY caller must be listed here
  "outputDirectory": "bin/Release/obf",
  "preservePublicApi": false,      // allow public members to be renamed
  "crossAssemblyRename": true      // rename shared public types AND rewrite the references in the siblings
}

Con esto en su lugar:

  1. Los miembros públicos de B se renombran.
  2. Las referencias de A a ellos se reescriben a los nuevos nombres, y A también se ofusca y se escribe.
  3. Despliegas A y B ofuscados juntos — el A original ya no resolvería los miembros renombrados.

La regla que hay que recordar

Cada ensamblado que llame a un miembro público que quieras renombrar debe estar en inputs. Si un llamador no está en la ejecución, seguirá referenciando el nombre antiguo y se romperá en tiempo de carga/JIT. Por lo tanto:

  • No puedes renombrar API pública de la que dependa código externo o de terceros (que no puedes incluir en la ejecución). Mantén preservePublicApi: true para esa superficie, o acótala con include/exclude abajo.
  • El valor predeterminado (preservePublicApi: true) existe precisamente para proteger a esos llamadores externos — solo se renombran los internos.

Mantener estables puntos de entrada públicos específicos

Incluso al renombrar miembros públicos, algunos nombres deben sobrevivir — una interfaz de plugin, un punto de entrada público, tipos creados por reflexión o DI, contratos de serialización. Usa:

  • exclude — fuerza la preservación de nombres específicos. Un nombre completo de tipo preserva el tipo y todos sus miembros; "Namespace.Type.Method" preserva un único miembro:
    "exclude": ["MyApp.Plugins.IPlugin", "MyApp.Api.PublicFacade"]
  • include — una lista de permitidos: cuando no está vacía, solo los tipos listados (y sus miembros) son elegibles para el renombrado y todo lo demás se preserva. Útil para renombrar solo tus bibliotecas internas dejando intacta una superficie de SDK pública.
  • autoDetect ya preserva automáticamente los objetivos de reflexión/serialización/[Obfuscation] — rara vez necesitas listarlos a mano.

Miembros virtuales, de interfaz, genéricos, campos, propiedades y eventos

Nebula preserva la consistencia de sobrescrituras / interfaces / genéricos entre ensamblados. Un método sobrescrito, una implementación de interfaz, un tipo o método genérico, y los campos, propiedades, eventos y sobrecargas públicos se renombran todos de forma consistente, y cada referencia entre ensamblados a ellos se reescribe. Esto está cubierto por nuestra suite de pruebas, que ofusca dos ensamblados interdependientes juntos (con crossAssemblyRename) y ejecuta la app que los referencia para confirmar que cada referencia — incluidas todas esas formas — sigue resolviéndose.

Nombres seguros (strong-naming) en todo el conjunto

Si vuelves a firmar con tu propia clave (strongNameKeyFile), Nebula actualiza la referencia de ensamblado de cada hermano para que lleve el nuevo token de clave pública, de modo que todo el conjunto se mantenga consistente y cargable.

Ejemplo resuelto

Dos ensamblados — SampleLibA.dll (una biblioteca) y SampleAppB.dll (una app que la llama):

{
  "inputs": ["out/SampleLibA.dll", "out/SampleAppB.dll"],
  "outputDirectory": "out/obf",
  "preservePublicApi": false,
  "crossAssemblyRename": true,
  "controlFlowObfuscation": true,
  "encryptStrings": true
}
nebula --config nebula.config.json

Ambos ensamblados salen ofuscados; las referencias de la app a los miembros de la biblioteca ahora renombrados se reescriben; despliegas ambos desde out/obf.

Regla general: incluye cada ensamblado que controles que referencie la superficie renombrada; mantén preservePublicApi: true (o usa exclude) para cualquier API de la que dependa código externo.