-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
We have a library we provide to internal partners that contains sensitive code that we don't want to be hijacked at runtime. So we require our partners to embed this assembly as a manifest resource and use AssemblyResolve at runtime to extract the assembly from the embedded resource and load it as a byte stream like below. Note: AssemblyResolve works in both new .NET and old .NET Framework.
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyName = "Acme.Foo.Bar";
// Do not resolve any other assemblies
if (!args.Name.StartsWith($"{assemblyName},"))
{
return null;
}
// Look through the manifest resource names for the assembly name
string apiAssemblyResourceName = null;
Assembly manifestHostAssembly = Assembly.GetExecutingAssembly();
foreach (string resourceName in manifestHostAssembly.GetManifestResourceNames())
{
if (resourceName.EndsWith($"{assemblyName}.dll"))
{
apiAssemblyResourceName = resourceName;
break;
}
}
if (apiAssemblyResourceName == null) throw new DllNotFoundException(assemblyName);
Assembly loadedAssembly = null;
using (Stream streamApiAssembly = manifestHostAssembly.GetManifestResourceStream(apiAssemblyResourceName))
{
// Given that the resource name was returned by reflection, the streamApiAssembly should rarely be null
if (streamApiAssembly == null) throw new DllNotFoundException(assemblyName);
#if NETCOREAPP
loadedAssembly = AssemblyLoadContext.Default.LoadFromStream(streamApiAssembly);
#else
byte[] assemblyBytes = new byte[streamApiAssembly.Length];
streamApiAssembly.Read(assemblyBytes, 0, assemblyBytes.Length);
loadedAssembly = Assembly.Load(assemblyBytes);
#endif
}
return loadedAssembly;
}This works great if you remove the generated .deps.json file which isn't a problem for our sample console apps. However, as partners are building more complicated WPF apps, they need to leave the .deps.json file in-place. This causes the app to fail because something is trying to load the assembly, which is embedded in the app dll and NOT on the filesystem. That load attempt fails before the app ever hits the AssemblyResolve event, preventing the app from starting.
Our current hack is to use a bit of PowerShell to remove the assembly from the libraries section of the .deps.json file:
function Remove-LibFromDependencyFile([string]$Path) {
$jsonDepth = 10
$depsJson = Get-Content $Path | ConvertFrom-Json -Depth $jsonDepth
$updatedDepsJson = $depsJson | ForEach-Object {
$jsonObj = $_
$name = $_.libraries.psobject.Members | Where-Object Name -match "^Acme\.Foo\.Bar\/" | ForEach-Object Name
$jsonObj.Libraries.psobject.Members.Remove($name)
$jsonObj
}
$updatedDepsJson | ConvertTo-Json -Depth $jsonDepth | Out-File $Path -Encoding ascii
}Is there a better way to accomplish this goal of having the .NET runtime NOT try to preload a specified assembly and let my app do the assembly resolution? Or maybe there's a way to keep the assembly out of the .deps.json file which would prevent said mechanism from trying to "pre-load" the assembly?
Somewhat related: Fody/Costura#282