当前位置: 附加器 >> 附加器发展 >> 反调试系列调试标志寄存器
调试标志寄存器
系统表中的特殊标志寄存器,即停留在进程内存中的、由操作系统设置的标志寄存器,可以用来指示进程正在被调试。这些标志寄存器的状态可以通过使用特定的API函数或检查内存中的系统表来验证。
这些技术是恶意软件最常使用的。
1.使用Win32API
以下技术使用现有的API函数(WinAPI或NativeAPI),这些函数检查进程内存中的系统结构,以寻找表明进程现在正在被调试的特定标志寄存器。
1.1.IsDebuggerPresent()
函数kernel32!IsDebuggerPresent()确定当前进程是否被用户模式的调试器如OllyDbg或x64dbg调试。一般来说,该函数只检查进程环境块(PEB)的BeingDebugged标志寄存器。
如果正在调试进程,可以使用以下代码终止进程:
汇编代码:
C/C++代码:
1.2.CheckRemoteDebuggerPresent()
函数kernel32!CheckRemoteDebuggerPresent()检查一个调试器(在同一台机器的不同进程中)是否连接到当前进程。
C/C++代码:
x86汇编:
x86-64汇编:
1.3.NtQueryInformationProcess()
函数ntdll!NtQueryInformationProcess()可以从一个进程中检索不同种类的信息。它接受一个ProcessInformationClass参数,该参数指定了你想得到的信息,并定义了ProcessInformation参数的输出类型。
1.3.1.ProcessDebugPort进程调试端口
可以使用ntdll!NtQueryInformationProcess()来检索进程的调试器端口号。有一个记录在案的类ProcessDebugPort,如果进程正在被调试,它会检索到一个等于0xFFFFFFFF(十进制-1)的DWORD值。
C/C++代码:
x86汇编:
x86-64汇编:
1.3.2.ProcessDebugFlags进程调试标志寄存器
一个叫做EPROCESS的内核结构代表一个进程对象,它包含NoDebugInherit字段。这个字段的反值可以通过一个无文件记录的类ProcessDebugFlags(0x1f)来检索。因此,如果返回值为0,则说明有调试器存在。
C/C++代码:
x86汇编:
x86-64汇编:
1.3.3.ProcessDebugObjectHandle进程调试对象句柄
当调试开始时,一个叫做"debugobject"的内核对象被创建。可以通过使用无文献记载的ProcessDebugObjectHandle(0x1e)类来查询这个句柄的值。
C/C++代码:
x86汇编:
x86-64汇编:
1.4.RtlQueryProcessHeapInformation()
ntdll!RtlQueryProcessHeapInformation()函数可以用来从当前进程的进程内存中读取堆标志寄存器。
C/C++代码:
1.5.RtlQueryProcessDebugInformation()
ntdll!RtlQueryProcessDebugInformation()函数可用于从被请求进程的进程内存中读取某些字段,包括堆标志寄存器。
C/C++代码:
1.6.NtQuerySystemInformation()
ntdll!NtQuerySystemInformation()函数接受一个参数,即要查询的信息类别。大多数类都没有被记录下来。这包括SystemKernelDebuggerInformation(0x23)类,它从WindowsNT开始就存在了。SystemKernelDebuggerInformation类返回两个标志寄存器的值。al中的KdDebuggerEnabled,和ah中的KdDebuggerNotPresent。因此,如果内核调试器存在,ah中的返回值为零。
C/C++代码:
反制措施
对于IsDebuggerPresent()。将进程环境块(PEB)的BeingDebugged标志设置为0。更多信息请参见BeingDebugged标志寄存器反制措施。
对于CheckRemoteDebuggerPresent()和NtQueryInformationProcess():由于CheckRemoteDebuggerPresent()调用NtQueryInformationProcess(),唯一的方法是拦截NtQueryInformationProcess()并在返回缓冲区设置以下值。
如果是ProcessDebugPort查询,则为0(或除-1外的任何值)。
如果是ProcessDebugFlags查询,则为非零值。
在ProcessDebugObjectHandle查询的情况下为0。
用RtlQueryProcessHeapInformation()、RtlQueryProcessDebugInformation()和NtQuerySystemInformation()函数反制这些检查的唯一方法是拦截它们并修改返回值:
RTL_PROCESS_HEAPS::HeapInformation::Heaps[0]:标志寄存器改为HEAP_GROWABLE,用于RtlQueryProcessHeapInformation()和RtlQueryProcessDebugInformation()。
SYSTEM_KERNEL_DEBUGGER_INFORMATION::DebuggerEnabled为0和系统_KERNEL_DEBUGGER_INFORMATION::DebuggerNotPresent为1。NtQuerySystemInformation()函数在查询SystemKernelDebuggerInformation的情况下。
2.手工检查
以下方法用于验证系统结构中的调试标志。他们手工检查进程的内存,而不使用特殊的调试API函数。
2.1.PEB!正在调试标志寄存器
这个方法只是检查PEB的BeingDebugged标志寄存器[的另一种方法,而不需要调用IsDebuggerPresent()。
32位进程:
64位进程:
WOW64进程:
C/C++代码:
2.2.NtGlobalFlag
进程环境块的NtGlobalFlag字段(32位Windows的0x68偏移,64位Windows的0xBC)默认为0。附加一个调试器并不改变NtGlobalFlag的值。但是,如果进程是由调试器创建的,则将设置以下标志寄存器:
FLG_HEAP_ENABLE_TAIL_CHECK(0x10)
FLG_HEAP_ENABLE_FREE_CHECK(0x20)
FLG_HEAP_VALIDATE_PARAMETERS(0x40)
调试器的存在可以通过检查这些标志寄存器的组合来检测。
32位进程:
64位进程:
WOW64进程:
C/C++代码:
2.3.堆标志寄存器
堆包含两个字段,它们会受到调试器存在的影响。具体如何影响,取决于Windows的版本。这些字段是标志寄存器(Flags)和强标志寄存器(ForceFlags)。
标志寄存器和强标志寄存器的值通常分别设置为HEAP_GROWABLE和0。
当调试器出现时,在WindowsNT、Windows和32位WindowsXP上,标志寄存器字段被设置为这些标志寄存器的组合。
HEAP_GROWABLE(2)
HEAP_TAIL_CHECKING_ENABLED(0x20)
HEAP_FREE_CHECKING_ENABLED(0x40)
HEAP_SKIP_VALIDATION_CHECKS(0x)
HEAP_VALIDATE_PARAMETERS_ENABLED(0x)
在64位的WindowsXP和WindowsVista及更高版本中,如果有调试器存在,Flags字段会被设置为这些标志寄存器组合:
HEAP_GROWABLE(2)
HEAP_TAIL_CHECKING_ENABLED(0x20)
HEAP_FREE_CHECKING_ENABLED(0x40)
HEAP_VALIDATE_PARAMETERS_ENABLED(0x)
当调试器出现时,ForceFlags字段被设置为这些标志寄存器的组合:
HEAP_TAIL_CHECKING_ENABLED(0x20)
HEAP_FREE_CHECKING_ENABLED(0x40)
HEAP_VALIDATE_PARAMETERS_ENABLED(0x)
C/C++代码:
2.3.堆保护
如果在NtGlobalFlag中设置了HEAP_TAIL_CHECKING_ENABLED标志寄存器,序列0xABABABAB将被附加在所分配的堆块的末端(在32位Windows中为2次,在64位Windows中为4次)。
如果在NtGlobalFlag中设置了HEAP_FREE_CHECKING_ENABLED标志寄存器,如果需要额外的字节来填充空的空间,直到下一个内存块,那么将附加序列0xFEEEFEEE。
C/C++代码:
反制措施
对于PEB!BeingDebugged标志寄存器:
将BeingDebugged标志设置为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
对于NtGlobalFlag:
将NtGlobalFlag设置为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
对于堆标志寄存器:
设置标志寄存器值为HEAP_GROWABLE,ForceFlags值为0,这可以通过DLL注入完成。如果你使用OllyDbg或x32/64dbg作为调试器,你可以选择各种反调试插件,如ScyllaHide。
对于堆保护:
手工修补32位的12字节和64位环境下的20字节的堆。拦截kernel32!HeapAlloc()并在其分配后修补堆。
备注:
本文投稿来自:作者王建达