win32平台下malloc的内部实现

win32平台下malloc的内部实现

by flyingkisser 2007.09.10
如果您不想看那一堆确实比较XX的汇编代码,我直接给您一个结论吧:

1.malloc是如何实现的?
malloc(Size)最终调用的是HeapAlloc(msvcrt!_crtheap,0,dwSize)

2.msvcrt.dll使用到的堆是如何初始化的?
msvcrt的dll入口函数初始化时,调用HeapCreate(0,1000h,0)来创建一个私有堆,并放入到全局变量msvcrt!_crtheap中,供以后的malloc使用。

malloc是C语言中用于内存分配的函数,其声明是
void *malloc( 
   size_t size 
);
只有一个size参数。
在windows平台下,类似于这种C接口的函数,都统一放进了称作run-time c library的动态链接库中,
即msvcrt.dll,当然,还会有msvcrt20.dll,msvcrt40.dll等不同版本的run-time c library。
下面以msvcrt.dll导出的malloc为主要分析对象。
为了可以方便地分析这个函数,我简单写了一段程序,先装载msvcrt.dll,找到malloc的导出地址,再
调用它,在调用前加入int 3断点,方便调试器断在malloc即将调用前的位置,也方便后面的其它分析。
程序代码片断如下:
--------------------------
_malloc proc 
 local @hDll,@lpStr
 pushad
 invoke LoadLibrary,string("msvcrt.dll")
 mov @hDll,eax
 invoke GetProcAddress,eax,string("malloc")
 int 3
 push 80
 call eax
 add esp,4
 push eax
 invoke GetProcAddress,@hDll,string("free")
 call eax
 add esp,4
 invoke FreeLibrary,@hDll
 popad
 ret
_malloc endp
--------------------------
用cdb加载这个程序,直接g运行,断在int 3处,这时运行一下k命令,得到
0:000> k
ChildEBP RetAddr
0012ff2c 77bfc3c9 ntdll!RtlAllocateHeap
0012ff6c 77bfc3e7 msvcrt!_heap_alloc+0xe0
0012ff78 77bfc42e msvcrt!_nh_malloc+0x13
0012ff88 004011f5 msvcrt!malloc+0x27
WARNING: Stack unwind information not available. Following frames may be wrong.
0012ffbc 00401211 image00400000+0x11f5
0012fff0 00000000 image00400000+0x1211
可以看出malloc的内部调用结构,
malloc-->_heap_alloc-->RtlAllocateHeap
即malloc最终调用的还是RtlAllocateHeap,当然,它是由ntdll导出的,但并没有相关文档。

现在问题大概清楚了,malloc是在堆上分配的内存,那么,新的问题就来了,malloc在哪个堆上分配的
内存呢?
熟悉windows堆相关的API的人都清楚,在一个进程中,堆分为默认堆和私有堆,默认堆是系统在进程
创建的时候创建的,由系统在需要进行一些内存分配操作时使用,用户没有权力去销毁默认堆。
私有堆是我们用户自己创建的,我们可以随意在私有堆上分配、释放内存,并可以随意销毁我们自己
创建的私有堆。
那么,malloc要在堆上分配内存,必须需要一个堆句柄,那么,这个堆句柄,对应的是系统默认堆
还是私有堆呢?如果是私有堆,这个私有堆是什么时候创建和初始化的呢?
我们知道,创建堆调用的API是kernel32.dll导出的HeapCreate(),而HeapCreate()又调用了ntdll导出
的RtlCreateHeap(),当然,只有HeapCreate()是有文档的。下面,我们只需要在RtlCreateHeap()下断
点就行了。
用cdb重新装载上面的小程序,用
bp ntdll!RtlCreateHeap
下断点,g执行,断下来,再用k查看当前调用堆栈
0:000> k
ChildEBP RetAddr
0012eaec 7c812bff ntdll!RtlCreateHeap
0012eb10 77bfa2f9 kernel32!HeapCreate+0x55
0012eb24 77beef48 msvcrt!_heap_init+0x1b
0012eb2c 77bef010 msvcrt!_core_crt_dll_init+0x10
0012ebd0 7c9211a7 msvcrt!__CRTDLL_INIT+0x9f
0012ebf0 7c93cbab ntdll!LdrpCallInitRoutine+0x14
0012ecf8 7c936178 ntdll!LdrpRunInitializeRoutines+0x344
0012efa4 7c9362da ntdll!LdrpLoadDll+0x3e5
0012f24c 7c801bb9 ntdll!LdrLoadDll+0x230
0012f2b4 7c80ae5c kernel32!LoadLibraryExW+0x18e
0012f2c8 77f1d126 kernel32!LoadLibraryW+0x11
0012f2f0 77f13de7 GDI32!GdiInitializeLanguagePack+0x15
0012f304 77d1a02d GDI32!GdiProcessSetup+0x11d
0012f444 77d1a133 user32!ClientThreadSetup+0x33
0012f448 7c92eae3 user32!__ClientThreadSetup+0x5
0012f454 77ef67d4 ntdll!KiUserCallbackDispatcher+0x13
0012f464 77d1f774 GDI32!NtGdiInit+0xc
0012f9f0 7c9211a7 user32!_UserClientDllInitialize+0x315
0012fa10 7c93cbab ntdll!LdrpCallInitRoutine+0x14
0012fb18 7c94173e ntdll!LdrpRunInitializeRoutines+0x344

可以看出,主要的调用线路是:
__CRTDLL_INIT-->_core_crt_dll_init-->_heap_init-->HeapCreate
即在msvcrt的dll入口函数的初始化工作时,调用kernel32的HeapCreate()创建一个堆。
让我们进入到_heap_init()内部看看

0:000> u msvcrt!_heap_init
msvcrt!_heap_init:
77bfa2de 8bff             mov     edi,edi
77bfa2e0 55               push    ebp
77bfa2e1 8bec             mov     ebp,esp
77bfa2e3 33c0             xor     eax,eax
77bfa2e5 394508           cmp     [ebp+0x8],eax 
77bfa2e8 6a00             push    0x0  ;dwMaximumSize=0,说明堆是可增长的
77bfa2ea 0f94c0           sete    al  ;如果参数1为0,则al=1,否则al=0
77bfa2ed 6800100000       push    0x1000 ;dwInitialSize=1000h,4K
77bfa2f2 50               push    eax  ;flOptions为0或1
77bfa2f3 ff150411be77     call dword ptr [msvcrt!_imp__HeapCreate (77be1104)]
HeapCreate(参数1==0 ? 1 :0,1000h,0)
77bfa2f9 85c0             test    eax,eax
77bfa2fb a31824c377       mov     [msvcrt!_crtheap (77c32418)],eax
77bfa300 7436             jz      msvcrt!_heap_init+0x5a (77bfa338)
77bfa302 e85ffeffff       call    msvcrt!__heap_select (77bfa166)
77bfa307 83f803           cmp     eax,0x3
77bfa30a a31c24c377       mov     [msvcrt!__active_heap (77c3241c)],eax

可以看出,调用HeapCreate的参数是
HeapCreate(参数1==0 ? 1 :0,1000h,0)

上面的参数1是指_core_crt_dll_init调用_heap_init的参数1

那么参数1是0还是非0呢?再看一下_core_crt_dll_init就知道了

0:000> u _core_crt_dll_init
msvcrt!_core_crt_dll_init:
77beef38 833da417c37700 cmp dword ptr [msvcrt!_core_crt_dll_init_completed (77c3
17a4)],0x0
77beef3f 751d             jnz     msvcrt!_core_crt_dll_init+0x26 (77beef5e)
77beef41 6a01             push    0x1 ;参数1是1
77beef43 e896b30000       call    msvcrt!_heap_init (77bfa2de)

可以看出_core_crt_dll_init调用_heap_init时,参数是1。

所以,msvcrt初始化时会调用HeapCreate(0,1000h,0)来创建一个私有堆,并放入到全局变量
msvcrt!_crtheap中,供以后的malloc使用

现在,我们搞清楚了二个问题,
msvcrt的dll入口函数初始化,调用eapCreate(0,1000h,0)来创建一个私有堆,并放入到全局变量
msvcrt!_crtheap中,供以后的malloc使用。
malloc()再最终调用HeapAlloc()来分配内存。那么,malloc()调用HeapAlloc()的具体参数又是
什么样的呢?

再回到第一次下断时运行k的结果
0012ff2c 77bfc3c9 ntdll!RtlAllocateHeap
0012ff6c 77bfc3e7 msvcrt!_heap_alloc+0xe0
只要进入_heap_alloc看看就行了

msvcrt!_heap_alloc+0xba:
77bfc3a3 8b7508           mov     esi,[ebp+0x8]  ;esi=dwSize
77bfc3a6 85f6             test    esi,esi
77bfc3a8 7501             jnz     msvcrt!_heap_alloc+0xc2 (77bfc3ab)
77bfc3aa 46               inc     esi    ;大小为0的话则赋值为1
77bfc3ab 833d1c24c37701   cmp dword ptr [msvcrt!__active_heap (77c3241c)],0x1
77bfc3b2 7406             jz      msvcrt!_heap_alloc+0xd1 (77bfc3ba)
77bfc3b4 83c60f           add     esi,0xf   ;把大小按16字节对齐
77bfc3b7 83e6f0           and     esi,0xfffffff0
77bfc3ba 56               push    esi   ;dwSize
77bfc3bb 6a00             push    0x0   ;0
77bfc3bd ff351824c377     push    dword ptr [msvcrt!_crtheap (77c32418)];msvcrt!_crtheap
77bfc3c3 ff15f410be77     call  dword ptr [msvcrt!_imp__HeapAlloc (77be10f4)]
HeapAlloc(msvcrt!_crtheap,0,dwSize)
77bfc3c9 e88db00000       call    msvcrt!_SEH_epilog (77c0745b)
77bfc3ce c3               ret

即malloc(Size)最终调用的是HeapAlloc(msvcrt!_crtheap,0,dwSize)

让我们再来把问题及答案总结一下

1.msvcrt.dll使用到的堆是如何初始化的?
msvcrt的dll入口函数初始化时,调用HeapCreate(0,1000h,0)来创建一个私有堆,并放入到全局变量
msvcrt!_crtheap中,供以后的malloc使用。

2.malloc是如何实现的?
malloc(Size)最终调用的是HeapAlloc(msvcrt!_crtheap,0,dwSize)

下面再附上HeapCreate及HeapAlloc的原型。

HANDLE HeapCreate(
  DWORD flOptions,
  SIZE_T dwInitialSize,
  SIZE_T dwMaximumSize
);
有效的flOptions是
HEAP_CREATE_ENABLE_EXECUTE 0x00040000
HEAP_GENERATE_EXCEPTIONS 0x00000004
HEAP_NO_SERIALIZE   0x00000001

LPVOID HeapAlloc(
  HANDLE hHeap,
  DWORD dwFlags,
  SIZE_T dwBytes
);
有效的dwFlags是
HEAP_GENERATE_EXCEPTIONS 0x00000004
HEAP_NO_SERIALIZE   0x00000001
HEAP_ZERO_MEMORY   0x00000008

 

msvcrt.dll这个版本中的malloc比较简单,往下没有逆太深,有时看看确实没有这个必要。
关键还是RtlAllocateHeap
malloc  分配指定大小的内存

void *malloc( 
   size_t size 
);


if(msvcrt!_crtheap==0 && msvcrt!_core_crt_dll_init()==0)
        return 0;
return msvcrt!_nh_malloc(dwSize,msvcrt!_newmode);

msvcrt!malloc:
77bfc407 8bff             mov     edi,edi
77bfc409 55               push    ebp
77bfc40a 8bec             mov     ebp,esp
77bfc40c 833d1824c37700   cmp     dword ptr [msvcrt!_crtheap (77c32418)],0x0
77bfc413 750b             jnz     msvcrt!malloc+0x19 (77bfc420)
77bfc415 e81e2bffff       call    msvcrt!_core_crt_dll_init (77beef38)
77bfc41a 85c0             test    eax,eax
77bfc41c 7502             jnz     msvcrt!malloc+0x19 (77bfc420)
77bfc41e 5d               pop     ebp
77bfc41f c3               ret
        ;如果全局变量msvcrt!_crtheap非0,跳到此
77bfc420 ff350818c377     push    dword ptr [msvcrt!_newmode (77c31808)]
77bfc426 ff7508           push    dword ptr [ebp+0x8]   ;参数dwSize
77bfc429 e8a6ffffff       call    msvcrt!_nh_malloc (77bfc3d4)
_nh_malloc(dwSize,msvcrt!_newmode)
77bfc42e 59               pop     ecx
77bfc42f 59               pop     ecx
77bfc430 5d               pop     ebp
77bfc431 c3               ret

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值