免費論壇 繁體 | 簡體
Sclub交友聊天~加入聊天室當版主
分享
返回列表 发帖

对idt hook的处理

现在有一些驱动保护对idt中断描述符表进行了一些处理,导致我们附加游戏的时候会直接重启或者蓝屏或者游戏退出的情况。那么本篇的内容实际是对教程中的知识点进行实践的内容,看本篇前希望你们明白第二十课的内容,不然会觉得莫名其妙。

好了,我们直接步入主题。

驱动保护既然hook了idt,自然会检测它hook的位置是否恢复,和对它自身函数做crc等等。那么我们直接恢复hook是没用的,就要另想方法了。
一般比较通用的方法有两种,对段基址进行处理和用vt,本篇就是用段基址处理hook后的idt。
在第二十课的时候我说过,windows中的虚拟地址是这样的:
段基址 + 段内偏移 = 虚拟地址
一般情况下段基址是0,所以段内偏移就相当于虚拟地址了。idt中所有中断函数的段基址也是默认为零的,而驱动保护就是对段内偏移进行修改,而达到hook的作用。由于段基址是零,所以修改段内偏移就等于修改了虚拟地址。我们要想办法绕过的话,就要对段基址动手脚,因为驱动保护基本没对段基址进行检测的,如果有的话,那这种办法就失灵了。但是我一般看到的都没有.......
怎么动手脚呢,我们可以这样处理:当段内偏移改变的时候,我们让段基址跟着改变。比如原始的段基址是0,段内偏移是0x1000,虚拟地址是0x1000,那就是:
0 + 0x1000 = 0x1000;
现在驱动保护修改了段内偏移,假如变成了0x500。那么:
0 + 0x500 = 0x500;
这样就实现了hook的目的。我们要绕过,就要这样做:
? + 0x500 = 0x1000;
就是段基址为多少的时候,使驱动保护提供的段内偏移和段基址相加还是等于以前的虚拟地址。
那么狠明显,0x1000 - 0x500 = 0x500;
段基址赋值为0x500就可以了。思路很简单的,但是由于概念本身就比较多,所以很多人被卡主了。
那好,现在问题解决了,只要发现改变了段内偏移,那我们就跟着改变段基址,使:
新段基址 + 新段内偏移 = 原始的虚拟地址
只要做到这样,就可以绕过游戏的hook。
但是另外一个问题来了,当驱动保护修改了段内偏移的时候,我们如何发现段内偏移给修改了呢?
一般最简单的方法是设置个定时器,要设置的尽量小点,不断的检测是否驱动保护修改了段内偏移,是的话就马上处理段基址。
还有其他的办法就是hook比较频繁被调用的函数,然后在过滤函数中对段内偏移做判断工作。系统中频繁被调用的函数有很多很多,大家可以根据需要去hook。
因为过驱动保护的时候我们为了简单,都会使用重载内核过掉内核hook,所以都会对kifastcallentry函数做手脚。而这个函数调用是非常非常频繁的,比定时器的最小周期还要频繁。如果你hook了kifastcallentry函数,又不想使代码量变多,你可以考虑在这个函数里面判断是够段内偏移被修改了。
我就是在kifastcallentry函数里面判断的,实际最好用定时器(思路清晰)!
下面我直接上代码把,实际这些代码都是写过的,我改动的地方不超过20行!由于我只为了表达思路,所以我简单的把以前的拼凑了一下,代码难看希望大家多多谅解。
  1. #ifndef _PUBLIC_H_
  2. #define _PUBLIC_H_

  3. #include "ntddk.h"

  4. #define WORD    USHORT
  5. #define DWORD   ULONG

  6. #define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) \
  7.     | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))

  8. typedef struct _IDTR{
  9.     USHORT   IDT_limit;
  10.     USHORT   IDT_LOWbase;
  11.     USHORT   IDT_HIGbase;
  12. }IDTR,*PIDTR;

  13. //idt结构定义
  14. typedef struct _IDTENTRY
  15. {
  16.     unsigned short LowOffset;
  17.     unsigned short selector;
  18.     unsigned char retention:5;
  19.     unsigned char zero1:3;
  20.     unsigned char gate_type:1;
  21.     unsigned char zero2:1;
  22.     unsigned char interrupt_gate_size:1;
  23.     unsigned char zero3:1;
  24.     unsigned char zero4:1;
  25.     unsigned char DPL:2;
  26.     unsigned char P:1;
  27.     unsigned short HiOffset;
  28. } IDTENTRY,*PIDTENTRY;

  29. //gdt结构定义
  30. typedef struct _KGDTENTRY {
  31.     USHORT  LimitLow;
  32.     USHORT  BaseLow;
  33.     union {
  34.         struct {
  35.             UCHAR   BaseMid;
  36.             UCHAR   Flags1;     // Declare as bytes to avoid alignment
  37.             UCHAR   Flags2;     // Problems.
  38.             UCHAR   BaseHi;
  39.         } Bytes;
  40.         struct {
  41.             ULONG   BaseMid : 8;
  42.             ULONG   Type : 5;
  43.             ULONG   Dpl : 2;
  44.             ULONG   Pres : 1;

  45.             ULONG   LimitHi : 4;
  46.             ULONG   Sys : 1;
  47.             ULONG   Reserved_0 : 1;
  48.             ULONG   Default_Big : 1;
  49.             ULONG   Granularity : 1;
  50.             ULONG   BaseHi : 8;
  51.         } Bits;
  52.     } HighWord;
  53. } KGDTENTRY, *PKGDTENTRY;

  54. //系统服务描述表定义
  55. #pragma pack(1)
  56. typedef struct ServiceDescriptorEntry {
  57.     unsigned int *ServiceTableBase;
  58.     unsigned int *ServiceCounterTableBase; //仅适用于checked build版本
  59.     unsigned int NumberOfServices;
  60.     unsigned char *ParamTableBase;
  61. } ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
  62. #pragma pack()

  63. __declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;

  64. //global
  65. USHORT g_FilterJmp[3];
  66. ULONG  g_uOrigInterruptFunc;

  67. BOOLEAN g_bHookInterrupt;
  68. //global
  69. ULONG   g_ntcreatefile;
  70. ULONG   g_fastcall_hookpointer;
  71. ULONG   g_goto_origfunc;

  72. //等会儿就用它做栈回溯吧
  73. typedef NTSTATUS
  74. (*NTCREATEFILE) (
  75.     __out PHANDLE FileHandle,
  76.     __in ACCESS_MASK DesiredAccess,
  77.     __in POBJECT_ATTRIBUTES ObjectAttributes,
  78.     __out PIO_STATUS_BLOCK IoStatusBlock,
  79.     __in_opt PLARGE_INTEGER AllocationSize,
  80.     __in ULONG FileAttributes,
  81.     __in ULONG ShareAccess,
  82.     __in ULONG CreateDisposition,
  83.     __in ULONG CreateOptions,
  84.     __in_bcount_opt(EaLength) PVOID EaBuffer,
  85.     __in ULONG EaLength
  86.     );

  87. //各种函数乱声明下:D
  88. void PageProtectOn();
  89. void PageProtectOff();
  90. ULONG SearchHookPointer(ULONG StartAddress);
  91. void __stdcall FilterInterruptFunc3();
  92. ULONG   GetInterruptFuncAddress(ULONG InterruptIndex);

  93. VOID HookInterruptFunc(ULONG InterruptIndex,ULONG NewInterruptFunc);

  94. void NewInterrupt3();

  95. #endif
复制代码
以上是头文件,下面才是重点:
  1. #include "public.h"

  2. void PageProtectOn()
  3. {
  4.     __asm{//恢复内存保护  
  5.         mov  eax,cr0
  6.         or   eax,10000h
  7.         mov  cr0,eax
  8.         sti
  9.     }
  10. }

  11. void PageProtectOff()
  12. {
  13.     __asm{//去掉内存保护
  14.         cli
  15.         mov  eax,cr0
  16.         and  eax,not 10000h
  17.         mov  cr0,eax
  18.     }
  19. }

  20. ULONG SearchHookPointer(ULONG StartAddress)
  21. {
  22.     ULONG   u_index;

  23.     UCHAR   *p = (UCHAR*)StartAddress;

  24.     for (u_index = 0;u_index < 200;u_index++)
  25.     {
  26.         if (*p==0x2B&&
  27.             *(p+1)==0xE1&&
  28.             *(p+2)==0xC1&&
  29.             *(p+3)==0xE9&&
  30.             *(p+4)==0x02)
  31.         {
  32.             return (ULONG)p;
  33.         }

  34.         p--;
  35.     }

  36.     return 0;
  37. }

  38. //过滤kifastcallentry函数
  39. void FilterKiFastCallEntry(ULONG ServiceTableBase,ULONG FuncIndex)
  40. {
  41. //这里就是不断的检测
  42.     HookInterruptFunc(3,(ULONG)NewInterrupt3);
  43. }

  44. __declspec(naked)
  45. void NewKiFastCallEntry()
  46. {
  47.     __asm{
  48.         pushad
  49.         pushfd
  50.          
  51.         push    eax
  52.         push    edi
  53.         call    FilterKiFastCallEntry

  54.         popfd
  55.         popad

  56.         sub     esp,ecx
  57.         shr     ecx,2
  58.         jmp     g_goto_origfunc
  59.     }
  60. }

  61. void UnHookKiFastCallEntry()
  62. {
  63.     UCHAR   str_origfuncode[5] = {0x2B,0xE1,0xC1,0xE9,0x02};

  64.     if (g_fastcall_hookpointer==0)
  65.     {   return; }

  66.     PageProtectOff();
  67.     RtlCopyMemory((PVOID)g_fastcall_hookpointer,str_origfuncode,5);
  68.     PageProtectOn();
  69. }

  70. void HookKiFastCallEntry(ULONG HookPointer)
  71. {
  72.     ULONG   u_temp;
  73.     UCHAR   str_jmp_code[5];

  74.     str_jmp_code[0] = 0xE9;

  75.     u_temp = (ULONG)NewKiFastCallEntry - HookPointer - 5;
  76.     *(ULONG*)&str_jmp_code[1] = u_temp;

  77.     PageProtectOff();

  78.     RtlCopyMemory((PVOID)HookPointer,str_jmp_code,5);

  79.     PageProtectOn();

  80. }

  81. NTSTATUS NewNtCreateFile (
  82.     __out PHANDLE FileHandle,
  83.     __in ACCESS_MASK DesiredAccess,
  84.     __in POBJECT_ATTRIBUTES ObjectAttributes,
  85.     __out PIO_STATUS_BLOCK IoStatusBlock,
  86.     __in_opt PLARGE_INTEGER AllocationSize,
  87.     __in ULONG FileAttributes,
  88.     __in ULONG ShareAccess,
  89.     __in ULONG CreateDisposition,
  90.     __in ULONG CreateOptions,
  91.     __in_bcount_opt(EaLength) PVOID EaBuffer,
  92.     __in ULONG EaLength
  93.     )
  94. {
  95.     ULONG   u_call_retaddr;

  96.     __asm{
  97.         pushad
  98.         mov     eax,[ebp+0x4]
  99.         mov     u_call_retaddr,eax
  100.         popad
  101.     }

  102.     g_fastcall_hookpointer = SearchHookPointer(u_call_retaddr);
  103.     if (g_fastcall_hookpointer==0)
  104.     {
  105.         KdPrint(("search failed."));
  106.     }else{
  107.         KdPrint(("search success."));
  108.     }

  109.     g_goto_origfunc = g_fastcall_hookpointer + 5;
  110.     HookKiFastCallEntry(g_fastcall_hookpointer);

  111.     PageProtectOff();
  112.     KeServiceDescriptorTable.ServiceTableBase[66] = (unsigned int)g_ntcreatefile;
  113.     PageProtectOn();

  114.     return ((NTCREATEFILE)g_ntcreatefile)(
  115.         FileHandle,\
  116.         DesiredAccess,\
  117.         ObjectAttributes,\
  118.         IoStatusBlock,\
  119.         AllocationSize,\
  120.         FileAttributes,\
  121.         ShareAccess,\
  122.         CreateDisposition,\
  123.         CreateOptions,\
  124.         EaBuffer,\
  125.         EaLength);
  126. }


  127. void SearchKiFastCallEntry()
  128. {

  129.     g_ntcreatefile = KeServiceDescriptorTable.ServiceTableBase[66];
  130.     PageProtectOff();
  131.     KeServiceDescriptorTable.ServiceTableBase[66] = (unsigned int)NewNtCreateFile;
  132.     PageProtectOn();
  133. }

  134. //////////////////////////////////////////////////////////////////////////
  135. //////////////////////////////////////////////////////////////////////////

  136. void __stdcall FilterInterrupt()
  137. {
  138.     KdPrint(("%s",(char*)PsGetCurrentProcess()+0x16c));
  139. }

  140. __declspec(naked)
  141. void NewInterrupt3OfOrigBase()
  142. {
  143.     __asm{
  144.         pushad
  145.         pushfd

  146.         push    fs
  147.         push    0x30
  148.         pop     fs

  149.         call    FilterInterrupt

  150.         pop     fs

  151.         popfd
  152.         popad

  153.         jmp     g_uOrigInterruptFunc
  154.     }
  155. }

  156. __declspec(naked)
  157. void NewInterrupt3()
  158. {
  159.     __asm{
  160.         jmp fword ptr[g_FilterJmp]
  161.     }
  162. }

  163. //这个函数功能是输入一个下标,返回该下标对应的中断地址
  164. ULONG   GetInterruptFuncAddress(ULONG InterruptIndex)
  165. {
  166.     IDTR        idtr;
  167.     IDTENTRY    *pIdtEntry;

  168.     __asm   SIDT    idtr;

  169.     pIdtEntry = (IDTENTRY *)MAKELONG(idtr.IDT_LOWbase,idtr.IDT_HIGbase);

  170.     return MAKELONG(pIdtEntry[InterruptIndex].LowOffset,pIdtEntry[InterruptIndex].HiOffset);
  171. }

  172. ULONG GetNewBase(ULONG NewInterruptFunc,ULONG OrigInterruptOffset)
  173. {
  174.     return (NewInterruptFunc - OrigInterruptOffset);
  175. }

  176. //这个函数负责具体的修改工作,实际这些函数我20课中都一个个写过一遍的
  177. VOID SetInterrupt(ULONG InterruptIndex,ULONG uNewBase,BOOLEAN bIsNew)
  178. {
  179.     ULONG           u_fnKeSetTimeIncrement;
  180.     UNICODE_STRING  usFuncName;
  181.     ULONG           u_index;
  182.     ULONG           *u_KiProcessorBlock;

  183.     IDTENTRY        *pIdtEntry;
  184.     PKGDTENTRY      pGdt;

  185.     RtlInitUnicodeString(&usFuncName,L"KeSetTimeIncrement");

  186.     u_fnKeSetTimeIncrement = (ULONG)MmGetSystemRoutineAddress(&usFuncName);
  187.     if (!MmIsAddressValid((PVOID)u_fnKeSetTimeIncrement))
  188.     {
  189.         return;
  190.     }

  191.     u_KiProcessorBlock = *(ULONG**)(u_fnKeSetTimeIncrement + 44);

  192.     u_index = 0;
  193.     while (u_KiProcessorBlock[u_index])
  194.     {
  195.         pIdtEntry = *(IDTENTRY**)(u_KiProcessorBlock[u_index] - 0xE8);
  196.         pGdt = *(PKGDTENTRY*)(u_KiProcessorBlock[u_index] - 0xE4);
  197.          
  198.         KdPrint(("%X-----%X",pIdtEntry,pGdt));

  199.         PageProtectOff();

  200.         if (bIsNew)
  201.         {
  202.             pIdtEntry[InterruptIndex].selector = 0xA8;
  203.             RtlCopyMemory(&pGdt[21],&pGdt[1],sizeof(KGDTENTRY));
  204.             pGdt[21].BaseLow = (USHORT)(uNewBase&0xffff);
  205.             pGdt[21].HighWord.Bytes.BaseMid = (UCHAR)((uNewBase>>16)&0xff);
  206.             pGdt[21].HighWord.Bytes.BaseHi = (UCHAR)(uNewBase>>24);
  207.         }else{
  208.             pIdtEntry[InterruptIndex].selector = 0x8;
  209.             memset(&pGdt[21],0,sizeof(KGDTENTRY));
  210.         }

  211.         PageProtectOn();

  212.         u_index++;
  213.     }
  214. }

  215. //注意这个函数,我是判断段内偏移是否修改完成或者恢复完成。就是说防止修改了一半或者恢复了一半我们就进行干涉,那样会马上悲剧的......所以这个函数很重要
  216. BOOLEAN QueryInterruptIsModify(ULONG uInterruptFunc,BOOLEAN bIsOrig)
  217. {
  218.     ULONG           u_fnKeSetTimeIncrement;
  219.     UNICODE_STRING  usFuncName;
  220.     ULONG           u_index;
  221.     ULONG           *u_KiProcessorBlock;

  222.     IDTENTRY        *pIdtEntry;
  223.     PKGDTENTRY      pGdt;

  224.     RtlInitUnicodeString(&usFuncName,L"KeSetTimeIncrement");

  225.     u_fnKeSetTimeIncrement = (ULONG)MmGetSystemRoutineAddress(&usFuncName);
  226.     if (!MmIsAddressValid((PVOID)u_fnKeSetTimeIncrement))
  227.     {
  228.         return FALSE;
  229.     }

  230.     u_KiProcessorBlock = *(ULONG**)(u_fnKeSetTimeIncrement + 44);

  231.     u_index = 0;
  232.     while (u_KiProcessorBlock[u_index])
  233.     {
  234.         pIdtEntry = *(IDTENTRY**)(u_KiProcessorBlock[u_index] - 0xE8);
  235.         pGdt = *(PKGDTENTRY*)(u_KiProcessorBlock[u_index] - 0xE4);
  236.          
  237.         if (bIsOrig)
  238.         {
  239.             if ((uInterruptFunc>>16)!=pIdtEntry[3].HiOffset||
  240.                 (uInterruptFunc & 0xffff)!=pIdtEntry[3].LowOffset)
  241.             {
  242.                 return FALSE;
  243.             }
  244.         }else{
  245.             if ((uInterruptFunc>>16)==pIdtEntry[3].HiOffset||
  246.                 (uInterruptFunc & 0xffff)==pIdtEntry[3].LowOffset)
  247.             {
  248.                 return FALSE;
  249.             }
  250.         }
  251.         u_index++;
  252.     }

  253.     return TRUE;
  254. }

  255. //这个函数先进行查询是否被修改,段内偏移修改了的话我们就跟着修改,这里我们使用一个变量g_bHookInterrupt来标记我们是否修改过段基址
  256. VOID HookInterruptFunc(ULONG InterruptIndex,ULONG NewInterruptFunc)
  257. {
  258.     ULONG   uNewBase;
  259.     ULONG   uInterrupFunc;

  260.     uInterrupFunc = GetInterruptFuncAddress(InterruptIndex);

  261.     if (QueryInterruptIsModify(g_uOrigInterruptFunc,TRUE)&&
  262.         g_bHookInterrupt==TRUE)
  263.     {
  264.         SetInterrupt(InterruptIndex,0,FALSE);
  265.         g_bHookInterrupt = FALSE;
  266.     }

  267.     if (QueryInterruptIsModify(g_uOrigInterruptFunc,FALSE)&&
  268.         g_bHookInterrupt==FALSE)
  269.     {
  270.         KdPrint(("hook!"));
  271.         uNewBase = NewInterruptFunc - uInterrupFunc;

  272.         *(ULONG*)g_FilterJmp = (ULONG)NewInterrupt3OfOrigBase;
  273.         g_FilterJmp[2] = 0x8;

  274.         SetInterrupt(InterruptIndex,uNewBase,TRUE);
  275.         g_bHookInterrupt = TRUE;
  276.     }
  277. }

  278. void UnHookInterruptFunc(ULONG InterruptIndex)
  279. {
  280.     SetInterrupt(InterruptIndex,0,FALSE);
  281. }

  282. VOID MyUnload(PDRIVER_OBJECT    pDriverObject)
  283. {
  284.     UnHookInterruptFunc(3);
  285.     UnHookKiFastCallEntry();
  286. }

  287. NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING Reg_Path)
  288. {
  289.     g_bHookInterrupt = FALSE;

  290.     g_uOrigInterruptFunc = GetInterruptFuncAddress(3);
  291.      
  292.     SearchKiFastCallEntry();
  293.      
  294.     SetInterrupt(3,0,FALSE);

  295.     pDriverObject->DriverUnload = MyUnload;
  296.     return STATUS_SUCCESS;
  297. }

  298. //////////////////////////////////////////////////////////////////////////
  299. //////////////////////////////////////////////////////////////////////////
复制代码

返回列表