scene模块要怎么用(Hips篇高操之正确的全局Patch)
scene模块要怎么用(Hips篇高操之正确的全局Patch)首先由Ring3进程指定策略,这里拿金山的两个模块开刀:当然,我选择的是最后一种,现在貌似运用这种方法做通信的不多,我也没怎么接触过,一切都是从0开始,那就试试吧...这也是初入安全时我一直想实现的一个东西,但是由于当时技术局限,很菜,其实今天来看只是当时没有找到好的学习方法,没有办法实现很是遗憾。然而今日在团队的带领下,我改进了自己的学习方法,学会了如何有效看待问题分析问题以及解决问题。在此感谢我的导师以及leader。可以看到,调用API加载模块的时候最后都逃不过ntdll!ZwMapViewOfSection,最后进入系统调用然后触发模块回调,我们唯一能做的就是在模块回调里干一些猥琐的事情。直接Patch?那是不可能的,因为模块回调有限制,这时候有EProcess.AddressCreationLock的限制不说,模块还未能初始化完成,直接去搞肯定是不正确的做法。那么可以想到的是用A
作者:看雪论坛 FaEry
潜水了两个月,由于工作的关系没有什么多余的时间发新帖了,由于临近端午准备休息一下,于是乎决定在周末搞点事情,恰逢北京今日又下起雨来,在这朦朦胧胧的雨天里给大家呈上一份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回应消息,否则这一次系统调用就返回了。
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
在ZwMapViewOfSection之后LdrFindOrMapDll会调用RtlImageNtHeaderEx校验DosHeader和PEHeader,不对就卸载
另外,由于minifilter驱动需要inf文件安装,为了避免这样的情况,我就用服务启动驱动了(即用即加载),不用手动去加,直接双击exe就行。
LZ测试平台是Win7 x64,其他平台未经过测试,应该可以兼容到Win10... exe与sys放同一层目录下.
当时技术局限,很菜,其实今天来看只是当时没有找到好的学习方法,没有办法实现很是遗憾。然而今日在团队的带领下,我改进了自己的学习方法,学会了如何有效看待问题分析问题以及解决问题。在此感谢我的导师以及leader。
这个一直想实现的东西就是用驱动拦截模块,看似很简单的一个问题但是实际上要操作起来并非易事,要看得懂代码,得先理解了这张图:
可以看到,调用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回应消息,否则这一次系统调用就返回了。
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
在ZwMapViewOfSection之后LdrFindOrMapDll会调用RtlImageNtHeaderEx校验DosHeader和PEHeader,不对就卸载
另外,由于minifilter驱动需要inf文件安装,为了避免这样的情况,我就用服务启动驱动了(即用即加载),不用手动去加,直接双击exe就行。
LZ测试平台是Win7 x64,其他平台未经过测试,应该可以兼容到Win10... exe与sys放同一层目录下.