PEB.NtGlobalFlag PEB另一个成员被称作NtGlobalFlag(偏移0x68),壳也通过它来检测程序是否用调试器加载。通常程序没有被调试时,NtGlobalFlag成员值为0,如果进程被调试这个成员通常值为0x70(代表下述标志被设置):
FLG_HEAP_ENABLE_TAIL_CHECK(0X10)
FLG_HEAP_ENABLE_FREE_CHECK(0X20)
FLG_HEAP_VALIDATE_PARAMETERS(0X40)
这些标志是在ntdll!LdrpInitializeExecutionOptions()里设置的。请注意PEB.NtGlobalFlag的默认值可以通过gflags.exe工具或者在注册表以下位置创建条目来修改:
HKLM\Software\Microsoft\Windows Nt\CurrentVersion\Image File Execution Options
Heap Flags 由于NtGlobalFlag标志的设置,堆也会打开几个标志,这个变化可以在ntdll!RtlCreateHeap()里观测到。通常情况下为进程创建的第一个堆会将其Flags和ForceFlags4分别设为0x02(HEAP_GROWABLE)和0 。然而当进程被调试时,这两个标志通常被设为0x50000062(取决于NtGlobalFlag)和0x40000060(等于Flags AND 0x6001007D)。默认情况下当一个被调试的进程创建堆时下列附加的堆标志将被设置:
HEAP_TAIL_CHECKING_ENABLED(0X20)
HEAP_FREE_CHECKING_ENABLED(0X40)
示例
下面的示例代码检查PEB.NtGlobalFlag是否等于0,为进程创建的第一个堆是否设置了附加标志(PEB.ProcessHeap):
;ebx = PEB
Mov ebx,[fs:0x30]
;Check if PEB.NtGlobalFlag != 0
Cmp dword [ebx+0x68],0
jne .debugger_found
;eax = PEB.ProcessHeap
Mov eax,[ebx+0x18]
;Check PEB.ProcessHeap.Flags
Cmp dword [eax+0x0c],2
jne .debugger_found
;Check PEB.ProcessHeap.ForceFlags
Cmp dword [eax+0x10],0
jne .debugger_found
对策
可以将 PEB.NtGlobalFlag和PEB.HeapProcess标志补丁为进程未被调试时的相应值。下面是一个补丁上述标志的ollyscript示例:
Var peb
var patch_addr
var process_heap
//retrieve PEB via a hardcoded TEB address( first thread: 0x7ffde000)
Mov peb,[7ffde000+30]
//patch PEB.NtGlobalFlag
Lea patch_addr,[peb+68]
mov [patch_addr],0
//patch PEB.ProcessHeap.Flags/ForceFlags
Mov process_heap,[peb+18]
lea patch_addr,[process_heap+0c]
mov [patch_addr],2
lea patch_addr,[process_heap+10]
mov [patch_addr],0
同样地Olly Advanced插件有设置PEB.NtGlobalFlag和PEB.ProcessHeap的选项。
-------------------------------------------------------------------------
以下由Coderui修改整理,在VC 6.0下调试通过。
编写日期:2008年05月31日(更新)
联系邮箱:
作者博客:http://hi.baidu.com/coderui
void PEB_NtGlobalFlag()
{
_asm
{
jmp Coderui; //先跳转到“Coderui”处去执行后边的判断代码。
Coderui_exit: //标志位。跳到这里后,然后执行退出代码。
retn; //用这招当退出,还会异常,够狠(其它地方也可以这么做)。
Coderui: //标志位。跳到这里后,可以继续执行后边的代码。
;//ebx = PEB
mov ebx,fs:[0x30];
;//Check if PEB.NtGlobalFlag != 0
cmp dword ptr [ebx+0x68],0;
jnz coderui_exit; //发现被调试,向“Coderui_exit”标志位处跳,执行退出代码。
;//eax = PEB.ProcessHeap
mov eax,[ebx+0x18];
;//Check PEB.ProcessHeap.Flags
cmp dword ptr [eax+0x0c],2;
jnz coderui_exit; //发现被调试,向“Coderui_exit”标志位处跳,执行退出代码。
;//Check PEB.ProcessHeap.ForceFlags
cmp dword ptr [eax+0x10],0;
jnz coderui_exit; //发现被调试,向“Coderui_exit”标志位处跳,执行退出代码。
}
}