上文講解了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:10015014↓o
.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);是擷取目前系統時間,并且會把這些資訊上報。
- 其他重點技術細節
- 程序子產品的枚舉
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;
};
- 程序參數的擷取
大緻可以看到如下,首先要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;
};
可以看到該結構中程序參數相關的各種資訊。
- 程序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;
需要擷取那個就可以選擇那一個。
本文大緻講解完畢,内部還有很多很有意思的技術細節由于篇幅原因,讀者可以自己深入挖掘,在做一個産品的時候,我們可以分析他人的産品,不僅可以了解他人的産品的長處和不足,同時也可以補充自己産品的不足的之處,一個好的産品就是在不斷的琢磨研究與推翻,更重要的是細節的展現才能做好産品。