快捷搜索:  汽车  科技

scene模块要怎么用(Hips篇高操之正确的全局Patch)

scene模块要怎么用(Hips篇高操之正确的全局Patch)首先由Ring3进程指定策略,这里拿金山的两个模块开刀:当然,我选择的是最后一种,现在貌似运用这种方法做通信的不多,我也没怎么接触过,一切都是从0开始,那就试试吧...这也是初入安全时我一直想实现的一个东西,但是由于当时技术局限,很菜,其实今天来看只是当时没有找到好的学习方法,没有办法实现很是遗憾。然而今日在团队的带领下,我改进了自己的学习方法,学会了如何有效看待问题分析问题以及解决问题。在此感谢我的导师以及leader。可以看到,调用API加载模块的时候最后都逃不过ntdll!ZwMapViewOfSection,最后进入系统调用然后触发模块回调,我们唯一能做的就是在模块回调里干一些猥琐的事情。直接Patch?那是不可能的,因为模块回调有限制,这时候有EProcess.AddressCreationLock的限制不说,模块还未能初始化完成,直接去搞肯定是不正确的做法。那么可以想到的是用A

作者:看雪论坛 FaEry

scene模块要怎么用(Hips篇高操之正确的全局Patch)(1)

潜水了两个月,由于工作的关系没有什么多余的时间发新帖了,由于临近端午准备休息一下,于是乎决定在周末搞点事情,恰逢北京今日又下起雨来,在这朦朦胧胧的雨天里给大家呈上一份Hips的高端操作,让大家燃一下。

这也是初入安全时我一直想实现的一个东西,但是由于

潜水了两个月,由于工作的关系没有什么多余的时间发新帖了,由于临近端午准备休息一下,于是乎决定在周末搞点事情,恰逢北京今日又下起雨来,在这朦朦胧胧的雨天里给大家呈上一份Hips的高端操作,让大家燃一下。

这也是初入安全时我一直想实现的一个东西,但是由于当时技术局限,很菜,其实今天来看只是当时没有找到好的学习方法,没有办法实现很是遗憾。然而今日在团队的带领下,我改进了自己的学习方法,学会了如何有效看待问题分析问题以及解决问题。在此感谢我的导师以及leader。

可以看到,调用API加载模块的时候最后都逃不过ntdll!ZwMapViewOfSection,最后进入系统调用然后触发模块回调,我们唯一能做的就是在模块回调里干一些猥琐的事情。直接Patch?那是不可能的,因为模块回调有限制,这时候有EProcess.AddressCreationLock的限制不说,模块还未能初始化完成,直接去搞肯定是不正确的做法。那么可以想到的是用Apc延迟去Patch模块,因为在ZwMapViewOfSection在内核中最后会返回至Ring3,在返回的过程中调用Apc去Patch这样就完美了。

话又说回来,模块回调中为每一个模块都插一个Apc? 这显然也不现实,因此这种方法最好是有一个Ring3层进程来制定策略,指定对哪一个进程加载哪一个模块时执行哪种Patch操作。那么这就涉及到Ring0与Ring3的通信问题。说到这里就有三种方法可供选择,一种是LPC(ZwConnetPort...),一种是内核可等待对象,还有一种就是Minifilter的双向通信机制。

当然,我选择的是最后一种,现在貌似运用这种方法做通信的不多,我也没怎么接触过,一切都是从0开始,那就试试吧...


NTSTATUS FyFsFilterStart(PDRIVER_OBJECT DriverObject){ NTSTATUS Status; PSECURITY_DESCRIPTOR sd = NULL; PFLT_FILTER FltFilter = NULL; OBJECT_ATTRIBUTES oa = {0}; UNICODE_STRING uniString; __try { Status = FltRegisterFilter(DriverObject &FilterRegistration &FltFilter); if (!NT_SUCCESS(Status)) { __leave; } Status = FltBuildDefaultSecurityDescriptor(&sd FLT_PORT_ALL_ACCESS); if (!NT_SUCCESS(Status)) { __leave; } RtlInitUnicodeString(&uniString FY_FSFILTER_PORT_NAME); InitializeObjectAttributes(&oa &uniString OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE NULL sd); Status = FltCreateCommunicationPort( FltFilter &g_FyFsFltGlobalCtx.ServerPort &oa NULL (PFLT_CONNECT_NOTIFY)FyFsFilterConnect (PFLT_DISCONNECT_NOTIFY)FyFsFilterDisconnect (PFLT_MESSAGE_NOTIFY)FyFsFilterMessage 1 ); if (!NT_SUCCESS(Status)) { __leave; } Status = FltStartFiltering(FltFilter); if (!NT_SUCCESS(Status)) { __leave; } g_FyFsFltGlobalCtx.Filter = FltFilter; DbgPrint("[%s] Init FileSystem--minifilter success" __FUNCTION__); } __except (EXCEPTION_exeCUTE_HANDLER) { if (sd != NULL) { FltFreeSecurityDescriptor( sd ); } if (g_FyFsFltGlobalCtx.ServerPort) { FltCloseCommunicationPort(g_FyFsFltGlobalCtx.ServerPort); } if (FltFilter) { FltUnregisterFilter(FltFilter); g_FyFsFltGlobalCtx.Filter = NULL; } } if (!NT_SUCCESS(Status) ) { if (sd != NULL) { FltFreeSecurityDescriptor( sd ); } if (g_FyFsFltGlobalCtx.ServerPort) { FltCloseCommunicationPort(g_FyFsFltGlobalCtx.ServerPort); } if (FltFilter) { FltUnregisterFilter(FltFilter); g_FyFsFltGlobalCtx.Filter = NULL; } } return Status;}

首先由Ring3进程指定策略,这里拿金山的两个模块开刀:

POLICY_DATA g_TotalPolicyList[] = { {L"*\\iexplore.exe" L"*\\kisfdpro64.dll"} {L"*\\iexplore.exe" L"*\\kshmpg.dll"} {L"*\\explorer.exe" L"*\\kisfdpro64.dll"} {L"*\\explorer.exe" L"*\\kshmpg.dll"} };BOOL FilterSetLoadImagePolicy(){ ULONG uIndex = 0; ULONG ulReturned = 0; HRESULT hr = S_OK; ULONG ulInputSize = sizeof(POLICY_DATA); FySendPolicyDat HipsData = {0}; HipsData.MessageType = Lowlayer_Msg_SetImagePolicy; HipsData.Signature = FY_FSFILTER_SIGNATURE; for (uIndex ; uIndex < sizeof(g_TotalPolicyList) / sizeof(g_TotalPolicyList[0]) ; uIndex ) { HipsData.DataLength = ulInputSize; memcpy(&HipsData.Data &g_TotalPolicyList[uIndex] sizeof(POLICY_DATA)); hr = FilterSendMessage( g_hPort &HipsData sizeof(FySendPolicyDat) NULL NULL &ulReturned); if (FAILED(hr)) { printf("[%s] FilterSendMessage GetLastError:%d\r\n" __FUNCTION__ GetLastError()); break; } } return TRUE;}

注册这个通信是奠定与Ring3进程通信的基础,应用层进程可以通过连接之后发送策略(即要对哪个进程的哪个模块进行监控),然后由模块回调在匹配策略之后对应用程序进行通知


VOID FyLoadImageNotifyRoutine( IN PUNICODE_STRING FullImageName IN HANDLE ProcessId IN PIMAGE_INFO ImageInfo ){ WCHAR lpszProcessName[MAX_PATH] = { 0 }; UNICODE_STRING uniProcessName; BOOLEAN bHitPolicy = FALSE; FyLoadImageInfo SendInfo = { 0 }; LARGE_INTEGER liTimeOut; ULONG ulCopySize; FYFSFLT_REPLY_DATA RelpyData; ULONG ulRelpyDataSize = sizeof(FYFSFLT_REPLY_DATA); NTSTATUS Status; if (!ImageInfo->SystemModeImage) { if (GetProcessImageFileNameSafeIrql(ProcessId lpszProcessName MAX_PATH)) { RtlInitUnicodeString(&uniProcessName lpszProcessName); if (g_ListHeaderProcessImagePolicy) { bHitPolicy = HitProcessImagePolicy(&uniProcessName FullImageName); } if (bHitPolicy) { DbgPrint("[%s] Hit Policy!!!\r\n" __FUNCTION__); DbgPrint("[%s] uniProcessName:%wZ\r\n" __FUNCTION__ &uniProcessName); DbgPrint("[%s] FullImageName:%wZ\r\n" __FUNCTION__ FullImageName); if (g_FyFsFltGlobalCtx.ClientPort) { liTimeOut.QuadPart = -150 * 1000000; // 15s if (uniProcessName.Length >= (MAX_PATH - 1) * sizeof(WCHAR)) ulCopySize = (MAX_PATH - 1) * sizeof(WCHAR); else ulCopySize = uniProcessName.Length; memcpy(SendInfo.wzProcessImageName uniProcessName.Buffer ulCopySize); if (FullImageName->Length >= (MAX_PATH - 1) * sizeof(WCHAR)) ulCopySize = (MAX_PATH - 1) * sizeof(WCHAR); else ulCopySize = FullImageName->Length; memcpy(SendInfo.wzImageName FullImageName->Buffer ulCopySize); SendInfo.ImageBase = ImageInfo->ImageBase; SendInfo.ImageSize = ImageInfo->ImageSize; SendInfo.ProcessId = ProcessId; SendInfo.ThreadId = PsGetCurrentThreadId(); SendInfo.bX64Image = (SIZE_T)CheckIsX64Image(ImageInfo->ImageBase); // Reply不能传NULL,否则卡不住,在Ring3 FilterGetMessage之后直接就飞走了,来不及在这次系统调用返回前插入Apc Status = FltSendMessage(g_FyFsFltGlobalCtx.Filter &g_FyFsFltGlobalCtx.ClientPort &SendInfo sizeof(SendInfo) &RelpyData &ulRelpyDataSize &liTimeOut); if (Status == STATUS_TIMEOUT) { DbgPrint("[%s] FltSendMessage return STATUS_TIMEOUT" __FUNCTION__); } DbgPrint("[%s] FltSendMessage finished" __FUNCTION__); } } } } }

这里一定要注意,FltSendMessage这个函数msdn上说的很清楚,如果RelpyData为NULL 只要是Ring3进程调用了FilterGetMessage取得了数据,那么这个函数不会再等待。而我们需要做的是在GetMessage之后发消息给驱动,让它为LoadImage这个当前线程插入Apc,那么在模块回调中必须卡死直到应用进程调用FilterRelpyMessage回应消息,否则这一次系统调用就返回了。

scene模块要怎么用(Hips篇高操之正确的全局Patch)(2)

Ring3进程拿到模块监控数据之后决定对该模块采取什么样的Patch措施,然后将决定结果送达至Ring0,这里我就以Patch基地址即DosHeader.e_magic为例了。

DWORD WINAPI FilterMessageProc(PVOID lpParam){ FyLoadImageRecvDat Msg = {0}; FyReplyData ReplyMsg = {0}; while (TRUE) { DWORD bytesReturned = 0; DWORD hResult; TCHAR buf[260] = {0}; hResult = FilterGetMessage( g_hPort (PFILTER_MESSAGE_HEADER)&Msg sizeof(Msg) NULL); if (hResult == S_OK) { // 拿到当前正在映射的模块信 // Now can Send Patch Info FySendPatchInfoDat HipsData = {0}; HipsData.MessageType = Lowlayer_Msg_BlockImage; HipsData.Signature = FY_FSFILTER_SIGNATURE; HipsData.DataLength = sizeof(FyPatchInfo); HipsData.Data.PatchType = 1; HipsData.Data.ProcessId = Msg.Data.ProcessId; HipsData.Data.ThreadId = Msg.Data.ThreadId; HipsData.Data.PatchAddress = (ULONG64)Msg.Data.ImageBase; *(DWORD*)HipsData.Data.lpPatchContent = 0x00905a4b; HipsData.Data.PatchSize = 4; // don't wait FilterSendMessage(g_hPort &HipsData sizeof(FySendPatchInfoDat) NULL NULL &bytesReturned); // Reply load image message ReplyMsg.ReplyHeader.MessageId = Msg.MsgHeader.MessageId; ReplyMsg.ReplyHeader.Status = 0; ReplyMsg.Signature = FY_FSFILTER_SIGNATURE; hResult = FilterReplyMessage( g_hPort (PFILTER_REPLY_HEADER)&ReplyMsg sizeof(ReplyMsg)); } } return 0;}

这里要提一点的是,32位进程与64位驱动通信的时候一定要注意结构体的对齐粒度,不然会吃大亏的...


NTSTATUS FyFsFilterMessage ( __in PVOID ConnectionCookie __in_bcount_opt(InputBufferSize) PVOID InputBuffer __in ULONG InputBufferSize __out_bcount_part_opt(OutputBufferSize *ReturnOutputBufferLength) PVOID OutputBuffer __in ULONG OutputBufferSize __out PULONG ReturnOutputBufferLength ){ NTSTATUS Status = STATUS_SUCCESS; ULONG Command; PVOID pContent = NULL; ULONG ContentLength = 0; UINT32 Signature; UNREFERENCED_PARAMETER(ConnectionCookie); PAGED_CODE();#if defined(_WIN64) if (IoIs32bitProcess( NULL )) { if (!IS_ALIGNED(OutputBuffer sizeof(ULONG))) { Status = STATUS_DATATYPE_MISALIGNMENT; return Status; } } else {#endif if (!IS_ALIGNED(OutputBuffer sizeof(PVOID))) { Status = STATUS_DATATYPE_MISALIGNMENT; return Status; }#if defined(_WIN64) }#endif if ((InputBuffer != NULL) && (InputBufferSize >= sizeof(FYFSFLT_SEND_DATA))) { __try { Command = ((PFYFSFLT_SEND_DATA) InputBuffer)->MessageType; Signature = ((PFYFSFLT_SEND_DATA) InputBuffer)->Signature; if (Signature != FY_FSFILTER_SIGNATURE) return Status; pContent = &((PFYFSFLT_SEND_DATA) InputBuffer)->Data; ContentLength = ((PFYFSFLT_SEND_DATA) InputBuffer)->DataLength; } __except( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } DbgPrint("[%s]Command:%x\n" __FUNCTION__ Command); switch (Command) { case Lowlayer_Msg_SetImagePolicy: DbgPrint("[%s] Lowlayer_Msg_SetImagePolicy\n" __FUNCTION__); SetProcessImagePolicy((PPOLICY_DATA)pContent ContentLength); break; case Lowlayer_Msg_BlockImage: DbgPrint("[%s] Lowlayer_Msg_BlockImage\n" __FUNCTION__); ExecuteKernelPatchProcedure((PFyPatchInfo)pContent ContentLength); break; default: break; } } return STATUS_SUCCESS;}

当然在Patch的时候还是不能忘了改页属性,这个我也不想多说了,一定调用的是ZwProtectVirtualMemory,不导出就去搜..

BOOLEAN ApcRealProcedure1(PFyPatchInfo lpPatchInfo){ BOOLEAN bRet = FALSE; NTSTATUS status; ULONG ulCompareSize; PVOID lpPatchAddr = lpPatchInfo->PatchAddress; SIZE_T PatchSize = lpPatchInfo->PatchSize; ULONG dwOldProtect; if ( lpPatchInfo->PatchAddress < MmSystemRangeStart ) ulCompareSize = (ULONG)RtlCompareMemory(lpPatchInfo->lpPatchContent lpPatchInfo->PatchAddress lpPatchInfo->PatchSize); if ( ulCompareSize != lpPatchInfo->PatchSize ) { DbgPrint("[%s] RtlCompareMemory:%d\r\n" __FUNCTION__ ulCompareSize); if ( g_ZwProtectVirtualMemory ) status = g_ZwProtectVirtualMemory(NtCurrentProcess() &lpPatchAddr &PatchSize PAGE_EXECUTE_READWRITE &dwOldProtect); else status = STATUS_UNSUCCESSFUL; if ( !NT_SUCCESS(status) ) { if (!g_dwSysBuildNumber) { DbgPrint("[%s] g_dwSysBuildNumber:%d\r\n" __FUNCTION__ g_dwSysBuildNumber); PsGetVersion(&g_dwSysMajorNumber &g_dwSysMinorNumber &g_dwSysBuildNumber NULL); } if (g_dwSysBuildNumber > 9600) bRet = CopyMemoryUsingMdl(lpPatchInfo->PatchAddress lpPatchInfo->lpPatchContent (ULONG)lpPatchInfo->PatchSize); } else { ProbeForWrite(lpPatchInfo->PatchAddress lpPatchInfo->PatchSize 1); memcpy(lpPatchInfo->PatchAddress lpPatchInfo->lpPatchContent lpPatchInfo->PatchSize) if ( g_ZwProtectVirtualMemory ) status = g_ZwProtectVirtualMemory(NtCurrentProcess() &lpPatchAddr &PatchSize dwOldProtect &dwOldProtect); } } } return bRet;}

正确的现象是在ntdll!NtMapViewOfSection刚回来的时候就已经执行了Patch操作,原本为0x905a4d

scene模块要怎么用(Hips篇高操之正确的全局Patch)(3)

在ZwMapViewOfSection之后LdrFindOrMapDll会调用RtlImageNtHeaderEx校验DosHeader和PEHeader,不对就卸载

scene模块要怎么用(Hips篇高操之正确的全局Patch)(4)

scene模块要怎么用(Hips篇高操之正确的全局Patch)(5)

另外,由于minifilter驱动需要inf文件安装,为了避免这样的情况,我就用服务启动驱动了(即用即加载),不用手动去加,直接双击exe就行。

LZ测试平台是Win7 x64,其他平台未经过测试,应该可以兼容到Win10... exe与sys放同一层目录下.

当时技术局限,很菜,其实今天来看只是当时没有找到好的学习方法,没有办法实现很是遗憾。然而今日在团队的带领下,我改进了自己的学习方法,学会了如何有效看待问题分析问题以及解决问题。在此感谢我的导师以及leader。

这个一直想实现的东西就是用驱动拦截模块,看似很简单的一个问题但是实际上要操作起来并非易事,要看得懂代码,得先理解了这张图:

scene模块要怎么用(Hips篇高操之正确的全局Patch)(6)

可以看到,调用API加载模块的时候最后都逃不过ntdll!ZwMapViewOfSection,最后进入系统调用然后触发模块回调,我们唯一能做的就是在模块回调里干一些猥琐的事情。直接Patch?那是不可能的,因为模块回调有限制,这时候有EProcess.AddressCreationLock的限制不说,模块还未能初始化完成,直接去搞肯定是不正确的做法。那么可以想到的是用Apc延迟去Patch模块,因为在ZwMapViewOfSection在内核中最后会返回至Ring3,在返回的过程中调用Apc去Patch这样就完美了。

话又说回来,模块回调中为每一个模块都插一个Apc? 这显然也不现实,因此这种方法最好是有一个Ring3层进程来制定策略,指定对哪一个进程加载哪一个模块时执行哪种Patch操作。那么这就涉及到Ring0与Ring3的通信问题。说到这里就有三种方法可供选择,一种是LPC(ZwConnetPort...),一种是内核可等待对象,还有一种就是Minifilter的双向通信机制。

当然,我选择的是最后一种,现在貌似运用这种方法做通信的不多,我也没怎么接触过,一切都是从0开始,那就试试吧...

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071NTSTATUS FyFsFilterStart(PDRIVER_OBJECT DriverObject){ NTSTATUS Status; PSECURITY_DESCRIPTOR sd = NULL; PFLT_FILTER FltFilter = NULL; OBJECT_ATTRIBUTES oa = {0}; UNICODE_STRING uniString; __try { Status = FltRegisterFilter(DriverObject &FilterRegistration &FltFilter); if (!NT_SUCCESS(Status)) { __leave; } Status = FltBuildDefaultSecurityDescriptor(&sd FLT_PORT_ALL_ACCESS); if (!NT_SUCCESS(Status)) { __leave; } RtlInitUnicodeString(&uniString FY_FSFILTER_PORT_NAME); InitializeObjectAttributes(&oa &uniString OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE NULL sd); Status = FltCreateCommunicationPort( FltFilter &g_FyFsFltGlobalCtx.ServerPort &oa NULL (PFLT_CONNECT_NOTIFY)FyFsFilterConnect (PFLT_DISCONNECT_NOTIFY)FyFsFilterDisconnect (PFLT_MESSAGE_NOTIFY)FyFsFilterMessage 1 ); if (!NT_SUCCESS(Status)) { __leave; } Status = FltStartFiltering(FltFilter); if (!NT_SUCCESS(Status)) { __leave; } g_FyFsFltGlobalCtx.Filter = FltFilter; DbgPrint("[%s] Init FileSystem--minifilter success" __FUNCTION__); } __except (EXCEPTION_EXECUTE_HANDLER) { if (sd != NULL) { FltFreeSecurityDescriptor( sd ); } if (g_FyFsFltGlobalCtx.ServerPort) { FltCloseCommunicationPort(g_FyFsFltGlobalCtx.ServerPort); } if (FltFilter) { FltUnregisterFilter(FltFilter); g_FyFsFltGlobalCtx.Filter = NULL; } } if (!NT_SUCCESS(Status) ) { if (sd != NULL) { FltFreeSecurityDescriptor( sd ); } if (g_FyFsFltGlobalCtx.ServerPort) { FltCloseCommunicationPort(g_FyFsFltGlobalCtx.ServerPort); } if (FltFilter) { FltUnregisterFilter(FltFilter); g_FyFsFltGlobalCtx.Filter = NULL; } } return Status;}

首先由Ring3进程指定策略,这里拿金山的两个模块开刀:


POLICY_DATA g_TotalPolicyList[] = { {L"*\\iexplore.exe" L"*\\kisfdpro64.dll"} {L"*\\iexplore.exe" L"*\\kshmpg.dll"} {L"*\\explorer.exe" L"*\\kisfdpro64.dll"} {L"*\\explorer.exe" L"*\\kshmpg.dll"} };BOOL FilterSetLoadImagePolicy(){ ULONG uIndex = 0; ULONG ulReturned = 0; HRESULT hr = S_OK; ULONG ulInputSize = sizeof(POLICY_DATA); FySendPolicyDat HipsData = {0}; HipsData.MessageType = Lowlayer_Msg_SetImagePolicy; HipsData.Signature = FY_FSFILTER_SIGNATURE; for (uIndex ; uIndex < sizeof(g_TotalPolicyList) / sizeof(g_TotalPolicyList[0]) ; uIndex ) { HipsData.DataLength = ulInputSize; memcpy(&HipsData.Data &g_TotalPolicyList[uIndex] sizeof(POLICY_DATA)); hr = FilterSendMessage( g_hPort &HipsData sizeof(FySendPolicyDat) NULL NULL &ulReturned); if (FAILED(hr)) { printf("[%s] FilterSendMessage GetLastError:%d\r\n" __FUNCTION__ GetLastError()); break; } } return TRUE;}

注册这个通信是奠定与Ring3进程通信的基础,应用层进程可以通过连接之后发送策略(即要对哪个进程的哪个模块进行监控),然后由模块回调在匹配策略之后对应用程序进行通知


VOID FyLoadImageNotifyRoutine( IN PUNICODE_STRING FullImageName IN HANDLE ProcessId IN PIMAGE_INFO ImageInfo ){ WCHAR lpszProcessName[MAX_PATH] = { 0 }; UNICODE_STRING uniProcessName; BOOLEAN bHitPolicy = FALSE; FyLoadImageInfo SendInfo = { 0 }; LARGE_INTEGER liTimeOut; ULONG ulCopySize; FYFSFLT_REPLY_DATA RelpyData; ULONG ulRelpyDataSize = sizeof(FYFSFLT_REPLY_DATA); NTSTATUS Status; if (!ImageInfo->SystemModeImage) { if (GetProcessImageFileNameSafeIrql(ProcessId lpszProcessName MAX_PATH)) { RtlInitUnicodeString(&uniProcessName lpszProcessName); if (g_ListHeaderProcessImagePolicy) { bHitPolicy = HitProcessImagePolicy(&uniProcessName FullImageName); } if (bHitPolicy) { DbgPrint("[%s] Hit Policy!!!\r\n" __FUNCTION__); DbgPrint("[%s] uniProcessName:%wZ\r\n" __FUNCTION__ &uniProcessName); DbgPrint("[%s] FullImageName:%wZ\r\n" __FUNCTION__ FullImageName); if (g_FyFsFltGlobalCtx.ClientPort) { liTimeOut.QuadPart = -150 * 1000000; // 15s if (uniProcessName.Length >= (MAX_PATH - 1) * sizeof(WCHAR)) ulCopySize = (MAX_PATH - 1) * sizeof(WCHAR); else ulCopySize = uniProcessName.Length; memcpy(SendInfo.wzProcessImageName uniProcessName.Buffer ulCopySize); if (FullImageName->Length >= (MAX_PATH - 1) * sizeof(WCHAR)) ulCopySize = (MAX_PATH - 1) * sizeof(WCHAR); else ulCopySize = FullImageName->Length; memcpy(SendInfo.wzImageName FullImageName->Buffer ulCopySize); SendInfo.ImageBase = ImageInfo->ImageBase; SendInfo.ImageSize = ImageInfo->ImageSize; SendInfo.ProcessId = ProcessId; SendInfo.ThreadId = PsGetCurrentThreadId(); SendInfo.bX64Image = (SIZE_T)CheckIsX64Image(ImageInfo->ImageBase); // Reply不能传NULL,否则卡不住,在Ring3 FilterGetMessage之后直接就飞走了,来不及在这次系统调用返回前插入Apc Status = FltSendMessage(g_FyFsFltGlobalCtx.Filter &g_FyFsFltGlobalCtx.ClientPort &SendInfo sizeof(SendInfo) &RelpyData &ulRelpyDataSize &liTimeOut); if (Status == STATUS_TIMEOUT) { DbgPrint("[%s] FltSendMessage return STATUS_TIMEOUT" __FUNCTION__); } DbgPrint("[%s] FltSendMessage finished" __FUNCTION__); } } } } }

这里一定要注意,FltSendMessage这个函数msdn上说的很清楚,如果RelpyData为NULL 只要是Ring3进程调用了FilterGetMessage取得了数据,那么这个函数不会再等待。而我们需要做的是在GetMessage之后发消息给驱动,让它为LoadImage这个当前线程插入Apc,那么在模块回调中必须卡死直到应用进程调用FilterRelpyMessage回应消息,否则这一次系统调用就返回了。

scene模块要怎么用(Hips篇高操之正确的全局Patch)(7)

Ring3进程拿到模块监控数据之后决定对该模块采取什么样的Patch措施,然后将决定结果送达至Ring0,这里我就以Patch基地址即DosHeader.e_magic为例了。


DWORD WINAPI FilterMessageProc(PVOID lpParam){ FyLoadImageRecvDat Msg = {0}; FyReplyData ReplyMsg = {0}; while (TRUE) { DWORD bytesReturned = 0; DWORD hResult; TCHAR buf[260] = {0}; hResult = FilterGetMessage( g_hPort (PFILTER_MESSAGE_HEADER)&Msg sizeof(Msg) NULL); if (hResult == S_OK) { // 拿到当前正在映射的模块信 // Now can Send Patch Info FySendPatchInfoDat HipsData = {0}; HipsData.MessageType = Lowlayer_Msg_BlockImage; HipsData.Signature = FY_FSFILTER_SIGNATURE; HipsData.DataLength = sizeof(FyPatchInfo); HipsData.Data.PatchType = 1; HipsData.Data.ProcessId = Msg.Data.ProcessId; HipsData.Data.ThreadId = Msg.Data.ThreadId; HipsData.Data.PatchAddress = (ULONG64)Msg.Data.ImageBase; *(DWORD*)HipsData.Data.lpPatchContent = 0x00905a4b; HipsData.Data.PatchSize = 4; // don't wait FilterSendMessage(g_hPort &HipsData sizeof(FySendPatchInfoDat) NULL NULL &bytesReturned); // Reply load image message ReplyMsg.ReplyHeader.MessageId = Msg.MsgHeader.MessageId; ReplyMsg.ReplyHeader.Status = 0; ReplyMsg.Signature = FY_FSFILTER_SIGNATURE; hResult = FilterReplyMessage( g_hPort (PFILTER_REPLY_HEADER)&ReplyMsg sizeof(ReplyMsg)); } } return 0;}

这里要提一点的是,32位进程与64位驱动通信的时候一定要注意结构体的对齐粒度,不然会吃大亏的...


NTSTATUS FyFsFilterMessage ( __in PVOID ConnectionCookie __in_bcount_opt(InputBufferSize) PVOID InputBuffer __in ULONG InputBufferSize __out_bcount_part_opt(OutputBufferSize *ReturnOutputBufferLength) PVOID OutputBuffer __in ULONG OutputBufferSize __out PULONG ReturnOutputBufferLength ){ NTSTATUS Status = STATUS_SUCCESS; ULONG Command; PVOID pContent = NULL; ULONG ContentLength = 0; UINT32 Signature; UNREFERENCED_PARAMETER(ConnectionCookie); PAGED_CODE();#if defined(_WIN64) if (IoIs32bitProcess( NULL )) { if (!IS_ALIGNED(OutputBuffer sizeof(ULONG))) { Status = STATUS_DATATYPE_MISALIGNMENT; return Status; } } else {#endif if (!IS_ALIGNED(OutputBuffer sizeof(PVOID))) { Status = STATUS_DATATYPE_MISALIGNMENT; return Status; }#if defined(_WIN64) }#endif if ((InputBuffer != NULL) && (InputBufferSize >= sizeof(FYFSFLT_SEND_DATA))) { __try { Command = ((PFYFSFLT_SEND_DATA) InputBuffer)->MessageType; Signature = ((PFYFSFLT_SEND_DATA) InputBuffer)->Signature; if (Signature != FY_FSFILTER_SIGNATURE) return Status; pContent = &((PFYFSFLT_SEND_DATA) InputBuffer)->Data; ContentLength = ((PFYFSFLT_SEND_DATA) InputBuffer)->DataLength; } __except( EXCEPTION_EXECUTE_HANDLER ) { return GetExceptionCode(); } DbgPrint("[%s]Command:%x\n" __FUNCTION__ Command); switch (Command) { case Lowlayer_Msg_SetImagePolicy: DbgPrint("[%s] Lowlayer_Msg_SetImagePolicy\n" __FUNCTION__); SetProcessImagePolicy((PPOLICY_DATA)pContent ContentLength); break; case Lowlayer_Msg_BlockImage: DbgPrint("[%s] Lowlayer_Msg_BlockImage\n" __FUNCTION__); ExecuteKernelPatchProcedure((PFyPatchInfo)pContent ContentLength); break; default: break; } } return STATUS_SUCCESS;}

当然在Patch的时候还是不能忘了改页属性,这个我也不想多说了,一定调用的是ZwProtectVirtualMemory,不导出就去搜...

BOOLEAN ApcRealProcedure1(PFyPatchInfo lpPatchInfo){ BOOLEAN bRet = FALSE; NTSTATUS status; ULONG ulCompareSize; PVOID lpPatchAddr = lpPatchInfo->PatchAddress; SIZE_T PatchSize = lpPatchInfo->PatchSize; ULONG dwOldProtect; if ( lpPatchInfo->PatchAddress < MmSystemRangeStart ) ulCompareSize = (ULONG)RtlCompareMemory(lpPatchInfo->lpPatchContent lpPatchInfo->PatchAddress lpPatchInfo->PatchSize); if ( ulCompareSize != lpPatchInfo->PatchSize ) { DbgPrint("[%s] RtlCompareMemory:%d\r\n" __FUNCTION__ ulCompareSize); if ( g_ZwProtectVirtualMemory ) status = g_ZwProtectVirtualMemory(NtCurrentProcess() &lpPatchAddr &PatchSize PAGE_EXECUTE_READWRITE &dwOldProtect); else status = STATUS_UNSUCCESSFUL; if ( !NT_SUCCESS(status) ) { if (!g_dwSysBuildNumber) { DbgPrint("[%s] g_dwSysBuildNumber:%d\r\n" __FUNCTION__ g_dwSysBuildNumber); PsGetVersion(&g_dwSysMajorNumber &g_dwSysMinorNumber &g_dwSysBuildNumber NULL); } if (g_dwSysBuildNumber > 9600) bRet = CopyMemoryUsingMdl(lpPatchInfo->PatchAddress lpPatchInfo->lpPatchContent (ULONG)lpPatchInfo->PatchSize); } else { ProbeForWrite(lpPatchInfo->PatchAddress lpPatchInfo->PatchSize 1); memcpy(lpPatchInfo->PatchAddress lpPatchInfo->lpPatchContent lpPatchInfo->PatchSize) if ( g_ZwProtectVirtualMemory ) status = g_ZwProtectVirtualMemory(NtCurrentProcess() &lpPatchAddr &PatchSize dwOldProtect &dwOldProtect); } } } return bRet;}

正确的现象是在ntdll!NtMapViewOfSection刚回来的时候就已经执行了Patch操作,原本为0x905a4d

scene模块要怎么用(Hips篇高操之正确的全局Patch)(8)

在ZwMapViewOfSection之后LdrFindOrMapDll会调用RtlImageNtHeaderEx校验DosHeader和PEHeader,不对就卸载

scene模块要怎么用(Hips篇高操之正确的全局Patch)(9)

scene模块要怎么用(Hips篇高操之正确的全局Patch)(10)

另外,由于minifilter驱动需要inf文件安装,为了避免这样的情况,我就用服务启动驱动了(即用即加载),不用手动去加,直接双击exe就行。

LZ测试平台是Win7 x64,其他平台未经过测试,应该可以兼容到Win10... exe与sys放同一层目录下.

猜您喜欢: