九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

admin 2023年6月6日09:40:34评论23 views字数 8415阅读28分3秒阅读模式

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

写在前边



1.本文原文为K A, Monnappa. 2018年发表的《Learning Malware Analysis》的章节选段,本文均为译者对相关内容的翻译及实践记录,仅供各位学术交流使用。另出于易读性考虑,对部分字句有所删改。 
2.如各位需要引用,请做原文引用,格式如下所示: 
[序号]K A, Monnappa. LearningMalware Analysis[M]. 2018.06. Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK. 
3.因文章整体内容较长,完整内容将会在本公众号拆分为多篇内容分别发出。本文为该系列的第四篇。
往期内容请参见合集《Learning Malware Analysis》





6. I/O处理

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


在讨论driverscan插件时,我们提到过driverscan从DRIVER_OBJECT结构中获取模块信息。想知道DRIVER_OBJECT结构是?这一点很快就会清楚。


在本节中,我们将了解用户模式和内核模式组件之间的交互、设备驱动程序的角色以及它与I/O管理器的交互。通常,一个rootkit由一个用户模式组件(EXE或DLL)和一个内核模式组件(设备驱动程序)组成。rootkit的用户模式组件使用特定的机制与内核模式组件通信。从取证的角度来看,必须了解这些通信的工作方式以及所涉及的组件。本节将帮助我们理解通信机制,并为接下来的主题奠定基础。


让我们试着理解当用户模式应用程序执行输入/输出(I/O)操作时发生了什么,以及在高级别上如何处理它。在讨论API调用流时,我使用了一个用户模式应用程序的示例使用WriteFile () API执行写操作,而最终调用NtWriteFile()系统服务程序在内核中执行(ntoskrnl.exe),然后将请求定向到I/O管理器,然后I/O管理器请求设备驱动程序执行I/O操作。


在这里,我们将再次详细讨论这个主题,重点是内核空间组件(主要是设备驱动程序和I/O管理器)。下面的图表说明了写请求的流程(其他类型的I/O请求,如read类似;它们只是使用了不同的api):

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


以下几点讨论了设备驱动程序和I/O管理器在高级别上的角色:

1、设备驱动程序通常创建一个或多个设备,并指定它可以为该设备处理什么类型的操作(打开、读取和写入)。它还指定处理这些操作的例程的地址。这些例程称为分派例程或IRP处理程序。


2、在创建设备之后,驱动程序会发布该设备,以便用户模式应用程序可以访问它。


3、用户模式应用程序可以使用API调用,如CreateFile,来打开处理发布的设备,并使用ReadFile和WriteFile API对设备进行读写等I/O操作。用于对文件进行I/O操作的api(如CreateFile、ReadWrite、WriteFile)也适用于设备。这是因为设备被视为一个虚拟文件。


4、当用户模式应用程序在发布的设备上执行I/O操作时,请求被路由到I/O管理器。I/O管理器通过传递IRP (I/O请求包)来确定处理设备的驱动程序,并请求驱动程序完成操作。IRP是一种数据结构,它包含关于执行什么操作以及I/O操作所需的缓冲区的信息。


驱动程序读取IRP,验证它,并在通知I/O管理器操作的状态之前完成所请求的操作。然后,I/O管理器将状态和数据返回给用户应用程序。


在这个阶段,前面的几点可能对我们来说很陌生,但不要因此而气馁:当完成这一部分时,自然就会清楚了。接下来,我们将研究设备驱动程序的角色,然后是I/O管理器的角色。




6.1 设备驱动程序的角色



当驱动加载到系统中时,I/O管理器创建一个驱动对象(DRIVER_OBJECT结构)。然后,I/O管理器调用驱动程序的初始化例程,DriverEntry(类似于main()或WinMain()函数),通过传递一个指针到DRIVER_OBJECT结构作为参数。一个驱动对象(DRIVER_OBJECT结构)代表系统上的一个单独的驱动。


DriverEntry例程将使用DRIVER_OBJECT用驱动的各种入口点来填充它,以处理特定的I/O请求。通常,在DriverEntry例程中,驱动程序创建一个代表逻辑或物理设备的设备对象(DEVICE_OBJECT结构)。设备是通过一个叫做IoCreateDevice或IoCreateDevice-Secure的API创建的。当驱动程序创建一个设备对象时,它可以有选择地为设备分配名称,它也可以创建多个设备。设备创建后,指向第一个创建的设备的指针在驱动程序对象中更新。


为了帮助大家更好地理解这一点,让我们列出已加载的内核模块,并查看一个简单内核模块的驱动程序对象。对于本例,我们将检查null.sys内核驱动程序。根据Microsoft文档,Null设备驱动程序在Unix环境中提供了与devNull等价的功能。当系统在内核初始化期间启动时阶段,null.Sys被加载到系统中。在内核模块清单中,我们可以看到这个null.Sys被装载在基地地址8bcde000:

kd> lm kstart end module name80ba2000 80baa000 kdcom (deferred) 81e29000 81e44000 luafv (deferred) [REMOVED]8bcde000 8bce5000 Null (deferred)

*左右滑动查看更多


当null.sys加载后,它的驱动对象(DRIVER_OBJECT结构)将在驱动初始化期间填充元数据信息。让我们看看它的驱动程序对象,以了解它包含什么样的信息。可以使用!drvobj扩展名命令显示驱动对象信息。从下面的输出中,驱动程序对象表示为空。Sys的地址是86a33180。设备对象列表下面的值86aa2750是指向null.sys创建的设备对象的指针。如果驱动程序创建了多个设备,你会在设备对象列表中看到多个条目:

kd> !drvobj NullDriver object (86a33180) is for:    DriverNull   Driver Extension List: (id , addr)   Device Object list:86aa2750


我们可以使用驱动程序对象地址86a33180来检查_DRIVER_OBJECT结构为空。可以通过dt (display type)命令查看。从下面的输出中,可以看到DriverStart字段保存了驱动程序的基址(0x8bcde000),DriverSize字段包含驱动的大小(0x7000),driverame是驱动对象的名称( driver Null)。DriverInit字段保存驱动初始化例程(DriverEntry)的指针。DriverUnload字段包含指向驱动程序卸载例程的指针,它通常会在卸载过程中释放驱动程序创建的资源。


MajorFunction字段是最重要的字段之一,它指向包含28个主要函数指针的表。这个表将使用分派例程的地址填充,我们将在本节的后面讨论MajorFunction表。前面介绍的driverscan插件会对驱动对象进行池标记扫描,并通过读取这些字段来获取与内核模块相关的信息,如基址、大小和驱动名称:

kd&gt; dt nt!_DRIVER_OBJECT 86a33180+0x000 Type : 0n4+0x002 Size : 0n168+0x004 DeviceObject : 0x86aa2750 _DEVICE_OBJECT +0x008 Flags : 0x12+0x00c DriverStart : 0x8bcde000 Void+0x010 DriverSize : 0x7000+0x014 DriverSection : 0x86aa2608 Void+0x018 DriverExtension : 0x86a33228 _DRIVER_EXTENSION +0x01c DriverName : _UNICODE_STRING "DriverNull" +0x024 HardwareDatabase : 0x82d86270 _UNICODE_STRING"REGISTRYMACHINEHARDWAREDESCRIPTIONSYSTEM"+0x028 FastIoDispatch : 0x8bce0000 _FAST_IO_DISPATCH +0x02c DriverInit : 0x8bce20bc long Null!GsDriverEntry+0 +0x030 DriverStartIo : (null)+0x034 DriverUnload : 0x8bce1040 void Null!NlsUnload+0 +0x038 MajorFunction : [28] 0x8bce107c<br></pre><p><br></p></section>

*左右滑动查看更多


DRIVER_OBJECT结构中的DeviceObject字段包含驱动程序(null.sys)创建的设备对象的指针。我们可以使用设备对象地址0x86aa2750来确定驱动程序创建的设备名称。在这种情况下,Null是驱动程序Null.sys创建的设备名称:

kd> !devobj 86aa2750Device object (86aa2750) is for:Null DriverNull DriverObject 86a33180Current Irp 00000000 RefCount 0 Type 00000015 Flags 00000040 Dacl 8c667558 DevExt 00000000 DevObjExt 86aa2808 ExtensionFlags (0x00000800) DOE_DEFAULT_SD_PRESENT Characteristics (0x00000100) FILE_DEVICE_SECURE_OPENDevice queue is not busy.

*左右滑动查看更多


我们还可以通过在display type (dt)命令旁边指定设备对象地址来查看实际的DEVICE_OBJECT结构,如下面的代码所示。如果驱动程序创建了多个设备,那么DEVICE_OBJECT结构中的NextDevice字段将指向下一个设备对象。由于null.sys driver只创建一个设备,NextDevice字段设置为空:

kd> dt nt!_DEVICE_OBJECT 86aa2750+0x000 Type : 0n3+0x002 Size : 0xb8+0x004 ReferenceCount : 0n0+0x008 DriverObject : 0x86a33180 _DRIVER_OBJECT +0x00c NextDevice : (null)      +0x010 AttachedDevice : (null)      +0x014 CurrentIrp : (null)      +0x018 Timer : (null)      +0x01c Flags : 0x40      +0x020 Characteristics : 0x100      +0x024 Vpb : (null)      +0x028 DeviceExtension : (null)      +0x02c DeviceType : 0x15      +0x030 StackSize : 1 ''      [REMOVED]

*左右滑动查看更多


从前面的输出中,我们可以看到DEVICE_OBJECT包含一个指向驱动对象的DriverObject字段。换句话说,关联的驱动程序可以从设备对象中确定。这就是当I/O管理器接收到特定设备的I/O请求时,它可以确定相关驱动程序的方式。这个概念可以通过以下图表来可视化:

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


您可以使用GUI工具,如DeviceTree查看驱动程序创建的设备。

DeviceTree:http://www.osronline.com/article.cfm?article=97

*左右滑动查看更多


下面是一个工具的屏幕截图,显示了Null设备创建的Null.sys驱动:

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


当一个驱动程序创建一个设备时,设备对象被放置在Windows对象管理器命名空间的device目录中。要查看对象管理器的名称空间信息,可以使用WinObj工具

WinObj工具https://docs.microsoft.com/en-us/sysinternals/downloads/WinObj

*左右滑动查看更多


下面的截图显示了Null创建的设备Null.sys在Device目录下。我们也可以看到其他驱动程序创建的设备:

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


运行在用户模式下的应用程序无法访问在device目录下创建的设备。换句话说,如果用户模式应用程序想要在设备上执行I/O操作,它不能通过传递设备的名称(如deviceNull)作为CreateFile函数的参数直接打开设备句柄。CreateFile函数不仅仅用于创建或打开文件,它还可以用于打开设备的句柄。


如果用户模式应用程序不能访问设备,那么它如何执行I/O操作?为了让用户模式应用程序可以访问设备,驱动程序需要发布设备。这是通过创建到设备的符号链接来完成的。驱动程序可以使用内核API IoCreateSymbolicLink来创建符号链接。


当为一个设备(如deviceNull)创建一个符号链接时,您可以在GLOBAL??对象管理器名称空间中的目录,也可以使用WinObj工具。在下面的截图中,您可以看到NUL是通过null.sys驱动的名为为DeviceNull设备创建的符号链接。

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

符号链接也被称为MS-DOS设备名。用户模式应用程序可以简单地使用符号链接的名称(MS-DOS设备名)来使用约定打开设备句柄 .<symboliclink name>。例如,要打开DeviceNull的句柄,用户模式应用程序必须只传递.NUL作为CreateFile函数的第一个参数(lpFilename),它返回设备的文件句柄。


具体地说,对象管理器目录GLOBAL中的任何符号链接。可以使用CreateFile函数打开。如下图所示,C:卷只是一个到DeviceHarddiskVolume1的符号链接。在Windows操作系统中,I/O操作是在虚拟文件上进行的。换句话说,设备、目录、管道和文件都被视为虚拟文件(可以使用CreateFile函数打开):

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


此时,我们知道驱动程序在其初始化过程中创建设备,并使用符号链接将其发布给用户应用程序使用。现在问题是驱动程序如何告诉I/O管理器它支持设备的什么类型的操作(打开、读、写,等等)?在初始化期间,驱动程序通常做的另一件事是用DRIVER_OBJECT结构中分派例程的地址更新Major函数表(分派例程数组)。


通过查看主要函数表,您可以了解驱动程序支持的操作类型(打开、读取、写入等),以及与特定操作关联的调度例程的地址。主函数表是一个包含28个函数指针的数组;索引值0到27表示一个特定的操作。


例如,索引值0对应于主函数代码IRP_MJ_CREATE,索引值3对应于主函数代码IRP_MJ_READ,以此类推。换句话说,如果应用程序想打开一个文件或设备对象的句柄,请求将被发送到I/O管理器,然后使用将IRP_MJ_CREATE主函数代码作为主函数表的索引,以查找将处理此请求的调度例程的地址。与读取操作相同,使用IRP_MJ_READ作为索引来确定分派例程的地址。


以下!drvobj命令显示由null.sys驱动程序填充的分派例程数组。驱动程序不支持的操作指向ntoskrnl.exe (nt)中的IopInvalidDeviceRequest。根据这个信息,我们可以判断为null.sys仅支持IRP_MJ_CREATE (open)、IRP_MJ_CLOSE (close)、IRP_MJ_READ (read)、IRP_MJ_WRITE (write)、IRP_MJ_QUERY_INFORMATION(查询信息)、IRP_MJ_LOCK_CONTROL(锁控制)操作。执行任何支持的操作的任何请求都将被分派到适当的分派例程。


例如,当用户应用程序执行写操作时,对设备的写请求将被分配到MajorFunction[IRP_MJ_WRITE]函数,该函数恰好位于null.sys驱动的卸载程序中的8bce107c地址。在nul.Sys的情况下,所有受支持的操作都分派给同一个操作地址,8bce107c。通常情况下,情况并非如此,我们会看到不同的例程地址用于处理不同的操作:

kd> !drvobj Null 2Driver object (86a33180) is for: DriverNullDriverEntry: 8bce20bc Null!GsDriverEntryDriverStartIo: 00000000DriverUnload: 8bce1040 Null!NlsUnloadAddDevice: 00000000Dispatch routines:[00] IRP_MJ_CREATE[01] IRP_MJ_CREATE_NAMED_PIPE[02] IRP_MJ_CLOSE[03] IRP_MJ_READ[04] IRP_MJ_WRITE[05] IRP_MJ_QUERY_INFORMATION[06] IRP_MJ_SET_INFORMATION[07] IRP_MJ_QUERY_EA[08] IRP_MJ_SET_EA[09] IRP_MJ_FLUSH_BUFFERS[0a] IRP_MJ_QUERY_VOLUME_INFORMATION 82ac5fbe nt!IopInvalidDeviceRequest[0b] IRP_MJ_SET_VOLUME_INFORMATION[0c] IRP_MJ_DIRECTORY_CONTROL[0d] IRP_MJ_FILE_SYSTEM_CONTROL[0e] IRP_MJ_DEVICE_CONTROL82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL  82ac5fbe nt!IopInvalidDeviceRequest[10] IRP_MJ_SHUTDOWN[11] IRP_MJ_LOCK_CONTROL[12] IRP_MJ_CLEANUP[13] IRP_MJ_CREATE_MAILSLOT[14] IRP_MJ_QUERY_SECURITY[15] IRP_MJ_SET_SECURITY[16] IRP_MJ_POWER[17] IRP_MJ_SYSTEM_CONTROL[18] IRP_MJ_DEVICE_CHANGE[19] IRP_MJ_QUERY_QUOTA[1a] IRP_MJ_SET_QUOTA[1b] IRP_MJ_PNP82ac5fbe nt!IopInvalidDeviceRequest8bce107c Null!NlsUnload+0x3c82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest82ac5fbe nt!IopInvalidDeviceRequest

*左右滑动查看更多


九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


我们还可以在DeviceTree工具中查看支持的操作,如下截图所示:

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


此时,我们知道驱动程序创建了设备,将其发布给用户应用程序使用,并且它还更新调度例程数组(主函数表),告诉I/O管理器它支持什么操作。现在,让我们看看I/O管理器的角色是什么,并理解如何将从用户应用程序接收的I/O请求分派给驱动程序。


(未完待续)



往期回顾


九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)


关于安恒信息安全服务团队
安恒信息安全服务团队由九维安全能力专家构成,其职责分别为:红队持续突破、橙队擅于赋能、黄队致力建设、绿队跟踪改进、青队快速处置、蓝队实时防御,紫队不断优化、暗队专注情报和研究、白队运营管理,以体系化的安全人才及技术为客户赋能。

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

原文始发于微信公众号(安恒信息安全服务):九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年6月6日09:40:34
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   九维团队-青队(处置)| 使用内存取证检测高级恶意软件(四)https://cn-sec.com/archives/1657117.html

发表评论

匿名网友 填写信息