The Heap

堆在初始化时, 会检查heap flags, 并视一些标志位的有无设置而对环境作出额外的改变. 像Themida就有采用这种方法来检测调试器.

比如:

  • 如果设置了HEAP_TAIL_CHECKING_ENABLED标志(见Heap Flags节), 那么在32位windows中就会在分配的堆块尾部附加2个0xABABABAB(64位环境就是4个).

  • 如果设置了HEAP_FREE_CHECKING_ENABLED(见Heap Flags节)标志, 那么当需要额外的字节来填充堆块尾部时, 就会使用0xFEEEFEEE(或一部分)来填充

那么, 一种新的检测调试器的方法就是来检查这些值.

堆指针已知

如果已知一个堆指针, 那么我们可以直接检查堆块里的数据. 然而在Windows Vista及更高版本中采用了堆保护机制(32位/64位都有), 使用了一个异或密钥来对堆块大小进行了加密. 虽然你可以选择是否使用密钥, 但是默认是使用的. 而且就堆块首部的位置, 在Windows NT/2000/XPWindows Vista及更高版本之间也是不相同的. 因此我们还需要将Windows版本也考虑在内.

可以使用以下32位代码来检测32位环境:

    xor ebx, ebx
    call GetVersion
    cmp al, 6
    sbb ebp, ebp
    jb l1
    ;Process Environment Block
    mov eax, fs:[ebx+30h]
    mov eax, [eax+18h] ;get process heap base
    mov ecx, [eax+24h] ;check for protected heap
    jecxz l1
    mov ecx, [ecx]
    test [eax+4ch], ecx
    cmovne ebx, [eax+50h] ;conditionally get heap key
l1: mov eax, <heap ptr>
    movzx edx, w [eax-8] ;size
    xor dx, bx
    movzx ecx, b [eax+ebp-1] ;overhead
    sub eax, ecx
    lea edi, [edx*8+eax]
    mov al, 0abh
    mov cl, 8
    repe scasb
    je being_debugged

或使用以下64位代码检测64位环境:

这里没有使用32位代码检测64位环境的样例, 因为64位的堆无法由32位的堆函数解析.

堆指针未知

如果无法得知堆指针, 我们可以使用kernel32HeapWalk()函数或ntdllRtlWalkHeap()函数(或甚至是kernel32GetCommandLine()函数). 返回的堆大小的值会被自动解密, 因此就不需要再关心windows的版本

可以使用以下32位代码来检测32位环境:

或使用以下64位代码检测64位环境:

这里没有使用32位代码检测64位环境的样例, 因为64位的堆无法由32位的堆函数解析.

Last updated