如何在不破坏沙箱的前提下让AppDomain的StackTrace包含文件名和行号
受限AppDomain沙箱中异常栈追踪无法显示行号的问题
我在受限制的AppDomain沙箱中运行代码,即便PDB文件可用,该AppDomain内抛出的异常栈追踪也不包含行号信息。尝试访问栈追踪的代码已拥有完全信任:程序集已签名,作为强名称程序集加载到该应用域中,因此我原本预期栈追踪会包含文件名和行号。但我不能将该AppDomain标记为完全信任,否则沙箱就失去了意义,请问如何才能让栈追踪包含文件名和行号信息?
更新
我已更新代码,展示了使用Assembly.LoadFile加载外部代码的逻辑。我最初的问题用了单个程序集,在我看来它和我的实际应用表现出的行为一致。但@simon-mourier给出的答案虽然能解决这个简化代码的问题,却不适用于我的实际应用,因此我更新了代码以反映真实场景。
以下示例代码复现了问题,共涉及两个程序集:
- 当前运行的(父)程序集,已签名且拥有完全信任
- 子代码程序集,未签名,不可授予信任权限
父程序集代码
// 执行的已签名程序集 using System; using System.IO; using System.Linq; using System.Reflection; using System.Security.Permissions; using System.Security.Policy; namespace Parent { public class Worker : MarshalByRefObject { private static string childpath = Path.Combine(Path.GetDirectoryName(typeof(Worker).Assembly.Location), "Child.dll"); private static void Main() { var w = new Worker(); w.TestExceptionStacktrace(); var adSandbox = GetInternetSandbox(); var handle = Activator.CreateInstanceFrom( adSandbox, typeof(Worker).Assembly.ManifestModule.FullyQualifiedName, typeof(Worker).FullName); w = (Worker)handle.Unwrap(); w.TestExceptionStacktrace(); } public void TestExceptionStacktrace() { TestInner(); } private void TestInner() { var ass = Assembly.LoadFile(childpath); var playMethod = ass.GetTypes()[0].GetMethod("Play"); try { playMethod.Invoke(null, Array.Empty<object>()); } catch (Exception e) { var s = e.ToString(); Console.WriteLine("栈追踪{0}包含子程集的行号信息:", s.Split(new[] { Environment.NewLine }, StringSplitOptions.None) .Single(x => x.Contains("Play()")).Contains("line") ? "" : "不"); Console.WriteLine($" {s}"); } } // ------------ 辅助方法 --------------------------------------- private static AppDomain GetInternetSandbox() { // 为所有程序集创建权限集 var hostEvidence = new Evidence(); hostEvidence.AddHostEvidence(new Zone( System.Security.SecurityZone.Internet)); var pset = System.Security.SecurityManager.GetStandardSandbox(hostEvidence); // 给权限集添加额外权限 pset.AddPermission(new FileIOPermission( FileIOPermissionAccess.PathDiscovery, typeof(Worker).Assembly.Location) ); pset.AddPermission(new FileIOPermission( FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, Path.GetDirectoryName(childpath)) ); // 指定沙箱使用的文件夹 var ads = new AppDomainSetup(); ads.ApplicationBase = System.IO.Directory.GetCurrentDirectory(); var fullTrustAssemblies = new[] { typeof(Worker).Assembly.Evidence.GetHostEvidence<StrongName>(), }; // 创建沙箱应用域 return AppDomain.CreateDomain("Sandbox", hostEvidence, ads, pset, fullTrustAssemblies); } } }
子程序集代码
// 子程序集(未签名) using System; namespace Child { public class Child { public static void Play() { var ad = AppDomain.CurrentDomain; Console.WriteLine("\r\n应用域 '{0}': IsFullyTrusted = {1}", ad.FriendlyName, ad.IsFullyTrusted); Console.WriteLine(" 当前程序集 {1} 的IsFullyTrusted = {0}", typeof(Child).Assembly.IsFullyTrusted, typeof(Child).Assembly); Console.WriteLine(" mscorlib 的IsFullyTrusted = {0}", typeof(int).Assembly.IsFullyTrusted); throw new Exception("示例异常"); } } }
运行输出
代码运行输出如下:
应用域 'Parent.exe': IsFullyTrusted = True 当前程序集 Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 的IsFullyTrusted = True mscorlib 的IsFullyTrusted = True 栈追踪包含子程序集的行号信息: System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Exception: 示例异常 在 Child.Child.Play() 位置 C:\Users\Bouke\Developer\SandboxStacktrace\Child\Child.cs:行号 20 (...) 应用域 'Sandbox': IsFullyTrusted = False 当前程序集 Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 的IsFullyTrusted = False mscorlib 的IsFullyTrusted = True 栈追踪不包含子程序集的行号信息: System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Exception: 示例异常 在 Child.Child.Play() (...)
补充说明
目前我测试发现使用Assembly.Load(byte[], byte[], SecurityContextSource.CurrentAssembly)可以实现显示行号的需求,但会破坏沙箱隔离(加载的程序集会获得完全信任);而使用Assembly.Load(byte[], byte[], SecurityContextSource.CurrentAppDomain)则无法生效。
内容的提问来源于stack exchange,提问作者Bouke
相关产品推荐
相关产品推荐

