首页
论坛
课程
招聘
[原创]厌烦了HeapMagic
2007-11-9 22:06 14107

[原创]厌烦了HeapMagic

2007-11-9 22:06
14107
最近摆弄OD,试了一下Shub-Nigurrath的xADT 1.3,没想到又被
ProcessHeap逮到了,实在有点火大! 想起Themida玩这个也很起
劲,干脆让被调试程序使用标准堆, 彻底了断;-)

下面的代码从W2K源码里找的,我自己也稀里糊涂,乱解释的地方
请指教.

看看heap.c:

RtlCreateHeap (
    IN ULONG Flags,
    IN PVOID HeapBase OPTIONAL,
    IN SIZE_T ReserveSize OPTIONAL,
    IN SIZE_T CommitSize OPTIONAL,
    IN PVOID Lock OPTIONAL,
    IN PRTL_HEAP_PARAMETERS Parameters OPTIONAL
    )

    ......

    PVOID NextHeapHeaderAddress;
    PHEAP_UNCOMMMTTED_RANGE UnCommittedRange, *pp;
    RTL_HEAP_PARAMETERS TempParameters;
    ULONG NtGlobalFlag = RtlGetNtGlobalFlags();

    /* RtlCreateHeap在ntoskrnl和ntdll各有1个, 从下面的
       条件编译指令看,似乎是同一个文件编译出来的. 内核的
       RtlGetNtGlobalFlags直接取的全局变量, ntdll内
       的实现则是从PEB取的
    */
   
#ifndef NTOS_KERNEL_RUNTIME

    PPEB Peb;

#else // NTOS_KERNEL_RUNTIME

    extern SIZE_T MmHeapSegmentReserve;
    extern SIZE_T MmHeapSegmentCommit;
    extern SIZE_T MmHeapDeCommitTotalFreeThreshold;
    extern SIZE_T MmHeapDeCommitFreeBlockThreshold;

#endif // NTOS_KERNEL_RUNTIME

    ......

    //
    //  If the caller does not want to skip heap validiation checks then we
    //  need to validate the rest of the flags but simply masking out only
    //  those flags that want on a create heap call
    //

    if (!(Flags & HEAP_SKIP_VALIDATION_CHECKS)) {

        if (Flags & ~HEAP_CREATE_VALID_MASK) {

            HeapDebugPrint(( "Invalid flags (%08x) specified to RtlCreateHeap\n", Flags ));
            HeapDebugBreak( NULL );

            Flags &= HEAP_CREATE_VALID_MASK;
        }
    }

    /*
    这个标记HEAP_SKIP_VALIDATION_CHECKS=0x10000000,多次使用,貌似可以不用
    调试堆,不过是从调用参数来的,不大好改,而且Heap相关函数不少 */

    //
    //  If nt global flags tells us to always do tail or free checking
    //  or to disable coalescing then force those bits set in the user
    //  specified flags
    //

    if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) {

        Flags |= HEAP_TAIL_CHECKING_ENABLED;
    }

    if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) {

        Flags |= HEAP_FREE_CHECKING_ENABLED;
    }

    if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {

        Flags |= HEAP_DISABLE_COALESCE_ON_FREE;
    }

    //
    //  In the non kernel case we also check if we should
    //  validate parameters, validate all, or do stack backtraces
    //

    Peb = NtCurrentPeb();

    if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) {

        Flags |= HEAP_VALIDATE_PARAMETERS_ENABLED;
    }

    if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) {

        Flags |= HEAP_VALIDATE_ALL_ENABLED;
    }

    if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {

        Flags |= HEAP_CAPTURE_STACK_BACKTRACES;
    }

    /* 测试NtGlobalFlag */

    ........

    #ifndef NTOS_KERNEL_RUNTIME

    //
    //  In the non kernel case check if we are creating a debug heap
    //  the test checks that skip validation checks is false.
    //

    if (DEBUG_HEAP( Flags )) {

        return RtlDebugCreateHeap( Flags,
                                   HeapBase,
                                   ReserveSize,
                                   CommitSize,
                                   Lock,
                                   Parameters );
    }

    #endif // NTOS_KERNEL_RUNTIME

    /* 这里是我们感兴趣的了,DEBUG_HEAP(Flags)为TRUE则
       创建调试堆(注意内核模式永远不会用调试堆),这个宏
       定义在heappriv.h内:

       #define HEAP_DEBUG_FLAGS   (HEAP_VALIDATE_PARAMETERS_ENABLED | \
                            HEAP_VALIDATE_ALL_ENABLED        | \
                            HEAP_CAPTURE_STACK_BACKTRACES    | \
                            HEAP_CREATE_ENABLE_TRACING       | \
                            HEAP_FLAG_PAGE_ALLOCS)
       #define DEBUG_HEAP(F)     ((F & HEAP_DEBUG_FLAGS) && !(F & HEAP_SKIP_VALIDATION_CHECKS))

      
       另外几个宏在heap.h内:

       #define HEAP_SIGNATURE                      (ULONG)0xEEFFEEFF
       #define HEAP_LOCK_USER_ALLOCATED            (ULONG)0x80000000
       #define HEAP_VALIDATE_PARAMETERS_ENABLED    (ULONG)0x40000000
       #define HEAP_VALIDATE_ALL_ENABLED           (ULONG)0x20000000
       #define HEAP_SKIP_VALIDATION_CHECKS         (ULONG)0x10000000
       #define HEAP_CAPTURE_STACK_BACKTRACES       (ULONG)0x08000000

       #define CHECK_HEAP_TAIL_SIZE HEAP_GRANULARITY
       #define CHECK_HEAP_TAIL_FILL 0xAB
       #define FREE_HEAP_FILL 0xFEEEFEEE   <------- MAGIC ;-)
       #define ALLOC_HEAP_FILL 0xBAADF00D  <------- MAGIC
   */

    看起来只要PEB内的NtGlobalFlag为0,就可以避开调试堆,下面是XP SP2下的代码:

    PAGE:004AC2B0 __stdcall MmCreatePeb(x, x, x) proc near
    ......
    PAGE:004AC38A                 mov     ecx, [ebp+var_1C]
    PAGE:004AC38D                 mov     [ecx+2], al
    PAGE:004AC390                 mov     eax, [ebp+var_1C]
    PAGE:004AC393                 mov     ecx, dword ptr _NtGlobalFlag <- 这里
    PAGE:004AC399                 mov     [eax+68h], ecx
    PAGE:004AC39C                 mov     eax, _MmCriticalSectionTimeout
    PAGE:004AC3A1                 mov     ecx, [ebp+var_1C]
    PAGE:004AC3A4                 mov     [ecx+70h], eax

    创建PEB时用的是内核全局变量,用WinDbg可以看到,全局NtGlobalFlag为0,
    但被调试时PEB内的NtGlobalFlag却是70h(如果不做手脚).

    下面是ntdll内的代码:

    VOID
    LdrpInitialize (
       IN PCONTEXT Context,
       IN PVOID SystemArgument1,
       IN PVOID SystemArgument2
    )

    Routine Description:

       This function is called as a User-Mode APC routine as the first
       user-mode code executed by a new thread. It's function is to initialize
       loader context, perform module initialization callouts...

    ......
  

//#if DBG
        if (TRUE)
//#else
//        if (Peb->BeingDebugged || Peb->ReadImageFileExecOptions)
//#endif
        {
            PWSTR pw;

            pw = (PWSTR)Peb->ProcessParameters->ImagePathName.Buffer;
            if (!(Peb->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
                pw = (PWSTR)((PCHAR)pw + (ULONG_PTR)(Peb->ProcessParameters));
                }
            UnicodeImageName.Buffer = pw;
            UnicodeImageName.Length = Peb->ProcessParameters->ImagePathName.Length;
            UnicodeImageName.MaximumLength = UnicodeImageName.Length;

            //
            //  Hack for NT4 SP4.  So we don't overload another GlobalFlag
            //  bit that we have to be "compatible" with for NT5, look for
            //  another value named "DisableHeapLookaside".
            //

            LdrQueryImageFileExecutionOptions( &UnicodeImageName,
                                               L"DisableHeapLookaside",
                                               REG_DWORD,
                                               &RtlpDisableHeapLookaside,
                                               sizeof( RtlpDisableHeapLookaside ),
                                               NULL
                                             );

            st = LdrQueryImageFileExecutionOptions( &UnicodeImageName,
                                                    L"GlobalFlag",
                                                    REG_DWORD,
                                                    &Peb->NtGlobalFlag,
                                                    sizeof( Peb->NtGlobalFlag ),
                                                    NULL
                                                  );
            if (!NT_SUCCESS( st )) {

                if (Peb->BeingDebugged) {

                    /***  PEB.NtGlobalFlag被改写了 ***/

                    Peb->NtGlobalFlag |= FLG_HEAP_ENABLE_FREE_CHECK |
                                         FLG_HEAP_ENABLE_TAIL_CHECK |
                                         FLG_HEAP_VALIDATE_PARAMETERS;
                    }
                }

#if defined(_WIN64)
            NtHeader = RtlImageNtHeader(Peb->ImageBaseAddress);
            if (NtHeader && NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
                UseWOW64 = TRUE;
            }
#endif
        }

        if ( Peb->NtGlobalFlag & FLG_HEAP_PAGE_ALLOCS ) {

            //
            // We will enable page heap (RtlpDebugPageHeap) only after
            // all other initializations for page heap are finished.
            //

            //
            // If page heap is enabled we need to disable any flag that
            // might force creation of debug heaps for normal NT heaps.
            // This is due to a dependency between page heap and NT heap
            // where the page heap within PageHeapCreate tries to create
            // a normal NT heap to accomodate some of the allocations.
            // If we do not disable these flags we will get an infinite
            // recursion between RtlpDebugPageHeapCreate and RtlCreateHeap.
            //

            /*** PEB.NtGlobalFlag被改写了 ***/

            Peb->NtGlobalFlag &= ~( FLG_HEAP_ENABLE_TAGGING      |
                FLG_HEAP_ENABLE_TAG_BY_DLL   |
                FLG_HEAP_ENABLE_TAIL_CHECK   |
                FLG_HEAP_ENABLE_FREE_CHECK   |
                FLG_HEAP_VALIDATE_PARAMETERS |
                FLG_HEAP_VALIDATE_ALL        |
                FLG_USER_STACK_TRACE_DB      );

  
  
大致就是这么个意思, 如果PEB.BeginDebugged为TRUE,就要使用调试堆. 至于
BeingDebugged在哪里被置1(或者能否不让其被置1,怀疑会影响调试)就没有管
了,在SoftICE里试了一下,在这里patch就行.

WinXP SP1/SP2,都在LdrpInitializeExecutionOptions内,下面是SP2的:

.text:7C942D56 __stdcall LdrpInitializeExecutionOptions(x, x) proc near
.text:7C942D56
.text:7C942D56                 mov     edi, edi
.text:7C942D58                 push    ebp
.text:7C942D59                 mov     ebp, esp
.text:7C942D5B                 sub     esp, 40h
.text:7C942D5E                 mov     eax, ___security_cookie
.text:7C942D63                 push    ebx
.text:7C942D64                 push    esi
.text:7C942D65                 mov     esi, [ebp+arg_4]
.text:7C942D68                 push    edi
.text:7C942D69                 mov     edi, [ebp+arg_0]
.text:7C942D6C                 mov     [ebp+var_4], eax
.text:7C942D6F                 lea     eax, [ebp+var_2C]
.text:7C942D72                 push    eax
.text:7C942D73                 xor     ebx, ebx
.text:7C942D75                 push    edi
.text:7C942D76                 mov     [ebp+var_38], edi
.text:7C942D79                 mov     [ebp+var_25], bl
.text:7C942D7C                 call    LdrpOpenImageFileOptionsKey(x,x)
.text:7C942D81                 test    eax, eax
.text:7C942D83                 jge     sub_7C95DE3C
.text:7C942D89                 test    dword ptr [esi+68h], 2000100h
.text:7C942D90                 jnz     sub_7C95DF14
.text:7C942D96                 cmp     [esi+2], bl   <----- BeginDebugged
.text:7C942D99                 jnz     loc_7C95DF23  <----- NOP这6字节
.text:7C942D9F
.text:7C942D9F loc_7C942D9F:                           
.text:7C942D9F                 mov     ecx, [ebp+var_4]
.text:7C942DA2                 mov     al, [ebp+var_25]
.text:7C942DA5                 pop     edi
.text:7C942DA6                 pop     esi
.text:7C942DA7                 pop     ebx
.text:7C942DA8                 call    __security_check_cookie(x)
.text:7C942DAD                 leave
.text:7C942DAE                 retn    8

Patch位置在SP1和SP2下不大相同,似乎没有合适的函数作搜索跳板,我是直接在
text区段找的,只有1处满足类似下面的代码序列:

.text:7C942D89                 test    dword ptr [esi+68h], 2000100h
.text:7C942D90                 jnz     sub_7C95DF14
.text:7C942D96                 cmp     [esi+2], bl
.text:7C942D99                 jnz     loc_7C95DF23

   
我这里是Hook了OD自己的WaitForDebugEvent的, 这样patch就行:

BOOL __stdcall COllyDbg::NewWaitForDebugEvent(
        LPDEBUG_EVENT lpDebugEvent,
        DWORD dwMilliseconds
        )
{
        DWORD   dwOldProt;
        DWORD   dwNewProt = PAGE_EXECUTE_READWRITE;
        BYTE    nops[6] = {0x90,0x90,0x90,0x90,0x90,0x90};
        ........

        switch(lpDebugEvent->dwDebugEventCode)
        {
        case CREATE_PROCESS_DEBUG_EVENT:
               
                if(m_PatchDebugHeap)
                {
                     VirtualProtectEx(lpDebugEvent->u.CreateProcessInfo.hProcess,
                                      (PVOID)m_PatchDebugHeap,
                                      6,
                                      dwNewProt,
                                      &dwOldProt)
                                      );
                       
                     WriteProcessMemory(lpDebugEvent->u.CreateProcessInfo.hProcess,
                                          (PVOID)m_PatchDebugHeap,
                                       nops,
                                       6,
                                       NULL
                                       );

                     VirtualProtectEx(lpDebugEvent->u.CreateProcessInfo.hProcess,
                                         (PVOID)m_PatchDebugHeap,
                                      6,
                                      dwOldProt,
                                      &dwNewProt
                                      );
                  }
                break;

               ......

m_PatchDebugHeap就是搜索出的patch地址.终于清静了;-)

【看雪培训】《Adroid高级研修班》2022年夏季班招生中!

收藏
点赞0
打赏
分享
最新回复 (26)
雪    币: 1018
活跃值: 活跃值 (9934)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
kanxue 活跃值 8 2007-11-9 22:19
2
0
太强大了,原来牛人的思维都是一致的。
前段时间,fotgot建议我逆一下PhantOm,可以冶本。
现在softworm能把其解决,省事了,以后直接拿来用了。;)
雪    币: 707
活跃值: 活跃值 (823)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
NONAME剑人 活跃值 3 2007-11-9 22:20
3
0
膜拜....
完全看不懂
雪    币: 226
活跃值: 活跃值 (11)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
heXer 活跃值 3 2007-11-9 22:42
4
0
让SM起火是很危险的
雪    币: 264
活跃值: 活跃值 (385)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12321 活跃值 2007-11-9 23:56
5
0
强贴留名,做个标记
雪    币: 5536
活跃值: 活跃值 (56)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
forgot 活跃值 26 2007-11-10 00:14
6
0
NtGlobalFlags 起火还是 BeingDebugged 放的,
在 LOAD_DLL EVENT 杀死它,过一会复活,复活起来再杀死就行了。
雪    币: 24971
活跃值: 活跃值 (2828)
能力值: ( LV15,RANK:3306 )
在线值:
发帖
回帖
粉丝
风间仁 活跃值 19 2007-11-10 01:55
7
0
留个脚印
雪    币: 185
活跃值: 活跃值 (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
沙加 活跃值 1 2007-11-10 08:52
8
0
好象用HIDE DEBUGGER 1.2.4插件就可以了
雪    币: 270
活跃值: 活跃值 (74)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
cxhcxh 活跃值 3 2007-11-10 09:02
9
0
起火了
小心烧死料
雪    币: 203
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
soychino 活跃值 2007-11-10 12:29
10
0
强大,学习!
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mydear256 活跃值 2007-11-10 13:35
11
0
无语啊,膜拜。
雪    币: 106
活跃值: 活跃值 (464)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
TeLeMan 活跃值 1 2007-11-10 13:40
12
0
XP系统上标准关闭调试堆的方法,设置环境变量_NO_DEBUG_HEAP=1即可。
雪    币: 205
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
okdodo 活跃值 2 2007-11-10 13:58
13
0
这个干脆  
雪    币: 246
活跃值: 活跃值 (11)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
Isaiah 活跃值 10 2007-11-10 14:09
14
0
学习看源码~
雪    币: 521
活跃值: 活跃值 (151)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
笨笨雄 活跃值 14 2007-11-10 18:34
15
0
看来反调试的时候也得把这个搞一下
雪    币: 217
活跃值: 活跃值 (36)
能力值: ( LV9,RANK:1210 )
在线值:
发帖
回帖
粉丝
softworm 活跃值 30 2007-11-10 20:57
16
0
这个我google时搜到了,不过更喜欢改代码
雪    币: 200
活跃值: 活跃值 (13)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
shoooo 活跃值 16 2007-11-10 21:04
17
0
SM起火了,太可怕了
雪    币: 231
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
heiyelang 活跃值 2007-11-10 21:12
18
0
这么好的帖子看不懂
但是慢慢会看懂的啊 ·~~
雪    币: 206
活跃值: 活跃值 (46)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
鸡蛋壳 活跃值 2007-11-11 09:47
19
0
一个壳怎么可能随意更改系统设置,这样的壳,还能混?
雪    币: 200
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hnld 活跃值 2007-11-11 16:47
20
0
太强了,虽然看不太懂。牛人
雪    币: 100
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
紫色的魚 活跃值 2007-11-13 11:11
21
0
暂时看不懂 留个名
雪    币: 201
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huhonghui 活跃值 2007-11-18 22:24
22
0
完全是天书,收藏一下待以后用
雪    币: 208
活跃值: 活跃值 (10)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
saga 活跃值 2 2007-11-21 11:26
23
0
佩服ing   希望有天自己也牛逼啊
雪    币: 1298
活跃值: 活跃值 (63)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
yijun8354 活跃值 12 2007-11-21 12:12
24
0
厉害,膜拜中~~~
雪    币: 25
活跃值: 活跃值 (11)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
xIkUg 活跃值 9 2007-11-22 17:26
25
0
很好,很强大
游客
登录 | 注册 方可回帖
返回