刺激战场
  • 1794阅读
  • 1回复

Win7 64位的SSDTHOOK(2)---64位SSDT hook的实现 [复制链接]

上一主题 下一主题
离线天道酬勤
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-11
— 本帖被 天道酬勤 从 驱动保护 移动到本区(2016-05-19) —
Hook之前要干掉PG
上篇文章知道了寻找SSDT表的方法,这篇记录一下如何实现SSDT表的Hook。
下面以Hook NtOpenProcess为例,之前我查SSDT表发现NtOpenProcess函数的标号为35,用XT等工具也能查看。
废话不多说,上代码。

  1.   
  2. //相关声明  
  3. __int64 __readmsr(int register);  
  4. unsigned __int64 __readcr0(void);  
  5. void __writecr0(  
  6.     unsigned __int64 Data  
  7.     );  
  8. void _disable(void);  
  9. void _enable(void);  
  10.   
  11. //_SYSTEM_SERVICE_TABLE结构声明  
  12. typedef struct _SYSTEM_SERVICE_TABLE{  
  13.     PVOID       ServiceTableBase;  
  14.     PVOID       ServiceCounterTableBase;  
  15.     ULONGLONG   NumberOfServices;  
  16.     PVOID       ParamTableBase;  
  17. } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;  
  18.   
  19. //_SERVICE_DESCRIPTOR_TABLE结构声明  
  20. typedef struct _SERVICE_DESCRIPTOR_TABLE{  
  21.     SYSTEM_SERVICE_TABLE ntoskrnl;  // ntoskrnl.exe (native api)  
  22.     SYSTEM_SERVICE_TABLE win32k;    // win32k.sys   (gdi/user)  
  23.     SYSTEM_SERVICE_TABLE Table3;    // not used  
  24.     SYSTEM_SERVICE_TABLE Table4;    // not used  
  25. }SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE;  
  26.   
  27. //声明要寻找进程名用的函数  
  28. NTKERNELAPI UCHAR * PsGetProcessImageFileName(PEPROCESS Process);  
  29.   
  30. //定义NTOPENPROCESS  
  31. typedef NTSTATUS (__stdcall *NTOPENPROCESS)(OUT PHANDLE  ProcessHandle,  
  32.                                     IN ACCESS_MASK  DesiredAccess,  
  33.                                     IN POBJECT_ATTRIBUTES  ObjectAttributes,  
  34.                                     IN OPTIONAL PCLIENT_ID  ClientId);  
  35. NTOPENPROCESS OldOpenProcess = NULL;  
  36. ULONG OldTpVal;  
  37.   
  38. //定义自己的NtOpenProcess  
  39. NTSTATUS __stdcall Fake_NtOpenProcess(OUT PHANDLE  ProcessHandle,  
  40.                                        IN ACCESS_MASK  DesiredAccess,  
  41.                                        IN POBJECT_ATTRIBUTES  ObjectAttributes,  
  42.                                        IN OPTIONAL PCLIENT_ID  ClientId)  
  43. {  
  44.     PEPROCESS process = NULL;  
  45.     NTSTATUS st = ObReferenceObjectByHandle(<span style="font-family: Arial, Helvetica, sans-serif;">ClientId->processid</span>  
  46. ,0,*PsProcessType,KernelMode,&process,NULL);  
  47.     DbgPrint("进入HOOK函数.\n");  
  48.     if (NT_SUCCESS(st))  
  49.     {  
  50.         if (!_stricmp((char*)PsGetProcessImageFileName(process),"CrackMe3.exe"))  
  51.         {  
  52.               
  53.             return STATUS_ACCESS_DENIED;  
  54.         }  
  55.         else  
  56.         {  
  57.             return OldOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);  
  58.         }  
  59.     }  
  60.     else  
  61.     {  
  62.         return STATUS_ACCESS_DENIED;  
  63.     }  
  64. }  
  65.   
  66. //关闭页面保护  
  67. KIRQL WPOFFx64()  
  68. {  
  69.     KIRQL irql=KeRaiseIrqlToDpcLevel();  
  70.     UINT64 cr0=__readcr0();  
  71.     cr0 &= 0xfffffffffffeffff;  
  72.     __writecr0(cr0);  
  73.     _disable();  
  74.     return irql;  
  75. }  
  76. //开启页面保护  
  77. void WPONx64(KIRQL irql)  
  78. {  
  79.     UINT64 cr0=__readcr0();  
  80.     cr0 |= 0x10000;  
  81.     _enable();  
  82.     __writecr0(cr0);  
  83.     KeLowerIrql(irql);  
  84. }  
  85.   
  86. //老外定位KeServiceDescriptorTable的方法  
  87. ULONGLONG GetKeServiceDescriptorTable64()  
  88. {  
  89.     char KiSystemServiceStart_pattern[] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";   //特征码  
  90.     ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;  
  91.     ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;  
  92.     UNICODE_STRING Symbol;  
  93.     ULONGLONG i, tbl_address, b;  
  94.     for (i = 0; i < CodeScanEnd - CodeScanStart; i++)  
  95.     {  
  96.         if (!memcmp((char*)(ULONGLONG)CodeScanStart +i, (char*)KiSystemServiceStart_pattern,13))  
  97.         {  
  98.             for (b = 0; b < 50; b++)  
  99.             {  
  100.                 tbl_address = ((ULONGLONG)CodeScanStart+i+b);  
  101.                 if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)  
  102.                     return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);  
  103.             }  
  104.         }  
  105.     }  
  106.     return 0;  
  107. }  
  108.   
  109. //根据KeServiceDescriptorTable找到SSDT基址  
  110. PULONG GetSSDTBaseAddress()  
  111. {  
  112.     PULONG addr = NULL;  
  113.     PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64();  
  114.     addr = (PULONG)(ssdt->ServiceTableBase);  
  115.     return addr;  
  116. }  
  117.   
  118. //根据标号找到SSDT表中函数的地址  
  119. ULONGLONG GetFuncAddr(ULONG id)  
  120. {  
  121.     LONG dwtmp = 0;  
  122.     ULONGLONG addr = 0;  
  123.     PULONG stb = NULL;  
  124.     stb = GetSSDTBaseAddress();  
  125.     dwtmp = stb[id];  
  126.     dwtmp = dwtmp >> 4;  
  127.     addr = (LONGLONG)dwtmp + (ULONGLONG)stb;  
  128.     DbgPrint("SSDT TABLE BASEADDRESS:%llx",addr);  
  129.     return addr;  
  130. }  
  131.   
  132. //设置函数的偏移地址,注意其中参数的处理。低四位放了参数个数减4个参数。如果参数小于等于4的时候为0  
  133. #define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1  
  134. #define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0  
  135. #define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0  
  136. ULONG GetOffsetAddress(ULONGLONG FuncAddr, CHAR paramCount)  
  137. {  
  138.     LONG dwtmp = 0,i;  
  139.     CHAR b = 0, bits[4] = {0};  
  140.     PULONG stb = NULL;  
  141.     stb = GetSSDTBaseAddress();  
  142.     dwtmp = (LONG)(FuncAddr - (ULONGLONG)stb);  
  143.     dwtmp = dwtmp << 4;  
  144.     if (paramCount>4)  
  145.     {  
  146.         paramCount = paramCount - 4;  
  147.     }  
  148.     else  
  149.     {  
  150.         paramCount = 0;  
  151.     }  
  152.     memcpy(&b,&dwtmp,1);  
  153.     for (i=0;i<4;i++)  
  154.     {  
  155.         bits = GETBIT(paramCount,i);  
  156.         if (bits)  
  157.         {  
  158.             SETBIT(b,i);  
  159.         }  
  160.         else  
  161.         {  
  162.             CLRBIT(b,i);  
  163.         }  
  164.     }  
  165.     memcpy(&dwtmp,&b,1);  
  166.     return dwtmp;  
  167. }  
  168.   
  169. //内核中用不到的方法,二次跳转用(自己的NtOpenProcess跳到KeBugCheckEx函数,然后再KeBugCheckEx函数跳到要Hook的NtOpenProcess)  
  170. VOID FuckKeBugCheckEx()  
  171. {  
  172.     KIRQL irql;  
  173.     ULONGLONG myfun;  
  174.     UCHAR jmp_code[]="\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";  
  175.     myfun=(ULONGLONG)Fake_NtOpenProcess;  
  176.     memcpy(jmp_code+6,&myfun,8);  
  177.     irql=WPOFFx64();  
  178.     memset(KeBugCheckEx,0x90,15);  
  179.     memcpy(KeBugCheckEx,jmp_code,14);  
  180.     WPONx64(irql);  
  181. }  
  182.   
  183. //Hook ssdt  
  184. VOID HookSSDT()  
  185. {  
  186.     KIRQL irql;  
  187.     LONG dwtmp = 0;  
  188.     PULONG stb = NULL;  
  189.     //1.get old address  
  190.     OldOpenProcess = (NTOPENPROCESS)GetFuncAddr(35);  
  191.     DbgPrint("Old_NtOpenProcess:%llx",(ULONGLONG)OldOpenProcess);  
  192.     //2.show new address  
  193.     stb = GetSSDTBaseAddress();  
  194.     //3.get offset value  
  195.     dwtmp = GetOffsetAddress((ULONGLONG)KeBugCheckEx,4);  
  196.     //set kebugcheckex  
  197.     FuckKeBugCheckEx();  
  198.     //4.record  old offset  value  
  199.     OldTpVal = stb[35];  
  200.     irql = WPOFFx64();  
  201.     stb[35] = GetOffsetAddress((ULONGLONG)KeBugCheckEx,2);  
  202.     WPONx64(irql);  
  203.     DbgPrint("KeBugCheckEx:%llx",(ULONGLONG)KeBugCheckEx);  
  204.     DbgPrint("New_NtOpenProcess:%llx",GetFuncAddr(35));  
  205. }  
  206.   
  207. //UN hook  
  208. VOID UnhookSSDT()  
  209. {  
  210.     KIRQL irql;  
  211.     PULONG stb=NULL;  
  212.     stb = GetSSDTBaseAddress();  
  213.     //老函数的地址复制回来  
  214.     irql=WPOFFx64();  
  215.     stb[35]=OldTpVal;  
  216.     WPONx64(irql);  
  217. }  



相关解释:
1.为什么要二次跳转?
WIN64内核里的每个驱动都不在同一个4GB里,4字节的整数只能表示4GB的范围,所以不管怎么修改这个4字节都不会跳到你的代理函数,因为你的驱动不可能跟NTOSKRNL在同一个4GB里面。
2.参数的处理:
函数地址的低四位存放了函数参数个数减4的数字,如果参数为5,那么低四位的数字为1,如果参数个数小于等于4个,低四位的数位0,可以再WINDBG里面查看到。

离线v2680267313

只看该作者 沙发  发表于: 2016-04-30
用户被禁言,该主题自动屏蔽!
快速回复
限100 字节
如果您提交过一次失败了,可以用”恢复数据”来恢复帖子内容
 
上一个 下一个