Hips篇高操之正确的全局Patch/拦截模块姿势

作者:看雪论坛 FaEry

Hips篇高操之正确的全局Patch/拦截模块姿势_第1张图片

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

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

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

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

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第2张图片

可以看到,调用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;

}

}

returnStatus;

}

首先由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"},

};

BOOLFilterSetLoadImagePolicy()

{

ULONGuIndex = 0;

ULONGulReturned = 0;

HRESULThr = S_OK;

ULONGulInputSize =sizeof(POLICY_DATA);

FySendPolicyDat HipsData = {0};

HipsData.MessageType = Lowlayer_Msg_SetImagePolicy;

HipsData.Signature = FY_FSFILTER_SIGNATURE;

for(uIndex ; 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;

}

}

returnTRUE;

}

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

VOIDFyLoadImageNotifyRoutine(

IN PUNICODE_STRING FullImageName,

INHANDLEProcessId,

IN PIMAGE_INFO ImageInfo

)

{

WCHARlpszProcessName[MAX_PATH] = { 0 };

UNICODE_STRING uniProcessName;

BOOLEANbHitPolicy = FALSE;

FyLoadImageInfo SendInfo = { 0 };

LARGE_INTEGER liTimeOut;

ULONGulCopySize;

FYFSFLT_REPLY_DATA RelpyData;

ULONGulRelpyDataSize =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回应消息,否则这一次系统调用就返回了。

Hips篇高操之正确的全局Patch/拦截模块姿势_第3张图片

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

DWORDWINAPI FilterMessageProc(PVOIDlpParam)

{

FyLoadImageRecvDat Msg = {0};

FyReplyData ReplyMsg = {0};

while(TRUE)

{

DWORDbytesReturned = 0;

DWORDhResult;

TCHARbuf[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));

}

}

return0;

}

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

NTSTATUS FyFsFilterMessage (

__inPVOIDConnectionCookie,

__in_bcount_opt(InputBufferSize)PVOIDInputBuffer,

__inULONGInputBufferSize,

__out_bcount_part_opt(OutputBufferSize,*ReturnOutputBufferLength)PVOIDOutputBuffer,

__inULONGOutputBufferSize,

__outPULONGReturnOutputBufferLength

)

{

NTSTATUS Status = STATUS_SUCCESS;

ULONGCommand;

PVOIDpContent = NULL;

ULONGContentLength = 0;

UINT32Signature;

UNREFERENCED_PARAMETER(ConnectionCookie);

PAGED_CODE();

#if defined(_WIN64)

if(IoIs32bitProcess( NULL )) {

if(!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) {

Status = STATUS_DATATYPE_MISALIGNMENT;

returnStatus;

}

}else{

#endif

if(!IS_ALIGNED(OutputBuffer,sizeof(PVOID))) {

Status = STATUS_DATATYPE_MISALIGNMENT;

returnStatus;

}

#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)

returnStatus;

pContent = &((PFYFSFLT_SEND_DATA) InputBuffer)->Data;

ContentLength = ((PFYFSFLT_SEND_DATA) InputBuffer)->DataLength;

} __except( EXCEPTION_EXECUTE_HANDLER ) {

returnGetExceptionCode();

}

DbgPrint("[%s]Command:%x\n", __FUNCTION__, Command);

switch(Command)

{

caseLowlayer_Msg_SetImagePolicy:

DbgPrint("[%s] Lowlayer_Msg_SetImagePolicy\n", __FUNCTION__);

SetProcessImagePolicy((PPOLICY_DATA)pContent, ContentLength);

break;

caseLowlayer_Msg_BlockImage:

DbgPrint("[%s] Lowlayer_Msg_BlockImage\n", __FUNCTION__);

ExecuteKernelPatchProcedure((PFyPatchInfo)pContent, ContentLength);

break;

default:

break;

}

}

returnSTATUS_SUCCESS;

}

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

BOOLEANApcRealProcedure1(PFyPatchInfo lpPatchInfo)

{

BOOLEANbRet = FALSE;

NTSTATUS status;

ULONGulCompareSize;

PVOIDlpPatchAddr = lpPatchInfo->PatchAddress;

SIZE_TPatchSize = lpPatchInfo->PatchSize;

ULONGdwOldProtect;

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);

}

}

}

returnbRet;

}

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第4张图片

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第5张图片
Hips篇高操之正确的全局Patch/拦截模块姿势_第6张图片

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

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

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

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第7张图片

可以看到,调用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;

}

}

returnStatus;

}

首先由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"},

};

BOOLFilterSetLoadImagePolicy()

{

ULONGuIndex = 0;

ULONGulReturned = 0;

HRESULThr = S_OK;

ULONGulInputSize =sizeof(POLICY_DATA);

FySendPolicyDat HipsData = {0};

HipsData.MessageType = Lowlayer_Msg_SetImagePolicy;

HipsData.Signature = FY_FSFILTER_SIGNATURE;

for(uIndex ; 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;

}

}

returnTRUE;

}

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

VOIDFyLoadImageNotifyRoutine(

IN PUNICODE_STRING FullImageName,

INHANDLEProcessId,

IN PIMAGE_INFO ImageInfo

)

{

WCHARlpszProcessName[MAX_PATH] = { 0 };

UNICODE_STRING uniProcessName;

BOOLEANbHitPolicy = FALSE;

FyLoadImageInfo SendInfo = { 0 };

LARGE_INTEGER liTimeOut;

ULONGulCopySize;

FYFSFLT_REPLY_DATA RelpyData;

ULONGulRelpyDataSize =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回应消息,否则这一次系统调用就返回了。

Hips篇高操之正确的全局Patch/拦截模块姿势_第8张图片

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

DWORDWINAPI FilterMessageProc(PVOIDlpParam)

{

FyLoadImageRecvDat Msg = {0};

FyReplyData ReplyMsg = {0};

while(TRUE)

{

DWORDbytesReturned = 0;

DWORDhResult;

TCHARbuf[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));

}

}

return0;

}

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

NTSTATUS FyFsFilterMessage (

__inPVOIDConnectionCookie,

__in_bcount_opt(InputBufferSize)PVOIDInputBuffer,

__inULONGInputBufferSize,

__out_bcount_part_opt(OutputBufferSize,*ReturnOutputBufferLength)PVOIDOutputBuffer,

__inULONGOutputBufferSize,

__outPULONGReturnOutputBufferLength

)

{

NTSTATUS Status = STATUS_SUCCESS;

ULONGCommand;

PVOIDpContent = NULL;

ULONGContentLength = 0;

UINT32Signature;

UNREFERENCED_PARAMETER(ConnectionCookie);

PAGED_CODE();

#if defined(_WIN64)

if(IoIs32bitProcess( NULL )) {

if(!IS_ALIGNED(OutputBuffer,sizeof(ULONG))) {

Status = STATUS_DATATYPE_MISALIGNMENT;

returnStatus;

}

}else{

#endif

if(!IS_ALIGNED(OutputBuffer,sizeof(PVOID))) {

Status = STATUS_DATATYPE_MISALIGNMENT;

returnStatus;

}

#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)

returnStatus;

pContent = &((PFYFSFLT_SEND_DATA) InputBuffer)->Data;

ContentLength = ((PFYFSFLT_SEND_DATA) InputBuffer)->DataLength;

} __except( EXCEPTION_EXECUTE_HANDLER ) {

returnGetExceptionCode();

}

DbgPrint("[%s]Command:%x\n", __FUNCTION__, Command);

switch(Command)

{

caseLowlayer_Msg_SetImagePolicy:

DbgPrint("[%s] Lowlayer_Msg_SetImagePolicy\n", __FUNCTION__);

SetProcessImagePolicy((PPOLICY_DATA)pContent, ContentLength);

break;

caseLowlayer_Msg_BlockImage:

DbgPrint("[%s] Lowlayer_Msg_BlockImage\n", __FUNCTION__);

ExecuteKernelPatchProcedure((PFyPatchInfo)pContent, ContentLength);

break;

default:

break;

}

}

returnSTATUS_SUCCESS;

}

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

BOOLEANApcRealProcedure1(PFyPatchInfo lpPatchInfo)

{

BOOLEANbRet = FALSE;

NTSTATUS status;

ULONGulCompareSize;

PVOIDlpPatchAddr = lpPatchInfo->PatchAddress;

SIZE_TPatchSize = lpPatchInfo->PatchSize;

ULONGdwOldProtect;

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);

}

}

}

returnbRet;

}

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第9张图片

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

Hips篇高操之正确的全局Patch/拦截模块姿势_第10张图片
Hips篇高操之正确的全局Patch/拦截模块姿势_第11张图片

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

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

你可能感兴趣的:(Hips篇高操之正确的全局Patch/拦截模块姿势)