Win7 64位的SSDTHOOK(2)---64位SSDT hook的实现

来源:互联网 发布:知乎日报吐槽合集 编辑:程序博客网 时间:2024/06/04 23:29

Hook之前要干掉PG:http://www.m5home.com/bbs/thread-5893-1-1.html

上篇文章知道了寻找SSDT表的方法,这篇记录一下如何实现SSDT表的Hook。

下面以Hook NtOpenProcess为例,之前我查SSDT表发现NtOpenProcess函数的标号为35,用XT等工具也能查看。

废话不多说,上代码。

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


相关解释:

1.为什么要二次跳转?

WIN64内核里的每个驱动都不在同一个4GB里,4字节的整数只能表示4GB的范围,所以不管怎么修改这个4字节都不会跳到你的代理函数,因为你的驱动不可能跟NTOSKRNL在同一个4GB里面。

2.参数的处理:

函数地址的低四位存放了函数参数个数减4的数字,如果参数为5,那么低四位的数字为1,如果参数个数小于等于4个,低四位的数位0,可以再WINDBG里面查看到。

阅读全文
0 0
原创粉丝点击