BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / security / #13439同步于 2007/9/17
Security机器人发帖

[合集] [原创]win32平台下malloc的内部实现

flyingkisser
2007/9/17镜像同步0 回复
☆─────────────────────────────────────☆ flyingkisser (齐天大猫) 于 (Mon Sep 10 10:13:24 2007) 提到: 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 ☆─────────────────────────────────────☆ rebirthatsix (茫犭者) 于 (Mon Sep 10 10:58:15 2007) 提到: 顶猫哥,看来你真想把heapalloc给逆向了。。 ☆─────────────────────────────────────☆ coolfantasy (Cool) 于 (Mon Sep 10 11:29:04 2007) 提到: ding ☆─────────────────────────────────────☆ zzm7000 (noise) 于 (Mon Sep 10 12:23:31 2007) 提到: 顶啊 ☆─────────────────────────────────────☆ pmps (pmps) 于 (Mon Sep 10 12:33:13 2007) 提到: 还好。看得懂。 ☆─────────────────────────────────────☆ CNLAS (Ich gewinne bestimmt……) 于 (Mon Sep 10 12:39:49 2007) 提到: VC8里malloc的源码。。。来验证猫哥的逆向结果。。。 /*** *malloc.c - Get a block of memory from the heap * * Copyright (c) Microsoft Corporation. All rights reserved. * *Purpose: * Defines the malloc() function. * *******************************************************************************/ #include <cruntime.h> #include <malloc.h> #include <internal.h> #include <mtdll.h> #include <dbgint.h> #include <rterr.h> #include <windows.h> #include <winheap.h> #include <rtcsup.h> extern int _newmode; /* malloc new() handler mode */ #ifndef _WIN64 void *V6_HeapAlloc(size_t size) { void *pvReturn = NULL; if ( size <= __sbh_threshold ) { _mlock( _HEAP_LOCK ); __try { pvReturn = __sbh_alloc_block((int)size); } __finally { _munlock( _HEAP_LOCK ); } } return pvReturn; } #ifdef CRTDLL void *V5_HeapAlloc(size_t size) { void * pvReturn = NULL; /* round up to the nearest paragraph */ if ( size ) { size = (size + _OLD_PARASIZE - 1) & ~(_OLD_PARASIZE - 1); } else { size = _OLD_PARASIZE; } if ( size <= __old_sbh_threshold ) { _mlock(_HEAP_LOCK); __try { pvReturn = __old_sbh_alloc_block(size >> _OLD_PARASHIFT); } __finally { _munlock(_HEAP_LOCK); } } return pvReturn; } #endif /* CRTDLL */ #endif /* _WIN64 */ #ifdef _DEBUG #define _heap_alloc _heap_alloc_base #endif /* _DEBUG */ /*** *void *_heap_alloc_base(size_t size) - does actual allocation * *Purpose: * Same as malloc() except the new handler is not called. * *Entry: * See malloc * *Exit: * See malloc * *Exceptions: * *******************************************************************************/ __forceinline void * __cdecl _heap_alloc (size_t size) { #ifndef _WIN64 void *pvReturn; #endif /* _WIN64 */ if (_crtheap == 0) { _FF_MSGBANNER(); /* write run-time error banner */ _NMSG_WRITE(_RT_CRT_NOTINIT); /* write message */ __crtExitProcess(255); /* normally _exit(255) */ } #ifdef _WIN64 return HeapAlloc(_crtheap, 0, size ? size : 1); #else /* _WIN64 */ if (__active_heap == __SYSTEM_HEAP) { return HeapAlloc(_crtheap, 0, size ? size : 1); } else if ( __active_heap == __V6_HEAP ) { if (pvReturn = V6_HeapAlloc(size)) { return pvReturn; } } #ifdef CRTDLL else if ( __active_heap == __V5_HEAP ) { if (pvReturn = V5_HeapAlloc(size)) { return pvReturn; } } #endif /* CRTDLL */ if (size == 0) size = 1; size = (size + BYTES_PER_PARA - 1) & ~(BYTES_PER_PARA - 1); return HeapAlloc(_crtheap, 0, size); #endif /* _WIN64 */ } /*** *void *malloc(size_t size) - Get a block of memory from the heap * *Purpose: * Allocate of block of memory of at least size bytes from the heap and * return a pointer to it. * * Calls the new appropriate new handler (if installed). * *Entry: * size_t size - size of block requested * *Exit: * Success: Pointer to memory block * Failure: NULL (or some error value) * *Uses: * *Exceptions: * *******************************************************************************/ 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; } 【 在 flyingkisser 的大作中提到: 】 ☆─────────────────────────────────────☆ flyingkisser (齐天大猫) 于 (Mon Sep 10 14:25:54 2007) 提到: 已经逆完了, RtlCreateHeap RtlAllocateHeap RtlFreeHeap RtlDestroyHeap 其中Create和Alloc操作也同时逆向了ring0的实现, 不过有些地方还不是很明白,所以暂时就不想拿出被笑话 ☆─────────────────────────────────────☆ flyingkisser (齐天大猫) 于 (Mon Sep 10 14:29:19 2007) 提到: 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 ☆─────────────────────────────────────☆ hukt (Ghost) 于 (Mon Sep 10 21:58:43 2007) 提到: ding技术贴~ ☆─────────────────────────────────────☆ LoveRose (江南西道|tuoqi-开始还债) 于 (Mon Sep 10 23:16:08 2007) 提到: 真棒 ☆─────────────────────────────────────☆ xiaojia164 (【顶你小分队】│虢虓湛卢) 于 (Tue Sep 11 00:28:37 2007) 提到: 看了半天,云里雾里的 弱弱的请教一下,研究这个有什么用途呢 ☆─────────────────────────────────────☆ CNLAS (Ich gewinne bestimmt……) 于 (Tue Sep 11 00:29:51 2007) 提到: 可以有资本赚更多的钱。。。恩。。。-_,- 【 在 xiaojia164 的大作中提到: 】 ☆─────────────────────────────────────☆ flyingkisser (齐天大猫) 于 (Tue Sep 11 01:21:22 2007) 提到: 修炼内功 ☆─────────────────────────────────────☆ yegle (yegle) 于 (Tue Sep 11 02:06:29 2007) 提到: 信安专业的人是不是都能做这个分析? ☆─────────────────────────────────────☆ zwz (ERA-The Mass) 于 (Tue Sep 11 02:11:00 2007) 提到: 汇编看的眼花,还是vc8那个看着亲切。。。。 T_T ☆─────────────────────────────────────☆ rebirthatsix (茫犭者) 于 (Tue Sep 11 07:54:53 2007) 提到: 内功,心法。。。钱=.= 要的就是这些 不过信息安全的覆盖面大了,做系统内核只是其中的一块 这年头web安全米也多阿。。。 ☆─────────────────────────────────────☆ coolfantasy (Cool) 于 (Tue Sep 11 09:18:06 2007) 提到: 就看到¥了。。。 ☆─────────────────────────────────────☆ rebirthatsix (茫犭者) 于 (Tue Sep 11 09:40:40 2007) 提到: 【 在 coolfantasy 的大作中提到: 】 去银行吧。。 ☆─────────────────────────────────────☆ flyingkisser (齐天大猫) 于 (Tue Sep 11 10:13:39 2007) 提到: 因为我要研究一下溢出保护,就要研究堆溢出,就要逆向堆管理函数, 同时还得逆向一些常用的使用堆的像malloc这类的内存管理函数。 ☆─────────────────────────────────────☆ xiaojia164 (【顶你小分队】│虢虓湛卢) 于 (Tue Sep 11 12:08:15 2007) 提到: 【 在 flyingkisser 的大作中提到: 】 原来如此。这个似乎太专业了,一般写程序的应该用不到吧 ☆─────────────────────────────────────☆ rebirthatsix (茫犭者) 于 (Tue Sep 11 18:02:00 2007) 提到: 【 在 xiaojia164 的大作中提到: 】 正是因为大部分程序员都这样想,或者连想都没想过 我们才有¥赚啊,还是$好 ☆─────────────────────────────────────☆ coolfantasy (Cool) 于 (Tue Sep 11 18:07:19 2007) 提到: 技术白痴路过... ☆─────────────────────────────────────☆ rebirthatsix (茫犭者) 于 (Tue Sep 11 18:49:08 2007) 提到: 【 在 coolfantasy 的大作中提到: 】 转型。。。 ☆─────────────────────────────────────☆ windam (windam) 于 (Tue Sep 11 23:31:41 2007) 提到: 强贴啊,顶。。。 嗯,逆向很需要耐心和毅力。。。PF一下。。。
订阅后,新回复会通过你的通知中心匿名送达。
0 条回复
暂无回复 · 你可以订阅本帖等待新回复。