微软轻量级系统监控工具sysmon原理与实现完全分析(下篇)

简介: 上文讲解了sysmon的ring3部分实现原理,本文则开始讲解ring0部分。Sysmon的ring0是一个minifilter类型的驱动,内部实现了进程信息、文件访问信息以及注册表访问信息的记录,下面开始具体讲解它的实现流程。 驱动DriverEntry的初始化 从DriverEntry(PDRIVER_OBJECT DriverObject, UNICODE_STRING *pRe

上文讲解了sysmon的ring3部分实现原理,本文则开始讲解ring0部分。Sysmon的ring0是一个minifilter类型的驱动,内部实现了进程信息、文件访问信息以及注册表访问信息的记录,下面开始具体讲解它的实现流程。

  • 驱动DriverEntry的初始化

从DriverEntry(PDRIVER_OBJECT DriverObject, UNICODE_STRING *pRegistry)的pRegistry中截取末尾名称去获取并计算出设备名和DosDevices的名字。

pDriverName = pRegistry->Buffer;

Len = pRegistry->Length >> 1;

pFirstName = &pDriverName[Len];

if ( pFirstName == pDriverName )

{

LABEL_8:

if ( *pFirstName != '\\' )

goto LABEL_10;

}

else

{

while ( *pFirstName != '\\' )

{

--pFirstName;

if ( pFirstName == pDriverName )

goto LABEL_8;

}

}

++pFirstName;

然后从pRegistry注册表中去获取sysmon的策略规则

使用RtlQueryRegistryValues函数,填入5个RTL_QUERY_REGISTRY_TABLE结构体

RTL_QUERY_REGISTRY_TABLE QueryRegTable[5];

RtlInitUnicodeString(&g_ProcessAccessNamesRule, 0);

memset(QueryRegTable, 0, 560u);

QueryRegTable[0].Flags = 1;

QueryRegTable[0].Name = L"Parameters";

QueryRegTable[3].EntryContext = &OptionRulesv18;

QueryRegTable[4].EntryContext = &hash_alogrithms;

QueryRegTable[1].Flags = 304;

QueryRegTable[1].Name = g_Name_ProcessAccessNames;

QueryRegTable[1].EntryContext = &g_ProcessAccessNamesRule;

QueryRegTable[1].DefaultType = 0x7000007;

QueryRegTable[1].DefaultData = &unk_10015C34;

QueryRegTable[1].DefaultLength = 4;

QueryRegTable[2].Flags = 304;

QueryRegTable[2].Name = L"ProcessAccessMasks";

QueryRegTable[2].EntryContext = &g_ProcessAccessMasksRule;

QueryRegTable[2].DefaultType = 0x3000000;

QueryRegTable[3].Flags = 304;

QueryRegTable[3].Name = (PWSTR)&g_wOption;

QueryRegTable[3].DefaultType = 0x4000000;

QueryRegTable[4].Flags = 304;

QueryRegTable[4].Name = (PWSTR)&g_wHashingalgorithm;

QueryRegTable[4].DefaultType = 0x4000000;

RtlQueryRegistryValues(0, g_SysmonRegisterPath.Buffer, QueryRegTable, 0, 0);

if ( !g_ProcessAccessNamesRule.Buffer

|| g_ProcessAccessNamesRule.Length <= 2u

|| g_ProcessAccessNamesRule.MaximumLength <= 4u )

{

RtlFreeUnicodeString(&g_ProcessAccessNamesRule);

RtlInitUnicodeString(&g_ProcessAccessNamesRule, 0);

}

g_OptionRules = (OptionRulesv18 >> 1) & 1;

对应的注册表键分别是L”Parameters”、L”ProcessAccessNames”、 L”ProcessAccessMasks” 、L” Option”、L” Hashingalgorithm”

然后再次获取L”Parameters”项下面的对应的L”Rules”的KeyValues信息,这里是驱动设置的规则。

下面展示出部分规则的数组

上面的过程结束后就开始判断操作系统是否支持flt

如果支持只实现IRP_MJ_CREATE、IRP_MJ_CLOSE 、IRP_MJ_DEVICE_CONTROL三个例程,后续会注册miniFlt过滤,如果不支持Flt就使用老的模式Sfilter的模式

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

if ( IsOpenPipeConnect && !IsSupportFlt )

{

DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[1] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_CREATE_MAILSLOT] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_POWER] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_DEVICE_CHANGE] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_QUERY_QUOTA] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

DriverObject->MajorFunction[IRP_MJ_SET_QUOTA] = (PDRIVER_DISPATCH)SysmonDispatchIrp;

}

然后就是常规过程,IoCreateDevice、IoCreateSymbolicLink。

然后根据操作系统是否支持FltRegisterFilter(Driver, &g_Registration, &g_pFilter);

具体创建了哪些minifilter,接着看结构体

OperationRegistration dd IRP_MJ_CREATE  ; DATA XREF: .data:10015014o

.rdata:10013454                 dd 0

.rdata:10013458                 dd offset PreOperation

.rdata:1001345C                 dd offset PostOperation

.rdata:10013460                 dd 0

.rdata:10013464                 dd IRP_MJ_CLEANUP

.rdata:10013468                 dd 0

.rdata:1001346C                 dd offset PreOperation

.rdata:10013470                 dd offset PostOperation

.rdata:10013474                 dd 0

.rdata:10013478                 dd IRP_MJ_SET_INFORMATION

.rdata:1001347C                 dd 0

.rdata:10013480                 dd offset PreOperation

.rdata:10013484                 dd offset PostOperation

.rdata:10013488                 dd 0

.rdata:1001348C                 dd IRP_MJ_CLOSE

.rdata:10013490                 dd 0

.rdata:10013494                 dd offset PreOperation

.rdata:10013498                 dd offset PostOperation

.rdata:1001349C                 dd 0

.rdata:100134A0                 dd IRP_MJ_CREATE_NAMED_PIPE

.rdata:100134A4                 dd 0

.rdata:100134A8                 dd offset PreOperation

.rdata:100134AC                 dd offset PostOperation

.rdata:100134B0                 dd 0

.rdata:100134B4                 dd IRP_MJ_OPERATION_END

.rdata:100134B8                 dd 0

.rdata:100134BC                 dd 0

.rdata:100134C0                 dd 0

.rdata:100134C4                 dd 0

从上可以看到minifilter过滤了IRP_MJ_CREATE、IRP_MJ_CLEANUP、IRP_MJ_SET_INFORMATION、IRP_MJ_CLOSE、IRP_MJ_CREATE_NAMED_PIPE

文件系统相关的注册完毕,然后就是设置一些进程、线程相关的回调函数例程

PsSetLoadImageNotifyRoutine(SysmonLoadImageNotifyRoutine);

PsSetCreateThreadNotifyRoutine(PsCreateThreadNotifyRoutine);

PsSetCreateProcessNotifyRoutine(PsCreateProcessNotifyRoutine, 0);

为了记录注册表sysmon还注册表注册表CmRegisterCallback(RegisterCallback, 0, &Cookie);回调,

为了记录进程open对象的事件注册了ob事件

g_bIsRegisterCallback = 1;

g_OperationRegistration.ObjectType = (POBJECT_TYPE *)PsProcessType;

g_OperationRegistration.Operations = 1;

g_OperationRegistration.PreOperation = PreProcessOperation;

g_OperationRegistration.PostOperation = PostProcessOperation;

g_CallbackRegistration.OperationRegistration = &g_OperationRegistration;

*(_DWORD *)&g_CallbackRegistration.Version = 0x10100;

g_CallbackRegistration.RegistrationContext = 0;

RtlInitUnicodeString(&g_CallbackRegistration.Altitude, L"1000");

Status = g_ObRegisterCallbacks(&g_CallbackRegistration, &RegistrationHandle);

为了获取管道的事件,它挂接了设备L\\Device\\NamedPipe,创建了L\\Device\\SysmonPipeFilter的过滤设备

至此sysmon的DriverEntry的初始化动作基本结束了。

  • IRP_MJ_DEVICE_CONTROL例程

Case 0x83400000:

打开驱动开启标志,并且获取且保存当前UI进程的句柄

Case  0x83400004:

Ring3请求事件信息,并返回到ring3的缓冲区

Case 0x83400008:

加载策略规则

Case 0x8340000C:

获取传入进程的相关信息(包括TokenUser、pTokenStatics、TokenGroup、TokenSeesion)

还会获取进程pImagePathName、pCommandLine、CurrentDirectory

获取进程的CreateTime

该事件类型为4或者1

  • 文件信息的记录

Minifilter的PreOperation(PFLT_CALLBACK_DATA pData, PFLT_RELATED_OBJECTS FltObjects, PVOID *CompletionContext)例程为主要的判断逻辑例程,先判断当前FileObject的路径是否为管道路径,管道事件直接记录上报事件

特别判断下IRP_MJ_SET_INFORMATION、IRP_MJ_CLEANUP,并且分别上报_,注意在判断IRP_MJ_SET_INFORMATION的时候只记录了RequestorMode是1即USER_MODE,并且是设置FileBasicInformation的请求。

PreOperation处理完毕,则PostOperation(PFLT_CALLBACK_DATA pData, PFLT_RELATED_OBJECTS pFltFileObj, PVOID CompletionContext, int Flags)对前者处理的上下文CompletionContext进行记录日志或者释放的处理,以IRP_MJ_SET_INFORMATION为例,PostOPerate则对PreOperate的CompletionContext的数据进行上报。

  • 注册表信息的记录

Sysmon初始化的时候注册了一个注册表过滤,CmRegisterCallback(RegisterCallback, 0, &Cookie);回调函数是NTSTATUS __stdcall RegisterCallback(PVOID CallbackContext, PVOID Argument1, PVOID Argument2),参数Argument1是过滤的注册表操作类型,sysmon过滤了0(RegNtDeleteKey   /  RegNtPreDeleteKey) 、4( RegNtRenameKey\RegNtPreRenameKey)、11(RegNtPostCreateKey)、15(RegNtPostDeleteKey)、16(RegNtPostSetValueKey)、17(RegNtPostDeleteValueKey)、19(RegNtPostRenameKey)27(RegNtPostCreateKeyEx)的注册表操作

  • 进程操作过滤

Sysmon注册了进程操作过滤,g_ObRegisterCallbacks(&g_CallbackRegistration, &RegistrationHandle);,

他只记录操作类型为OB_OPERATION_HANDLE_CREATE,并且只记录A进程操作B进程,A和B不是同一个进程,注意RtlWalkFrameChain这个函数是获取当前操作线程的线程栈,KeQuerySystemTime(&pOpenInfo.CreateTime);是获取当前系统时间,并且会把这些信息上报。

  • 其他重点技术细节
  1. 进程模块的枚举

ZwQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &ProcessInformation, 0x18u, 0)获取ProcessInformation的信息,从PebBaseAddress = ProcessInformation.PebBaseAddress;取得进程PEB的地址,在PEB结构中得到LDR的地址,LDR是进程加载模块的结构体,

struct _PEB

{

UCHAR InheritedAddressSpace;

UCHAR ReadImageFileExecOptions;

UCHAR BeingDebugged;

UCHAR BitField;

PVOID Mutant;

PVOID ImageBaseAddress;

PPEB_LDR_DATA Ldr;

PRTL_USER_PROCESS_PARAMETERS ProcessParameters;

PVOID SubSystemData;

PVOID ProcessHeap;

PRTL_CRITICAL_SECTION FastPebLock;

PVOID AtlThunkSListPtr;

PVOID IFEOKey;

ULONG CrossProcessFlags;

unsigned __int32 ProcessInJob : 1;

unsigned __int32 ProcessInitializing : 1;

unsigned __int32 ReservedBits0 : 30;

union

{

PVOID KernelCallbackTable;

PVOID UserSharedInfoPtr;

};

ULONG SystemReserved[1];

。。。。。。

}

PPEB_LDR_DATA Ldr;这个就是加载模块的结构,有三种加载表内存加载表,加载顺序表,初始化加载表从中可以枚举出模块信息。

struct _PEB_LDR_DATA

{

ULONG Length;

UCHAR Initialized;

PVOID SsHandle;

LIST_ENTRY InLoadOrderModuleList;

LIST_ENTRY InMemoryOrderModuleList;

LIST_ENTRY InInitializationOrderModuleList;

};

  1. 进程参数的获取

大致可以看到如下,首先要KeStackAttachProcess进程的空间,然后获取PEB地址,从PEB中的到ProcessParameters的结构

ProcessParameters结构如下:

struct _RTL_USER_PROCESS_PARAMETERS

{

ULONG MaximumLength;

ULONG Length;

ULONG Flags;

ULONG DebugFlags;

PVOID ConsoleHandle;

ULONG ConsoleFlags;

PVOID StandardInput;

PVOID StandardOutput;

PVOID StandardError;

CURDIR CurrentDirectory;

UNICODE_STRING DllPath;

UNICODE_STRING ImagePathName;

UNICODE_STRING CommandLine;

PVOID Environment;

ULONG StartingX;

ULONG StartingY;

ULONG CountX;

ULONG CountY;

ULONG CountCharsX;

ULONG CountCharsY;

ULONG FillAttribute;

ULONG WindowFlags;

ULONG ShowWindowFlags;

UNICODE_STRING WindowTitle;

UNICODE_STRING DesktopInfo;

UNICODE_STRING ShellInfo;

UNICODE_STRING RuntimeData;

RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32];

ULONG EnvironmentSize;

};

可以看到该结构中进程参数相关的各种信息。

  1. 进程Token相关信息的获取

都是通过ZwQueryInformationToken函数去获取,只是是使用不同的ClassInformation类去获取,定义如下

typedef enum _TOKEN_INFORMATION_CLASS {

  TokenUser                             ,

  TokenGroups                           ,

  TokenPrivileges                       ,

  TokenOwner                            ,

  TokenPrimaryGroup                     ,

  TokenDefaultDacl                      ,

  TokenSource                           ,

  TokenType                             ,

  TokenImpersonationLevel               ,

  TokenStatistics                       ,

  TokenRestrictedSids                   ,

  TokenSessionId                        ,

  TokenGroupsAndPrivileges              ,

  TokenSessionReference                 ,

  TokenSandBoxInert                     ,

  TokenAuditPolicy                      ,

  TokenOrigin                           ,

  TokenElevationType                    ,

  TokenLinkedToken                      ,

  TokenElevation                        ,

  TokenHasRestrictions                  ,

  TokenAccessInformation                ,

  TokenVirtualizationAllowed            ,

  TokenVirtualizationEnabled            ,

  TokenIntegrityLevel                   ,

  TokenUIAccess                         ,

  TokenMandatoryPolicy                  ,

  TokenLogonSid                         ,

  TokenIsAppContainer                   ,

  TokenCapabilities                     ,

  TokenAppContainerSid                  ,

  TokenAppContainerNumber               ,

  TokenUserClaimAttributes              ,

  TokenDeviceClaimAttributes            ,

  TokenRestrictedUserClaimAttributes    ,

  TokenRestrictedDeviceClaimAttributes  ,

  TokenDeviceGroups                     ,

  TokenRestrictedDeviceGroups           ,

  TokenSecurityAttributes               ,

  TokenIsRestricted                     ,

  TokenProcessTrustLevel                ,

  TokenPrivateNameSpace                 ,

  TokenSingletonAttributes              ,

  TokenBnoIsolation                     ,

  TokenChildProcessFlags                ,

  MaxTokenInfoClass

} TOKEN_INFORMATION_CLASS, *PTOKEN_INFORMATION_CLASS;

需要获取那个就可以选择那一个。

本文大致讲解完毕,内部还有很多很有意思的技术细节由于篇幅原因,读者可以自己深入挖掘,在做一个产品的时候,我们可以分析他人的产品,不仅可以了解他人的产品的长处和不足,同时也可以补充自己产品的不足的之处,一个好的产品就是在不断的琢磨研究与推翻,更重要的是细节的体现才能做好产品。

相关文章
|
3月前
|
监控 负载均衡 关系型数据库
读写分离实战:提升MySQL并发处理能力
本文深入解析了MySQL读写分离的核心策略,涵盖架构设计、应用层及中间件实现、负载均衡、数据一致性保障、故障处理、性能监控与实战部署方案,助你构建高性能数据库架构,提升系统吞吐量与可用性。
|
7月前
|
传感器 存储 Java
Android 3D效果的实现
本文详细讲解了如何在Android中实现3D效果,基于官方Demo并结合实际需求进行调整。通过传感器(Sensor)获取设备旋转数据,利用OpenGL ES绘制3D立方体,实现了动态旋转的视觉效果。文章分为需求分析、效果展示、实现步骤及源码解析,涵盖传感器注册与注销、OpenGL核心方法使用等内容,适合初学者学习参考。文末附完整代码,便于实践操作。
243 0
Android 3D效果的实现
|
存储 监控 安全
深入解析Sysmon日志:增强网络安全与威胁应对的关键一环
在不断演进的网络安全领域中,保持对威胁的及时了解至关重要。Sysmon日志在这方面发挥了至关重要的作用,通过提供有价值的见解,使组织能够加强其安全姿态。Windows在企业环境中是主导的操作系统,因此深入了解Windows事件日志、它们的独特特性和局限性,并通过Sysmon进行增强,变得至关重要。
380 1
|
存储
在使用 realloc 函数时,如何避免数据丢失?
在使用 realloc 函数动态调整内存大小时,为避免数据丢失,应先将原指针保存到临时变量中,调用 realloc 后检查返回值是否为 NULL,若为 NULL 则保留原指针,否则更新指针并释放临时变量。
|
机器人 Linux 开发工具
小白必看!入门嵌入式你需要了解这些!
【9月更文挑战第23天】在科技迅速发展的今天,嵌入式系统已广泛应用,覆盖了从智能家居到工业自动化等多个领域。本文将向你介绍嵌入式系统的基础概念,其特点,应用范围,并指导你如何掌握必要的知识和技能,包括电路基础、C语言编程、微处理器架构等,以及推荐的学习路径与方法。对于初学者来说,这是一份不错的指南。
995 1
|
安全 Linux
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
【Linux】详解用户态和内核态&&内核中信号被处理的时机&&sigaction信号自定义处理方法
297 1
|
消息中间件 缓存 算法
社招一年半面经分享(含阿里美团头条京东滴滴)
重点放在专业技能和项目经验两块1.你的简历就是你给面试官提供的考点,简历上的东西必须自己Hold住,万一自己写的东西被问住了,会很尴尬,给面试官留下的印象也不好,所以就是会啥写啥2.技术栈最好不要写精通,你敢写面试官就敢问,被问倒了很尴尬的,写熟悉,了解就行怎么投简历我这里强烈建议找人内推,这样简历通过的概率大些,如果找不到,可以试试脉脉,我就是从脉脉投的简历,把状态改成寻找机会就行,会有很多人找你的推荐一个简历制作模版,我一直用的,https://www.polebrief.com/index算法这个该刷还是得刷,别偷懒,我个人感觉刷完下面几个已经够了,大家可以根据自己的基础情况选择剑指Of
1168 1
|
关系型数据库 MySQL 数据安全/隐私保护
【安装指南】MySQL和Navicat下载、安装及使用详细教程
【安装指南】MySQL和Navicat下载、安装及使用详细教程
1246 0
|
XML 监控 数据格式
利用powershell进行windows日志分析
0x00 前言   Windows 中提供了 2 个分析事件日志的 PowerShell cmdlet:一个是Get-WinEvent,超级强大,但使用起来比较麻烦;另一个是Get-EventLog,使得起来相当简单,可以实时筛选,接下来,我们利用PowerShell 来自动筛选 Windows 事件日志。
2766 0