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

    xiaoxiao2022-06-29  43

    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   首先感谢老大(Tesla.Angela)对我的帮助      //相关声明   __int64 __readmsr(int register);   unsigned __int64 __readcr0(void);   void __writecr0(        unsigned __int64 Data        );   void _disable(void);   void _enable(void);      //_SYSTEM_SERVICE_TABLE结构声明   typedef struct _SYSTEM_SERVICE_TABLE{       PVOID       ServiceTableBase;        PVOID       ServiceCounterTableBase;        ULONGLONG   NumberOfServices;        PVOID       ParamTableBase;    } SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE;      //_SERVICE_DESCRIPTOR_TABLE结构声明   typedef struct _SERVICE_DESCRIPTOR_TABLE{       SYSTEM_SERVICE_TABLE ntoskrnl;  // ntoskrnl.exe (native api)       SYSTEM_SERVICE_TABLE win32k;    // win32k.sys   (gdi/user)       SYSTEM_SERVICE_TABLE Table3;    // not used       SYSTEM_SERVICE_TABLE Table4;    // not used   }SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE;      //声明要寻找进程名用的函数   NTKERNELAPI UCHAR * PsGetProcessImageFileName(PEPROCESS Process);      //定义NTOPENPROCESS   typedef NTSTATUS (__stdcall *NTOPENPROCESS)(OUT PHANDLE  ProcessHandle,                                       IN ACCESS_MASK  DesiredAccess,                                       IN POBJECT_ATTRIBUTES  ObjectAttributes,                                        IN OPTIONAL PCLIENT_ID  ClientId);   NTOPENPROCESS OldOpenProcess = NULL;   ULONG OldTpVal;      //定义自己的NtOpenProcess   NTSTATUS __stdcall Fake_NtOpenProcess(OUT PHANDLE  ProcessHandle,                                          IN ACCESS_MASK  DesiredAccess,                                          IN POBJECT_ATTRIBUTES  ObjectAttributes,                                           IN OPTIONAL PCLIENT_ID  ClientId)   {       PEPROCESS process = NULL;       NTSTATUS st = ObReferenceObjectByHandle(<span style="font-family: Arial, Helvetica, sans-serif;">ClientId->processid</span>   ,0,*PsProcessType,KernelMode,&process,NULL);       DbgPrint("进入HOOK函数.\n");       if (NT_SUCCESS(st))       {           if (!_stricmp((char*)PsGetProcessImageFileName(process),"CrackMe3.exe"))           {                              return STATUS_ACCESS_DENIED;           }           else           {               return OldOpenProcess(ProcessHandle,DesiredAccess,ObjectAttributes,ClientId);           }        }       else       {           return STATUS_ACCESS_DENIED;       }   }      //关闭页面保护   KIRQL WPOFFx64()   {       KIRQL irql=KeRaiseIrqlToDpcLevel();       UINT64 cr0=__readcr0();       cr0 &= 0xfffffffffffeffff;       __writecr0(cr0);       _disable();       return irql;   }   //开启页面保护   void WPONx64(KIRQL irql)   {       UINT64 cr0=__readcr0();       cr0 |= 0x10000;       _enable();       __writecr0(cr0);       KeLowerIrql(irql);   }      //老外定位KeServiceDescriptorTable的方法   ULONGLONG GetKeServiceDescriptorTable64()    {       char KiSystemServiceStart_pattern[] = "\x8B\xF8\xC1\xEF\x07\x83\xE7\x20\x25\xFF\x0F\x00\x00";   //特征码       ULONGLONG CodeScanStart = (ULONGLONG)&_strnicmp;       ULONGLONG CodeScanEnd = (ULONGLONG)&KdDebuggerNotPresent;       UNICODE_STRING Symbol;       ULONGLONG i, tbl_address, b;       for (i = 0; i < CodeScanEnd - CodeScanStart; i++)       {           if (!memcmp((char*)(ULONGLONG)CodeScanStart +i, (char*)KiSystemServiceStart_pattern,13))           {                for (b = 0; b < 50; b++)               {                   tbl_address = ((ULONGLONG)CodeScanStart+i+b);                   if (*(USHORT*) ((ULONGLONG)tbl_address ) == (USHORT)0x8d4c)                       return ((LONGLONG)tbl_address +7) + *(LONG*)(tbl_address +3);               }           }       }       return 0;   }      //根据KeServiceDescriptorTable找到SSDT基址   PULONG GetSSDTBaseAddress()   {       PULONG addr = NULL;       PSYSTEM_SERVICE_TABLE ssdt = (PSYSTEM_SERVICE_TABLE)GetKeServiceDescriptorTable64();       addr = (PULONG)(ssdt->ServiceTableBase);       return addr;   }      //根据标号找到SSDT表中函数的地址   ULONGLONG GetFuncAddr(ULONG id)   {       LONG dwtmp = 0;       ULONGLONG addr = 0;       PULONG stb = NULL;       stb = GetSSDTBaseAddress();       dwtmp = stb[id];       dwtmp = dwtmp >> 4;       addr = (LONGLONG)dwtmp + (ULONGLONG)stb;       DbgPrint("SSDT TABLE BASEADDRESS:%llx",addr);       return addr;   }      //设置函数的偏移地址,注意其中参数的处理。低四位放了参数个数减4个参数。如果参数小于等于4的时候为0   #define SETBIT(x,y) x|=(1<<y) //将X的第Y位置1   #define CLRBIT(x,y) x&=~(1<<y) //将X的第Y位清0   #define GETBIT(x,y) (x & (1 << y)) //取X的第Y位,返回0或非0   ULONG GetOffsetAddress(ULONGLONG FuncAddr, CHAR paramCount)   {       LONG dwtmp = 0,i;       CHAR b = 0, bits[4] = {0};       PULONG stb = NULL;       stb = GetSSDTBaseAddress();       dwtmp = (LONG)(FuncAddr - (ULONGLONG)stb);       dwtmp = dwtmp << 4;       if (paramCount>4)       {           paramCount = paramCount - 4;       }       else       {           paramCount = 0;       }       memcpy(&b,&dwtmp,1);       for (i=0;i<4;i++)       {           bits[i] = GETBIT(paramCount,i);           if (bits[i])           {               SETBIT(b,i);           }           else           {               CLRBIT(b,i);           }       }       memcpy(&dwtmp,&b,1);       return dwtmp;   }      //内核中用不到的方法,二次跳转用(自己的NtOpenProcess跳到KeBugCheckEx函数,然后再KeBugCheckEx函数跳到要Hook的NtOpenProcess)   VOID FuckKeBugCheckEx()   {       KIRQL irql;       ULONGLONG myfun;       UCHAR jmp_code[]="\xFF\x25\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";       myfun=(ULONGLONG)Fake_NtOpenProcess;       memcpy(jmp_code+6,&myfun,8);       irql=WPOFFx64();       memset(KeBugCheckEx,0x90,15);       memcpy(KeBugCheckEx,jmp_code,14);       WPONx64(irql);   }      //Hook ssdt   VOID HookSSDT()   {       KIRQL irql;       LONG dwtmp = 0;       PULONG stb = NULL;       //1.get old address       OldOpenProcess = (NTOPENPROCESS)GetFuncAddr(35);       DbgPrint("Old_NtOpenProcess:%llx",(ULONGLONG)OldOpenProcess);       //2.show new address       stb = GetSSDTBaseAddress();       //3.get offset value       dwtmp = GetOffsetAddress((ULONGLONG)KeBugCheckEx,4);       //set kebugcheckex       FuckKeBugCheckEx();       //4.record  old offset  value       OldTpVal = stb[35];       irql = WPOFFx64();       stb[35] = GetOffsetAddress((ULONGLONG)KeBugCheckEx,2);       WPONx64(irql);       DbgPrint("KeBugCheckEx:%llx",(ULONGLONG)KeBugCheckEx);       DbgPrint("New_NtOpenProcess:%llx",GetFuncAddr(35));   }      //UN hook   VOID UnhookSSDT()   {       KIRQL irql;       PULONG stb=NULL;       stb = GetSSDTBaseAddress();       //老函数的地址复制回来       irql=WPOFFx64();       stb[35]=OldTpVal;       WPONx64(irql);   }  

    相关解释:

    1.为什么要二次跳转?

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

    2.参数的处理:

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

    转载请注明原文地址: https://ju.6miu.com/read-1125006.html

    最新回复(0)