找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 4759|回复: 1

重识new之四

[复制链接]
发表于 2014-10-14 11:12:10 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 元始天尊 于 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可以从异常处理例程中除上述情况以外的情况下直接调用,或在异常处理所触发的回调函数中调用也是允许的。
先来看一个例子:

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <malloc.h>

  4. int main()
  5. {
  6.     int     size;
  7.     int     numberRead = 0;
  8.     int     errcode = 0;
  9.     void    *p = NULL;
  10.     void    *pMarker = NULL;

  11.     while (numberRead == 0)
  12.     {
  13.         printf_s("Enter the number of bytes to allocate "
  14.                  "using _malloca: ");
  15.         numberRead = scanf_s("%d", &size);
  16.     }

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

  20.     __try
  21.     {
  22.         if (size > 0)
  23.         {
  24.             p =  _malloca( size );
  25.         }
  26.         else
  27.         {
  28.             printf_s("Size must be a positive number.");
  29.         }
  30.         _freea( p );
  31.     }

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

  36.         // If the stack overflows, use this function to restore.
  37.         errcode = _resetstkoflw();
  38.         if (errcode)
  39.         {
  40.             printf("Could not reset the stack!");
  41.             _exit(1);
  42.         }
  43.     };
  44. }
复制代码

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

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

  1. #define _ALLOCA_S_THRESHOLD     1024
  2. #define _ALLOCA_S_STACK_MARKER  0xCCCC
  3. #define _ALLOCA_S_HEAP_MARKER   0xDDDD

  4. #if defined(_M_IX86)
  5. #define _ALLOCA_S_MARKER_SIZE   8
  6. #elif defined(_M_X64)
  7. #define _ALLOCA_S_MARKER_SIZE   16
  8. #elif defined(_M_ARM)
  9. #define _ALLOCA_S_MARKER_SIZE   8
  10. #elif !defined (RC_INVOKED)
  11. #error Unsupported target platform.
  12. #endif

  13. ......

  14. #if !defined(__midl) && !defined(RC_INVOKED)
  15. #pragma warning(push)
  16. #pragma warning(disable:6540)
  17. __inline void *_MarkAllocaS(_Out_opt_ __crt_typefix(unsigned int*) void *_Ptr, unsigned int _Marker)
  18. {
  19.     if (_Ptr)
  20.     {
  21.         *((unsigned int*)_Ptr) = _Marker;
  22.         _Ptr = (char*)_Ptr + _ALLOCA_S_MARKER_SIZE;
  23.     }
  24.     return _Ptr;
  25. }
  26. #pragma warning(pop)
  27. #endif

  28. #if defined(_DEBUG)
  29. #if !defined(_CRTDBG_MAP_ALLOC)
  30. #undef _malloca
  31. #define _malloca(size) \
  32. __pragma(warning(suppress: 6255)) \
  33.         _MarkAllocaS(malloc((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER)
  34. #endif
  35. #else
  36. #undef _malloca
  37. #define _malloca(size) \
  38. __pragma(warning(suppress: 6255)) \
  39.     ((((size) + _ALLOCA_S_MARKER_SIZE) <= _ALLOCA_S_THRESHOLD) ? \
  40.         _MarkAllocaS(_alloca((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_STACK_MARKER) : \
  41.         _MarkAllocaS(malloc((size) + _ALLOCA_S_MARKER_SIZE), _ALLOCA_S_HEAP_MARKER))
  42. #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相同。

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <malloc.h>

  4. int main()
  5. {
  6.     int     size = 1000;
  7.     int     errcode = 0;
  8.     void    *pData = NULL;

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

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

  27.         // 使用下面的函数恢复函数栈
  28.         errcode = _resetstkoflw();
  29.         if (errcode)
  30.         {
  31.             printf_s("Could not reset the stack!\n");
  32.             _exit(1);
  33.         }
  34.     };
  35. }
复制代码

反汇编得到:

  1. .text:004113F0                         ; int __cdecl main()
  2. .text:004113F0                         _main           proc near               ; CODE XREF: j__mainj
  3. .text:004113F0
  4. .text:004113F0                         pAllocaBase     = dword ptr -120h
  5. .text:004113F0                         cbSize          = dword ptr -11Ch
  6. .text:004113F0                         var_114         = dword ptr -114h
  7. .text:004113F0                         allocaList      = dword ptr -48h
  8. .text:004113F0                         pData           = dword ptr -3Ch
  9. .text:004113F0                         errcode         = dword ptr -30h
  10. .text:004113F0                         size            = dword ptr -24h
  11. .text:004113F0                         var_1C          = dword ptr -1Ch
  12. .text:004113F0                         ms_exc          = CPPEH_RECORD ptr -18h
  13. .text:004113F0
  14. .text:004113F0 55                                      push    ebp
  15. .text:004113F1 8B EC                                   mov     ebp, esp
  16. .text:004113F3 6A FE                                   push    0FFFFFFFEh
  17. .text:004113F5 68 80 6F 41 00                          push    offset stru_416F80
  18. .text:004113FA 68 82 10 41 00                          push    offset j___except_handler4
  19. .text:004113FF 64 A1 00 00 00 00                       mov     eax, large fs:0
  20. .text:00411405 50                                      push    eax
  21. .text:00411406 81 C4 F0 FE FF FF                       add     esp, 0FFFFFEF0h
  22. .text:0041140C 53                                      push    ebx
  23. .text:0041140D 56                                      push    esi
  24. .text:0041140E 57                                      push    edi
  25. .text:0041140F 8D BD E0 FE FF FF                       lea     edi, [ebp+pAllocaBase]
  26. .text:00411415 B9 42 00 00 00                          mov     ecx, 42h
  27. .text:0041141A B8 CC CC CC CC                          mov     eax, 0CCCCCCCCh
  28. .text:0041141F F3 AB                                   rep stosd
  29. .text:00411421 A1 00 80 41 00                          mov     eax, ___security_cookie
  30. .text:00411426 31 45 F8                                xor     [ebp+ms_exc.registration.ScopeTable], eax
  31. .text:00411429 33 C5                                   xor     eax, ebp
  32. .text:0041142B 89 45 E4                                mov     [ebp+var_1C], eax
  33. .text:0041142E 50                                      push    eax
  34. .text:0041142F 8D 45 F0                                lea     eax, [ebp+ms_exc.registration]
  35. .text:00411432 64 A3 00 00 00 00                       mov     large fs:0, eax
  36. .text:00411438 89 65 E8                                mov     [ebp+ms_exc.old_esp], esp
  37. .text:0041143B C7 45 B8 00 00 00 00                    mov     [ebp+allocaList], 0
  38. .text:00411442 C7 45 DC E8 03 00 00                    mov     [ebp+size], 3E8h
  39. .text:00411449 C7 45 D0 00 00 00 00                    mov     [ebp+errcode], 0
  40. .text:00411450 C7 45 C4 00 00 00 00                    mov     [ebp+pData], 0
  41. .text:00411457 C7 45 FC 00 00 00 00                    mov     [ebp+ms_exc.registration.TryLevel], 0
  42. .text:0041145E 83 7D DC 00                             cmp     [ebp+size], 0
  43. .text:00411462 7E 6F                                   jle     short loc_4114D3
  44. .text:00411464 81 7D DC 00 04 00 00                    cmp     [ebp+size], 400h
  45. .text:0041146B 7D 66                                   jge     short loc_4114D3
  46. .text:0041146D 8B 45 DC                                mov     eax, [ebp+size]
  47. .text:00411470 83 C0 24                                add     eax, 24h
  48. .text:00411473 89 85 E4 FE FF FF                       mov     [ebp+cbSize], eax
  49. .text:00411479 8B 85 E4 FE FF FF                       mov     eax, [ebp+cbSize]
  50. .text:0041147F E8 4E FC FF FF                          call    j___alloca_probe_16
  51. .text:00411484 89 A5 E0 FE FF FF                       mov     [ebp+pAllocaBase], esp
  52. .text:0041148A 89 65 E8                                mov     [ebp+ms_exc.old_esp], esp
  53. .text:0041148D 8D 4D B8                                lea     ecx, [ebp+allocaList]
  54. .text:00411490 51                                      push    ecx             ; pAllocaInfoList
  55. .text:00411491 8B 95 E4 FE FF FF                       mov     edx, [ebp+cbSize] ; cbSize
  56. .text:00411497 8B 8D E0 FE FF FF                       mov     ecx, [ebp+pAllocaBase] ; pAllocaBase
  57. .text:0041149D E8 90 FB FF FF                          call    j_@_RTC_AllocaHelper@12 ; _RTC_AllocaHelper(x,x,x)
  58. .text:004114A2 83 85 E0 FE FF FF 20                    add     [ebp+pAllocaBase], 20h
  59. .text:004114A9 8B 95 E0 FE FF FF                       mov     edx, [ebp+pAllocaBase]
  60. .text:004114AF 89 55 C4                                mov     [ebp+pData], edx
  61. .text:004114B2 8B F4                                   mov     esi, esp
  62. .text:004114B4 8B 45 C4                                mov     eax, [ebp+pData]
  63. .text:004114B7 50                                      push    eax
  64. .text:004114B8 8B 4D DC                                mov     ecx, [ebp+size]
  65. .text:004114BB 51                                      push    ecx
  66. .text:004114BC 68 58 58 41 00                          push    offset Format   ; "Allocated %d bytes of stack at 0x%p"
  67. .text:004114C1 FF 15 C0 92 41 00                       call    ds:__imp__printf_s
  68. .text:004114C7 83 C4 0C                                add     esp, 0Ch
  69. .text:004114CA 3B F4                                   cmp     esi, esp
  70. .text:004114CC E8 79 FC FF FF                          call    j___RTC_CheckEsp
  71. .text:004114D1 EB 17                                   jmp     short loc_4114EA
  72. .text:004114D3                         ; ---------------------------------------------------------------------------
  73. .text:004114D3
  74. .text:004114D3                         loc_4114D3:                             ; CODE XREF: _main+72j
  75. .text:004114D3                                                                 ; _main+7Bj
  76. .text:004114D3 8B F4                                   mov     esi, esp
  77. .text:004114D5 68 84 58 41 00                          push    offset aTriedToAllocat ; "Tried to allocate too many bytes.\n"
  78. .text:004114DA FF 15 C0 92 41 00                       call    ds:__imp__printf_s
  79. .text:004114E0 83 C4 04                                add     esp, 4
  80. .text:004114E3 3B F4                                   cmp     esi, esp
  81. .text:004114E5 E8 60 FC FF FF                          call    j___RTC_CheckEsp
  82. .text:004114EA
  83. .text:004114EA                         loc_4114EA:                             ; CODE XREF: _main+E1j
  84. .text:004114EA C7 45 FC FE FF FF FF                    mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
  85. .text:004114F1 E9 97 00 00 00                          jmp     loc_41158D
  86. .text:004114F6                         ; ---------------------------------------------------------------------------
  87. .text:004114F6
  88. .text:004114F6                         $LN10:                                  ; DATA XREF: .rdata:stru_416F80o
  89. .text:004114F6 8B 45 EC                                mov     eax, [ebp+ms_exc.exc_ptr] ; Exception filter 0 for function 4113F0
  90. .text:004114F9 8B 08                                   mov     ecx, [eax]
  91. .text:004114FB 8B 11                                   mov     edx, [ecx]
  92. .text:004114FD 89 95 EC FE FF FF                       mov     [ebp+var_114], edx
  93. .text:00411503 81 BD EC FE FF FF FD 00+                cmp     [ebp+var_114], 0C00000FDh
  94. .text:0041150D 75 0C                                   jnz     short loc_41151B
  95. .text:0041150F C7 85 E4 FE FF FF 01 00+                mov     [ebp+cbSize], 1
  96. .text:00411519 EB 0A                                   jmp     short loc_411525
  97. .text:0041151B                         ; ---------------------------------------------------------------------------
  98. .text:0041151B
  99. .text:0041151B                         loc_41151B:                             ; CODE XREF: _main+11Dj
  100. .text:0041151B C7 85 E4 FE FF FF 00 00+                mov     [ebp+cbSize], 0
  101. .text:00411525
  102. .text:00411525                         loc_411525:                             ; CODE XREF: _main+129j
  103. .text:00411525 8B 85 E4 FE FF FF                       mov     eax, [ebp+cbSize]
  104. .text:0041152B
  105. .text:0041152B                         $LN12:
  106. .text:0041152B C3                                      retn
  107. .text:0041152C                         ; ---------------------------------------------------------------------------
  108. .text:0041152C
  109. .text:0041152C                         $LN11:                                  ; DATA XREF: .rdata:stru_416F80o
  110. .text:0041152C 8B 65 E8                                mov     esp, [ebp+ms_exc.old_esp] ; Exception handler 0 for function 4113F0
  111. .text:0041152F 8B F4                                   mov     esi, esp
  112. .text:00411531 68 B0 58 41 00                          push    offset a_allocaFailed ; "_alloca failed!\n"
  113. .text:00411536 FF 15 C0 92 41 00                       call    ds:__imp__printf_s
  114. .text:0041153C 83 C4 04                                add     esp, 4
  115. .text:0041153F 3B F4                                   cmp     esi, esp
  116. .text:00411541 E8 04 FC FF FF                          call    j___RTC_CheckEsp
  117. .text:00411546 8B F4                                   mov     esi, esp
  118. .text:00411548 FF 15 BC 92 41 00                       call    ds:__imp___resetstkoflw
  119. .text:0041154E 3B F4                                   cmp     esi, esp
  120. .text:00411550 E8 F5 FB FF FF                          call    j___RTC_CheckEsp
  121. .text:00411555 89 45 D0                                mov     [ebp+errcode], eax
  122. .text:00411558 83 7D D0 00                             cmp     [ebp+errcode], 0
  123. .text:0041155C 74 28                                   jz      short loc_411586
  124. .text:0041155E 8B F4                                   mov     esi, esp
  125. .text:00411560 68 C4 58 41 00                          push    offset aCouldNotResetT ; "Could not reset the stack!\n"
  126. .text:00411565 FF 15 C0 92 41 00                       call    ds:__imp__printf_s
  127. .text:0041156B 83 C4 04                                add     esp, 4
  128. .text:0041156E 3B F4                                   cmp     esi, esp
  129. .text:00411570 E8 D5 FB FF FF                          call    j___RTC_CheckEsp
  130. .text:00411575 8B F4                                   mov     esi, esp
  131. .text:00411577 6A 01                                   push    1               ; Code
  132. .text:00411579 FF 15 C8 92 41 00                       call    ds:__imp___exit
  133. .text:0041157F                         ; ---------------------------------------------------------------------------
  134. .text:0041157F 3B F4                                   cmp     esi, esp
  135. .text:00411581 E8 C4 FB FF FF                          call    j___RTC_CheckEsp
  136. .text:00411586
  137. .text:00411586                         loc_411586:                             ; CODE XREF: _main+16Cj
  138. .text:00411586 C7 45 FC FE FF FF FF                    mov     [ebp+ms_exc.registration.TryLevel], 0FFFFFFFEh
  139. .text:0041158D
  140. .text:0041158D                         loc_41158D:                             ; CODE XREF: _main+101j
  141. .text:0041158D EB 02                                   jmp     short loc_411591
  142. .text:0041158F                         ; ---------------------------------------------------------------------------
  143. .text:0041158F EB 02                                   jmp     short loc_411593
  144. .text:00411591                         ; ---------------------------------------------------------------------------
  145. .text:00411591
  146. .text:00411591                         loc_411591:                             ; CODE XREF: _main:loc_41158Dj
  147. .text:00411591 33 C0                                   xor     eax, eax
  148. .text:00411593
  149. .text:00411593                         loc_411593:                             ; CODE XREF: _main+19Fj
  150. .text:00411593 52                                      push    edx
  151. .text:00411594 8B CD                                   mov     ecx, ebp        ; frame
  152. .text:00411596 50                                      push    eax
  153. .text:00411597 8D 15 CC 15 41 00                       lea     edx, v          ; v
  154. .text:0041159D FF 75 B8                                push    [ebp+allocaList] ; allocaList
  155. .text:004115A0 E8 9B FB FF FF                          call    j_@_RTC_CheckStackVars2@12 ; _RTC_CheckStackVars2(x,x,x)
  156. .text:004115A5 58                                      pop     eax
  157. .text:004115A6 5A                                      pop     edx
  158. .text:004115A7 8D A5 D0 FE FF FF                       lea     esp, [ebp-130h]
  159. .text:004115AD 8B 4D F0                                mov     ecx, [ebp+ms_exc.registration.Next]
  160. .text:004115B0 64 89 0D 00 00 00 00                    mov     large fs:0, ecx
  161. .text:004115B7 59                                      pop     ecx
  162. .text:004115B8 5F                                      pop     edi
  163. .text:004115B9 5E                                      pop     esi
  164. .text:004115BA 5B                                      pop     ebx
  165. .text:004115BB 8B 4D E4                                mov     ecx, [ebp+var_1C]
  166. .text:004115BE 33 CD                                   xor     ecx, ebp        ; cookie
  167. .text:004115C0 E8 59 FA FF FF                          call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
  168. .text:004115C5 8B E5                                   mov     esp, ebp
  169. .text:004115C7 5D                                      pop     ebp
  170. .text:004115C8 C3                                      retn
复制代码

关键代码为:
.text:0041146D 8B 45 DC                                mov     eax, [ebp+size]
.text:00411470 83 C0 24                                add     eax, 24h
.text:00411473 89 85 E4 FE FF FF                       mov     [ebp+cbSize], eax
.text:00411479 8B 85 E4 FE FF FF                       mov     eax, [ebp+cbSize]
.text:0041147F E8 4E FC FF FF                          call    j___alloca_probe_16
.text:00411484 89 A5 E0 FE FF FF                       mov     [ebp+pAllocaBase], esp
.text:0041148A 89 65 E8                                mov     [ebp+ms_exc.old_esp], esp
.text:0041148D 8D 4D B8                                lea     ecx, [ebp+allocaList]
.text:00411490 51                                      push    ecx             ; pAllocaInfoList
.text:00411491 8B 95 E4 FE FF FF                       mov     edx, [ebp+cbSize] ; cbSize
.text:00411497 8B 8D E0 FE FF FF                       mov     ecx, [ebp+pAllocaBase] ; 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     [ebp+pAllocaBase], 20h
.text:004114A9 8B 95 E0 FE FF FF                       mov     edx, [ebp+pAllocaBase]
.text:004114AF 89 55 C4                                mov     [ebp+pData], edx
很疑惑地,在__alloca_probe_16调用之前,发生了add eax,24h和mov eax,[ebp+cbSize],而在之后发生了mov [ebp+pAllocaBase], 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)


  1. .text:004116A0                         ; void __fastcall _RTC_AllocaHelper(_RTC_ALLOCA_NODE *pAllocaBase, unsigned int cbSize, _RTC_ALLOCA_NODE **pAllocaInfoList)
  2. .text:004116A0                         @_RTC_AllocaHelper@12 proc near         ; CODE XREF: _RTC_AllocaHelper(x,x,x)j
  3. .text:004116A0
  4. .text:004116A0                         pAllocaInfoList = dword ptr  8
  5. .text:004116A0
  6. .text:004116A0                         pAllocaBase = ecx
  7. .text:004116A0                         cbSize = edx
  8. .text:004116A0 55                                      push    ebp
  9. .text:004116A1 8B EC                                   mov     ebp, esp
  10. .text:004116A3 53                                      push    ebx
  11. .text:004116A4 56                                      push    esi
  12. .text:004116A5 8B F1                                   mov     esi, pAllocaBase
  13. .text:004116A7 8B DA                                   mov     ebx, cbSize
  14. .text:004116A9 85 F6                                   test    esi, esi
  15. .text:004116AB 74 1F                                   jz      short loc_4116CC
  16. .text:004116AD 85 DB                                   test    ebx, ebx
  17. .text:004116AF 74 1B                                   jz      short loc_4116CC
  18. .text:004116B1 8B 55 08                                mov     cbSize, [ebp+pAllocaInfoList]
  19. .text:004116B4 85 D2                                   test    cbSize, cbSize
  20. .text:004116B6 74 14                                   jz      short loc_4116CC
  21. .text:004116B8 57                                      push    edi
  22. .text:004116B9 B0 CC                                   mov     al, 0CCh
  23. .text:004116BB 8B FE                                   mov     edi, esi
  24. .text:004116BD 8B CB                                   mov     pAllocaBase, ebx
  25. .text:004116BF F3 AA                                   rep stosb
  26. .text:004116C1 8B 02                                   mov     eax, [cbSize]
  27. .text:004116C3 89 46 04                                mov     [esi+4], eax
  28. .text:004116C6 89 5E 0C                                mov     [esi+0Ch], ebx
  29. .text:004116C9 89 32                                   mov     [cbSize], esi
  30. .text:004116CB 5F                                      pop     edi
  31. .text:004116CC
  32. .text:004116CC                         loc_4116CC:                             ; CODE XREF: _RTC_AllocaHelper(x,x,x)+Bj
  33. .text:004116CC                                                                 ; _RTC_AllocaHelper(x,x,x)+Fj ...
  34. .text:004116CC 5E                                      pop     esi
  35. .text:004116CD 5B                                      pop     ebx
  36. .text:004116CE 5D                                      pop     ebp
  37. .text:004116CF C2 04 00                                retn    4
  38. .text:004116CF                         @_RTC_AllocaHelper@12 endp
复制代码


翻译可得:

  1. void main()
  2. {
  3.         ......
  4.         int size=1024,cbsize=size+sizeof(_RTC_ALLOCA_NODE)+4;
  5.         _RTC_ALLOCA_NODE* pAllocaBase=__alloca_probe_16(cbsize);
  6.         _RTC_AllocaHelper(pAllocaBase,cbsize,NULL);
  7.         void* pData=(void*)(pAllocaBase+1);
  8.         ......
  9. }

  10. //这个结构体来自于reactos
  11. #pragma pack(push,1)
  12. typedef struct _RTC_ALLOCA_NODE
  13. {
  14.          __int32 guard1;
  15.         struct _RTC_ALLOCA_NODE *next;
  16.         #if (defined(_X86_) && !defined(__x86_64))
  17.                 __int32 dummypad;
  18.         #endif
  19.                 size_t allocaSize;
  20.         #if (defined(_X86_) && !defined(__x86_64))
  21.                 __int32 dummypad2;
  22.         #endif
  23.                 __int32 guard2[3];
  24. }_RTC_ALLOCA_NODE;
  25. #pragma pack(pop)

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

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

复制代码


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

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

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

  4. public  _alloca_probe_8
  5. _alloca_probe_16 proc                   ; 16 byte aligned alloca

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

  16.         end


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

  41. _chkstk endp
  42.         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用法如下:

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

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

  6. int main( void )
  7. {
  8.    long *buffer;

  9.    buffer = (long *)calloc( 40, sizeof( long ) );
  10.    if( buffer != NULL )
  11.       printf( "Allocated 40 long integers\n" );
  12.    else
  13.       printf( "Can't allocate memory\n" );
  14.    free( buffer );
  15. }
复制代码

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

  1. void * __cdecl _calloc_base (size_t num, size_t size)
  2. {
  3.     int errno_tmp = 0;
  4.     void * pv = _calloc_impl(num, size, &errno_tmp);

  5.     if ( pv == NULL && errno_tmp != 0 && _errno())
  6.     {
  7.         errno = errno_tmp; // recall, #define errno *_errno()
  8.     }
  9.     return pv;
  10. }
  11. void * __cdecl _calloc_impl (size_t num, size_t size, int * errno_tmp)
  12. {
  13.         size_t  size_orig;
  14.         void *  pvReturn;

  15.         /* ensure that (size * num) does not overflow */
  16.         if (num > 0)
  17.         {
  18.             _VALIDATE_RETURN_NOEXC((_HEAP_MAXREQ / num) >= size, ENOMEM, NULL);
  19.         }
  20.         size_orig = size = size * num;


  21.         /* force nonzero size */
  22.         if (size == 0)
  23.             size = 1;

  24.         for (;;)
  25.         {
  26.             pvReturn = NULL;

  27.             if (size <= _HEAP_MAXREQ)
  28.             {
  29.                 if (pvReturn == NULL)
  30.                     pvReturn = HeapAlloc(_crtheap, HEAP_ZERO_MEMORY, size);
  31.             }

  32.             if (pvReturn || _newmode == 0)
  33.             {
  34.                 RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, size_orig, 0));
  35.                 if (pvReturn == NULL)
  36.                 {
  37.                     if ( errno_tmp )
  38.                         *errno_tmp = ENOMEM;
  39.                 }
  40.                 return pvReturn;
  41.             }

  42.             /* call installed new handler */
  43.             if (!_callnewh(size))
  44.             {
  45.                 if ( errno_tmp )
  46.                     *errno_tmp = ENOMEM;
  47.                 return NULL;
  48.             }

  49.             /* new handler was successful -- try to allocate again */
  50.         }
  51. }

复制代码


现在来分析_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。


  1. #include <stdio.h>
  2. #include <malloc.h>
  3. #include <stdlib.h>

  4. int main( void )
  5. {
  6.    char *bufchar;
  7.    printf( "Allocate a 512 element buffer\n" );
  8.    if( (bufchar = (char *)calloc( 512, sizeof( char ) )) == NULL )
  9.       exit( 1 );
  10.    printf( "Allocated %d bytes at %Fp\n",
  11.          _msize( bufchar ), (void *)bufchar );
  12.    if( (bufchar = (char *)_expand( bufchar, 1024 )) == NULL )
  13.       printf( "Can't expand" );
  14.    else
  15.       printf( "Expanded block to %d bytes at %Fp\n",
  16.             _msize( bufchar ), (void *)bufchar );
  17.    // Free memory
  18.    free( bufchar );
  19.    exit( 0 );
  20. }
复制代码

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

  1. void * __cdecl _expand_base (void * pBlock, size_t newsize)
  2. {
  3.         void *      pvReturn;

  4.         size_t oldsize;

  5.         /* validation section */
  6.         _VALIDATE_RETURN(pBlock != NULL, EINVAL, NULL);
  7.         if (newsize > _HEAP_MAXREQ) {
  8.             errno = ENOMEM;
  9.             return NULL;
  10.         }

  11.         if (newsize == 0)
  12.         {
  13.             newsize = 1;
  14.         }

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

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

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

  26.         if (pvReturn)
  27.         {
  28.             RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));
  29.             RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0));
  30.         }

  31.         return pvReturn;
  32. }
复制代码


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

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

  1. //release版
  2. void operator delete( void * p )
  3. {
  4.     RTCCALLBACK(_RTC_Free_hook, (p, 0));

  5.     free( p );
  6. }
  7. //debug版
  8. void operator delete( void *pUserData )
  9. {
  10.         _CrtMemBlockHeader * pHead;

  11.         RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));

  12.         if (pUserData == NULL)
  13.             return;

  14.         _mlock(_HEAP_LOCK);  /* block other threads */
  15.         __TRY

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

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

  20.             _free_dbg( pUserData, pHead->nBlockUse );

  21.         __FINALLY
  22.             _munlock(_HEAP_LOCK);  /* release other threads */
  23.         __END_TRY_FINALLY

  24.         return;
  25. }
复制代码

可见delete->free

realloc
分析方法和前面相同

  1. void * __cdecl _realloc_base (void * pBlock, size_t newsize)
  2. {
  3.         void *      pvReturn;
  4.         size_t      origSize = newsize;

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

  8.         //  if ptr is nonNULL and size is zero, call free and return NULL
  9.         if (newsize == 0)
  10.         {
  11.             _free_base(pBlock);
  12.             return(NULL);
  13.         }


  14.         for (;;) {

  15.             pvReturn = NULL;
  16.             if (newsize <= _HEAP_MAXREQ)
  17.             {
  18.                 if (newsize == 0)
  19.                     newsize = 1;
  20.                 pvReturn = HeapReAlloc(_crtheap, 0, pBlock, newsize);
  21.             }
  22.             else
  23.             {
  24.                 _callnewh(newsize);
  25.                 errno = ENOMEM;
  26.                 return NULL;
  27.             }

  28.             if ( pvReturn || _newmode == 0)
  29.             {
  30.                 if (pvReturn)
  31.                 {
  32.                     RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));
  33.                     RTCCALLBACK(_RTC_Allocate_hook, (pvReturn, newsize, 0));
  34.                 }
  35.                 else
  36.                 {
  37.                     errno = _get_errno_from_oserr(GetLastError());
  38.                 }
  39.                 return pvReturn;
  40.             }

  41.             //  call installed new handler
  42.             if (!_callnewh(newsize))
  43.             {
  44.                 errno = _get_errno_from_oserr(GetLastError());
  45.                 return NULL;
  46.             }

  47.             //  new handler was successful -- try to allocate again
  48.         }
  49. }
复制代码

得到realloc->HeapReAlloc

malloc

  1. void * __cdecl _malloc_base (size_t size)
  2. {
  3.     void *res = NULL;

  4.     //  validate size
  5.     if (size <= _HEAP_MAXREQ) {
  6.         for (;;) {

  7.             //  allocate memory block
  8.             res = _heap_alloc(size);

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

  11.             if (res != NULL)
  12.             {
  13.                 break;
  14.             }
  15.             if (_newmode == 0)
  16.             {
  17.                 errno = ENOMEM;
  18.                 break;
  19.             }

  20.             //  call installed new handler
  21.             if (!_callnewh(size))
  22.                 break;

  23.             //  new handler was successful -- try to allocate again
  24.         }
  25.     } else {
  26.         _callnewh(size);
  27.         errno = ENOMEM;
  28.         return NULL;
  29.     }

  30.     RTCCALLBACK(_RTC_Allocate_hook, (res, size, 0));
  31.     if (res == NULL)
  32.     {
  33.         errno = ENOMEM;
  34.     }
  35.     return res;
  36. }
  37. __forceinline void * __cdecl _heap_alloc (size_t size)

  38. {

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

  41.         _NMSG_WRITE(_RT_CRT_NOTINIT);  /* write message */
  42.         __crtExitProcess(255);  /* normally _exit(255) */
  43.     }

  44.     return HeapAlloc(_crtheap, 0, size ? size : 1);
  45. }
复制代码

得到malloc->_heap_alloc->HeapAlloc

free

  1. void __cdecl _free_base (void * pBlock)
  2. {

  3.         int retval = 0;


  4.         if (pBlock == NULL)
  5.             return;

  6.         RTCCALLBACK(_RTC_Free_hook, (pBlock, 0));

  7.         retval = HeapFree(_crtheap, 0, pBlock);
  8.         if (retval == 0)
  9.         {
  10.             errno = _get_errno_from_oserr(GetLastError());
  11.         }
  12. }
复制代码

得到free->HeapFree

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


回复

使用道具 举报

发表于 2014-10-14 14:31:06 | 显示全部楼层
支持!虽然你的技术文档并不能在发表之后就立即有人看到,但是一定会被百度收录,将来就会被很多人看到。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-22 17:34 , Processed in 0.034408 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表