.NET Core应用中Catch块无法捕获AppLocker/WDAC拦截的FileLoadException问题
我写了一段用于验证WDAC/AppLocker规则的代码,意图是通过实例化一个被拦截的类型来触发心跳验证,并且用try-catch捕获异常让后续代码继续执行:
try { var appcontrolHeartbeat = new Class1(); } catch (FileLoadException e) { log.Info( $"AppcontrolHeartbeat.dll was blocked. This file is intentionally added to generate a heartbeat signal. Caught by FileLoadException"); } catch (Exception ex) { log.Info($"AppcontrolHeartbeat.dll was blocked. This file is intentionally added to generate a heartbeat signal. Caught by {ex.Source}"); }
在启用AppLocker或WDAC的设备上,Class1所在的AppControlHeartbeat.dll会被拦截,这符合预期,但实际运行时抛出了未处理的FileLoadException,日志如下:
.\WDACEventLogCollectorCore.exe
Unhandled exception. System.IO.FileLoadException: Could not load file or assembly 'C:\Users\UserProfile\OneDrive\WDACLogCollectorCore\net8.0-windows10.0.19041.0\AppControlHeartbeat.dll'. Your organization used Device Guard to block this app. Contact your support person for more info. (0x800711C7)
File name: 'C:\Users\UserProfile\OneDrive\WDACLogCollectorCore\net8.0-windows10.0.19041.0\AppControlHeartbeat.dll'
at WDACEventLogCollector.Program.Main(String[] args)
想请教:为什么这个异常没被catch块捕获?.NET Core环境会对此有影响吗?
原因分析
核心问题是程序集加载时机不在try块的执行范围内。
在.NET Core/.NET 5+(包括你使用的.NET 8)中,当包含new Class1()的方法(此处为Main方法)被JIT编译时,CLR会提前解析方法中引用的所有外部类型,此时会触发AppControlHeartbeat.dll的加载操作。这个加载过程发生在方法开始执行之前,也就是try块还未启动运行,因此抛出的异常无法被方法内部的try-catch捕获。
对比.NET Framework,它的程序集加载时机更延迟(通常在第一次实际使用类型时才加载),所以同样的代码在.NET Framework中可能正常捕获异常,但.NET Core的JIT编译和程序集加载机制做了优化,提前解析依赖,导致异常发生在try块之外。
解决方案
要让程序集加载操作落在try块内部,需要用反射延迟加载程序集和创建实例,避免JIT编译阶段提前加载。修改后的代码示例:
try { // 手动加载目标程序集 var assembly = Assembly.LoadFrom("AppControlHeartbeat.dll"); // 获取Class1类型(替换为实际的命名空间+类型名) var type = assembly.GetType("YourNamespace.Class1"); // 创建类型实例 var appcontrolHeartbeat = Activator.CreateInstance(type); } catch (FileLoadException e) { log.Info($"AppcontrolHeartbeat.dll was blocked. This file is intentionally added to generate a heartbeat signal. Caught by FileLoadException"); } catch (Exception ex) { log.Info($"AppcontrolHeartbeat.dll was blocked. This file is intentionally added to generate a heartbeat signal. Caught by {ex.Source}"); }
这样,程序集加载和类型实例化操作都在try块内部执行,异常就能被正常捕获,后续代码也能继续运行。
内容的提问来源于stack exchange,提问作者Kim Oppalfens

