Win7 64位的SSDTHOOK(2)---64位SSDT hook的实现
http://blog.csdn.net/zfdyq0/article/details/26753797
Hook之前要干掉PG:http://www.m5home.com/bbs/thread-5893-1-1.html
上篇文章知道了寻找SSDT表的方法,这篇记录一下如何实现SSDT表的Hook。
下面以Hook NtOpenProcess为例,之前我查SSDT表发现NtOpenProcess函数的标号为35,用XT等工具也能查看。
废话不多说,上代码。
- 首先感谢老大(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里面查看到。