元始天尊 发表于 2014-10-14 11:12:10

重识new之四

本帖最后由 元始天尊 于 2014-10-14 23:12 编辑

_malloca
void* _malloca(size_t size);
    MSDN里是这么描述的:在栈上分配内存,是_alloca的安全性增强版本。返回指针是根据对象大小对齐,如果size是0则返回长度0的合法指针。如果地址空间无法分配会抛出一个栈溢出异常,该异常不是C++异常,需要使用SEH。_malloca和_alloca的区别在于_alloc无论大小总是在栈上分配,且无需free释放内存。而_malloca需要使用_freea释放内存,在调试模式下,_malloca总是在堆上分配。在异常处理时显式调用_malloca有一些限制,x86架构处理器异常处理例程会自动控制函数栈帧,在执行操作时并不基于当前闭合函数栈帧,这一点在Windows NT SEH和C++异常处理的catch语句中很常见。因此在以下情况显示调用_malloca,在执行异常处理例程后会产生程序崩溃。
        Windows NT SEH异常过滤表达式:__except(_malloca())
        Windows NT SEH最终执行表达式:__finally(_malloca())
        C++ 异常处理 catch语句
    然而_malloca可以从异常处理例程中除上述情况以外的情况下直接调用,或在异常处理所触发的回调函数中调用也是允许的。
先来看一个例子:

#include <windows.h>
#include <stdio.h>
#include <malloc.h>

int main()
{
    int   size;
    int   numberRead = 0;
    int   errcode = 0;
    void    *p = NULL;
    void    *pMarker = NULL;

    while (numberRead == 0)
    {
      printf_s("Enter the number of bytes to allocate "
               "using _malloca: ");
      numberRead = scanf_s("%d", &size);
    }

    // Do not use try/catch for _malloca,
    // use __try/__except, since _malloca throws
    // Structured Exceptions, not C++ exceptions.

    __try
    {
      if (size > 0)
      {
            p =_malloca( size );
      }
      else
      {
            printf_s("Size must be a positive number.");
      }
      _freea( p );
    }

    // Catch any exceptions that may occur.
    __except( GetExceptionCode() == STATUS_STACK_OVERFLOW )
    {
      printf_s("_malloca failed!\n");

      // If the stack overflows, use this function to restore.
      errcode = _resetstkoflw();
      if (errcode)
      {
            printf("Could not reset the stack!");
            _exit(1);
      }
    };
}

input:1000
output:Enter the number of bytes to allocate using _malloca: 1000

VS中在_malloca上右键转到定义,发现源码位于malloc.h

#define _ALLOCA_S_THRESHOLD   1024
#define _ALLOCA_S_STACK_MARKER0xCCCC
#define _ALLOCA_S_HEAP_MARKER   0xDDDD

#if defined(_M_IX86)
#define _ALLOCA_S_MARKER_SIZE   8
#elif defined(_M_X64)
#define _ALLOCA_S_MARKER_SIZE   16
#elif defined(_M_ARM)
#define _ALLOCA_S_MARKER_SIZE   8
#elif !defined (RC_INVOKED)
#error Unsupported target platform.
#endif

......

#if !defined(__midl) && !defined(RC_INVOKED)
#pragma warning(push)
#pragma warning(disable:6540)
__inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker)
{
    if (_Ptr)
    {
      *((unsigned int*)_Ptr) = _Marker;
      _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
    }
    return _Ptr;
}
#pragma warning(pop)
#endif

#if defined(_DEBUG)
#if !defined(_CRTDBG_MAP_ALLOC)
#undef _malloca
#define _malloca(size) \
__pragma(warning(suppress: 6255)) \
      _MarkAllocaS(malloc((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER)
#endif
#else
#undef _malloca
#define _malloca(size) \
__pragma(warning(suppress: 6255)) \
    ((((size) + _ALLOCA_S_MARKER_SIZE) <= _ALLOCA_S_THRESHOLD) ? \
      _MarkAllocaS(_alloca((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_STACK_MARKER) : \
      _MarkAllocaS(malloc((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER))
#endif


    可以分析出DEBUG版下,宏调用了malloc进行分配,之后使用_MarkAllocaS对分配内存进行一些处理(后面讨论),而RELEASE版下,宏先判断要分配的内存是否过大,该门限为_ALLOCA_S_THRESHOLD-_ALLOCA_S_MARKER_SIZE=1016,如果超过该值则调用malloc,否则调用_alloca。从字面意思上可以知道_ALLOCA_S_HEAP_MARKER这个标志位说明该内存区是在堆上分配的,而_ALLOCA_S_STACK_MARKER标志是在栈上分配的。在malloc或_alloca分配成功后,总会调用_MarkAllocaS进行调整。结合字面意思和5行C语言代码可知,在执行过内存分配后,返回的指针前sizeof(unigned int*)字节为分配内存类型标志,之后指针调整到空闲位置丢给用户操作。那么所有的问题都落在_alloca和malloc的源码上,下面会进行分析。

_alloca(我第一次见栈上分配内存是在逆向一个易语言程序时,用的是sub esp,而微软这个函数是第一次见)
void* _alloca(size_t size);
该函数只在程序栈中分配字节,而函数退出时该空间会自动释放,因此无需手动释放。用此函数的限制和_malloca相同。

#include <windows.h>
#include <stdio.h>
#include <malloc.h>

int main()
{
    int   size = 1000;
    int   errcode = 0;
    void    *pData = NULL;

    // 注意:不要使用try/catch,而要使用__try/__except,因为_alloca抛出SEH而不是C++异常
    __try {
      // 使用_alloca分配太大的空间很容易崩溃,推荐1024字节以下的空间
      if (size > 0 && size < 1024)
      {
            pData = _alloca( size );
            printf_s( "Allocated %d bytes of stack at 0x%p",
                      size, pData);
      }
      else
      {
            printf_s("Tried to allocate too many bytes.\n");
      }
    }

    // 如果溢出
    __except( GetExceptionCode() == STATUS_STACK_OVERFLOW )
    {
      printf_s("_alloca failed!\n");

      // 使用下面的函数恢复函数栈
      errcode = _resetstkoflw();
      if (errcode)
      {
            printf_s("Could not reset the stack!\n");
            _exit(1);
      }
    };
}

反汇编得到:

.text:004113F0                         ; int __cdecl main()
.text:004113F0                         _main         proc near               ; CODE XREF: j__mainj
.text:004113F0
.text:004113F0                         pAllocaBase   = dword ptr -120h
.text:004113F0                         cbSize          = dword ptr -11Ch
.text:004113F0                         var_114         = dword ptr -114h
.text:004113F0                         allocaList      = dword ptr -48h
.text:004113F0                         pData         = dword ptr -3Ch
.text:004113F0                         errcode         = dword ptr -30h
.text:004113F0                         size            = dword ptr -24h
.text:004113F0                         var_1C          = dword ptr -1Ch
.text:004113F0                         ms_exc          = CPPEH_RECORD ptr -18h
.text:004113F0
.text:004113F0 55                                    push    ebp
.text:004113F1 8B EC                                 mov   ebp, esp
.text:004113F3 6A FE                                 push    0FFFFFFFEh
.text:004113F5 68 80 6F 41 00                        push    offset stru_416F80
.text:004113FA 68 82 10 41 00                        push    offset j___except_handler4
.text:004113FF 64 A1 00 00 00 00                     mov   eax, large fs:0
.text:00411405 50                                    push    eax
.text:00411406 81 C4 F0 FE FF FF                     add   esp, 0FFFFFEF0h
.text:0041140C 53                                    push    ebx
.text:0041140D 56                                    push    esi
.text:0041140E 57                                    push    edi
.text:0041140F 8D BD E0 FE FF FF                     lea   edi,
.text:00411415 B9 42 00 00 00                        mov   ecx, 42h
.text:0041141A B8 CC CC CC CC                        mov   eax, 0CCCCCCCCh
.text:0041141F F3 AB                                 rep stosd
.text:00411421 A1 00 80 41 00                        mov   eax, ___security_cookie
.text:00411426 31 45 F8                              xor   , eax
.text:00411429 33 C5                                 xor   eax, ebp
.text:0041142B 89 45 E4                              mov   , eax
.text:0041142E 50                                    push    eax
.text:0041142F 8D 45 F0                              lea   eax,
.text:00411432 64 A3 00 00 00 00                     mov   large fs:0, eax
.text:00411438 89 65 E8                              mov   , esp
.text:0041143B C7 45 B8 00 00 00 00                  mov   , 0
.text:00411442 C7 45 DC E8 03 00 00                  mov   , 3E8h
.text:00411449 C7 45 D0 00 00 00 00                  mov   , 0
.text:00411450 C7 45 C4 00 00 00 00                  mov   , 0
.text:00411457 C7 45 FC 00 00 00 00                  mov   , 0
.text:0041145E 83 7D DC 00                           cmp   , 0
.text:00411462 7E 6F                                 jle   short loc_4114D3
.text:00411464 81 7D DC 00 04 00 00                  cmp   , 400h
.text:0041146B 7D 66                                 jge   short loc_4114D3
.text:0041146D 8B 45 DC                              mov   eax,
.text:00411470 83 C0 24                              add   eax, 24h
.text:00411473 89 85 E4 FE FF FF                     mov   , eax
.text:00411479 8B 85 E4 FE FF FF                     mov   eax,
.text:0041147F E8 4E FC FF FF                        call    j___alloca_probe_16
.text:00411484 89 A5 E0 FE FF FF                     mov   , esp
.text:0041148A 89 65 E8                              mov   , esp
.text:0041148D 8D 4D B8                              lea   ecx,
.text:00411490 51                                    push    ecx             ; pAllocaInfoList
.text:00411491 8B 95 E4 FE FF FF                     mov   edx, ; cbSize
.text:00411497 8B 8D E0 FE FF FF                     mov   ecx, ; pAllocaBase
.text:0041149D E8 90 FB FF FF                        call    j_@_RTC_AllocaHelper@12 ; _RTC_AllocaHelper(x,x,x)
.text:004114A2 83 85 E0 FE FF FF 20                  add   , 20h
.text:004114A9 8B 95 E0 FE FF FF                     mov   edx,
.text:004114AF 89 55 C4                              mov   , edx
.text:004114B2 8B F4                                 mov   esi, esp
.text:004114B4 8B 45 C4                              mov   eax,
.text:004114B7 50                                    push    eax
.text:004114B8 8B 4D DC                              mov   ecx,
.text:004114BB 51                                    push    ecx
.text:004114BC 68 58 58 41 00                        push    offset Format   ; "Allocated %d bytes of stack at 0x%p"
.text:004114C1 FF 15 C0 92 41 00                     call    ds:__imp__printf_s
.text:004114C7 83 C4 0C                              add   esp, 0Ch
.text:004114CA 3B F4                                 cmp   esi, esp
.text:004114CC E8 79 FC FF FF                        call    j___RTC_CheckEsp
.text:004114D1 EB 17                                 jmp   short loc_4114EA
.text:004114D3                         ; ---------------------------------------------------------------------------
.text:004114D3
.text:004114D3                         loc_4114D3:                           ; CODE XREF: _main+72j
.text:004114D3                                                               ; _main+7Bj
.text:004114D3 8B F4                                 mov   esi, esp
.text:004114D5 68 84 58 41 00                        push    offset aTriedToAllocat ; "Tried to allocate too many bytes.\n"
.text:004114DA FF 15 C0 92 41 00                     call    ds:__imp__printf_s
.text:004114E0 83 C4 04                              add   esp, 4
.text:004114E3 3B F4                                 cmp   esi, esp
.text:004114E5 E8 60 FC FF FF                        call    j___RTC_CheckEsp
.text:004114EA
.text:004114EA                         loc_4114EA:                           ; CODE XREF: _main+E1j
.text:004114EA C7 45 FC FE FF FF FF                  mov   , 0FFFFFFFEh
.text:004114F1 E9 97 00 00 00                        jmp   loc_41158D
.text:004114F6                         ; ---------------------------------------------------------------------------
.text:004114F6
.text:004114F6                         $LN10:                                  ; DATA XREF: .rdata:stru_416F80o
.text:004114F6 8B 45 EC                              mov   eax, ; Exception filter 0 for function 4113F0
.text:004114F9 8B 08                                 mov   ecx,
.text:004114FB 8B 11                                 mov   edx,
.text:004114FD 89 95 EC FE FF FF                     mov   , edx
.text:00411503 81 BD EC FE FF FF FD 00+                cmp   , 0C00000FDh
.text:0041150D 75 0C                                 jnz   short loc_41151B
.text:0041150F C7 85 E4 FE FF FF 01 00+                mov   , 1
.text:00411519 EB 0A                                 jmp   short loc_411525
.text:0041151B                         ; ---------------------------------------------------------------------------
.text:0041151B
.text:0041151B                         loc_41151B:                           ; CODE XREF: _main+11Dj
.text:0041151B C7 85 E4 FE FF FF 00 00+                mov   , 0
.text:00411525
.text:00411525                         loc_411525:                           ; CODE XREF: _main+129j
.text:00411525 8B 85 E4 FE FF FF                     mov   eax,
.text:0041152B
.text:0041152B                         $LN12:
.text:0041152B C3                                    retn
.text:0041152C                         ; ---------------------------------------------------------------------------
.text:0041152C
.text:0041152C                         $LN11:                                  ; DATA XREF: .rdata:stru_416F80o
.text:0041152C 8B 65 E8                              mov   esp, ; Exception handler 0 for function 4113F0
.text:0041152F 8B F4                                 mov   esi, esp
.text:00411531 68 B0 58 41 00                        push    offset a_allocaFailed ; "_alloca failed!\n"
.text:00411536 FF 15 C0 92 41 00                     call    ds:__imp__printf_s
.text:0041153C 83 C4 04                              add   esp, 4
.text:0041153F 3B F4                                 cmp   esi, esp
.text:00411541 E8 04 FC FF FF                        call    j___RTC_CheckEsp
.text:00411546 8B F4                                 mov   esi, esp
.text:00411548 FF 15 BC 92 41 00                     call    ds:__imp___resetstkoflw
.text:0041154E 3B F4                                 cmp   esi, esp
.text:00411550 E8 F5 FB FF FF                        call    j___RTC_CheckEsp
.text:00411555 89 45 D0                              mov   , eax
.text:00411558 83 7D D0 00                           cmp   , 0
.text:0041155C 74 28                                 jz      short loc_411586
.text:0041155E 8B F4                                 mov   esi, esp
.text:00411560 68 C4 58 41 00                        push    offset aCouldNotResetT ; "Could not reset the stack!\n"
.text:00411565 FF 15 C0 92 41 00                     call    ds:__imp__printf_s
.text:0041156B 83 C4 04                              add   esp, 4
.text:0041156E 3B F4                                 cmp   esi, esp
.text:00411570 E8 D5 FB FF FF                        call    j___RTC_CheckEsp
.text:00411575 8B F4                                 mov   esi, esp
.text:00411577 6A 01                                 push    1               ; Code
.text:00411579 FF 15 C8 92 41 00                     call    ds:__imp___exit
.text:0041157F                         ; ---------------------------------------------------------------------------
.text:0041157F 3B F4                                 cmp   esi, esp
.text:00411581 E8 C4 FB FF FF                        call    j___RTC_CheckEsp
.text:00411586
.text:00411586                         loc_411586:                           ; CODE XREF: _main+16Cj
.text:00411586 C7 45 FC FE FF FF FF                  mov   , 0FFFFFFFEh
.text:0041158D
.text:0041158D                         loc_41158D:                           ; CODE XREF: _main+101j
.text:0041158D EB 02                                 jmp   short loc_411591
.text:0041158F                         ; ---------------------------------------------------------------------------
.text:0041158F EB 02                                 jmp   short loc_411593
.text:00411591                         ; ---------------------------------------------------------------------------
.text:00411591
.text:00411591                         loc_411591:                           ; CODE XREF: _main:loc_41158Dj
.text:00411591 33 C0                                 xor   eax, eax
.text:00411593
.text:00411593                         loc_411593:                           ; CODE XREF: _main+19Fj
.text:00411593 52                                    push    edx
.text:00411594 8B CD                                 mov   ecx, ebp      ; frame
.text:00411596 50                                    push    eax
.text:00411597 8D 15 CC 15 41 00                     lea   edx, v          ; v
.text:0041159D FF 75 B8                              push    ; allocaList
.text:004115A0 E8 9B FB FF FF                        call    j_@_RTC_CheckStackVars2@12 ; _RTC_CheckStackVars2(x,x,x)
.text:004115A5 58                                    pop   eax
.text:004115A6 5A                                    pop   edx
.text:004115A7 8D A5 D0 FE FF FF                     lea   esp,
.text:004115AD 8B 4D F0                              mov   ecx,
.text:004115B0 64 89 0D 00 00 00 00                  mov   large fs:0, ecx
.text:004115B7 59                                    pop   ecx
.text:004115B8 5F                                    pop   edi
.text:004115B9 5E                                    pop   esi
.text:004115BA 5B                                    pop   ebx
.text:004115BB 8B 4D E4                              mov   ecx,
.text:004115BE 33 CD                                 xor   ecx, ebp      ; cookie
.text:004115C0 E8 59 FA FF FF                        call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:004115C5 8B E5                                 mov   esp, ebp
.text:004115C7 5D                                    pop   ebp
.text:004115C8 C3                                    retn

关键代码为:
.text:0041146D 8B 45 DC                              mov   eax,
.text:00411470 83 C0 24                              add   eax, 24h
.text:00411473 89 85 E4 FE FF FF                     mov   , eax
.text:00411479 8B 85 E4 FE FF FF                     mov   eax,
.text:0041147F E8 4E FC FF FF                        call    j___alloca_probe_16
.text:00411484 89 A5 E0 FE FF FF                     mov   , esp
.text:0041148A 89 65 E8                              mov   , esp
.text:0041148D 8D 4D B8                              lea   ecx,
.text:00411490 51                                    push    ecx             ; pAllocaInfoList
.text:00411491 8B 95 E4 FE FF FF                     mov   edx, ; cbSize
.text:00411497 8B 8D E0 FE FF FF                     mov   ecx, ; pAllocaBase
.text:0041149D E8 90 FB FF FF                        call    j_@_RTC_AllocaHelper@12 ; _RTC_AllocaHelper(x,x,x)
.text:004114A2 83 85 E0 FE FF FF 20                  add   , 20h
.text:004114A9 8B 95 E0 FE FF FF                     mov   edx,
.text:004114AF 89 55 C4                              mov   , edx
很疑惑地,在__alloca_probe_16调用之前,发生了add eax,24h和mov eax,,而在之后发生了mov , esp
那么大胆做出猜测:
1.add eax,24h,说明这24h字节用来实现内存管理或字节对齐之类功能
2.__alloca_probe_16为接受一个参数的函数,该参数通过eax传递,进行的操作是修改esp,因此esp可以看做执行结果
3.另一个函数原型为void __fastcall _RTC_AllocaHelper(_RTC_ALLOCA_NODE *pAllocaBase, unsigned int cbSize, _RTC_ALLOCA_NODE **pAllocaInfoList)


.text:004116A0                         ; void __fastcall _RTC_AllocaHelper(_RTC_ALLOCA_NODE *pAllocaBase, unsigned int cbSize, _RTC_ALLOCA_NODE **pAllocaInfoList)
.text:004116A0                         @_RTC_AllocaHelper@12 proc near         ; CODE XREF: _RTC_AllocaHelper(x,x,x)j
.text:004116A0
.text:004116A0                         pAllocaInfoList = dword ptr8
.text:004116A0
.text:004116A0                         pAllocaBase = ecx
.text:004116A0                         cbSize = edx
.text:004116A0 55                                    push    ebp
.text:004116A1 8B EC                                 mov   ebp, esp
.text:004116A3 53                                    push    ebx
.text:004116A4 56                                    push    esi
.text:004116A5 8B F1                                 mov   esi, pAllocaBase
.text:004116A7 8B DA                                 mov   ebx, cbSize
.text:004116A9 85 F6                                 test    esi, esi
.text:004116AB 74 1F                                 jz      short loc_4116CC
.text:004116AD 85 DB                                 test    ebx, ebx
.text:004116AF 74 1B                                 jz      short loc_4116CC
.text:004116B1 8B 55 08                              mov   cbSize,
.text:004116B4 85 D2                                 test    cbSize, cbSize
.text:004116B6 74 14                                 jz      short loc_4116CC
.text:004116B8 57                                    push    edi
.text:004116B9 B0 CC                                 mov   al, 0CCh
.text:004116BB 8B FE                                 mov   edi, esi
.text:004116BD 8B CB                                 mov   pAllocaBase, ebx
.text:004116BF F3 AA                                 rep stosb
.text:004116C1 8B 02                                 mov   eax,
.text:004116C3 89 46 04                              mov   , eax
.text:004116C6 89 5E 0C                              mov   , ebx
.text:004116C9 89 32                                 mov   , esi
.text:004116CB 5F                                    pop   edi
.text:004116CC
.text:004116CC                         loc_4116CC:                           ; CODE XREF: _RTC_AllocaHelper(x,x,x)+Bj
.text:004116CC                                                               ; _RTC_AllocaHelper(x,x,x)+Fj ...
.text:004116CC 5E                                    pop   esi
.text:004116CD 5B                                    pop   ebx
.text:004116CE 5D                                    pop   ebp
.text:004116CF C2 04 00                              retn    4
.text:004116CF                         @_RTC_AllocaHelper@12 endp


翻译可得:

void main()
{
        ......
        int size=1024,cbsize=size+sizeof(_RTC_ALLOCA_NODE)+4;
        _RTC_ALLOCA_NODE* pAllocaBase=__alloca_probe_16(cbsize);
        _RTC_AllocaHelper(pAllocaBase,cbsize,NULL);
        void* pData=(void*)(pAllocaBase+1);
        ......
}

//这个结构体来自于reactos
#pragma pack(push,1)
typedef struct _RTC_ALLOCA_NODE
{
       __int32 guard1;
        struct _RTC_ALLOCA_NODE *next;
        #if (defined(_X86_) && !defined(__x86_64))
                __int32 dummypad;
        #endif
                size_t allocaSize;
        #if (defined(_X86_) && !defined(__x86_64))
                __int32 dummypad2;
        #endif
                __int32 guard2;
}_RTC_ALLOCA_NODE;
#pragma pack(pop)

void __fastcall _RTC_AllocaHelper(_RTC_ALLOCA_NODE *pAllocaBase, unsigned int cbSize, _RTC_ALLOCA_NODE **pAllocaInfoList)
{//初始化已分配空间,可以用于维护调试版函数栈
if(pAllocaBase && cbSize && pAllocaInfoList)//由于最后一个参数在本例中为0,这个函数实际相当于没有执行
{
   memset(pAllocaBase,0xCC,cbSize);//经常在调试版程序栈空间看到0xCC"烫烫烫烫烫"对吧,就是这样的。。。
   pAllocaBase->next=*pAllocaInfoList;//链接到前一个结构;
   pAllocaBase->allocaSize=cbSize;
   *pAllocaInfoList=pAllocaBase;//自此可知,上述结构形成链表,pAllocaInfoList指向当前结构
}
}

//__alloca_probe_16代码下面会进行分析



以上是debug版的情况,如果尝试用release版查看反汇编代码,会发现只有push和call __alloca_probe_16部分,可知add eax,24和AllocaHelper只是调试版本用于内存管理的。所以重点落在该函数的解析上

进入源代码查看,__alloca_probe_16用来按16字节对齐内存,而chkstk子例程进行实际分配操作:
alloca16.asm

; _alloca_probe_16, _alloca_probe_8 - 按8/16字节对齐例程
;输入:EAX = 栈帧大小
;输出:调整EAX,修改esp.

public_alloca_probe_8
_alloca_probe_16 proc                   ; 16 byte aligned alloca

      push    ecx
      lea   ecx, + 8          ; 父函数栈顶(call _alloca_probe_16和push ecx)
      sub   ecx, eax                ;
      and   ecx, (16 - 1)         ; 计算地址低4位未对齐偏移
      add   eax, ecx                ; 增加cbSize使其对齐
      sbb   ecx, ecx                ; 如果cbSize溢出,ecx = 0xFFFFFFFF,否则ecx = 0
      or      eax, ecx                ; 如果溢出,则eax = 0xFFFFFFFF
      pop   ecx                     ; 还原ecx
      jmp   _chkstk                        ; eax存储修正cbSize,并交给_chkstk处理
_alloca_probe_16 endp

      end


public_alloca_probe
_chkstk proc
_alloca_probe    =_chkstk
      push    ecx
      lea   ecx, + 8 - 4      ; 考虑到之后的ret指令对未来esp的修改
      sub   ecx, eax                ; 分配栈空间,ecx存储更新后的栈位置
      sbb   eax, eax                ; 如果申请空间过大,eax = 0xFFFFFFFF,否则eax = 0
      not   eax                     ;
      and   ecx, eax                ; ecx = 0 | ecx = ecx
      mov   eax, esp                ;
      and   eax, not ( _PAGESIZE_ - 1) ; 得到当前栈位置所处页面地址
cs10:
      cmp   ecx, eax                ;
      jb      short cs20            ; 如果新的栈位置小于页面地址
      mov   eax, ecx                ;
      pop   ecx
      xchg    esp, eax                ; 更新esp,原始esp存储在eax中
      mov   eax, dword ptr     ; 当前esp指向返回地址
      mov   dword ptr , eax    ; 修正函数栈帧,使其可以正确返回
      ret
cs20:
      sub   eax, _PAGESIZE_         ; 获取上一个页面
      test    dword ptr ,eax   ; 探测页面权限
      jmp   short cs10                ; 如果没有产生异常则跳转,如果出现异常,则直接进入父函数的异常处理中

_chkstk endp
      end



calloc
void* calloc(size_t num,size_t size);
    calloc用来分配数组空间,同样返回指针是根据对象类型对齐的,每个对象都被初始化为0,如果待分配内存超过_HEAP_MAXREQ或分配失败则设置errno为ENOMEM,calloc内部调用了malloc函数使用_set_new_mode函数设置回调模式,该回调用于处理分配失败情况,默认情况下,分配失败后malloc不会调用新回调分配内存,然后我们可以通过提前调用_set_new_mode(1)或者链接NEWMODE.OBJ修改这种默认行为.calloc用法如下:

// crt_calloc.c
// This program uses calloc to allocate space for
// 40 long integers. It initializes each element to zero.

#include <stdio.h>
#include <malloc.h>

int main( void )
{
   long *buffer;

   buffer = (long *)calloc( 40, sizeof( long ) );
   if( buffer != NULL )
      printf( "Allocated 40 long integers\n" );
   else
      printf( "Can't allocate memory\n" );
   free( buffer );
}

其源码分别位于calloc.c和calloc_impl.c:

void * __cdecl _calloc_base (size_t num, size_t size)
{
    int errno_tmp = 0;
    void * pv = _calloc_impl(num, size, &errno_tmp);

    if ( pv == NULL && errno_tmp != 0 && _errno())
    {
      errno = errno_tmp; // recall, #define errno *_errno()
    }
    return pv;
}
void * __cdecl _calloc_impl (size_t num, size_t size, int * errno_tmp)
{
      size_tsize_orig;
      void *pvReturn;

      /* ensure that (size * num) does not overflow */
      if (num > 0)
      {
            _VALIDATE_RETURN_NOEXC((_HEAP_MAXREQ / num) >= size, ENOMEM, NULL);
      }
      size_orig = size = size * num;


      /* force nonzero size */
      if (size == 0)
            size = 1;

      for (;;)
      {
            pvReturn = NULL;

            if (size <= _HEAP_MAXREQ)
            {
                if (pvReturn == NULL)
                  pvReturn = HeapAlloc(_crtheap, HEAP_ZERO_MEMORY, size);
            }

            if (pvReturn || _newmode == 0)
            {
                RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, size_orig, 0));
                if (pvReturn == NULL)
                {
                  if ( errno_tmp )
                        *errno_tmp = ENOMEM;
                }
                return pvReturn;
            }

            /* call installed new handler */
            if (!_callnewh(size))
            {
                if ( errno_tmp )
                  *errno_tmp = ENOMEM;
                return NULL;
            }

            /* new handler was successful -- try to allocate again */
      }
}



现在来分析_calloc_impl执行流程:
1.先检查申请大小是否超出门限,若申请大小为0则强制为1
2.使用HeapAlloc分配内存并清零。如果成功则返回,否则执行_callnewh,即定义的失败处理函数,如果该回调函数返回0则原函数返回0退出,如果该回调函数返回非0,则原函数重复执行2直到成功。(_callnewh最终调用了NtQueryInformationProcess 0x24)
可见calloc并没有像MSDN说的那样调用了malloc。。。另外,没看到有异常处理机制。

_expand
用于扩展或缩小已分配内存

用于改变已分配内存区大小
void* _expand(void* memblock,size_t newsize);
    该函数会检测地址的内存权限,如果不通过移动内存无法得到足够的空间,该函数会返回空,该函数不会分配小于请求大小的内存区。该函数不通过移动内存块的方式增缩已分配堆内存,在64位下该函数不会缩小内存区,大小小于16k的内存块都是在低碎片堆中分配的,在这种情况下_expand不会对内存块做任何变动直接返回memblock
同样该函数会检测参数合法性,且size不能超过门限值_HEAP_MAXREQ。


#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>

int main( void )
{
   char *bufchar;
   printf( "Allocate a 512 element buffer\n" );
   if( (bufchar = (char *)calloc( 512, sizeof( char ) )) == NULL )
      exit( 1 );
   printf( "Allocated %d bytes at %Fp\n",
         _msize( bufchar ), (void *)bufchar );
   if( (bufchar = (char *)_expand( bufchar, 1024 )) == NULL )
      printf( "Can't expand" );
   else
      printf( "Expanded block to %d bytes at %Fp\n",
            _msize( bufchar ), (void *)bufchar );
   // Free memory
   free( bufchar );
   exit( 0 );
}

查看_expand源码为,分析过程和前面类似:

void * __cdecl _expand_base (void * pBlock, size_t newsize)
{
      void *      pvReturn;

      size_t oldsize;

      /* validation section */
      _VALIDATE_RETURN(pBlock != NULL, EINVAL, NULL);
      if (newsize > _HEAP_MAXREQ) {
            errno = ENOMEM;
            return NULL;
      }

      if (newsize == 0)
      {
            newsize = 1;
      }

      oldsize = (size_t)HeapSize(_crtheap, 0, pBlock);

      pvReturn = HeapReAlloc(_crtheap, HEAP_REALLOC_IN_PLACE_ONLY, pBlock, newsize);

      if (pvReturn == NULL)
      {
            /* 如果使用了低碎片堆则返回原指针. */
            if (oldsize <= 0x4000 /* 低碎片堆最多申请16KB内存 */
                  && newsize <= oldsize && _is_LFH_enabled())
                pvReturn = pBlock;
            else
                errno = _get_errno_from_oserr(GetLastError());
      }

      if (pvReturn)
      {
            RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));
            RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0));
      }

      return pvReturn;
}


可见_expand函数最终调用了HeapReAlloc函数

一个小插曲:其实这些内存管理的库函数普遍使用debug和release两个版本,debug源码在dbg*.c(pp)中可以找到,同时调试的时候是可以直接定位进去的,而release版函数通常在函数名所在文件,比如delete在delete.cpp中;而debug版源码内存管理函数通常会有一种_CrtMemBlockHeader的结构体,这些都需要自己摸索。
delete

//release版
void operator delete( void * p )
{
    RTCCALLBACK(_RTC_Free_hook, (p, 0));

    free( p );
}
//debug版
void operator delete( void *pUserData )
{
      _CrtMemBlockHeader * pHead;

      RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

      if (pUserData == NULL)
            return;

      _mlock(_HEAP_LOCK);/* block other threads */
      __TRY

            /* get a pointer to memory block header */
            pHead = pHdr(pUserData);

             /* verify block type */
            _ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));

            _free_dbg( pUserData, pHead->nBlockUse );

      __FINALLY
            _munlock(_HEAP_LOCK);/* release other threads */
      __END_TRY_FINALLY

      return;
}

可见delete->free

realloc
分析方法和前面相同

void * __cdecl _realloc_base (void * pBlock, size_t newsize)
{
      void *      pvReturn;
      size_t      origSize = newsize;

      //if ptr is NULL, call malloc
      if (pBlock == NULL)
            return(_malloc_base(newsize));

      //if ptr is nonNULL and size is zero, call free and return NULL
      if (newsize == 0)
      {
            _free_base(pBlock);
            return(NULL);
      }


      for (;;) {

            pvReturn = NULL;
            if (newsize <= _HEAP_MAXREQ)
            {
                if (newsize == 0)
                  newsize = 1;
                pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize);
            }
            else
            {
                _callnewh(newsize);
                errno = ENOMEM;
                return NULL;
            }

            if ( pvReturn || _newmode == 0)
            {
                if (pvReturn)
                {
                  RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));
                  RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0));
                }
                else
                {
                  errno = _get_errno_from_oserr(GetLastError());
                }
                return pvReturn;
            }

            //call installed new handler
            if (!_callnewh(newsize))
            {
                errno = _get_errno_from_oserr(GetLastError());
                return NULL;
            }

            //new handler was successful -- try to allocate again
      }
}

得到realloc->HeapReAlloc

malloc

void * __cdecl _malloc_base (size_t size)
{
    void *res = NULL;

    //validate size
    if (size <= _HEAP_MAXREQ) {
      for (;;) {

            //allocate memory block
            res = _heap_alloc(size);

            //if successful allocation, return pointer to memory
            //if new handling turned off altogether, return NULL

            if (res != NULL)
            {
                break;
            }
            if (_newmode == 0)
            {
                errno = ENOMEM;
                break;
            }

            //call installed new handler
            if (!_callnewh(size))
                break;

            //new handler was successful -- try to allocate again
      }
    } else {
      _callnewh(size);
      errno = ENOMEM;
      return NULL;
    }

    RTCCALLBACK(_RTC_Allocate_hook, (res, size, 0));
    if (res == NULL)
    {
      errno = ENOMEM;
    }
    return res;
}
__forceinline void * __cdecl _heap_alloc (size_t size)

{

    if (_crtheap == 0) {
      _FF_MSGBANNER();    /* write run-time error banner */

      _NMSG_WRITE(_RT_CRT_NOTINIT);/* write message */
      __crtExitProcess(255);/* normally _exit(255) */
    }

    return HeapAlloc(_crtheap, 0, size ? size : 1);
}

得到malloc->_heap_alloc->HeapAlloc

free

void __cdecl _free_base (void * pBlock)
{

      int retval = 0;


      if (pBlock == NULL)
            return;

      RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));

      retval = HeapFree(_crtheap, 0, pBlock);
      if (retval == 0)
      {
            errno = _get_errno_from_oserr(GetLastError());
      }
}

得到free->HeapFree

现在所有问题都集中在了HeapAlloc HeapFree HeapReAlloc上


0xAA55 发表于 2014-10-14 14:31:06

支持!虽然你的技术文档并不能在发表之后就立即有人看到,但是一定会被百度收录,将来就会被很多人看到。
页: [1]
查看完整版本: 重识new之四