.Net内存加载与卸载探讨
2023-8-21 13:43:11 Author: mp.weixin.qq.com(查看原文) 阅读量:7 收藏

简介

随着攻防博弈的发展,无文件攻击在各项场景中出现频率越来越高,研究无文件攻击的实现与检测对于抵御外部攻击有着重要的意义。.Net程序集的内存加载是无文件攻击实施的手段之一,目前现有的加载技术大多适用于Net FrameWork框架, 本文针对.Net Core和.Net FrameWork中的加载方式和卸载方式进行了分析探讨

01 加载

.Net程序集加载应用很普遍,像冰蝎中aspx马,就是使用Assembly.Load来加载各种功能的程序集从而实现webshell控制。yso.net工具中有条反序列化利用链通过Assembly.load直接加载恶意程序集。asp.net内存马也可以通过Assembly.load注入,还可以通过多阶段加载方式来进行防御规避。

根据微软官方文档,.NET中的程序集加载方式有下面几种:

Assembly.Load

此方式将程序集加载到当前上下文

Assembly.LoadFrom

此方式将程序集加载到当前上下文

Assembly.LoadFile

此方式将程序集加载到当前上下文

Assembly.ReflectionOnlyLoad 

AssemblyReflectionOnlyLoadFrom

程序集加载到反射上下文,无法执行代码

AppDomain.CreateInstance 

创建指定类型的实例

AppDomain.CreateInstanceAndUnwrap

返回创建的实例的代理, 把其他应用程序域中的变量传到当前的应用程序域

Type.GetType 


AppDomain.Load 


AssemblyLoadContext.Load

.Net Core中的新增,允许加载同一程序集的不同版本到不同上下文

以上加载方式,Assembly.Load和AssemblyLoadContext.Load支持字节流加载。

Assembly.LoadFrom 加载dll文件及依赖项。它还可以从远程加载程序集,支持http、file等协议,但从.Net FrameWork 4后,默认禁止远程加载。此外,如果目标程序集已经加载过,LoadFrom()不会重新进行加载。

Assembly.LoadFile 仅加载dll不加载依赖项。

AssemblyLoadContext.Load是Net core中为替代Appdomain提出的新的进程内隔离解决方案。Load会第一次解析、加载和返回程序集。如果第一次返回null,则加载程序会尝试将程序集加载到默认上下文AssemblyLoadContext.Default中。如果AssemblyLoadContext.Default无法解析程序集,则原始AssemblyLoadContext将有第二次机会解析程序集。运行时引发Resolving事件,我们可以自定义Resolving事件来编写依赖项解析行为。下面这个例子,将Test.dll加载到新的上下文中,Test.dll会打印出当前的上下文名字。

AssemblyLoadContext assemblyLoadContext =  new AssemblyLoadContext("New1",true); //true允许unloadvar assem = assemblyLoadContext.LoadFromStream(new MemoryStream( File.ReadAllBytes("Test.dll")));assem.GetType("Test.Test").GetMethod("Run").Invoke(null,null);

运行结果如下,可以看到,程序集加载到了新的上下文中:

如果只是想在其他Appdomain执行方法,可以调用domain.DoCallBack()。CrossAppDomainDelegate是委托类型,签名如下:

[System.Runtime.InteropServices.ComVisible(true)]public delegate void CrossAppDomainDelegate();
public void DoCallBack (CrossAppDomainDelegate callBackDelegate);

举个例子,运行该代码,可以把字节码加载到新的Appdomain中:

public static void Main(){    AppDomain domain = AppDomain.CreateDomain("MyDomain");    domain.DoCallBack(new CrossAppDomainDelegate(CallbackTest));}
public static void CallbackTest(){    Assembly.Load(System.IO.File.ReadAllBytes(@"E.dll"));   var s =  AppDomain.CurrentDomain.FriendlyName;}

02 卸载

在.NET Core 中,System.Runtime.Loader.AssemblyLoadContext类处理程序集的卸载。在Net FrameWork中,如果我们想要卸载单独的程序集,那是做不到的,想要在 .NET Framework 中卸载程序集,必须卸载包含它的所有应用程序域。假设内存加载了一个恶意程序集到新的应用程序域,来看一下应用程序域如何被卸载。卸载应用程序域使用AppDomain.Unload 方法。

下面看一段代码,这段代码的作用是弹窗,编译成程序集E.dll

namespace E{    public class E    {        public void Run()        {            try            {                System.Windows.Forms.MessageBox.Show("Run","Title");            }            catch (Exception)            {            }        }    }}

下面代码读取了E.dll的字节码,新创建一个Appdomain,然后以Load(byte[])方法加载程序集E.dll到新应用程序域当中,反射调用Run方法,最后卸载Appdomain。

byte[] bytes = File.ReadAllBytes("E.dll"); // dll  bytes  AppDomain domain = AppDomain.CreateDomain("Test");    Assembly assembly = domain.Load(bytes);    MethodInfo method = assembly.GetType("E.E").GetMethod("Run");    if (method != null)    {        object o = assembly.CreateInstance("E.E");        try        {            method.Invoke(o, null);        }        catch (TargetInvocationException ex)        {            Console.WriteLine(ex.ToString());        }    }

看起来没什么错误,但一旦运行后,就会报下面异常。

其原因是Appdomain.Load(byte[])加载的程序集,在Appdomain间是无法共享类型信息的。如果默认Appdomain没有加载这个程序集,那么就不会有关于此程序集的类型信息,第二个Appdomian用Load(Byte[])加载后,要把程序集对象传递到默认Appdomain中,但默认Appdomain找不到此对象的信息,所以会报错。解决方式就是使用跨应用程序域访问。

一般来说,有两种方式进行跨域通信,marshalbyrefobject和Serializable,前者是引用传递,后者是值传递。看个引用传递的例子。首先创建Proxy类继承MarshalByObject,MarshalByObject类属于基元类型,在默认Appdomain中存在。然后调用CreateInstanceAndUnwrap得到Proxy类的代理类。CreateInstanceAndUnwrap方法是CreateInstance和Objecthandle.Unwrap方法的结合,Proxy类实际上在MyDomain中实例化,然后通过Objecthandle.Unwrap包装后传递给默认Appdomain,这样便实现了跨AppDomain的对象传递。之后Proxy类的方法调用,实际上也是在Mydomain中执行的。

void Main(){    byte[] classbytes =  File.ReadAllBytes("E.dll");    AppDomainSetup domaininfo = new AppDomainSetup();    domaininfo.ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);    System.Security.Policy.Evidence adevidence = AppDomain.CurrentDomain.Evidence;    AppDomain currentDomain = AppDomain.CreateDomain("MyDomain", adevidence, domaininfo);    Type type = typeof(Proxy);    Proxy p = (Proxy)currentDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, type.FullName);    p.LoadAssembly(classbytes);    AppDomain.Unload(currentDomain);}
public class Proxy: MarshalByObject{     public Object LoadAssembly(byte[] bytes)        {            Console.WriteLine(Appdomain.Currentdomain.FriendlyName);            Assembly assembly =  AppDomain.CurrentDomain.Load(bytes);            MethodInfo method = assembly.GetType("E.E").GetMethod("Run");            if (method != null)            {                object o = assembly.CreateInstance("E.E");                try                {                    var res = method.Invoke(o, null);                    return res;                }                catch (TargetInvocationException ex)                {                    Console.WriteLine(ex.ToString());                                  }            }            return null;        }}

运行结果如下:

03 总结

本文主要针对.Net Core和.Net FrameWork框架下的程序集加载技术进行了总结,针对程序集的卸载技术进行了分析,指出了使用无文件加载后,如何正确卸载程序集的方式。

附录:参考链接

[1]https://learn.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext?view=net-7.0

[2]https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/understanding-assemblyloadcontext

[3]https://learn.microsoft.com/en-us/dotnet/api/system.runtime.remoting.objecthandle?view=net-7.0

[4]https://3gstudent.github.io/%E4%BB%8E%E5%86%85%E5%AD%98%E5%8A%A0%E8%BD%BD.NET%E7%A8%8B%E5%BA%8F%E9%9B%86(Assembly.Load)%E7%9A%84%E5%88%A9%E7%94%A8%E5%88%86%E6%9E%90

绿盟科技天元实验室专注于新型实战化攻防对抗技术研究。

研究目标包括:漏洞利用技术、防御绕过技术、攻击隐匿技术、攻击持久化技术等蓝军技术,以及攻击技战术、攻击框架的研究。涵盖Web安全、终端安全、AD安全、云安全等多个技术领域的攻击技术研究,以及工业互联网、车联网等业务场景的攻击技术研究。通过研究攻击对抗技术,从攻击视角提供识别风险的方法和手段,为威胁对抗提供决策支撑。

M01N Team公众号

聚焦高级攻防对抗热点技术

绿盟科技蓝军技术研究战队

官方攻防交流群

网络安全一手资讯

攻防技术答疑解惑

扫码加好友即可拉群


文章来源: https://mp.weixin.qq.com/s?__biz=MzkyMTI0NjA3OA==&mid=2247492142&idx=1&sn=393418ca792f2a4dcf5300a9ec63aa84&chksm=c184223ff6f3ab29a9fa591ecf76a93d34976115fdc1e84130869add83dc96aa1920b0f48585&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh