静态反调试虽然容易绕过,但是由于种类繁多,如果病毒结合了很多种静态反调试而对其了解不多的话,也不好办,所以了解更多的
方法不至于束手无策.
1.利用PEB的BeingDebugged 字段
已知fs:[0x30]指向PEB, PEB地址+0x2 处的一个字节的数据表示是否在被调试,如果是1则是,0则不是
当进程被附加时,系统会将该值置为1
IsDebuggerPresent() API就是利用这个原理工作的:
IsDebuggerPresent的反汇编代码:
751E9E0A mov eax,dword ptr fs:[00000030h] 751E9E10 movzx eax,byte ptr [eax+2] 751E9E14 ret
所以通过调试器启动后或附加后将该值修改为0即可规避
2.Ldr的内存区域特征
启动方式调试程序时,peb中的Ldr的内存是从堆中分配的,未使用的区域被大量填充0xfeeefeee.但附加方式却不会出现这种情况.
例如来到Ldr处(peb+0xc):这里测试Ldr地址是0x00241ea0
下拉:
通过附加的方式则不会出现这种特征. 此外,这种特征在xp以上的系统就没有了
3.Process Heap (peb+0x18)
_heap结构节选:(xp sp3)
其中Flags和ForceFlags字段被调试时将不同
当正常运行时前者为0x2,后者为0x0, 启动调试时将变成其他值:
附加后查看:
此外该特征在xp以上系统将不存在.附加方式调试也不会出现. 规避方法就是修改这2个值
4.NtGlobalFlag (peb+0x68)
该字段正常运行和被附加调试时为0,被启动调试时为其他值
5.NtQueryInformationProcess
原型:
NTSTATUS NtQueryInformationProcess(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
第2个参数是个枚举类型. 主要有3个值与调试有关:
ProcessDebugPort = 7
ProcessDebugObjectHandle = 30
ProcessDebugFlags = 31
当ProcessInformationClass 参数为7时,ProcessInformation返回调试状态.
即当处于非调试状态将传入变量置0,否则置0xffffffff
同理,当ProcessInformationClass 参数为30时,ProcessInformation返回调试对象句柄
即当调试状态时将传入变量置句柄值,否则置0
此外,API CheckRemoteDebuggerPresent就是通过这个方式来检测是否存在调试器
当ProcessInformationClass 参数为31时,ProcessInformation返回调试标记
即当调试状态时将传入变量置0,否则置1
规避方法:在该API开头处直接改为ret (后面可能还要加数字平衡栈),让它获取不到任何信息.如:
ntdll中的代码:
773DD300 mov eax,18h
773DD305 call dword ptr fs:[0C0h]
773DD30C ret 14h
改为:
773DD300 C2 1400 retn 0x14
773DD303 90 nop
773DD304 90 nop
773DD305 64:FF15 C000000>call dword ptr fs:[0xC0]
773DD30C C2 1400 retn 0x14
6.基于调试环境: NtQuerySystemInformation
当操作系统开启内核调试模式时,会有一定特征,可以通过NtQuerySystemInformation API来获取
NTSTATUS NtQuerySystemInformation( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
SYSTEM_INFORMATION_CLASS是个枚举类型:
当为0x23时为获取系统是否在被调试信息存放到第2个参数指向的结构中,该结构是2个1字节的结构,当处于调试时这2字节都会被写入1.
规避方法同样是调试时修改API,让其直接返回
7.NtQueryObject
该API获取系统内核对象信息,原型:
NTSTATUS NtQueryObject( _In_opt_ HANDLE Handle, _In_ OBJECT_INFORMATION_CLASS ObjectInformationClass, _Out_opt_ PVOID ObjectInformation, _In_ ULONG ObjectInformationLength, _Out_opt_ PULONG ReturnLength );
第2个参数是个枚举类型:
typedef enum _OBJECT_INFORMATION_CLASS {
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation =3,
ObjectHandleInformation
} OBJECT_INFORMATION_CLASS;
传入3即获取所有对象信息,然后检测是否存在调试对象
规避方法:修改API开头代码直接返回.
8. ZwSetInformationThread
NTSTATUS ZwSetInformationThread( _In_ HANDLE ThreadHandle, _In_ THREADINFOCLASS ThreadInformationClass, _In_ PVOID ThreadInformation, _In_ ULONG ThreadInformationLength );
当第一个参数传入本线程句柄,第2个参数传入0x11,后2个参数传入0即可将本线程设置为隐藏,从而使调试器收不到调试事件而无法调试.
9.DebugActiveProcessStop
BOOL DebugActiveProcessStop( DWORD dwProcessId);
该API使进程从调试器中detach出来,最后调试和被调试进程都终止.
10.TLS回调函数
利用tls回调函数可以将以上方法利用起来.
11.其他静态检测思路
比如探测od窗口名,进程名, 是否运行在虚拟机中等等方式