TP的学习笔记:实现安全Inline Hook模块+动态确定SSDT服务号(NT5)
我记得我第一次搞Hook的时候,是直接拿黑客防线09年的《Ring0中Inline Hook Shadow SSDT实现窗体保护》来搞得。当时里面的附件是Hook了ObOpenObjectByPointer实现的进程保护,但是就在某一天,它蓝屏了。。。后来才听说了暴力修改WP标志位是一种不安全不稳定的办法,于是就诞生了本文。本文提到的技术,参考了《SSDT和Shadow SSDT完全解析(二)》的文章。主要就是用MDL的方式避开只读保护罢了。本文只需要简单的修改一点点就可实现通用的Win32 Inline Hook SSDT模块。首先先讲如何避开只读保护。
既然暴力修改WP标志位是不稳定的方法,那我们就不能用这个方法。这里选用MDL映射的方式。首先创建一个MDL,在MmCreateMdl的第二个参数填入要Hook的函数地址,第三个参数填入5。这主要是因为jmp xxxxxxxx指令是5个字节的。
MDL创建完成后,用MmBuildMdlForNonPagedPool建立一段内存区域,修改MDL->MdlFlags的值,再用MmMapLockedPages建立映射。最后就可以用RtlCopyMemory来修改前五个字节了。(顺带着说一点,如果是想用地址替换的方式实现钩子的话,建议用InterlockedExchange函数)代码如下:
//HOOK函数
void Hook (PVOID Func,PVOID New_Func,PVOID Proxy_Func,PMDL MdlForFunc,PVOID Msct)
{
ULONG PatchSize;
BYTE g_HookCode = { 0xE9, 0, 0, 0, 0 };//相对跳转
BYTE Jmp_Orig_Code = { 0xEA, 0, 0, 0, 0, 0x08, 0x00 }; //绝对地址跳转
PatchSize=GetPatchSize(Func,5);//获得要Patch的字节数
//构造Proxy_Func
RtlCopyMemory((PBYTE)Proxy_Func,(PBYTE)Func,PatchSize);//实现原函数头
*((PULONG)(Jmp_Orig_Code+1))=(ULONG)((PBYTE)Func+PatchSize );//原函数+N 地址
RtlCopyMemory((PBYTE)Proxy_Func+PatchSize,Jmp_Orig_Code,7);//绝对地址跳转
*((ULONG*)(g_HookCode+1))=(ULONG)New_Func-(ULONG)Func-5;//计算JMP 地址
MdlForFunc=MmCreateMdl(NULL,Func,5);
if(MdlForFunc)
{
MmBuildMdlForNonPagedPool(MdlForFunc);
MdlForFunc->MdlFlags=MdlForFunc->MdlFlags|MDL_MAPPED_TO_SYSTEM_VA;
Msct=MmMapLockedPages(MdlForFunc,KernelMode);
RtlCopyMemory(Func,g_HookCode,5);
}
}
实现了Hook,就要实现Unhook,方法很简单,先用RtlCopyMemory恢复前五个字节,接着用MmUnmapLockedPages取消映射,再用IoFreeMdl释放掉Mdl就可以了。代码如下:
//UnHook函数
void UnHook (PVOID Func,PVOID Proxy_Func,PMDL MdlForFunc,PVOID Msct)
{
RtlCopyMemory(Func,Proxy_Func,5);
if(MdlForFunc)
{
MmUnmapLockedPages(Msct,MdlForFunc);
IoFreeMdl(MdlForFunc);
}
}
这里的Hook函数和Unhook函数参数比较多。。。如果是小白仅仅要调用这个Hook/Unhook函数,那么在Hook函数中,第一个参数填入要Hook的地址,第二个参数填入伪函数的地址,第三个参数填入代理函数的地址,第四个参数填入对应的MDL,第五个填入MSCT(其实是MappedSystemCallTable的缩写),其实后两个参数已经在Hook函数里处理完成了,调用之前不用理睬,声明一下变量就行。至于Unhook,也很简单,调用的时候,第一个参数输入被Hook的函数地址,第二个参数输入代理函数的地址,第三个参数输入为函数定义的MDL,第四个填入对应的MSCT即可。
完成了Hook和Unhook,接着就是动态确认服务号。由于Win32的SSDT和Win64的SSDT不一样,不是那种随机的,所以有些人可能会用硬编码了。我记得www.vsyide.com上有个查看SDT的小工具,可以确定多个系统的SSDT/SSSDT的函数服务号,这个工具大概长着个样子:
于是便可以惊喜的发现它只能看XP,2003,Vista,2008,win7,win8这六个系统。。。那么win10呢。。。所以用硬编码不是个好选择
那么我们如何取得Index呢?在内核里有相同数量的NtXXX函数和ZwXXX函数。那我们看看ZwTerminateProcess的反汇编结果:
可以发现第一个字节是0x8b,第二到第五个字节是0x00000101,也就是0x101,这就是我们要的服务编号!
所以我们的动态获取Index的思路就是取得ZwXXX函数的地址,然后读地址+1,长度为4的数据,这个数据就是Index了,代码如下:
ULONG DynamicGetNtTerminateProcessIndex()
{
UNICODE_STRING uniFuncName;
UCHAR tmp=0;
ULONG IndexOf_NtTerminateProcess=0;
//取得ZwTerminateProcess的函数地址
RtlInitUnicodeString(&uniFuncName,L"ZwTerminateProcess");
ZwTerminateProcess=MmGetSystemRoutineAddress(&uniFuncName);
//先判断一下有木有被Hook
RtlCopyMemory(&tmp,ZwTerminateProcess,1);
if(tmp==0xb8)
{
//取得Index
RtlCopyMemory(&IndexOf_NtTerminateProcess,(PVOID)((ULONG)ZwTerminateProcess+1),4);
}
return IndexOf_NtTerminateProcess;
}
上述代码就是取得NtTerminateProcess的Index的代码了。
要做到通用,只需要修改RtlInitUnicodeString这一行中的字符串就行了,别忘了把"Nt"改成"Zw"{:soso_e113:}
那么就开始测试结果吧,下面贴出XP和2k3的测试结果图片。
本文提及的技术,均在windows xp和windows server 2003内测试通过。 拿我哥们的win7,win8的机子测试了一下,竟然蓝屏了。。。算了这篇文章应该加个括号,其中要注上NT5测试通过。。。有兴趣的朋友帮忙改一下吧,蓝屏提示的是只读内存发生写操作【于是就不懂了,不是用MDL映射避开了写保护了么⊙▽⊙】 1.引用我的名言而不标明出处也是一种很欠扁的行为{:soso_e113:}
2.和TP有啥关系 Tesla.Angela 发表于 2015-7-4 10:11
1.引用我的名言而不标明出处也是一种很欠扁的行为
2.和TP有啥关系 ...
tangptr→tp。。。下次再表明出处 tangptr@126.com 发表于 2015-7-4 11:07
tangptr→tp。。。下次再表明出处
我还以为你说TENCENT GAME PROTECTION
页:
[1]