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

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里面查看到。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值