`

Hook过滤架构搭建,仿照360(转)

阅读更多
http://bbs.pediy.com/archive/index.php?t-116033.html
仿照了下360 的过滤架构,搭建了个Hook 框架,360的Hook架构的确很优秀,我觉得很值得我们学习与研究。这里我按照大牛们已经逆向出来的思路实现了下代码(都逆向出来了坐下代码工作不会怎么样吧?….只是学习架构)。不要鄙视我等代码工………,好吧大牛们想BS就BS吧,我表示毫无压力~~~~,我是菜鸟我怕谁!

废话不多说发代码,如果有错误和白痴的地方请指出,我水平有限…….
搭建这个架构大致需要以下几个模块,一是安装KiFastCallEntry的Hook模块,二是FakeKiFastCallEntry代理模块,三是SysCallFilter系统调用是否过滤的判断模块。其余的模块主要是过滤函数了,还有个获取KiFastCallEntry的patch地址的模块,最后是释放模块和初始化模块,这样大致的架构就搭建起来了。
接下来看看每个模块是怎么工作的。



首先是安装KiFastCallEntry的Hook模块,这个原理我就不多说了,大家都懂的

/************************************************************************


* 函数名称:HookKiFastCallEntry


* 功能描述:安装KiFastCallEntry钩子


* 参数列表:





* 返回值:状态


*************************************************************************/


NTSTATUS HookKiFastCallEntry()


{


NTSTATUS status=STATUS_SUCCESS;


if (!GetKiFastCallEntryPatchAddr())


{


KdPrint(("(HookKiFastCallEntry) GetKiFastCallEntryPatchAddr failed"));


return STATUS_UNSUCCESSFUL;


}


RtlCopyMemory(OriginalHead2,(PVOID)PatchAddr,5);


*(ULONG *)(ReplaceHead2+1)=(ULONG)FakeKiFastCallEntry-(PatchAddr+5);


KIRQL Irql;


Irql=WOFF();


//写入新的函数头


RtlCopyMemory((BYTE *)PatchAddr,ReplaceHead2,5);


WON(Irql);


return status;




}
这个模块有个地方就是GetKiFastCallEntryPatchAddr()获取KiFastCallEntry的Patch点这个有点小技巧,大家可以学习下,360是用SetEvent钩子栈回朔实现的,这个大家听了应该都能明白,就是调用函数时候会PUSH 返回到的EIP,这个EIP就是KiFastCallEntry中的了。
要patch的地方是按特征码搜索的,这个是xp sp3的

BOOL GetKiFastCallEntryPatchAddr()


{


ULONG ulCallNum;


PULONG pHookAddr;


PBYTE pCode;


ULONG i;


BOOL bRet=true;


KIRQL Irql;


hFakeEvent=(HANDLE)FakeHandle;


ulCallNum=*(PULONG)((PBYTE)ZwSetEvent+1);


pHookAddr=(PULONG)(pSysCallFilterInfo->ulSSDTAddr+ulCallNum*4);


RealNtSetEvent=*pHookAddr;//保存真实地址


Irql=WOFF();


*pHookAddr=(ULONG)FakeNtSetEvent; // 写入代理地址


WON(Irql);


ZwSetEvent(hFakeEvent,NULL);


Irql=WOFF();


*pHookAddr=RealNtSetEvent; // 写回真实地址


WON(Irql);


if (MmIsAddressValid((PVOID)BackTrackingAddr))


{


pCode=(PBYTE)BackTrackingAddr;


for (i=0;i<SearchByte;i++)


{


if (*(pCode-i)==0xe1&&*(pCode-i-1)==0x2b)


{


PatchAddr=(ULONG)(pCode-i-1);


break;


}


if (*(pCode-i)==0xfc&&*(pCode-i-1)==0x8b)


{


RetAddress=(ULONG)(pCode-i-1);


}





}


}


if (!PatchAddr||!RetAddress)


{


bRet=false;


}


return bRet;

}





这个代理函数里面获取EIP


NTSTATUS FakeNtSetEvent (


__in HANDLE EventHandle,


__out_opt PLONG PreviousState


)


{


NTSTATUS status=STATUS_SUCCESS;


if (EventHandle!=hFakeEvent||ExGetPreviousMode()==UserMode)// 不是自己调用,或者调用来自UserMode,直接调用原函数


{


status=((NTSETEVENT)RealNtSetEvent)(&EventHandle, PreviousState);


}


else


{


_asm


{


mov eax,dword ptr [ebp+4h]


mov BackTrackingAddr,eax


}


}


return status;

}


安装好Hook后就是Hook的代理函数了


这段代码 ……..好吧被BS咱也莫有办法,代理函数传入三个参数,这三个参数的含义可以参考内核情景分析一书中有详细介绍,给SysCallFileter来判断是否过滤。


_declspec (naked) NTSTATUS FakeKiFastCallEntry()


{


_asm


{


mov edi,edi


pushfd


pushad


push edi


push ebx


push eax


call SysCallfilter


mov dword ptr [esp+10h],eax


popad


popfd


sub esp, ecx


shr ecx, 2


push RetAddress


retn





}

}


接下来就是判断过滤的函数了,这里我略去了SHADOW SSDT,这段代码也……


/************************************************************************


* 函数名称:SysCallfilter


* 功能描述:过滤系统调用


* 参数列表:


ULONG SysCallNum:系统调用号


ULONG FunAddr:系统调用函数入口地址


ULONG ServiceBase:系统调用表指针


* 返回值:过滤则返回代理函数地址,否则返回真实地址


*************************************************************************/


ULONG SysCallfilter(ULONG SysCallNum,ULONG FunAddr,ULONG ServiceBase)


{





if( ServiceBase==pSysCallFilterInfo->ulSSDTAddr&&SysCallNum<=pSysCallFilterInfo->ulSSDTNum)


{


if(pSysCallFilterInfo->SSDTSwitchTable[SysCallNum]&&HookOrNot(SysCallNum,FALSE))


{


return pSysCallFilterInfo->ProxySSDTTable[SysCallNum];//


}


}


return FunAddr;




}




这个模块可以考虑添加适当的过滤规则,但最好效率点,这里我没加什么过滤,主要是搭建框架。





/************************************************************************


* 函数名称:HookOrNot


* 功能描述:判断是否过滤系统调用


* 参数列表:


ULONG SysCallNum:系统调用号


BOOL Flags:SSDT还是SDOWSSDT标志


* 返回值:返回表示不过滤,表示过滤


*************************************************************************/


ULONG HookOrNot(ULONG SysCallNum,BOOL Flags)


{


if (ExGetPreviousMode()==KernelMode)


{


return 0;


}


if (Flags)


{


return 1;


}


else


return 1;

}




好了基本功能模块搭建好了,现在就要初始化这些模块内所要使用的数据结构,来运作起来。


初始化里面我直接把savessdttable原始函数表填充为文件获取的原始地址表了,这里大家可以不必这么做。


/************************************************************************


* 函数名称:InitSysCallFilter


* 功能描述:初始化系统调用过滤


* 参数列表:





* 返回值:状态


*************************************************************************/


NTSTATUS InitSysCallFilter()


{


NTSTATUS status=STATUS_SUCCESS;


PVOID FileBuffer,FunBuffer;


ULONG ulSSDTLimit;


PKSERVICE_TABLE_DESCRIPTOR pServiceDescriptor;


//init





//Init SysCallFilterInfo buffer


pSysCallFilterInfo=(PSYSCALL_FILTER_INFO_TABLE)ExAllocatePoolWithTag(


NonPagedPool,


sizeof(SYSCALL_FILTER_INFO_TABLE),


MM_TAG_FILT);


RtlZeroMemory(pSysCallFilterInfo,sizeof(SYSCALL_FILTER_INFO_TABLE));


//Init SSDT address


pServiceDescriptor=(PKSERVICE_TABLE_DESCRIPTOR)GetKeServiceDescriptorTable();


pSysCallFilterInfo->ulSSDTAddr=(ULONG)pServiceDescriptor->Base;


//Init SSDT Table





FileBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);


FunBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);


if (!FileBuffer||!FunBuffer)


{


KdPrint(("(InitSysCallFilter) MmBuffer FunBuffer failed"));


return STATUS_UNSUCCESSFUL;


}


status=EnumOriginalSSDT(FileBuffer,FunBuffer,&ulSSDTLimit);


if (!NT_SUCCESS(status))


{


KdPrint(("(InitSysCallFilter) EnumOriginalSSDT failed"));


ExFreePool(FileBuffer);


ExFreePool(FunBuffer);


return STATUS_UNSUCCESSFUL;


}


memcpy(pSysCallFilterInfo->SavedSSDTTable,FileBuffer,ulSSDTLimit*4);


ExFreePool(FileBuffer);


ExFreePool(FunBuffer);


pSysCallFilterInfo->ulSSDTNum=ulSSDTLimit;


//Init Proxy SSDT table


pSysCallFilterInfo->ProxySSDTTable[97]=(ULONG)FakeNtLoadDriver;


//这里就可以随意添加Hook,相当方便


//Init SSDT Swicth table


pSysCallFilterInfo->SSDTSwitchTable[97]=1;


//记得要开开关


return status;


}





最后是释放清理模块了。


void UnHookKiFastCallEntry()


{


KIRQL Irql;


if (*(PULONG)OriginalHead2)


{


Irql=WOFF();


//写回原来的函数头


RtlCopyMemory((BYTE *)PatchAddr,OriginalHead2,5);


WON(Irql);


}


};





NTSTATUS FreeSysCallFilter()


{


NTSTATUS status=STATUS_SUCCESS;


UnHookKiFastCallEntry();


if (pSysCallFilterInfo)


{


ExFreePool(pSysCallFilterInfo);


}


return status;


}


这里顺带发个过滤函数以及R3通信架构的搭建好了


这个过滤是NtLoadDriver的


NTSTATUS FakeNtLoadDriver( __in PUNICODE_STRING DriverServiceName)


{


PEPROCESS pCurProcess;


DRIVER_TRANS_INFO DriverTransInfo;


if (DriverServiceName==NULL)


{


return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);


}


DriverTransInfo.Size=sizeof(DRIVER_TRANS_INFO);


pCurProcess=PsGetCurrentProcess();


if (pCurProcess)


{


GetProcessFullPathW((ULONG)pCurProcess,DriverTransInfo.ProcessFullPath);


}


RtlStringCchCopyW(DriverTransInfo.WarmReason,MAX_REASON*sizeof(WCHAR),L"尝试加载驱动,一旦加载驱动进程将会获得最高权限,允许此操作将可能导致危险发生,驱动文件为:");


RtlStringCchCatW(DriverTransInfo.WarmReason,MAX_PATH*sizeof(WCHAR),DriverServiceName->Buffer);


if (!GoOrNot((PVOID)&DriverTransInfo,TYPE_DRIVER_MONITOR))


{


return STATUS_ACCESS_DENIED;


}


else


{


return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);


}




}




然后是GoOrNot与R3通信等待R3命令


BOOL GoOrNot(__in PVOID pMonitorInfo,__in ULONG Type)


{


BOOL bRet=false;


switch (Type)


{


case TYPE_DRIVER_MONITOR:


bRet=GetUserCommand(g_DeviceExtension->DriverMonitorInfo.pNotifyEvent,


g_DeviceExtension->DriverMonitorInfo.SharedMemInfo.pShareMemory,


pMonitorInfo,

sizeof(DRIVER_TRANS_INFO));
break;

default:


;


}


return bRet;




}

BOOL GetUserCommand(__in PKEVENT pNotifyEvent,


__in PVOID pShareMemory,


__in PVOID pTransInfo,


__in ULONG pTransLen)


{


BOOL bRet;


PDRIVER_TRANS_INFO pDriverTransInfo;


memcpy(pShareMemory,pTransInfo,pTransLen);


KeSetEvent(pNotifyEvent,0,false);


KeWaitForSingleObject(


pNotifyEvent,


Executive,


KernelMode,


false,


NULL);


pDriverTransInfo=(PDRIVER_TRANS_INFO)pShareMemory;


if (pDriverTransInfo->Command==COMMAND_GO)


{


bRet=true;


}


else if (pDriverTransInfo->Command==COMMAND_STOP)


{


bRet=false;


}


return bRet;

}
这里发段R3和R0共享内存的,用的是内核创建pool在建MDL映射到用户空间的方法。

BOOL CreateSharedMemory(__out PSHARE_MEMORY_INFO pShareMemInfo,
__in ULONG MemorySize)
{
BOOL bRet=true;
PMDL pMdl;
PVOID UserVAToReturn;
PIO_STACK_LOCATION pIoStackLocation;
ULONG ulBufferLengthOut;
PVOID pSharedBuffer;
pSharedBuffer=ExAllocatePoolWithTag(NonPagedPool,MemorySize,MM_TAG_ANTI);
if (!pSharedBuffer)
{
KdPrint(("(IrpCreateSharedMemory) pSharedBuffer allocate failed"));
return false;
}
pMdl=IoAllocateMdl(pSharedBuffer,MemorySize,false,false,NULL);
if (!pMdl)
{
KdPrint(("(IrpCreateSharedMemory) IoAllocateMdl( failed"));
ExFreePool(pSharedBuffer);
return false;
}
MmBuildMdlForNonPagedPool(pMdl);
UserVAToReturn=MmMapLockedPagesSpecifyCache(pMdl,
UserMode,
MmCached,
NULL,
false,
NormalPagePriority);
if (!UserVAToReturn)
{
IoFreeMdl(pMdl);
ExFreePool(pSharedBuffer);
return false;
}
RtlZeroMemory(pSharedBuffer,MemorySize);
KdPrint(("UserVAToReturn:0x%08x",UserVAToReturn));
//输出
pShareMemInfo->pShareMemory=pSharedBuffer;
pShareMemInfo->pSharedMdl=pMdl;
pShareMemInfo->UserVA=(ULONG)UserVAToReturn;
return bRet;
}





R3的创建事件和开线程我就不发了,很简单大家可以自己尝试下。


最后附下整个架构的部分数据结构


//GoOrNot Type宏定义


#define TYPE_DRIVER_MONITOR 0x01


//GoOrNot Command宏定义


#define COMMAND_GO 0x01


#define COMMAND_STOP 0x02


//危险拦截提示语句


#define WARM_DRI_LOAD L"尝试加载驱动,一旦加载驱动进程将会获得系统最高权限,允许此操作将可能导致危险发生,驱动文件路径:"


//************数据定义***************************************************


typedef struct _SYSCALL_FILTER_INFO_TABLE


{


ULONG ulSSDTAddr;


ULONG ulSHADOWSSDTAddr;


ULONG ulSSDTNum;


ULONG ulSHADOWSSDTNum;


ULONG SavedSSDTTable[SSDT_FILTER_NUM]; //SSDT原始函数地址表


ULONG ProxySSDTTable[SHADOWSSDT_FILTER_NUM]; //SSDT代理函数地址表


ULONG SavedShadowSSDTTable[SSDT_FILTER_NUM]; //ShadowSSDT原始函数地址表


ULONG ProxyShadowSSDTTable[SHADOWSSDT_FILTER_NUM]; //ShadowSSDT代理函数地址表


ULONG SSDTSwitchTable[SSDT_FILTER_NUM]; //SSDT Hook开关表


ULONG ShadowSSDTSwitchTable[SHADOWSSDT_FILTER_NUM];//ShadowSSDT Hook开关表

}SYSCALL_FILTER_INFO_TABLE,*PSYSCALL_FILTER_INFO_TABLE;

好的宏定义也可以简化工程,这里大家可自行考虑。





这样差不多整个架构就搭建起来了,一个小型的监控系统就可以完成了。优秀的架构的确可以事半功倍,不过过滤函数的规则其实才是重中之重呀……………..。大家可以多讨论下,这个过滤规则是很需要仔细研究的。当然你要藏着咱也没办法呵…………


最后说下:


由于这个源代码是我一个大文件里面的一部分,所以整个也不好给出,其实也没必要给出来,毕竟没多少技术含量,大家都可以写得出的,这里只是就框架总结下而已。全部发了也没意思一大堆你也不想看,还不如发点核心的,然后你也可以尝试搭建下自己的更有乐趣呢!

第一次发这种贴,如果有不当之处敬请谅解。

游猎夕阳
2010-07-01, 13:49:49
这,第一次发怎么格式变了.........预览还好好的怎么发出来,间距这么大啊悲剧

kanxue
2010-07-01, 13:56:30
这,第一次发怎么格式变了.........预览还好好的怎么发出来,间距这么大啊悲剧

不用预览,你在记事本排好版,直接复制粘贴上来即可。

游猎夕阳
2010-07-01, 14:05:11
哦第一次发长段的没经验谢谢了!

游猎夕阳
2010-07-01, 14:11:54
补发下上面的太难看了

仿照了下360 的过滤架构,搭建了个Hook 框架,360的Hook架构的确很优秀,我觉得很值得我们学习与研究。这里我按照大牛们已经逆向出来的思路实现了下代码(都逆向出来了坐下代码工作不会怎么样吧?….只是学习架构)。不要鄙视我等代码工………,好吧大牛们想BS就BS吧,我表示毫无压力~~~~,我是菜鸟我怕谁!

废话不多说发代码,如果有错误和白痴的地方请指出,我水平有限…….
搭建这个架构大致需要以下几个模块,一是安装KiFastCallEntry的Hook模块,二是FakeKiFastCallEntry代理模块,三是SysCallFilter系统调用是否过滤的判断模块。其余的模块主要是过滤函数了,还有个获取KiFastCallEntry的patch地址的模块,最后是释放模块和初始化模块,这样大致的架构就搭建起来了。
接下来看看每个模块是怎么工作的。

首先是安装KiFastCallEntry的Hook模块,这个原理我就不多说了,大家都懂的
/************************************************************************
* 函数名称:HookKiFastCallEntry
* 功能描述:安装KiFastCallEntry钩子
* 参数列表:

* 返回值:状态
*************************************************************************/
NTSTATUS HookKiFastCallEntry()
{
NTSTATUS status=STATUS_SUCCESS;
if (!GetKiFastCallEntryPatchAddr())
{
KdPrint(("(HookKiFastCallEntry) GetKiFastCallEntryPatchAddr failed"));
return STATUS_UNSUCCESSFUL;
}
RtlCopyMemory(OriginalHead2,(PVOID)PatchAddr,5);
*(ULONG *)(ReplaceHead2+1)=(ULONG)FakeKiFastCallEntry-(PatchAddr+5);
KIRQL Irql;
Irql=WOFF();
//写入新的函数头
RtlCopyMemory((BYTE *)PatchAddr,ReplaceHead2,5);
WON(Irql);
return status;

}
这个模块有个地方就是GetKiFastCallEntryPatchAddr()获取KiFastCallEntry的Patch点这个有点小技巧,大家可以学习下,360是用SetEvent钩子栈回朔实现的,这个大家听了应该都能明白,就是调用函数时候会PUSH 返回到的EIP,这个EIP就是KiFastCallEntry中的了。
要patch的地方是按特征码搜索的,这个是xp sp3的
BOOL GetKiFastCallEntryPatchAddr()
{
ULONG ulCallNum;
PULONG pHookAddr;
PBYTE pCode;
ULONG i;
BOOL bRet=true;
KIRQL Irql;
hFakeEvent=(HANDLE)FakeHandle;
ulCallNum=*(PULONG)((PBYTE)ZwSetEvent+1);
pHookAddr=(PULONG)(pSysCallFilterInfo->ulSSDTAddr+ulCallNum*4);
RealNtSetEvent=*pHookAddr;//保存真实地址
Irql=WOFF();
*pHookAddr=(ULONG)FakeNtSetEvent; // 写入代理地址
WON(Irql);
ZwSetEvent(hFakeEvent,NULL);
Irql=WOFF();
*pHookAddr=RealNtSetEvent; // 写回真实地址
WON(Irql);
if (MmIsAddressValid((PVOID)BackTrackingAddr))
{
pCode=(PBYTE)BackTrackingAddr;
for (i=0;i<SearchByte;i++)
{
if (*(pCode-i)==0xe1&&*(pCode-i-1)==0x2b)
{
PatchAddr=(ULONG)(pCode-i-1);
break;
}
if (*(pCode-i)==0xfc&&*(pCode-i-1)==0x8b)
{
RetAddress=(ULONG)(pCode-i-1);
}

}
}
if (!PatchAddr||!RetAddress)
{
bRet=false;
}
return bRet;
}

这个代理函数里面获取EIP
NTSTATUS FakeNtSetEvent (
__in HANDLE EventHandle,
__out_opt PLONG PreviousState
)
{
NTSTATUS status=STATUS_SUCCESS;
if (EventHandle!=hFakeEvent||ExGetPreviousMode()==UserMode)// 不是自己调用,或者调用来自UserMode,直接调用原函数
{
status=((NTSETEVENT)RealNtSetEvent)(&EventHandle, PreviousState);
}
else
{
_asm
{
mov eax,dword ptr [ebp+4h]
mov BackTrackingAddr,eax
}
}
return status;
}

安装好Hook后就是Hook的代理函数了
这段代码 ……..好吧被BS咱也莫有办法,代理函数传入三个参数,这三个参数的含义可以参考内核情景分析一书中有详细介绍,给SysCallFileter来判断是否过滤。
_declspec (naked) NTSTATUS FakeKiFastCallEntry()
{
_asm
{
mov edi,edi
pushfd
pushad
push edi
push ebx
push eax
call SysCallfilter
mov dword ptr [esp+10h],eax
popad
popfd
sub esp, ecx
shr ecx, 2
push RetAddress
retn

}
}

接下来就是判断过滤的函数了,这里我略去了SHADOW SSDT,这段代码也……
/************************************************************************
* 函数名称:SysCallfilter
* 功能描述:过滤系统调用
* 参数列表:
ULONG SysCallNum:系统调用号
ULONG FunAddr:系统调用函数入口地址
ULONG ServiceBase:系统调用表指针
* 返回值:过滤则返回代理函数地址,否则返回真实地址
*************************************************************************/
ULONG SysCallfilter(ULONG SysCallNum,ULONG FunAddr,ULONG ServiceBase)
{

if( ServiceBase==pSysCallFilterInfo->ulSSDTAddr&&SysCallNum<=pSysCallFilterInfo->ulSSDTNum)
{
if(pSysCallFilterInfo->SSDTSwitchTable[SysCallNum]&&HookOrNot(SysCallNum,FALSE))
{
return pSysCallFilterInfo->ProxySSDTTable[SysCallNum];//
}
}
return FunAddr;

}

这个模块可以考虑添加适当的过滤规则,但最好效率点,这里我没加什么过滤,主要是搭建框架。

/************************************************************************
* 函数名称:HookOrNot
* 功能描述:判断是否过滤系统调用
* 参数列表:
ULONG SysCallNum:系统调用号
BOOL Flags:SSDT还是SDOWSSDT标志
* 返回值:返回表示不过滤,表示过滤
*************************************************************************/
ULONG HookOrNot(ULONG SysCallNum,BOOL Flags)
{
if (ExGetPreviousMode()==KernelMode)
{
return 0;
}
if (Flags)
{
return 1;
}
else
return 1;
}

好了基本功能模块搭建好了,现在就要初始化这些模块内所要使用的数据结构,来运作起来。
初始化里面我直接把savessdttable原始函数表填充为文件获取的原始地址表了,这里大家可以不必这么做。
/************************************************************************
* 函数名称:InitSysCallFilter
* 功能描述:初始化系统调用过滤
* 参数列表:

* 返回值:状态
*************************************************************************/
NTSTATUS InitSysCallFilter()
{
NTSTATUS status=STATUS_SUCCESS;
PVOID FileBuffer,FunBuffer;
ULONG ulSSDTLimit;
PKSERVICE_TABLE_DESCRIPTOR pServiceDescriptor;
//init

//Init SysCallFilterInfo buffer
pSysCallFilterInfo=(PSYSCALL_FILTER_INFO_TABLE)ExAllocatePoolWithTag(
NonPagedPool,
sizeof(SYSCALL_FILTER_INFO_TABLE),
MM_TAG_FILT);
RtlZeroMemory(pSysCallFilterInfo,sizeof(SYSCALL_FILTER_INFO_TABLE));
//Init SSDT address
pServiceDescriptor=(PKSERVICE_TABLE_DESCRIPTOR)GetKeServiceDescriptorTable();
pSysCallFilterInfo->ulSSDTAddr=(ULONG)pServiceDescriptor->Base;
//Init SSDT Table

FileBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);
FunBuffer=ExAllocatePoolWithTag(NonPagedPool,(SSDT_MAX_NUM)*sizeof(ULONG),MM_TAG_FILT);
if (!FileBuffer||!FunBuffer)
{
KdPrint(("(InitSysCallFilter) MmBuffer FunBuffer failed"));
return STATUS_UNSUCCESSFUL;
}
status=EnumOriginalSSDT(FileBuffer,FunBuffer,&ulSSDTLimit);
if (!NT_SUCCESS(status))
{
KdPrint(("(InitSysCallFilter) EnumOriginalSSDT failed"));
ExFreePool(FileBuffer);
ExFreePool(FunBuffer);
return STATUS_UNSUCCESSFUL;
}
memcpy(pSysCallFilterInfo->SavedSSDTTable,FileBuffer,ulSSDTLimit*4);
ExFreePool(FileBuffer);
ExFreePool(FunBuffer);
pSysCallFilterInfo->ulSSDTNum=ulSSDTLimit;
//Init Proxy SSDT table
pSysCallFilterInfo->ProxySSDTTable[97]=(ULONG)FakeNtLoadDriver;
//这里就可以随意添加Hook,相当方便
//Init SSDT Swicth table
pSysCallFilterInfo->SSDTSwitchTable[97]=1;
//记得要开开关
return status;
}

最后是释放清理模块了。
void UnHookKiFastCallEntry()
{
KIRQL Irql;
if (*(PULONG)OriginalHead2)
{
Irql=WOFF();
//写回原来的函数头
RtlCopyMemory((BYTE *)PatchAddr,OriginalHead2,5);
WON(Irql);
}
};

NTSTATUS FreeSysCallFilter()
{
NTSTATUS status=STATUS_SUCCESS;
UnHookKiFastCallEntry();
if (pSysCallFilterInfo)
{
ExFreePool(pSysCallFilterInfo);
}
return status;
}
这里顺带发个过滤函数以及R3通信架构的搭建好了
这个过滤是NtLoadDriver的
NTSTATUS FakeNtLoadDriver( __in PUNICODE_STRING DriverServiceName)
{
PEPROCESS pCurProcess;
DRIVER_TRANS_INFO DriverTransInfo;
if (DriverServiceName==NULL)
{
return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);
}
DriverTransInfo.Size=sizeof(DRIVER_TRANS_INFO);
pCurProcess=PsGetCurrentProcess();
if (pCurProcess)
{
GetProcessFullPathW((ULONG)pCurProcess,DriverTransInfo.ProcessFullPath);
}
RtlStringCchCopyW(DriverTransInfo.WarmReason,MAX_REASON*sizeof(WCHAR),L"尝试加载驱动,一旦加载驱动进程将会获得最高权限,允许此操作将可能导致危险发生,驱动文件为:");
RtlStringCchCatW(DriverTransInfo.WarmReason,MAX_PATH*sizeof(WCHAR),DriverServiceName->Buffer);
if (!GoOrNot((PVOID)&DriverTransInfo,TYPE_DRIVER_MONITOR))
{
return STATUS_ACCESS_DENIED;
}
else
{
return ((NTLOADDRIVER)pSysCallFilterInfo->SavedSSDTTable[97])(DriverServiceName);
}

}

然后是GoOrNot与R3通信等待R3命令
BOOL GoOrNot(__in PVOID pMonitorInfo,__in ULONG Type)
{
BOOL bRet=false;
switch (Type)
{
case TYPE_DRIVER_MONITOR:
bRet=GetUserCommand(g_DeviceExtension->DriverMonitorInfo.pNotifyEvent,
g_DeviceExtension->DriverMonitorInfo.SharedMemInfo.pShareMemory,
pMonitorInfo,
sizeof(DRIVER_TRANS_INFO));
break;
default:
;
}
return bRet;

}
//获取用户层命令
BOOL GetUserCommand(__in PKEVENT pNotifyEvent,
__in PVOID pShareMemory,
__in PVOID pTransInfo,
__in ULONG pTransLen)
{
BOOL bRet;
PDRIVER_TRANS_INFO pDriverTransInfo;
memcpy(pShareMemory,pTransInfo,pTransLen);
KeSetEvent(pNotifyEvent,0,false);
KeWaitForSingleObject(
pNotifyEvent,
Executive,
KernelMode,
false,
NULL);
pDriverTransInfo=(PDRIVER_TRANS_INFO)pShareMemory;
if (pDriverTransInfo->Command==COMMAND_GO)
{
bRet=true;
}
else if (pDriverTransInfo->Command==COMMAND_STOP)
{
bRet=false;
}
return bRet;
}

这里发段R3和R0共享内存的,用的是内核创建pool在建MDL映射到用户空间的方法。
BOOL CreateSharedMemory(__out PSHARE_MEMORY_INFO pShareMemInfo,
__in ULONG MemorySize)
{
BOOL bRet=true;
PMDL pMdl;
PVOID UserVAToReturn;
PIO_STACK_LOCATION pIoStackLocation;
ULONG ulBufferLengthOut;
PVOID pSharedBuffer;
pSharedBuffer=ExAllocatePoolWithTag(NonPagedPool,MemorySize,MM_TAG_ANTI);
if (!pSharedBuffer)
{
KdPrint(("(IrpCreateSharedMemory) pSharedBuffer allocate failed"));
return false;
}
pMdl=IoAllocateMdl(pSharedBuffer,MemorySize,false,false,NULL);
if (!pMdl)
{
KdPrint(("(IrpCreateSharedMemory) IoAllocateMdl( failed"));
ExFreePool(pSharedBuffer);
return false;
}
MmBuildMdlForNonPagedPool(pMdl);
UserVAToReturn=MmMapLockedPagesSpecifyCache(pMdl,
UserMode,
MmCached,
NULL,
false,
NormalPagePriority);
if (!UserVAToReturn)
{
IoFreeMdl(pMdl);
ExFreePool(pSharedBuffer);
return false;
}
RtlZeroMemory(pSharedBuffer,MemorySize);
KdPrint(("UserVAToReturn:0x%08x",UserVAToReturn));
//输出
pShareMemInfo->pShareMemory=pSharedBuffer;
pShareMemInfo->pSharedMdl=pMdl;
pShareMemInfo->UserVA=(ULONG)UserVAToReturn;
return bRet;
}
R3的创建事件和开线程我就不发了,很简单大家可以自己尝试下。
最后附下整个架构的部分数据结构

//GoOrNot Type宏定义
#define TYPE_DRIVER_MONITOR 0x01
//GoOrNot Command宏定义
#define COMMAND_GO 0x01
#define COMMAND_STOP 0x02
//危险拦截提示语句
#define WARM_DRI_LOAD L"尝试加载驱动,一旦加载驱动进程将会获得系统最高权限,允许此操作将可能导致危险发生,驱动文件路径:"
//************数据定义***************************************************
typedef struct _SYSCALL_FILTER_INFO_TABLE
{
ULONG ulSSDTAddr;
ULONG ulSHADOWSSDTAddr;
ULONG ulSSDTNum;
ULONG ulSHADOWSSDTNum;
ULONG SavedSSDTTable[SSDT_FILTER_NUM]; //SSDT原始函数地址表
ULONG ProxySSDTTable[SHADOWSSDT_FILTER_NUM]; //SSDT代理函数地址表
ULONG SavedShadowSSDTTable[SSDT_FILTER_NUM]; //ShadowSSDT原始函数地址表
ULONG ProxyShadowSSDTTable[SHADOWSSDT_FILTER_NUM]; //ShadowSSDT代理函数地址表
ULONG SSDTSwitchTable[SSDT_FILTER_NUM]; //SSDT Hook开关表
ULONG ShadowSSDTSwitchTable[SHADOWSSDT_FILTER_NUM];//ShadowSSDT Hook开关表
}SYSCALL_FILTER_INFO_TABLE,*PSYSCALL_FILTER_INFO_TABLE;
好的宏定义也可以简化工程,这里大家可自行考虑。

这样差不多整个架构就搭建起来了,一个小型的监控系统就可以完成了。优秀的架构的确可以事半功倍,不过过滤函数的规则其实才是重中之重呀……………..。大家可以多讨论下,这个过滤规则是很需要仔细研究的。当然你要藏着咱也没办法呵…………
最后说下:
由于这个源代码是我一个大文件里面的一部分,所以整个也不好给出,其实也没必要给出来,毕竟没多少技术含量,大家都可以写得出的,这里只是就框架总结下而已。全部发了也没意思一大堆你也不想看,还不如发点核心的,然后你也可以尝试搭建下自己的更有乐趣呢!
第一次发这种贴,如果有不当之处敬请谅解。
分享到:
评论

相关推荐

    Delphi LSP Hook 过滤广告

    **Delphi LSP Hook 过滤广告技术详解** 在IT领域,开发人员常常需要处理各种网络数据,其中包括可能存在的广告信息。"Delphi LSP Hook 过滤广告"是一种技术手段,利用Delphi编程环境来实现本地系统服务提供商...

    API HOOK易语言IP端口拦截转向源码

    API Hook是一种技术,它允许程序员拦截系统调用或者应用程序接口(API)的调用,以便在调用实际功能之前或之后执行自定义代码。在易语言中,API Hook的实现通常涉及创建一个动态链接库(DLL),并在目标进程中注入这...

    1.3.8.1版(18-04-02)360加固保Hook

    《1.3.8.1版(18-04-02)360加固保Hook:深入解析Xposed框架与360加固技术》 在移动应用开发领域,安全性和隐私保护至关重要,其中360加固保作为一款广受欢迎的安卓应用加固工具,为开发者提供了强大的安全防护。...

    Hook Api,hook ReadFile,hook WriteFile,hook LoadLibrary

    通过Hook它们,开发者可以实现诸如日志记录、数据过滤、性能分析等功能。例如,当其他程序尝试读取文件时,我们的Hook代码先执行,记录下读取操作的信息,甚至改变返回值,影响实际的读取结果。 同样,LoadLibrary...

    企业微信hook,PC企业微信hook,企业微信hook教程,易语言企业微信hook,pc微信hook小程序,pc微信hook全套教程,pc微信hook最新版本,企业微信hook 加好友,pc.zip

    企业微信hook,PC企业微信hook,企业微信hook教程,易语言企业微信hook,pc微信hook小程序,pc微信hook全套教程,pc微信hook最新版本,企业微信hook 加好友,pc企业微信hook全套教程,PC企业微信HOOK接口在前面的内容中读者...

    Inline Hook_inlinehook_x86_x64_64位HOOK_

    x86架构通常使用JMP或CALL指令来跳转到Hook处理程序,而x64架构则更倾向于使用RIP相对寻址,因为x64指令集更倾向于减少绝对地址的使用。Inline Hook在x64上需要考虑到更大的指令长度和寄存器使用的变化。 提供的...

    千寻hook收集特别版

    千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook收集特别版 千寻hook...

    x64 APIHOOK

    然而,在64位系统(x64架构)中,由于寻址方式和指令集的不同,实现API Hook变得更为复杂。64位ASM(汇编语言)的语法和结构与32位不同,增加了API Hook的难度。 首先,我们要理解64位汇编中的关键变化。在x64架构...

    Easyhook示例教程

    另外,EasyHook 也支持64位和32位操作系统,对于不同架构的系统都能很好地支持。 5. EasyHook 的应用 EasyHook 广泛应用于各种领域,包括反调试、反反调试、游戏外挂、安全软件等。在这些领域中,EasyHook 都发挥...

    APIHook例子 hook api

    APIHook是一种技术,用于在应用程序调用特定API(应用程序编程接口)时,插入自定义代码以拦截、修改或增强原始API的功能。这个例子是关于如何Hook Windows Socket API,即Winsock,来监控或改变网络通信行为。让...

    易语言x64-hook模块源码+实列

    在给定的资源中,"易语言x64-hook模块源码+实例"是关于易语言在x64架构下进行钩子(Hook)技术实现的资料包。钩子技术是Windows编程中的一种高级技巧,它允许程序监控并响应系统中的特定事件或行为。 1. **x64架构...

    Hook MessageBox 全局Hook

    全局Hook是一种技术,它允许一个程序监控或修改其他程序的行为,而无需修改这些程序的代码。在本场景中,我们关注的是Hook MessageBox,也就是对Windows系统中的消息框函数MessageBox进行拦截。MessageBox是Windows ...

    EasyHook-2.7.6270.0.zip_c# easy hook_easyhook_easyhook 2.7_eas

    EasyHook是一款强大的、开源的.NET库,用于在托管代码中实现远程函数钩子(Remote Function Hooking)。在标题和描述中提到的"EasyHook-2.7.6270.0.zip"是一个包含EasyHook库版本2.7.6270.0的压缩包,适用于C#开发者...

    C#EasyHook_easyhook_

    【C# EasyHook】是一个强大的库,用于在.NET Framework中实现远程函数调用(RDI,Remote Function Invocation)和钩子技术。EasyHook为开发者提供了一种高效、稳定且易于使用的解决方案,允许他们在运行时拦截和修改...

    微信Hook教程(易语言)

    ### 微信Hook教程(易语言) #### 一、Hook技术概述 Hook技术在软件开发领域扮演着重要的角色,尤其在实现特定功能扩展或者调试分析时。本教程将重点介绍如何利用易语言进行微信Hook操作。 #### 二、Hook原理简述...

    EasyHook远程进程注入并hook api的实现

    本篇将详细讲解如何利用EasyHook库实现远程进程API Hook。 EasyHook是一个开源的.NET库,它提供了一种在Windows平台上进行远程钩子(Hook)的技术。Hook技术允许开发者拦截系统调用或特定函数的执行,以便在调用...

    易语言 微信 hook 2.6

    《易语言微信hook 2.6技术解析及应用探索》 在信息技术日新月异的今天,编程语言的创新和应用不断推动着软件开发的进步。其中,“易语言”以其独特的汉字编程理念,降低了编程的门槛,让更多的人能够参与到程序设计...

    Hook经典分析 关于QQ Hook的应用 钩子

    QQ Hook是一种技术手段,主要涉及计算机程序中的钩子(Hook)机制。钩子在编程领域中,是指一种允许开发者在特定事件发生时插入自定义处理代码的机制。它允许程序拦截并处理系统或应用程序级别的事件,例如键盘输入...

    Hook自己的程序

    通过分析和学习这个示例,开发者可以了解如何在实际项目中对文本处理函数进行Hook,以实现特定的功能,如日志记录、文本内容过滤等。 总结,Hook技术是程序员的得力工具,能够灵活地扩展和控制程序的行为。理解和...

    HOOK系列实例实战班【无KEY版】

    12、HOOK 载入DLL命令,实现过滤DLL。13、HOOK 卸载DLL命令,实现过滤卸载。14、HOOK 游戏API 实现加速。15、游戏功能HOOK实现鼠标自动点击。16、HOOK植物大战僵尸实现无限小车。17、HOOK游戏功能技巧讲解。18、Hook...

Global site tag (gtag.js) - Google Analytics