|
[招聘]深圳QQ电脑管家诚招安全工程师
路过帮顶,尝试总是没亏吃的,抱怨很多时候是没能正确评价自身的实力! |
|
|
|
[原创]深入分析Win7的对象引用跟踪机制
前来学习....... |
|
[注意]一个邪恶的设想
我哪个区......你干脆放狮子座算了 |
|
[原创]注册表文件解析完整解决方案
下个支持下呵 |
|
[讨论]论坛里有多少人是在校大学生
我是呀,华科的,大三了,信息安全的 |
|
几个星期前写的容错处理库,希望能互相启发
谢谢分享 下个学习下 |
|
[分享]RegMon源码
啥东东....下了鉴定下 |
|
[求助] 下载工具下载数据时,不是使用的 recv 或者WSARecv 吗?
迅雷有可能自己底层实现和封装了收发函数,并不一定要调用标准接口 |
|
Hook过滤架构搭建,仿照360
呵你真仔细,打反了谢谢提醒 |
|
Hook过滤架构搭建,仿照360
完整工程目前没办法给呢,请谅解,能发我就发了 |
|
Hook过滤架构搭建,仿照360
哇居然有精华激动ing..........版主神速......... |
|
Hook过滤架构搭建,仿照360
补发下上面的太难看了 仿照了下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; 好的宏定义也可以简化工程,这里大家可自行考虑。 这样差不多整个架构就搭建起来了,一个小型的监控系统就可以完成了。优秀的架构的确可以事半功倍,不过过滤函数的规则其实才是重中之重呀……………..。大家可以多讨论下,这个过滤规则是很需要仔细研究的。当然你要藏着咱也没办法呵………… 最后说下: 由于这个源代码是我一个大文件里面的一部分,所以整个也不好给出,其实也没必要给出来,毕竟没多少技术含量,大家都可以写得出的,这里只是就框架总结下而已。全部发了也没意思一大堆你也不想看,还不如发点核心的,然后你也可以尝试搭建下自己的更有乐趣呢! 第一次发这种贴,如果有不当之处敬请谅解。 |
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值