如何检测和管理加载的 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 执行的磁盘/注册表/网络活动?的主要内容,如果未能解决你的问题,请参考以下文章