win32平台下malloc的实现

转自http://hi.baidu.com/numax/blog/item/ff9e3d0eeb1f7acf7acbe1cb.html

 

如果您不想看那一堆确实比较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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值