找回密码
 加入我们

QQ登录

只需一步,快速开始

搜索
查看: 4336|回复: 3

TP的学习笔记:稳定安全的Call Hook组件以及对二次跳转的学习

[复制链接]

78

主题

190

回帖

9

精华

贵宾会员

积分
15605
发表于 2015-7-28 13:13:22 | 显示全部楼层 |阅读模式
记得上次我发了个帖子叫《Call Hook KeInsertQueueApc总蓝屏》的帖子,地址http://www.m5home.com/bbs/thread-8517-1-1.html
这段时间重写了一遍黑客防线上胡文亮的文章《函数CALL地址替换实现深度钩子》,发现这里面有一些小BUG的存在。和Head Inline Hook一样,我认为Call Hook也需要有锁定页面,提升中断请求级并使用自旋锁。由于Call Hook是替换四个字节的Inline Hook,所以最好使用InterlockedExchange函数来替换值,而不是用memcpy或者*(PULONG)(Address)=0xXXXXXXXX的方式暴力修改。
本文以Hook NtOpenProcess和NtOpenThread为例,其替换的地址是PsLookupProcessByProcessId和PsLookupThreadByThreadId,主要是因为XueTr(现在的PCHunter)的自我保护是替换了ObOpenObjectByPointer,我们替换这个地址没意思。
首先先讲一下经过我修改之后的Call Hook的过程:
在这个函数里搜索每一个call指令,若call指令后面跟着的相对地址是我们要替换的地址,那么就开始HOOK操作。
Hook操作的流程是先用MDL锁定页面,然后提升中断请求级到DPC级别,在这个级别下申请并使用自旋锁,然后计算call指令后的相对地址,并用InterlockedExchange函数替换地址,替换完成后,释放自旋锁,再降低中断请求级,解锁页面,释放MDL就好了。
代码如下:
  1. BOOL CallAddrHook(PVOID StartAddr,PVOID OldAddr,PVOID NewAddr)
  2. {
  3.         PUCHAR cPtr, pOpcode;
  4.         ULONG Length,Tmp;
  5.         PMDL pMdl;
  6.         for (cPtr=StartAddr;(ULONG)cPtr<(ULONG)StartAddr+0x1000;cPtr += Length)
  7.         {
  8.                 Length = SizeOfCode(cPtr, &pOpcode);//计算当前指令长度
  9.                 if (!Length) break;
  10.                 if (Length ==5 && *cPtr==0xE8)// 当前长度5 且第一字节为E8
  11.                 {//因为CALL用的是相对偏移  所以我们还需要进行计算相对偏移
  12.                         if ( (ULONG)OldAddr-(ULONG)cPtr-5 == *(PULONG)(cPtr+1)) //判断当前是否为OldAddr的CALL相对地址
  13.                         {
  14.                                 Tmp=(ULONG)NewAddr-(ULONG)cPtr-5;//我们的CALL地址相对偏移
  15.                                 pMdl=IoAllocateMdl((PVOID)(cPtr+1),4,FALSE,FALSE,NULL);
  16.                                 if(pMdl)
  17.                                 {
  18.                                         __try
  19.                                         {
  20.                                                 MmProbeAndLockPages(pMdl,KernelMode,IoWriteAccess);
  21.                                         }
  22.                                         __except(EXCEPTION_EXECUTE_HANDLER)
  23.                                         {
  24.                                                 IoFreeMdl(pMdl);
  25.                                         }
  26.                                         //进行HOOK操作
  27.                                         f_oldirql=KeRaiseIrqlToDpcLevel();
  28.                                         KeInitializeSpinLock(&f_spinlock);
  29.                                         KeAcquireSpinLockAtDpcLevel(&f_spinlock);
  30.                                         __asm
  31.                                         {   
  32.                                                 cli
  33.                                                 mov  eax,cr0
  34.                                                 and  eax,not 10000h
  35.                                                 mov  cr0,eax
  36.                                         }
  37.                                         InterlockedExchange((PULONG)(cPtr+1),Tmp);//直接替换为我们的FAKE函数地址   
  38.                                         __asm
  39.                                         {   
  40.                                                 mov  eax,cr0
  41.                                                 or   eax,10000h
  42.                                                 mov  cr0,eax
  43.                                                 sti
  44.                                         }
  45.                                         KeReleaseSpinLockFromDpcLevel(&f_spinlock);
  46.                                         KeLowerIrql(f_oldirql);
  47.                                         IoFreeMdl(pMdl);
  48.                                         return TRUE;
  49.                                 }
  50.                         }
  51.                 }
  52.         }
  53.         return FALSE;
  54. }
复制代码

而UnHook的时候,把原本OldAddr改成NewAddr就可以了。
接下来,是演示Hook和Unhook的过程,这里用到了二次跳转:
  1. void StartHook()
  2. {
  3.         //deal with NTKERNELAPI
  4.         UNICODE_STRING uniFuncName1;
  5.         UNICODE_STRING uniFuncName2;
  6.         UNICODE_STRING uniFuncName3;
  7.         UNICODE_STRING uniFuncName4;
  8.         RtlInitUnicodeString(&uniFuncName1,L"PsLookupProcessByProcessId");
  9.         RtlInitUnicodeString(&uniFuncName2,L"PsLookupThreadByThreadId");
  10.         RtlInitUnicodeString(&uniFuncName3,L"ZwOpenProcess");
  11.         RtlInitUnicodeString(&uniFuncName4,L"ZwOpenThread");
  12.         PsLookupProcessByProcessId=MmGetSystemRoutineAddress(&uniFuncName1);
  13.         PsLookupThreadByThreadId=MmGetSystemRoutineAddress(&uniFuncName2);
  14.         ZwOpenProcess=MmGetSystemRoutineAddress(&uniFuncName3);
  15.         ZwOpenThread=MmGetSystemRoutineAddress(&uniFuncName4);
  16.         //deal with SSDT Function
  17.         RtlCopyMemory(&IndexOf_NtOpenProcess,(PVOID)((ULONG)ZwOpenProcess+1),4);
  18.         RtlCopyMemory(&IndexOf_NtOpenThread,(PVOID)((ULONG)ZwOpenThread+1),4);
  19.         pNtOpenProcess=(PVOID)GetSSDTCurAddr(IndexOf_NtOpenProcess);
  20.         pNtOpenThread=(PVOID)GetSSDTCurAddr(IndexOf_NtOpenThread);
  21.         //deal with second jump
  22.         *(PULONG)((ULONG)&SjBytes+1)=(ULONG)fake_PsLookupProcessByProcessId;
  23.         Sj1=ExAllocatePool(NonPagedPool,6);
  24.         RtlCopyMemory(Sj1,&SjBytes,6);
  25.         *(PULONG)((ULONG)&SjBytes+1)=(ULONG)fake_PsLookupThreadByThreadId;
  26.         Sj2=ExAllocatePool(NonPagedPool,6);
  27.         RtlCopyMemory(Sj2,&SjBytes,6);
  28.         //Start to Hook
  29.         CallAddrHook(pNtOpenProcess,PsLookupProcessByProcessId,Sj1);
  30.         CallAddrHook(pNtOpenThread,PsLookupThreadByThreadId,Sj2);
  31. }

  32. void StopHook()
  33. {
  34.         CallAddrHook(pNtOpenProcess,Sj1,PsLookupProcessByProcessId);
  35.         CallAddrHook(pNtOpenThread,Sj2,PsLookupThreadByThreadId);
  36.         //deal with second jump
  37.         ExFreePool(Sj1);
  38.         ExFreePool(Sj2);
  39. }
复制代码

我刚看到二次跳转这玩意的时候觉得好牛逼。。。现在觉得其实也就那么一回事,这里的二次跳转原理很简单,申请一段六字节的NonPagedPool,然后在第一个字节写上0x68,第六个字节写上0xC3,第二到第五个字节写上伪函数的绝对地址,它就是一个push xxxxxxxx ret方式的跳转!当然也可以在第一和第二个字节写上0xFF和0x25,第三到第六个字节写上绝对地址,也就是一个jmp dword ptr方式跳转。
伪函数和上次的TP学习笔记的差不多,代码如下:
  1. NTSTATUS fake_PsLookupProcessByProcessId(IN ULONG ProcessId,OUT PEPROCESS *Process)
  2. {
  3.         NTSTATUS st;
  4.         PEPROCESS txps;
  5.         st=PsLookupProcessByProcessId(ProcessId,&txps);
  6.         if(txps==ProtectedProcess)
  7.         {
  8.                 st=0xC0000022;
  9.         }
  10.         else
  11.         {
  12.                 st=PsLookupProcessByProcessId(ProcessId,Process);
  13.         }
  14.         return st;
  15. }

  16. NTSTATUS fake_PsLookupThreadByThreadId(IN ULONG ThreadId,OUT PETHREAD *Thread)
  17. {
  18.         NTSTATUS st;
  19.         PETHREAD txtd;
  20.         PEPROCESS txps;
  21.         st=PsLookupThreadByThreadId(ThreadId,&txtd);
  22.         txps=IoThreadToProcess(txtd);
  23.         if(txps==ProtectedProcess)
  24.         {
  25.                 st=0xC0000022;
  26.         }
  27.         else
  28.         {
  29.                 st=PsLookupThreadByThreadId(ThreadId,Thread);
  30.         }
  31.         return st;
  32. }
复制代码

效果很不错,在Windows XP和Windows Server 2003上均测试通过,若要在Windows 7等NT6的操作系统上使用,请Call Hook PsOpenProcess和PsOpenThread的PsLookupProcessByProcessId和PsLookupThreadByThreadId。
测试效果如图所示:
XP的测试效果:

xp测试效果

xp测试效果

2003的测试效果:

2k3测试效果

2k3测试效果

PCHunter还是不错的,连跳的第二个地址都检测到了。。。
p.s:本文所带的附件描述摘自Hovi.Delphic的帖子,由于本文是安利给大家的,不设权限,不设水晶币{:soso_e113:}

CallHook.zip

56.91 KB, 下载次数: 2469

下载代码不回帖是一种很欠扁的行为

评分

参与人数 1水晶币 +60 收起 理由
Tesla.Angela + 60 赞一个!

查看全部评分

857

主题

2632

回帖

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

积分
36130
发表于 2015-7-28 15:25:46 | 显示全部楼层
过PCH的二次跳转检测不难。第二次跳转别直接跳,弄点无意义代码类似于NOP、MOV EDI,EDI之类的再跳。

78

主题

190

回帖

9

精华

贵宾会员

积分
15605
 楼主| 发表于 2015-7-28 17:03:45 | 显示全部楼层
没有什么卵用啊,该检测出来的还是检测出来了,我都挂了100个NOP了。。。

857

主题

2632

回帖

2

精华

管理员

此生无悔入华夏,  长居日耳曼尼亚。  

积分
36130
发表于 2015-8-3 08:47:57 | 显示全部楼层
光NOP没用,要弄点垃圾代码。比如mov edi,edi之类的。
您需要登录后才可以回帖 登录 | 加入我们

本版积分规则

快速回复 返回顶部 返回列表