Crack_Qs 发表于 2014-9-19 23:55:32

VS2013下的new[]和delete[]分析

本帖最后由 Crack_Qs 于 2014-9-20 07:11 编辑

编译模式:Debug
编译环境:Microsoft Visual Studio Ultimate 2013 (12.0.30501.00) Update 2
//////////////////////////////////////////////////////////////////////////////
一.        New[]:
C++代码:
    int *lpNum = new int;

汇编代码:
    PUSH 0x40                           ; new申请的空间大小
    CALL XXXXXXXX                         ; CALL NEW
    ADDESP, 0x4                         ; _cdecl调用约定的平栈
    MOVDWORD PTR SS:, EAX   ; 把返回值(申请空间的地址)给临时变量(猜测用于保存)
    MOVEAX, DWORD PTR SS:   ; 从临时变量中取出地址
    MOVDWORD PTR SS:, EAX      ; 赋值给函数内的指针

看一下New里面干了什么:

    PUSH    EBP                              ; void *New(int nSize);
    MOV   EBP, ESP                         ; ebp+0x8 nSize的栈空间
    SUB   ESP, 0x10
    MOV   EAX, DWORD PTR SS:      ; 取nSize
    PUSH    EAX                              ; 压入nSize
    CALL    malloc                           ; call malloc
    ADD   ESP, 0x4                         ; _cdecl调用约定的平栈
    MOV   DWORD PTR SS:, EAX      ; 返回值为malloc申请的地址
    CMP   DWORD PTR SS:, 0x0      ; 如果返回值 == NULL
    JNZ   SHORT msvcr120.0FB0C2A3
    MOV   ECX, DWORD PTR SS:      ; 取大小
    PUSH    ECX                              ; 压入大小
    CALL    _callnewh                        ; _callnewh(size)
    ADD   ESP, 0x4                         ; _cdecl调用约定的平栈
    TEST    EAX, EAX                         ; 如果返回值等于 NULL
    JNZ   SHORT msvcr120.0FB0C2A1
    LEA   ECX, DWORD PTR SS:
    CALL    bad_alloc                        ; call bad_alloc
    PUSH    msvcr120.0FB753D8
    LEA   EDX, DWORD PTR SS:
    PUSH    EDX
    CALL    _CxxThrowException               ; CALL _CxxThrowException
    JMP   SHORT msvcr120.0FB0C266
    MOV   EAX, DWORD PTR SS:      ; 不等于NULL,返回值给EAX,返回到main函数
    MOV   ESP, EBP
    POP   EBP
    RETN

因VS2013无法查看其实现代码,以下为个人猜测如有错误忘请不吝赐教:

void* _cdecl operator new(unsigned int nSize)_THROW1(_STD bad_alloc)
{
    void *lpBuf;
    while ((lpBuf = malloc(nSize)) == NULL)
    {
      if (_callnewh(nSize) == NULL)
      {
            static const std::bad_alloc nomem;
            _RAISE(nomem);
      }
    }
    return lpBuf;
}

二.        Delete[]:
C++代码:
    int *lpNum = new int;
    delete lpNum;

汇编代码:
MOV   EAX, DWORD PTR SS:      ; 从变量中取出lpNum地址
MOV   DWORD PTR SS:, EAX   ; 放入临时变量EBP-0xD4
MOV   ECX, DWORD PTR SS:   ; 从临时变量中取出地址
PUSH    ECX                              ; 压入参数
CALL    NewDELET.013011DB                ; call delete
ADD   ESP, 0x4                         ; _cdecl调用约定平栈
CMP   DWORD PTR SS:, 0x0   ; 判断临时变量EBP-0xD4中的地址值是否等于NULL
JNZ   SHORT NewDELET.01301A54
MOV   DWORD PTR SS:, 0x0   ; 如果临时变量EBP-0xD4不为NULL就把临时变量EBP-0xE8填为NULL
JMP   SHORT NewDELET.01301A64
MOV   DWORD PTR SS:, 0x8123   ; 给lpNum传入一个值0x8123
MOV   EDX, DWORD PTR SS:      ; 从lpNum取出值
MOV   DWORD PTR SS:, EDX   ; 临时变量EBP-0xE8填为0x8123

看一下Delete里面干了什么:

PUSH    EBP                                  ; void _cdecl operator delete (void *pBuf)
MOV   EBP, ESP                           ; EBP+0x8 pBuf的栈空间
PUSH    -0x2
PUSH    msvcr120.52499400
PUSH    msvcr120.5243FEF0
MOV   EAX, DWORD PTR FS:
PUSH    EAX
ADD   ESP, -0xC
PUSH    EBX
PUSH    ESI
PUSH    EDI
MOV   EAX, DWORD PTR DS:
XOR   DWORD PTR SS:, EAX
XOR   EAX, EBP
PUSH    EAX
LEA   EAX, DWORD PTR SS:
MOV   DWORD PTR FS:, EAX
CMP   DWORD PTR SS:, 0x0          ; 判断pBuf是否 == NULL
JNZ   SHORT msvcr120.5243A9CB
JMP   msvcr120.5243AA68                  ; 跳向函数尾部 (return)
PUSH    0x4                                  ; 4
CALL    msvcr120._lock                     ; call _lock
ADD   ESP, 0x4                           ; _cdecl调用约定平栈
MOV   DWORD PTR SS:, 0x0
MOV   EAX, DWORD PTR SS:          ; pBuf - 0x20
SUB   EAX, 0x20
MOV   DWORD PTR SS:, EAX         ; 临时变量 = pBuf - 0x20
MOV   ECX, DWORD PTR SS:         ; 取出运算后的pBuf
MOV   EDX, DWORD PTR DS:         ; 取pBuf+0x14由此可以判断ebp-0x1c是一个结构体变量结果为1 是个逻辑值
AND   EDX, 0xFFFF
CMP   EDX, 0x4                           ; [+0x14] & 0xffff != 4
JE      SHORT msvcr120.5243AA3C
MOV   EAX, DWORD PTR SS:
CMP   DWORD PTR DS:, 0x1         ; [+0x14] != 0x1
JE      SHORT msvcr120.5243AA3C
MOV   ECX, DWORD PTR SS:
MOV   EDX, DWORD PTR DS:
AND   EDX, 0xFFFF                        ; [+0x14] & 0xffff != 0x2
CMP   EDX, 0x2
JE      SHORT msvcr120.5243AA3C
MOV   EAX, DWORD PTR SS:
CMP   DWORD PTR DS:, 0x3         ; [+0x14] != 0x3
JE      SHORT msvcr120.5243AA3C
PUSH    msvcr120.52321BF8                  ; UNICODE "_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)"
PUSH    msvcr120.5230127C                  ; UNICODE "%s"
PUSH    0x0                                  ; NULL
PUSH    0x34                                 ; __LINE__
PUSH    msvcr120.52321C48                  ; __FILE__
PUSH    0x2
CALL    msvcr120._CrtDbgReportW            ; call _CrtDbgReportW : _CrtDbgReportW(0x2,?,0x34,0x0,%s,_BLOCK_TYPE_IS_VALID(pHead->nBlockUse))
ADD   ESP, 0x18                            ; _cdecl调用约定平栈
CMP   EAX, 0x1                           ; if( _CrtDbgReportW == 1)
JNZ   SHORT msvcr120.5243AA3C
INT3
MOV   EDX, DWORD PTR SS:
MOV   EAX, DWORD PTR DS:
PUSH    EAX                                  ; push [+0x14]
MOV   ECX, DWORD PTR SS:
PUSH    ECX                                  ; push pBuf
CALL    msvcr120._free_dbg                   ; call _free_dbg : _free_dbg(pbuf,[+0x14])
ADD   ESP, 0x8                           ; _cdecl调用约定平栈
MOV   DWORD PTR SS:, -0x2
CALL    msvcr120.5243AA5D                  ; 调用下面的 _unlock
JMP   SHORT msvcr120.5243AA68
PUSH    0x4
CALL    msvcr120._unlock                     ; call _unlock : _unlock(4)
ADD   ESP, 0x4                           ; _cdecl调用约定平栈
RETN
MOV   ECX, DWORD PTR SS:         ; 函数尾部
MOV   DWORD PTR FS:, ECX
POP   ECX
POP   EDI
POP   ESI
POP   EBX
MOV   ESP, EBP
POP   EBP
RETN

因VS2013无法查看其实现代码,以下为个人猜测如有错误忘请不吝赐教:

void _cdecl operator delete (void *pBuf)
{
    _CrtMemBlockHeader * pHead;

    if (pBuf == NULL)
    {
      return;
    }

    _lock(4);

    pHead = ((_CrtMemBlockHeader *)pBuf) - 0x20;

    if ((pHead->nBlockUse & 0xFFFF) == 4
      || (pHead->nBlockUse) == 1 || (pHead->nBlockUse & 0xFFFF) == 2
      || (pHead->nBlockUse) == 3
      && (1 == _CrtDbgReport(0x2, __FILE__, __LINE__, NULL,
      (pHead->nBlockUse & 0xFFFF) == 4
      ||(pHead->nBlockUse) == 1 || (pHead->nBlockUse & 0xFFFF) == 2
      || (pHead->nBlockUse) == 3) )
      )
    {
      __asm
      {
            int 3
      }
    }

    _free_dbg( pBuf, pHead->nBlockUse );
   _munlock(4)
    return;
}

int _CrtDbgReport( int reportType, const char *filename, int linenumber, const char *moduleName, const char *format [, argument] ... );



Crack_Qs 发表于 2014-9-19 23:59:02

一楼备用,请大家指教

sndncel 发表于 2014-9-20 08:33:04

{:lol:}谢谢楼主的分析呀,不过我还真是看不太懂呀。。。。可能C基础太差了吧。看来的多努力学习一下这方面的知识呀。。。。

wanap571 发表于 2014-9-20 09:57:20

我不懂但我支持楼主的好东西,

开心啦 发表于 2014-9-20 12:11:19

支持一下了,感谢

whypro 发表于 2014-9-23 08:15:27

本帖最后由 whypro 于 2014-9-23 08:20 编辑

有源代码非要装逼,网上分析文章一裤衩,在这板块太装逼了。
/***
*new.cxx - defines C++ new routine
*
*       Copyright (c) Microsoft Corporation.All rights reserved.
*
*Purpose:
*       Defines C++ new routine.
*
*******************************************************************************/


#ifdef _SYSCRT
#include <cruntime.h>
#include <crtdbg.h>
#include <malloc.h>
#include <new.h>
#include <stdlib.h>
#include <winheap.h>
#include <rtcsup.h>
#include <internal.h>

void * operator new( size_t cb )
{
    void *res;

    for (;;) {

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

      //if successful allocation, return pointer to memory

      if (res)
            break;

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

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

    RTCCALLBACK(_RTC_Allocate_hook, (res, cb, 0));

    return res;
}
#else/* _SYSCRT */

#include <cstdlib>
#include <new>

_C_LIB_DECL
int __cdecl _callnewh(size_t size) _THROW1(_STD bad_alloc);
_END_C_LIB_DECL

void *__CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
      {       // try to allocate size bytes
      void *p;
      while ((p = malloc(size)) == 0)
                if (_callnewh(size) == 0)
                {       // report no memory
                        _THROW_NCEE(_XSTD bad_alloc, );
                }

      return (p);
      }

/*
* Copyright (c) 1992-2002 by P.J. Plauger.ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
V3.13:0009 */
#endif/* _SYSCRT */



whypro 发表于 2014-9-23 08:36:53

/***
*dbgdel.cpp - defines C++ scalar delete routine, debug version
*
*       Copyright (c) Microsoft Corporation.All rights reserved.
*
*Purpose:
*       Defines C++ scalar delete() routine.
*
*******************************************************************************/

#ifdef _DEBUG

#include <cruntime.h>
#include <malloc.h>
#include <mtdll.h>
#include <dbgint.h>
#include <rtcsup.h>

/***
*void operator delete() - delete a block in the debug heap
*
*Purpose:
*       Deletes any type of block.
*
*Entry:
*       void *pUserData - pointer to a (user portion) of memory block in the
*                         debug heap
*
*Return:
*       <void>
*
*******************************************************************************/

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;
}

#endif/* _DEBUG */

whypro 发表于 2014-9-23 08:37:03

本帖最后由 whypro 于 2014-9-23 08:45 编辑

123

Crack_Qs 发表于 2014-9-23 09:03:48

whypro 发表于 2014-9-23 08:37
123
第一点,vs2013确实跟进去没有源码(最起码我这不行,有方法的话欢迎指教)
第二点,别的一些版本确实可以跟进去,但是我对比着跟过,个别细节不一样
第三点,我最近研究vs13的东西,你不喜欢那就算了,我的代码是实打实逆出来的,而非网上抄袭。

small-q 发表于 2014-9-23 09:39:58

哇,这代码分析得不错,支持楼主!!!加油
页: [1] 2
查看完整版本: VS2013下的new[]和delete[]分析