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代码了。。。。。。 文章很好! 呵呵 ,讲究的人做事态度毕竟不一样 文章很好!
页:
[1]