通过.NET实现Gargoyle(一种内存扫描的对抗技术)
Gargoyle是一种帮助恶意软件逃脱内存扫描的技术,由Josh Lospinoso于2017年对外公布,不过只是作为概念验证发布的,它的强项在于确保注入的代码在大多数情况下是不可执行(隐藏)的,从而使内存扫描技术难以识别出恶意代码。客观的说,这是一项很棒的技术,因为研究人员曾经使用 Cobalt Strike (一款非常优秀的后渗透平台)进行过测试,除此之外还用WinDBG( 查看和调试windows内核的一些东西难免需要用到WinDbg)和Volatility插件(Volatility是一款开源内存取证框架,能够对导出的内存镜像进行分析,通过获取内核数据结构,使用插件获取内存的详细情况以及系统的运行状态。)来进行过检测。
由于Powershell支持较高版本的.net,所以研究人员发现通过.NET实现Gargoyle也是可能的。
动态.NET代码执行
目前许多攻击性技术都尽可能避免让恶意代码在目标设备上的硬盘上工作,这样做的目的是为了避免传统的杀毒扫描并避免在硬盘留下可以进行取证的痕迹,这就导致了内存取证技术的发展,以帮助高效打击恶意攻击。对于本机代码,传统上使用反射DLL注入(reflective DLL injection)技术,Reflective DLL Injection提出来就是解决这个问题的,完全不需要在硬盘上写入inject.dll,而是直接在内存当中操作;process hollowing(现代恶意软件常用的一种进程创建技术)以及许多其他类似技术来执行。但是,.NET可以通过使用动态程序集加载和反射,以非常简单的逃避机制来实现以上这些技术的目标。
例如,在PowerShell中研究人员可以看到如何在内存中动态加载.NET程序集,并使用反射技术来实例化类并进行调用。
对于本文的详细背景信息,如果你感兴趣,可以阅读《如何检测恶意使用的.NET》,其中有更多详细信息。此外,本文的一些信息也在Joe Desimone发布的一篇精彩文章中有所介绍,他在其中还演示了如何使用他发布的Get-ClrReflection.ps1 PowerShell脚本检测内存中加载的.NET程序集。
.NET定时器和回调
最开始的Gargoyle技术使用的是本机Windows定时器对象,以便在定义的时间间隔内执行回调。然后,当定时器到期时,使用由内核传送到目标进程的APC执行这些操作。但在.NET的实现过程中,它采用了更准时和高级的定时器实现方法。
正如研究人员从Timer()类的构造函数中看到的那样,研究人员可以指定.NET回调方法和在定时器上提供的参数。不过其中的一个限制是,回调必须与TimerCallback委托匹配,具体的过程研究人员可以在下面看到。
定时器是系统常用的集之一,程序员可以根据自己的需求定制一个定时器类型,也可以使用.net内建的定时器类型。在.net中一共为程序员提供了3种定时器:
· System.Windows.Forms.Timer类型
· System.Threading.Timer类型
· System.Timers.Timer类型
概括来说,这3种类型都实现了定时的功能。程序员通常需要做的是为定时器设置一个间断时间,设置定时器到时的处理方法,然后可以等待定时器不断地定时和出发时间处理。
由此,我猜测此处使用的应该是System.Threading.Timer类,TimerCallback委托就是其中的建构函数,使用TimerCallback委托来指定由调用的方法Timer, 在创建计时器的线程中,此方法不会执行,它在系统提供一个单独的线程池线程中执行。由于这种委托条件的限制,研究人员仅限于使用此签名调用方法。在理想的情况下,研究人员可能希望纯粹指定对Assembly.Load()的回调,将程序集的字节数组作为状态参数,从而确保恶意代码执行。遗憾的是,委托不匹配,并且在加载程序集时自动运行代码并不像在DllMain()函数中放置代码那样简单,就像本机DLL的情况一样。因此,研究人员需要制作一个经过简化的封装器(Wrapper)来处理内存中恶意程序集的加载和执行,然后对计时器进行清理和重新调度。
加载的实现
现在我们有了一个.NET timer原语,它允许我们定义回调,不过要实现加载,
还需要进行以下3步的操作:
1.研究人员需要自定义一些加载代码,且这些代码需要永久加载。因此,研究人员的办法是尽可能使他们简单化且无攻击性,以便将其隐藏起来;
2.研究人员无法将程序集单个的进行卸载(具体原因,请点此查看),因此为了清理植入的设备中的具备全部恶意功能的程序集,研究人员需要将其加载到新的应用程序域(Appdomain)中,然后卸载,AppDomain类似于一个轻量级进程,它是 .net/mono 代码运行时的一个逻辑容器;
3.研究人员需要找到一些方法来加载.NET加载程序集,然后调用一个方法来创建.NET定时器,以便将来加载研究人员用来测试的恶意程序集。
在本文中,“AssemblyLoader”就是研究人员自定义的最简单的无害加载程序集,“DemoAssembly”将是一个PoC程序集,表示在实际使用中具有完整功能的恶意植入。
以下是一段C#代码,涵盖了上述第1点和第2点所需的大部分内容:
public class AssemblyLoaderProxy : MarshalByRefObject, IAssemblyLoader
{
public void Load(byte[] bytes)
{
var assembly = AppDomain.CurrentDomain.Load(bytes);
var type = assembly.GetType("DemoAssembly.DemoClass");
var method = type.GetMethod("HelloWorld");
var instance = Activator.CreateInstance(type, null);
Gargoyle是一种帮助恶意软件逃脱内存扫描的技术,由Josh Lospinoso于2017年对外公布,不过只是作为概念验证发布的,它的强项在于确保注入的代码在大多数情况下是不可执行(隐藏)的,从而使内存扫描技术难以识别出恶意代码。客观的说,这是一项很棒的技术,因为研究人员曾经使用 Cobalt Strike (一款非常优秀的后渗透平台)进行过测试,除此之外还用WinDBG( 查看和调试windows内核的一些东西难免需要用到WinDbg)和Volatility插件(Volatility是一款开源内存取证框架,能够对导出的内存镜像进行分析,通过获取内核数据结构,使用插件获取内存的详细情况以及系统的运行状态。)来进行过检测。
由于Powershell支持较高版本的.net,所以研究人员发现通过.NET实现Gargoyle也是可能的。
动态.NET代码执行
目前许多攻击性技术都尽可能避免让恶意代码在目标设备上的硬盘上工作,这样做的目的是为了避免传统的杀毒扫描并避免在硬盘留下可以进行取证的痕迹,这就导致了内存取证技术的发展,以帮助高效打击恶意攻击。对于本机代码,传统上使用反射DLL注入(reflective DLL injection)技术,Reflective DLL Injection提出来就是解决这个问题的,完全不需要在硬盘上写入inject.dll,而是直接在内存当中操作;process hollowing(现代恶意软件常用的一种进程创建技术)以及许多其他类似技术来执行。但是,.NET可以通过使用动态程序集加载和反射,以非常简单的逃避机制来实现以上这些技术的目标。
例如,在PowerShell中研究人员可以看到如何在内存中动态加载.NET程序集,并使用反射技术来实例化类并进行调用。
对于本文的详细背景信息,如果你感兴趣,可以阅读《如何检测恶意使用的.NET》,其中有更多详细信息。此外,本文的一些信息也在Joe Desimone发布的一篇精彩文章中有所介绍,他在其中还演示了如何使用他发布的Get-ClrReflection.ps1 PowerShell脚本检测内存中加载的.NET程序集。
.NET定时器和回调
最开始的Gargoyle技术使用的是本机Windows定时器对象,以便在定义的时间间隔内执行回调。然后,当定时器到期时,使用由内核传送到目标进程的APC执行这些操作。但在.NET的实现过程中,它采用了更准时和高级的定时器实现方法。
正如研究人员从Timer()类的构造函数中看到的那样,研究人员可以指定.NET回调方法和在定时器上提供的参数。不过其中的一个限制是,回调必须与TimerCallback委托匹配,具体的过程研究人员可以在下面看到。
定时器是系统常用的集之一,程序员可以根据自己的需求定制一个定时器类型,也可以使用.net内建的定时器类型。在.net中一共为程序员提供了3种定时器:
· System.Windows.Forms.Timer类型
· System.Threading.Timer类型
· System.Timers.Timer类型
概括来说,这3种类型都实现了定时的功能。程序员通常需要做的是为定时器设置一个间断时间,设置定时器到时的处理方法,然后可以等待定时器不断地定时和出发时间处理。
由此,我猜测此处使用的应该是System.Threading.Timer类,TimerCallback委托就是其中的建构函数,使用TimerCallback委托来指定由调用的方法Timer, 在创建计时器的线程中,此方法不会执行,它在系统提供一个单独的线程池线程中执行。由于这种委托条件的限制,研究人员仅限于使用此签名调用方法。在理想的情况下,研究人员可能希望纯粹指定对Assembly.Load()的回调,将程序集的字节数组作为状态参数,从而确保恶意代码执行。遗憾的是,委托不匹配,并且在加载程序集时自动运行代码并不像在DllMain()函数中放置代码那样简单,就像本机DLL的情况一样。因此,研究人员需要制作一个经过简化的封装器(Wrapper)来处理内存中恶意程序集的加载和执行,然后对计时器进行清理和重新调度。
copyright 无奈人生
加载的实现
现在我们有了一个.NET timer原语,它允许我们定义回调,不过要实现加载,
还需要进行以下3步的操作:
1.研究人员需要自定义一些加载代码,且这些代码需要永久加载。因此,研究人员的办法是尽可能使他们简单化且无攻击性,以便将其隐藏起来;
2.研究人员无法将程序集单个的进行卸载(具体原因,请点此查看),因此为了清理植入的设备中的具备全部恶意功能的程序集,研究人员需要将其加载到新的应用程序域(Appdomain)中,然后卸载,AppDomain类似于一个轻量级进程,它是 .net/mono 代码运行时的一个逻辑容器;
3.研究人员需要找到一些方法来加载.NET加载程序集,然后调用一个方法来创建.NET定时器,以便将来加载研究人员用来测试的恶意程序集。
在本文中,“AssemblyLoader”就是研究人员自定义的最简单的无害加载程序集,“DemoAssembly”将是一个PoC程序集,表示在实际使用中具有完整功能的恶意植入。
以下是一段C#代码,涵盖了上述第1点和第2点所需的大部分内容:
public class AssemblyLoaderProxy : MarshalByRefObject, IAssemblyLoader
{
public void Load(byte[] bytes)
{
无奈人生安全网
var assembly = AppDomain.CurrentDomain.Load(bytes);
var type = assembly.GetType("DemoAssembly.DemoClass");
var method = type.GetMethod("HelloWorld");
var instance = Activator.CreateInstance(type, null);
www.wnhack.com