欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]抛砖引玉—硬件断点的检测和反检测

[原创]抛砖引玉—硬件断点的检测和反检测

硬件断点的检测和反检测
1、硬件断点设置的方法
     有矛必有盾,有盾必有矛,说硬件断点的检测和反检测之前我们先来谈谈硬件断点设置的常规方法
方法1:
     采用SetThreadContext直接设置指定线程的调试寄存器,由于这种方法简单实用,深受广大人民群众喜爱,具体怎么搞,附件代码中的SetHwBreakPoint函数有详细的code。
方法2:
     采用SEH或者VEH等异常处理技术设置硬件断点,原理是自己搞个SEH或者VEH,然后自己触发一个异常(比如除0),跳到自己的异常处理函数中,这里只说SEH的异常处理函数结构:SEHHandler proc C _pstExceptionRecord:DWORD, _pstSEH:DWORD, _pstContext:DWORD, _pDispatcherContext:DWORD。第3个参数_pstContext就是异常线程的所有调试器结构指针,聪明的朋友肯定就知道该怎么搞了。不清楚的朋友可以去看看SEH实现的过程。
2、硬件断点的检测方法
方法1的检测方法:
    GetThreadContext就是SetThreadContext的盾,把调试寄存器取过来送纪委检查检查就知道有没有人在干坏事了。
方法2的检测方法:
     其实同样是用方法2设置硬件断点的手段,只不过在SEH的异常处理函数中用_pstContext取调试寄存器送纪委就是了,当然用SEH只能检测当前线程的,要检测整个程序的还是用VEH好,方法类似。我附件里的SEHCheckHardPoint和SEHHandler就是一个简单的例子。


;名称:SEHCheckHardPoint 

;功能:构建一个SEH,检测硬件断点

;参数:无

;返回:无

SEHCheckHardPoint proc

	assume	fs:nothing

	push	offset SEHTest_Ret						;压入安全代码的地址

	push	ebp										;保存ebp

	push	SEHHandler								;压入SEH回调函数

	push	fs:[0]									;保存原SEH

	mov		fs:[0], esp								;设置当前SEH

	xor		eax, eax

	mov		DWORD ptr [eax], 1						;人为的产生一个异常	

	SEHTest_Ret:

	pop		fs:[0]									;恢复原SEH

	add		esp, 12									;平衡堆栈

	ret

SEHCheckHardPoint endp

;名称:SEHHandler

;功能:SEH的异常处理函数,为cdecl调用

;参数:_pstExceptionRecord = 异常信息结构指针

;参数:_pstSEH = SEH结构指针

;参数:_pstContext = 发生异常线程的寄存器结构指针

;参数:_pDispatcherContext

;返回:无

SEHHandler proc C _pstExceptionRecord:DWORD, _pstSEH:DWORD, _pstContext:DWORD, _pDispatcherContext:DWORD

	pushad

	mov		ebx, _pstSEH

	mov		eax, [ebx + 8]

	mov		esi, _pstContext

	assume	esi:ptr CONTEXT

	mov		[esi].regEbp, eax						;恢复ebp

	mov		eax, [ebx + 12]

	mov		[esi].regEip, eax

	invoke	VK_PrintfMsg, CTXT("Dr0 = 0x%X"), [esi].iDr0    ;显示Dr0的值,如果不是0,说明被设硬件断点了

	assume	esi:nothing

	popad

	mov		eax, ExceptionContinueExecution

	ret

SEHHandler endp 


这个例子只简单的检查Dr0是否被设置了硬件断点,当然你也可以检查多个。
3、硬件断点的反检测方法
方法1的反检测方法:
     方法1的反检测方法也很简单,估计很多朋友都会,就是在内核中Hook NtGetThreadContext,检测是否是我们的目标进程,如果是则将调试寄存器全部清0后返回。当然也有针对这种方法的检测方法,就是用SetThreadContext自己设置一下调试寄存器,然后调用GetThreadContext检查是不是和自己设置的相符,如果不相符或者为0说明有人动过手脚了。这种反检测方法ring3下也可以搞Hook 目标进程的GetThreadContext,只是有些程序也会检测。
方法2的反检测方法:
     方法2的检测方法没怎么找到资料,个人探索了下。开始的方法是逆向检测进程,用个人写的工具“APIMonitor”里就有检测异常的功能,可以找到产生异常的地址,找到异常后就可以找到异常处理函数,然后把异常处理函数干掉就可以了。当然这种方法通用性差,说不定人家还把异常处理函数给加花或者VMP什么的,还不定好逆向。这里给大家分享一种个人通用方法:
     Hook KiUserExceptionDispatcher:
KiUserExceptionDispatcher的函数定义:
VOID KiUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context);
这个是我电脑上反汇编的KiUserExceptionDispatcher头部的一些代码:

7C92EAEC >  8B4C24 04       mov ecx,dword ptr ss:[esp+0x4]              

7C92EAF0    8B1C24          mov ebx,dword ptr ss:[esp]               

7C92EAF3    51              push ecx                                ; pContext

7C92EAF4    53              push ebx                                ; 压入pExceptionRecord

7C92EAF5    E8 C78C0200     call ntdll.7C9577C1                     ; 调用RtlDispatchException

7C92EAFA    0AC0            or al,al                                ; 判断返回是否为0,即是否调用成功

7C92EAFC    74 0C           je short ntdll.7C92EB0A                 ;失败则跳

7C92EAFE    5B              pop ebx

7C92EAFF    59              pop ecx

7C92EB00    6A 00           push 0x0

7C92EB02    51              push ecx

7C92EB03    E8 11EBFFFF     call ntdll.ZwContinue                   ; 调用RtlDispatchException成功则继续ZwContinue执行异常原来的代码


     KiUserExceptionDispatcher是ring3下的异常分发函数,首先他会调用RtlDispatchException,对VEH,SEH的异常处理函数所传递的Context结构指针就是这个函数干的,所以我们在调用此函数之前把调试寄存器清0,但在KiUserExceptionDispatcher将调试寄存器清0会将我们设置的硬件断点清除,所以我们在调用完RtlDispatchException后要再次设置我们的硬件断点。

;名称:HookKiUserExceptionDispatcher 

;功能:Hook KiUserExceptionDispatcher的函数头,跳至Hooked_KiUserExceptionDispatcher去执行

;参数:无

;返回:失败返回0,成功返回1

HookKiUserExceptionDispatcher proc 

	local	@hNtdll:DWORD

	local	@dwOldProtect:DWORD

	invoke	GetModuleHandle, CTXT("ntdll.dll")

	mov		@hNtdll, eax

	invoke	GetProcAddress, @hNtdll, CTXT("KiUserExceptionDispatcher")

	test	eax, eax

	jz		HookKiUserExceptionDispatcher_Ret

	mov		pKiUserExceptionDispatcher, eax

	invoke	VirtualProtect, pKiUserExceptionDispatcher, 7, PAGE_EXECUTE_READWRITE, addr @dwOldProtect

	test	eax, eax

	jz		HookKiUserExceptionDispatcher_Ret

	mov		eax, offset Hooked_KiUserExceptionDispatcher				;hook KiUserExceptionDispatcher跳转至Hooked_KiUserExceptionDispatcher执行

	sub		eax, pKiUserExceptionDispatcher

	sub		eax, 5

	mov		edx, pKiUserExceptionDispatcher

	mov		BYTE ptr [edx], 0E9h

	inc		edx

	mov		[edx], eax

	mov		eax, 1

HookKiUserExceptionDispatcher_Ret:

	ret

HookKiUserExceptionDispatcher endp

;名称:Hooked_KiUserExceptionDispatcher 

;功能:将调试寄存器清0,然后调用RtlDispatchException,调用完后再次设置硬件断点,返回原函数继续执行

Hooked_KiUserExceptionDispatcher:

	mov		eax, [esp + 4]								;取pContext,并将调试寄存器置0

	assume	eax:ptr CONTEXT

	mov		[eax].iDr0, 0								

	mov		[eax].iDr1, 0

	mov		[eax].iDr2, 0

	mov		[eax].iDr3, 0

	mov		[eax].iDr6, 0

	mov		[eax].iDr7, 0

	assume	eax:nothing

	mov		ecx, DWORD ptr [esp + 4]					;调用原代码跳回执行KiUserExceptionDispatcher

	mov		ebx, DWORD ptr [esp]

	push	ecx

	push	ebx

	mov		eax, cAddrRtlDispatchException

	call	eax											;调用RtlDispatchException

	mov		ecx, DWORD ptr [esp + 4]					;重新设置硬件断点

	assume	ecx:ptr CONTEXT

	push	HardPointAddr

	pop		[ecx].iDr0

	mov		[ecx].iDr7, 405h

	assume	ecx:nothing

	push	edx

	mov		edx, pKiUserExceptionDispatcher

	add		edx, 14

	mov		[esp], edx									;跳过11 Hook的call,执行下一条指令

	ret


注:RtlDispatchException这里我用的是硬编码,不同的电脑上可能会有不同,测试代码的朋友注意修正
4、测试代码:

InitDialog	proc _hWnd:DWORD

	invoke	GetDlgItem, _hWnd, IDC_BTN1

	mov		hButton1, eax

	invoke	GetDlgItem, _hWnd, IDC_BTN2

	mov		hButton2, eax

	mov		eax, _hWnd

	mov		hDlgmain, eax

	;...................此处添加一些需要初始化的代码...........................

	mov		HardPointAddr, offset ProcButton2Click

	invoke	SetHwBreakPoint, HardPointAddr

	invoke	HookKiUserExceptionDispatcher

	;...................此处添加一些需要初始化的代码...........................

	ret

InitDialog	endp



这里设置硬件断点的位置就是程序“测试断点”按钮的处理函数首地址,我们点击“检测断点”按钮会弹出如下消息框:

可以看出DR0寄存器为0,说明没检测到
但我们点击“测试断点”按钮,就华丽丽的弹出微软的错误框了,错误码是0x80000004,就是硬件断点异常

我们点“取消”,来到OD中就可以看到0x004012B2就是我们“测试断点”按钮的处理函数首地址了,F9一下,就会弹出“如果硬件断点继续执行,就会看到此消息”的消息了

这里有朋友会说,谁知道是不是你的反检测成功了还是你的检测方法垃圾啊,别急,我们把invoke        HookKiUserExceptionDispatcher注释掉,再次编译后点击“测试断点”弹出如下消息框:

这是DR0就不等于0了,说明检测方法是没有问题的。
有兴趣的朋友可以下载附件中的代码拿去编译一下,我是用MASM Plus编译的。
代码: 硬件断点检测与反检测.zip

上传的附件 硬件断点检测与反检测.zip
继续执行消息框.jpg
检测到硬件断点.jpg
未检测到硬件断点.jpg
异常错误框.jpg
软件界面.jpg

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功