tangptr@126.com 发表于 2015-8-28 21:26:31

TP的学习笔记:用映射MDL方式实现安全的Hook引擎

本文将提及到两种Inline Hook,分别是修改头五个字节的jmp Hook以及修改Call指令后4个字节的Call Hook。特点在于没有内嵌汇编,纯C的代码。
首先先谈Head Inline Hook。
首先挂钩的时候要注意哪些问题呢?
1.写入保护。写入保护位于cr0寄存器第15位,为一代表开启,为零代表关闭。但实验证明修改cr0寄存器属于危险行为,而且在不使用类似于__writecr0之类的WDK内置宏的情况下,是需要内嵌汇编的。这里将使用映射MDL的方式实现绕过写入保护。
2.指令屑。其实这已经不能算是问题了,因为有反汇编引擎。但如果仅仅修改5个字节,虽然执行不存在问题,但像WinDbg之类的软件查看函数就会出现异常,而且,假设要对一个不能只Patch5个字节的函数来说,我们Hook之后,别的驱动想对这个函数进行Call Hook就会出现问题。所以,还得把剩余的字节全部填充NOP指令才行。
void Hook (PVOID Func,PVOID New_Func,PVOID Proxy_Func)
{
        PMDL MdlForFunc;
        PVOID Msct;
        PVOID HookCode=NULL;
        ULONG PatchSize;
        KIRQL oldIrql;
        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 地址
        HookCode=ExAllocatePool(NonPagedPool,PatchSize);
        RtlCopyMemory(HookCode,g_HookCode,5);
        memset((PVOID)((ULONG)HookCode+5),0x90,PatchSize-5);
        MdlForFunc=MmCreateMdl(NULL,Func,PatchSize);
        if(MdlForFunc)
        {
                MmBuildMdlForNonPagedPool(MdlForFunc);
                MdlForFunc->MdlFlags=MdlForFunc->MdlFlags|MDL_MAPPED_TO_SYSTEM_VA;
                __try
                {
                        MmProbeAndLockPages(MdlForFunc,KernelMode,IoWriteAccess);
                }
                      __except(EXCEPTION_EXECUTE_HANDLER)
                {
                        IoFreeMdl(MdlForFunc);;
                }
                Msct=MmMapLockedPagesSpecifyCache(MdlForFunc,KernelMode,MmWriteCombined,NULL,FALSE,0);
                oldIrql=KeRaiseIrqlToDpcLevel();
                RtlCopyMemory(Msct,HookCode,PatchSize);
                KeLowerIrql(oldIrql);
                MmUnmapLockedPages(Msct,MdlForFunc);
                MmUnlockPages(MdlForFunc);
                IoFreeMdl(MdlForFunc);
        }
        ExFreePool(HookCode);
}
还有UnHook的代码:
void UnHook (PVOID Func,PVOID Proxy_Func)
{
        PMDL MdlForFunc;
        PVOID Msct;
        ULONG PatchSize;
        KIRQL f_oldirql;
        PatchSize=GetPatchSize(Proxy_Func,5);
        MdlForFunc=MmCreateMdl(NULL,Func,PatchSize);
        if(MdlForFunc)
        {
                MmBuildMdlForNonPagedPool(MdlForFunc);
                MdlForFunc->MdlFlags=MdlForFunc->MdlFlags|MDL_MAPPED_TO_SYSTEM_VA;
                __try
                {
                        MmProbeAndLockPages(MdlForFunc,KernelMode,IoWriteAccess);
                }
                      __except(EXCEPTION_EXECUTE_HANDLER)
                {
                        IoFreeMdl(MdlForFunc);
                }
                Msct=MmMapLockedPagesSpecifyCache(MdlForFunc,KernelMode,MmWriteCombined,NULL,FALSE,0);
                f_oldirql=KeRaiseIrqlToDpcLevel();
                RtlCopyMemory(Msct,Proxy_Func,PatchSize);
                KeLowerIrql(f_oldirql);
                MmUnmapLockedPages(Msct,MdlForFunc);
                MmUnlockPages(MdlForFunc);
                IoFreeMdl(MdlForFunc);
        }
}
说白了这里只是补充了第一期TP的学习笔记中《TP的学习笔记:实现安全Inline Hook模块+动态确定SSDT服务号(NT5)》一文的代码而已。并且还修正了NT6系列操作系统蓝屏的问题,也补充了修复指令屑BUG的问题。
接下来是Call Hook。
其实Call Hook就没啥说的了,同样是把暴力修改cr0寄存器改成了映射MDL,不过我发现上次的TP的学习笔记中使用自旋锁是没必要的,因此改良后代码是这样的:
void CallAddrHook(PVOID StartAddr,PVOID OldAddr,PVOID NewAddr)
{
        PUCHAR cPtr, pOpcode;
        ULONG Length,Tmp;
        PMDL pMdl;
        PVOID Msct;
        for (cPtr=StartAddr;(ULONG)cPtr<(ULONG)StartAddr+0x1000;cPtr += Length)
        {
                Length = SizeOfCode(cPtr, &pOpcode);//计算当前指令长度
                if (!Length) break;
                if (Length ==5 && *cPtr==0xE8)// 当前长度5 且第一字节为E8
                {//因为CALL用的是相对偏移所以我们还需要进行计算相对偏移
                        if((ULONG)OldAddr-(ULONG)cPtr-5==*(PULONG)(cPtr+1)) //判断当前是否为OldAddr的CALL相对地址
                        {
                                Tmp=(ULONG)NewAddr-(ULONG)cPtr-5;//我们的CALL地址相对偏移
                                pMdl=MmCreateMdl(NULL,(PVOID)(cPtr+1),4);
                                if(pMdl)
                                {
                                        //进行HOOK操作
                                        MmBuildMdlForNonPagedPool(pMdl);
                                        pMdl->MdlFlags=pMdl->MdlFlags|MDL_MAPPED_TO_SYSTEM_VA;
                                        __try
                                        {
                                                MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
                                        }
                                        __except(EXCEPTION_EXECUTE_HANDLER)
                                        {
                                                IoFreeMdl(pMdl);
                                        }
                                        Msct=MmMapLockedPagesSpecifyCache(pMdl,KernelMode,MmWriteCombined,NULL,FALSE,0);
                                        f_oldirql=KeRaiseIrqlToDpcLevel();
                                        InterlockedExchange(Msct,Tmp);//直接替换为我们的FAKE函数地址   
                                        KeLowerIrql(f_oldirql);
                                        MmUnmapLockedPages(Msct,pMdl);
                                        MmUnlockPages(pMdl);
                                        IoFreeMdl(pMdl);
                                }
                        }
                }
        }
}
希望这篇文章发表后,我不会再看到那些改cr0的Hook代码了。。。。。。

Tesla.Angela 发表于 2015-8-30 12:28:10

文章很好!

radarhp 发表于 2015-9-8 23:35:07

呵呵 ,讲究的人做事态度毕竟不一样

louk78 发表于 2017-7-4 13:10:36

文章很好!
页: [1]
查看完整版本: TP的学习笔记:用映射MDL方式实现安全的Hook引擎