如何检测和管理加载的 DLL 执行的磁盘/注册表/网络活动?

Posted

技术标签:

【中文标题】如何检测和管理加载的 DLL 执行的磁盘/注册表/网络活动?【英文标题】:How to detect and manage disk/registry/network activity performed by a loaded DLL? 【发布时间】:2019-08-26 23:45:19 【问题描述】:

假设我的应用程序实现了一个插件/扩展系统,它允许用户从外部 DLL 文件动态加载和执行代码,我将如何管理 磁盘注册表方面的安全性网络 访问(读/写/删除)?

更具体地说,我希望能够知道插件/扩展何时将执行磁盘/注册表/网络操作,拦截该操作并根据一组预定义规则允许或拒绝它,或者只是通过询问用户是否允许对该特定插件进行某种类型的访问。

【问题讨论】:

这应该是健全性检查还是安全边界?对于后者,您唯一的选择是 (1) 以不同用户身份运行的单独进程或 (2) 解释(或由您控制的编译器编译的 JIT)脚本语言,它根本无法直接处理系统调用。根本没有办法阻止进程内二进制代码为所欲为(达到您可以做的极限)。 值得注意的是,用户模式代码总是可以逃避其他用户模式代码拦截它的尝试。只有几种方法: (a) 将合法参数传递给已批准的接口,但在将文件名传递给操作系统之前,从另一个线程覆盖文件名。 (b) 绕过批准的接口并直接动态链接到操作系统功能。 (c) 完全不调用任何库 API 函数,而是加载参数并进行系统调用。 @BenVoigt 粗略地说,我的插件/扩展实现的工作方式是它从有效的扩展 DLL 加载入口点,然后这些扩展 DLL 可以通过主机应用程序执行任何操作。我正在寻求实现一个能够检测磁盘/注册表/网络操作并根据一组规则或用户输入(类似于防病毒软件的工作原理)阻止或允许此类操作的监控系统。 @BenVoigt 让第二个进程监视加载扩展的进程听起来是个好主意,我会接受它,在这方面,我更喜欢 c# 代码示例来演示一个进程如何检测另一个磁盘/注册表/网络活动并暂停它,直到用户允许或拒绝该操作(我猜这就是 AV 的工作方式)。所以是的,我会走这条路。 @IneedHelp:您将需要内核模式代码来执行此操作。您的问题是您在选择实现之后尝试修补访问控制,而实际上需要在编写单行代码之前对其进行设计。 【参考方案1】:

使用 AppDomains 和代码访问安全性可以实现一些控制。您可以创建沙盒 AppDomain 并将其授予您希望插件访问的目录的权限。如果插件试图访问它们之外的文件,你会得到 SecurityException。

例如,如果我们有以下 ClassLib.dll 插件:

namespace ClassLib

    public class Class1
    
        public static string DoWork(string path)
        
            return System.IO.File.ReadAllText(path);
        
    

我们可以像这样使用受限权限调用它:

using System;
using System.Collections;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;

namespace ConsoleApplication1

    class Program
    
        //Path to plugin library
        public static string libname = @"C:\PROJECTS\ConsoleApp1\bin\Debug\ClassLib.dll";

        //Invokes plugin library in restricted AppDomain
        //Grants access to the directory specified by allowedPath parameter
        static void InvokeLibrary(string allowedPath="")
        
            var path = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;

            //create restricted permission set
            Evidence evidence = new Evidence();
            evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
            PermissionSet permissionSet = SecurityManager.GetStandardSandbox(evidence);

            //grant read access to plugin library file, so it can at least load successfully
            permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, libname));
            permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, libname));

            //grant read access to
            if (allowedPath != "")
            
                permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, allowedPath));
                        

            AppDomainSetup appDomainSetup = new AppDomainSetup();
            appDomainSetup.ApplicationBase = System.IO.Path.GetDirectoryName(path);

            //create AppDomain with specified permissions
            AppDomain domain = AppDomain.CreateDomain(
                "MyDomain",
                evidence,
                appDomainSetup,
                permissionSet
                );

            //create remoting wrapper
            Type type = typeof(Wrapper);
            Wrapper wrapper = (Wrapper)domain.CreateInstanceAndUnwrap(
                type.Assembly.FullName,
                type.FullName);

            //invoke method          
            InvokeResult res = wrapper.Invoke(libname,"c:\\dir\\file.txt");

            if (res.Success) Console.WriteLine("Invoke result: " + res.Result);
            else 
                Console.WriteLine("Invoke error: " + res.Error.GetType().ToString());
                if (res.Error is SecurityException)
                
                    Console.WriteLine("Demanded permissions:\n" + (res.Error as SecurityException).Demanded);
                
                                

            AppDomain.Unload(domain);
        

        static void Main(string[] args)
        
            //invoke plugin without explicit permissions
            Console.WriteLine("First attempt...");
            InvokeLibrary();

            //invoke plugin with specified directory access
            Console.WriteLine("Second attempt...");
            InvokeLibrary("c:\\dir\\");   

            Console.ReadKey();
        

    

    //Object to pass between app domains
    public class InvokeResult : MarshalByRefObject
    
        public bool Success = false;
        public string Result="";
        public Exception Error =null;
    

    //Wrapper for remoting
    public class Wrapper : MarshalByRefObject
    
        public Exception lasterror;

        public InvokeResult Invoke(string lib, string arg)
        
            InvokeResult res = new InvokeResult();

            try
            
                //load plugin                
                Assembly ass = Assembly.LoadFile(lib);
                Type t = ass.GetType("ClassLib.Class1");
                MethodInfo mi = t.GetMethod("DoWork", BindingFlags.Static | BindingFlags.Public);

                //invoke plugin method
                res.Result = (string)mi.Invoke(null, new object[]  arg );
                res.Success = true;

            
            catch (TargetInvocationException ex)
            
                res.Success = false;
                if (ex.InnerException != null) res.Error = ex.InnerException;
                else res.Error = ex;
            
            return res;
        
    


/* Output:

First attempt...
Invoke error: System.Security.SecurityException
Demanded permissions:
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Read="c:\dir\file.txt"/>

Second attempt...
Invoke result: The content of file.txt 
*/

【讨论】:

【参考方案2】:

对于绝对解决方案,您必须使用内核驱动程序来拦截请求。但是如果你不需要100%,你可以开发一个类似API Monitor的工具。

工具钩子API函数,(很少有这样的库,MSDetour,Mhook等)并且可以记录通话或通话中断。通过检查堆栈也可以知道哪个模块调用了 API。

【讨论】:

以上是关于如何检测和管理加载的 DLL 执行的磁盘/注册表/网络活动?的主要内容,如果未能解决你的问题,请参考以下文章

如何判断一个dll是不是可以被劫持

如何将带有 dll 的应用程序从内存加载到 AppDomain 中并执行它?

如何检测使用 LoadLibraryEx 加载的模块

磁盘和文件系统管理

“Dll已加载,但对Dll的调用失败”解决办法

模块dynwrapx.dll已加载 调用失败