首页
论坛
课程
招聘
[原创]汇编与驱动-采用SSDT Hook NtOpenProcess保护进程
2012-7-9 21:05 8016

[原创]汇编与驱动-采用SSDT Hook NtOpenProcess保护进程

2012-7-9 21:05
8016
作为一个热爱汇编的人被迫使用C或者C++写驱动是一件很难受的事情,特别是在做一些邪恶的事情的时候,你总会发现汇编就是一只恶魔之手,而C或者C++总是缺乏那么一点魔力,还好,VC支持内联汇编,那么我们用VC的优势给我们写驱动搭一个平台,使用他们丰富的资源,然后用我们的汇编编写核心代码,做我们想做的事情,虽然我的最终目标是全部用汇编解决一切问题,但在目前汇编资源越来越少的情况下我们不得不做一些屈服。
    废话说了很多,下面我就奉上一个估计很多同学都学过的一个例子,《郁金香驱动教程》的032课-自写驱动保护XX进程,原理郁金香老师已经讲得很清楚了,C++的代码教程里也有,我就不重复了。我的任务是采用内联汇编改写其中关键的代码,当然,作为一个汇编的热爱者,客户端exe文件我用汇编写的,同时对郁金香的客户端和驱动端代码都进行了完善,同时将客户端编写改为Windows界面,使界面更加友好。
软件运行后界面如下:


     下面开始解释一下代码,没什么技术含量,汇编按自己的风格写的,高手直接无视
先说说用户端:
里面有一个DDKDriver.inc的文件,这里面主要是定义了一个汇编常用的宏,还有几个驱动用的宏和一些字符定义(- -汇编的资源太缺乏,只能从VC里整点用用),写界面的部分我就不说了,源代码里有。

		invoke	CreateFile, addr szDriverLinkName,\
							GENERIC_READ or GENERIC_WRITE ,\
							0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL 
		mov		hDevice, eax
		.if eax == INVALID_HANDLE_VALUE
			
			invoke	GetLastError
			invoke	wsprintf, addr szMsgBuffer, CTXT("打开设备失败,错误码=%d"), eax
			invoke	Nt_MsgBox, addr szMsgBuffer
			invoke	SendMessage, hDlgmain, WM_CLOSE, NULL, NULL
		.endif	

在对话框窗口初始化部分我们用CreateFile打开一个设备,注意第一个参数的命名“addr szDriverLinkName”
szDriverLinkName	db "\\.\SSDTHook_NtOpenProcess",0

这里不能用郁金香教程你的写法"\\\\.\\SSDTHook_NtOpenProcess",因为汇编是不需要转义符的
打开设备后,我们检查设备的句柄,如果出错,则报一个出错码,发送一个窗口关闭的消息退出,所以一定要先把驱动加载起来,再运行用户端。

;名称:Nt_SSDTHook 
;功能:Hook指定进程PID的程序
;参数:_hDevice=设备句柄
;参数:_HookProcessID=要Hook进程的PID
;返回:eax,eax=1表示成功,eax=0表示失败

Nt_SSDTHook proc _hDevice:DWORD, _HookProcessID:DWORD
	local @InputBuffer:DWORD
	local @RetNum:DWORD
	
	push	esi
	push	ebx
	
	mov		eax, _HookProcessID
	mov		@InputBuffer, eax
	lea		esi, @InputBuffer
	lea		ebx, @RetNum
	
	invoke	DeviceIoControl, _hDevice, SSDTHook_code, esi, 4, NULL, 0, ebx, NULL
	mov		eax, @RetNum
	
	pop		ebx
	pop		esi
	ret
Nt_SSDTHook endp

;名称:Nt_SSDTUnHook 
;功能:卸载Hook
;参数:_hDevice=设备句柄
;返回:eax

Nt_SSDTUnHook  proc _hDevice:DWORD
	local @RetNum:DWORD
	
	push	ebx
	
	lea		ebx, @RetNum
	
	invoke	DeviceIoControl, _hDevice, SSDTUnHook_code, NULL, 0, NULL, 0, ebx, NULL
	mov		eax, @RetNum

	pop		ebx

	ret
Nt_SSDTUnHook  endp

其实用户端的Hook和UnHook 很简单,就是发送一个IRP给驱动端,同时接受驱动端返回的信息,判断是否Hook或UnHook成功,当然,Nt_SSDTHook发送IRP时还要传递一个要保护进程的PID,SSDTHook_code和SSDTUnHook_code这两个控制码都在 DDKDriver.inc 定义好了

接下来讲一讲驱动端的代码,其他公共的部分就不解释了,基本驱动的框架就是那样,我们直接拿来用就是了,当然我编译的时候用的是VS2005和DDK,还有一个DDKWizard,网上有教程,这里我们就不讲了,编译不成功的同学很有可能是编译环境搭建有问题。

NTSTATUS MyNtOpenProcess(OUT PHANDLE ProcessHandle,  IN ACCESS_MASK AccessMask,  IN POBJECT_ATTRIBUTES ObjectAttributes,  IN PCLIENT_ID ClientId )
{
	NTSTATUS Return_NtOpenProcess;
	DbgPrint("进入MyNtOpenProcess\n");
	//调用原NtOpenProcess
	_asm
	{
		push	ClientId
		push ObjectAttributes
		push AccessMask
		push ProcessHandle
		call	SSDT_NtOpenProcess
		
		mov	Return_NtOpenProcess, eax
	}
	if (HookProcessID == ClientId->UniqueProcess)
	{
		ProcessHandle = NULL;
		Return_NtOpenProcess = STATUS_ACCESS_DENIED;
		DbgPrint("NtOpenProcess被保护\n");
	} 
	return Return_NtOpenProcess;
}

这里我们先写一个MyNtOpenProcess以替代原NtOpenProcess, 郁金香教程中用的是C++的写法,这里我们用内联汇编来代替。

		push	ClientId
		push ObjectAttributes
		push AccessMask
		push ProcessHandle
		call	SSDT_NtOpenProcess
		
		mov	Return_NtOpenProcess, eax

其实代替的方法很简单,就是把NtOpenProcess的4个参数直接入栈即可,当然注意顺序要相反,然后call我们Hook前得到NtOpenProcess的原地址,注意call后不要再去平衡堆栈,因为Window 内核函数和API一样,是自己恢复堆栈的
调用原NtOpenProcess后,我们就判断ClientId和我们从用户端程序得到的要保护的进程的PID是否相等,相等就将ProcessHandle=NULL,不相等则直接返回


NTSTATUS SSDTHook_NtOpenProcess()
{
	if(HookFlag_NtOpenProcess == 0)
	{
		DbgPrint("开始Hook NtOpenProcess\n");
		_asm
		{
			pushad
				mov	ebx, KeServiceDescriptorTable
				mov	eax, 122
				mov	ebx, [ebx]						//取SSDT表基址
			shl	eax, 2							//函数索引号*4
				add	ebx, eax
				mov	SSDT_NtOpenProcess_Addr, ebx  //得到SSDT表中指向NtOpenProcess的地址的地址
				mov	eax, [ebx]
			mov	SSDT_NtOpenProcess, eax         //得到原NtOpenProcess的地址

				cli	                            //去除写保护
				mov	eax, cr0
				and	eax, 0FFFEFFFFh
				mov  cr0, eax

				mov eax, MyNtOpenProcess        //写入我们自己的NtOpenProcess地址
				mov	[ebx], eax

				mov  eax, cr0                   //恢复写保护
				or		eax, 10000h
				mov	cr0, eax
				sti
				popad
		}
		HookFlag_NtOpenProcess=1;
		DbgPrint("NtOpenProcess原地址:%x\n", (int) SSDT_NtOpenProcess);
		DbgPrint("Hook NtOpenProcess后的地址:%x\n", (int) MyNtOpenProcess);
	}
	else
	{
		DbgPrint("NtOpenProcess已被Hook\n");
	}

	return STATUS_SUCCESS;
}


下面是UnHook的部分,主要是将SSDT表中NtOpenProcess的原地址重新写入

NTSTATUS SSDTUnHook_NtOpenProcess()
{
	if (HookFlag_NtOpenProcess == 1)
	{
		_asm
		{
			pushad
				cli		
				mov	eax, cr0
				and	eax, 0FFFEFFFFh
				mov  cr0, eax

				mov ebx, SSDT_NtOpenProcess_Addr     //得到SSDT表中指向NtOpenProcess的地址的地址  
				mov	eax, SSDT_NtOpenProcess
				mov [ebx], eax                      //恢复SSDT表中NtOpenProcess的地址

				mov  eax, cr0
				or		eax, 10000h
				mov	cr0, eax
				sti
			popad
		}
		DbgPrint("UnHook还原NtOpenProcess完毕\n");
		HookFlag_NtOpenProcess =0;
	}
	else
	{
		DbgPrint("NtOpenProcess已被还原\n");
	}
	return STATUS_SUCCESS;
}


这里我们看到驱动关键的部分用汇编写后程序要精简很多,对于操作内存汇编是相当容易的,实际上郁金香教程的去除写保护和恢复写保护也是用汇编写的,当然我的代码和他的稍微有点不同,内联汇编给我们一个有力的工具,让汇编继续在驱动中发挥它强大的能力,在RING 0级别下更加能畅行无阻。
当然这个保护是简单的,破解的方法也很容易,把要保护的进程关闭,然后再重新打开,一般他的PID就改变了,我们就Hook不到了。

下面附上源代码:
用户端: SSDTHook_NtOpenProcess(用户端).zip
驱动端: SSDTHook_NtOpenProcess(驱动端).zip

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (4)
雪    币: 619
活跃值: 活跃值 (50)
能力值: ( LV9,RANK:210 )
在线值:
发帖
回帖
粉丝
organic 活跃值 3 2012-7-9 21:18
2
0
有没有用汇编写驱动的群,求++
雪    币: 939
活跃值: 活跃值 (434)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
elianmeng 活跃值 1 2012-7-10 10:59
3
0
公司要求的是效率,不是炫技
真正 厉害的是 看反汇编代码 和看源码 没有什么区别
雪    币: 277
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wuzhiwen 活跃值 2013-6-5 14:52
4
0
顶楼上,什么语言到最后还是不变成汇编,明明这代码用汇编更麻烦,为什么不用C呢?
就好像开发程序可以用记事本,也可以用VC,你还是坚持用记事本吗?
雪    币: 397
活跃值: 活跃值 (709)
能力值: ( LV3,RANK:25 )
在线值:
发帖
回帖
粉丝
KooJiSung 活跃值 2013-6-5 15:19
5
0
应该是一个过程吧,以前也是汇编死忠
游客
登录 | 注册 方可回帖
返回