x64驱动基础教程 17

栏目: 数据库 · SQL Server · 发布时间: 7年前

内容简介:HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似, 但在代码实现上更麻烦了, 下面一步步说。一、获得 KeServiceDescriptorTableShadow 的地址这个跟获得 KeServiceDescriptorTable 差不多,唯一不同就是特征码:lkd> uf KiSystemCall64

HOOK 和 UNHOOK SHADOW SSDT 跟之前的 HOOK/UNHOOK SSDT 类似, 但在代码实现上更麻烦了, 下面一步步说。

一、获得 KeServiceDescriptorTableShadow 的地址这个跟获得 KeServiceDescriptorTable 差不多,唯一不同就是特征码:

lkd> uf KiSystemCall64

Flow analysis was incomplete, some code may be missing

nt!KiSystemCall64:

fffff800`03cc7ec0 0f01f8 swapgs

fffff800`03cc7ec3 654889242510000000 mov qword ptr gs:[10h],rsp

【省略大量无关代码】

nt!KiSystemServiceRepeat:

fffff800`03cc7ff2 4c8d1547782300 lea r10,[nt!KeServiceDescriptorTable

(fffff800`03eff840)]

fffff800`03cc7ff9 4c8d1d80782300 lea r11,[nt!KeServiceDescriptorTableShadow

(fffff800`03eff880)]

【省略大量无关代码】

特征码就是就是我染成蓝色的那三个字节。先用__readmsr(0xC0000082)就是得到 KiSystemCall64 的地址,然后往下搜索 0x500 字节,当找到特征码后就能计算出 KeServiceDescriptorTableShadow 的地址了:

ULONGLONG GetKeServiceDescriptorTableShadow64()
{
	PUCHAR StartSearchAddress = (PUCHAR)__readmsr(0xC0000082);
	PUCHAR EndSearchAddress = StartSearchAddress + 0x500;
	PUCHAR i = NULL;
	UCHAR b1=0,b2=0,b3=0;
	ULONG templong=0;
	ULONGLONG addr=0;
	for(i=StartSearchAddress;i<EndSearchAddress;i++)
	{
		if( MmIsAddressValid(i) && MmIsAddressValid(i+1) && MmIsAddressValid(i+2) )
		{
			b1=*i;
			b2=*(i+1);
			b3=*(i+2);
			if( b1==0x4c && b2==0x8d && b3==0x1d ) //4c8d1d
			{
				memcpy(&templong,i+3,4);
				addr = (ULONGLONG)templong + (ULONGLONG)i + 7;
				return addr;
			}
		}
	}
	return 0;
}

二、根据 INDEX 获得 SSSDT 函数在内核里的地址原理跟获得 SSDT 函数在在内核里的地址差不多,先获得 W32pServiceTable的地址,然后再获得每个函数的偏移地址,在把偏移地址与 W32pServiceTable相加。为什么下面的计算公式是 W32pServiceTable + 4 * (index-0x1000)呢?其实这只是个理解上的问题。 SSDT 函数的起始 INDEX 是 0x0, SSSDT 函数的起始 INDEX 是 0x1000,但函数地址在 W32pServiceTable 是从基址开始记录的(假设 W32pServiceTable 的地址是 0xfffff800~80000000,第 0 个函数的地址就记录在 0xfffff800~80000000,第 1 个函数的地址就记录在0xfffff800~80000004,第 2 个函数的地址就记录在 0xfffff800~80000008,以此类推)。注意:这步必须在 GUI 线程里执行!

ULONGLONG GetSSSDTFuncCurAddr64(ULONG64 Index)
{
	ULONGLONG W32pServiceTable=0, qwTemp=0;
	LONG dwTemp=0;
	PSYSTEM_SERVICE_TABLE pWin32k;
	pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow +
	sizeof(SYSTEM_SERVICE_TABLE));
	W32pServiceTable=(ULONGLONG)(pWin32k->ServiceTableBase);
	qwTemp = W32pServiceTable + 4 * (Index-0x1000);
	dwTemp = *(PLONG)qwTemp;
	dwTemp = dwTemp >> 4;
	qwTemp = W32pServiceTable + (LONG64)dwTemp;
	return qwTemp;
}

三、修改 SSSDT 里的地址还是跟 SSDT 类似,修改 W32pServiceTable+4*index 地址的 DWORD 值(偏移地址值)。注意:这步必须在 GUI 线程里执行!

VOID ModifySSSDT(ULONG64 Index, ULONG64 Address)
{
	ULONGLONG W32pServiceTable=0, qwTemp=0;
	LONG dwTemp=0;
	PSYSTEM_SERVICE_TABLE pWin32k;
	KIRQL irql;
	pWin32k = (PSYSTEM_SERVICE_TABLE)((ULONG64)KeServiceDescriptorTableShadow +
	sizeof(SYSTEM_SERVICE_TABLE)); //4*8
	W32pServiceTable=(ULONGLONG)(pWin32k->ServiceTableBase);
	qwTemp = W32pServiceTable + 4 * (Index-0x1000);
	dwTemp = (LONG)(Address - W32pServiceTable);
	dwTemp = dwTemp << 4; //DbgPrint("*(PLONG)qwTemp: %x,
	dwTemp: %x",*(PLONG)qwTemp,dwTemp);
	irql=WPOFFx64();
	*(PLONG)qwTemp = dwTemp;
	WPONx64(irql);
}

四、实现 SHADOW SSDT HOOK

由于 SSSDT 函数的地址采用“基址+偏移”的方式,限制了代理函数的地址必须跟 WIN32K.SYS 在同一个 4GB。所以我们还是要采用二次跳转的方式,代理函数要写两个。一个代理函数和 WIN32K.SYS 在同一个 4GB,它的唯一作用就是跳转到第二个代理函数;第二个代理函数就是过滤参数,根据参数进行返回进行不同的处理。我们首先需要找到一个跟 WIN32K.SYS 在同一个 4GB 的地址,来填写 JMP。而且, 这个地址必须带可执行属性。要知道,在 Ring 3 下修改内存属性可以用 NtProtectVirtualMemory,在内核里却没有对应的函数。如果用MDL 那一套函数, MmGetSystemAddressForMdlSafe 返回的地址却不跟WIN32K.SYS 在同一个 4GB。在上几期讲述 SSDT HOOK 时,我把第一个代理函数写到了 KeBugCheckEx 里,因为 KeBugCheckEx 在正常情况下是不可能被执行到的。而在 WIN32K.SYS 里,很难找到哪个函数不被执行到。我经过仔细排查,发现 WIN7 的 SSSDT 里多了一个函数,名为 NtUserWindowFromPhysicalPoint,它在 USER32.DLL 对应的函数明显是 WindowFromPhysicalPoint,经 MSDN 上查证,它是从 VISTA 才开始有的。不同于 WindowFromPoint, MSDN 对WindowFromPhysicalPoint 的描述是: Retrieves a handle to the windowthat contains the specified physical point。简单说吧,我感觉这个函数很少被调用,所以废掉它影响应该不大。废掉它很简单,直接返回STATUS_SUCCESS 即可。汇编代码是: [xor rax,rax]+[ret]。这段汇编代码只有四个字节,填充完这四个字节后,后面的字节可以全部填充 NOP 直到函数结束。当然,我在代码里只覆盖了 23 字节,并没有覆盖全部字节。这只是个人的处理风格问题,没什么特别意义。第一个代理函数用机器码写成,总共就 14 个字节,前 6 字节为 ff 15 0000 00 00,后 8 字节为第二个代理函数的地址(JMP QWORD PTR)。第二个代理函数才是真正的代理函数,用 C 语言写成:

ULONG64 ProxyNtUserPostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	if( NtUserQueryWindow(hWnd,0)==MyProcessId &&PsGetCurrentProcessId()!=(HANDLE)MyProcessId )
	{
		DbgPrint("Do not fuck with me!");
		return 0;
	}
	else
	{
		DbgPrint("OriNtUserPostMessage called!");
		return NtUserPostMessage(hWnd,Msg,wParam,lParam);
	}
}

以下就是构造第一个代理函数和 UNHOOK SSSDT 的代码。 UNHOOK SSSDT 的代码很简单,只要把原始函数的地址填回去即可(注意:这一步必须在 GUI 线程里执行):

VOID FuckFunc()
{
	KIRQL irql;
	UCHAR
	fuckcode[]="\x48\x33\xC0\xC3\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90
	\x90\x90\x90";
	irql=WPOFFx64();
	memcpy((PVOID)AddressNtUserWindowFromPhysicalPoint,fuckcode,23);
	WPONx64(irql);
}
VOID HOOK_SSSDT()
{
	KIRQL irql;
	ULONG64 myfun;
	UCHAR jmp_code[]="\xFF\x25\x00\x00\x00\x00\x90\x90\x90\x90\x90\x90\x90\x90";
	//代理函数地址
	myfun=(ULONGLONG)ProxyNtUserPostMessage;
	//填充 shellcode
	memcpy(jmp_code+6,&myfun,8);
	//写入 shellcode
	FuckFunc();
	irql=WPOFFx64();
	memcpy((PVOID)(AddressNtUserWindowFromPhysicalPoint+4),jmp_code,14);
	WPONx64(irql);
	//修改记录原始地址的地方
	ModifySSSDT(IndexOfNtUserPostMessage, AddressNtUserWindowFromPhysicalPoint+4);
	DbgPrint("HOOK_SSSDT OK!");
}
VOID UNHOOK_SSSDT()
{
	ModifySSSDT(IndexOfNtUserPostMessage, (ULONG64)NtUserPostMessage);
	DbgPrint("UNHOOK_SSSDT OK!");
}

五、效果测试

编写一个程序,利用 PostMessage 对指定进程进行窗口攻击(窗口攻击在某些时候能使进程崩溃,在 360 支持 WIN7X64 的早期,我就用窗口攻击的方法突破了 360 在 WIN64 下的自我保护):

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
	DWORD pid,wpid,i,j;
	HWND hWnd;
	st:
	system("cls");
	printf("Input pid: ");
	scanf("%ld",&pid);
	for(i=100; i<0xffffff; i+=2)
	{
		GetWindowThreadProcessId(i,&wpid);
		if(wpid==pid && IsWindowVisible((HWND)i)==1)
		{
			hWnd=i;
			for(j=0; j<0x10000; j++)
			{
				PostMessage(hWnd,j,0,0);
			}
		}
	}
	printf("OK!");
	getchar();
	getchar();
	goto st;
	return 0;
}

接下来进行简单的对比测试,对比在 SSSDT HOOK 前后的效果。

HOOK 之后被 WIN64AST 检测到地址异常:

x64驱动基础教程 17

HOOK SSSDT 前,在『窗体攻击程序』里输入测试程序 PID,测试程序被秒杀:

x64驱动基础教程 17

HOOK SSSDT 后,在『窗体攻击程序』里输入测试程序 PID,测试程序没事:

x64驱动基础教程 17

六、 获得 Shadow SSDT 的原始地址

获得 Shadow SSDT 的原始地址跟获得 SSDT 原始地址差不多,由于我已经详细描述过如何获得 SSDT 的原始地址,在此我就不详细讲了。要注意的是,你不能直接读取 WIN32K.SYS 的内容,需要把 WIN32K.SYS 复制一份出来再读取。恢复 SSSDT HOOK 时只需把原始地址写入 W32pServiceTable 指定偏移即可。

void GetOriAddress()
{
	ULONG64 W32pServiceTable, Win32kBase, Win32kImageBase, Win32kInProcess=0, retv;
	IoControl(hMyDrv ,CTL_CODE_GEN(0x806), NULL, 0, &W32pServiceTable, 8);
	Win32kBase = GetWin32kBase();
	CopyFileA("c:\\windows\\system32\\win32k.sys","c:\\win32k.dll",0);
	Win32kImageBase = GetWin32kImageBase();
	printf("W32pServiceTable: %llx\n", W32pServiceTable);
	printf("WIN32K.SYS base: %llx\n", Win32kBase);
	printf("WIN32K.SYS image base: %llx\n\n\n", Win32kImageBase);
	ULONG index=0;
	if ( Win32kInProcess==0 )
	Win32kInProcess = (ULONGLONG)LoadLibraryExA("c:\\win32k.dll",0,
	DONT_RESOLVE_DLL_REFERENCES);
	for(index=0;index<825;index++) //825 是 WIN7X64 上 SSSDT 的函数个数
	{
		ULONGLONG RVA=W32pServiceTable-Win32kBase;
		ULONGLONG temp=*(PULONGLONG)(Win32kInProcess+RVA+8*(ULONGLONG)index);
		ULONGLONG RVA_index=temp-Win32kImageBase;
		retv = RVA_index+Win32kBase;
		printf("Shadow SSDT Function[%ld]: %llx\n",index,retv);
		if(index % 100 ==0)
		{
			printf("Press any key to continue......\n");
			getchar();
		}
	}
}

运行效果如下

x64驱动基础教程 17


以上所述就是小编给大家介绍的《x64驱动基础教程 17》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python Data Structures and Algorithms

Python Data Structures and Algorithms

Benjamin Baka / Packt Publishing / 2017-5-30 / USD 44.99

Key Features A step by step guide, which will provide you with a thorough discussion on the analysis and design of fundamental Python data structures.Get a better understanding of advanced Python c......一起来看看 《Python Data Structures and Algorithms》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具