CoTaskMemAlloc/CoTaskMemFree과 윈도우 Heap의 관계
지난 글에서 다뤘지만,
windbg - CoTaskMemFree/FreeCoTaskMem에서 발생한 덤프 분석 사례
; https://www.sysnet.pe.kr/2/0/12058
당연히 윈도우 환경에서의 메모리 사용이니 CoTaskMemAlloc도,
CoTaskMemAlloc function
; https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemalloc
내부적으로는 Windows Heap에 의존할 수밖에 없습니다. 그럼 어떻게 의존하는지 간단하게 살펴볼까요? ^^
#include <iostream>
#include <combaseapi.h>
int main()
{
int size = 20;
LPVOID pVoid = CoTaskMemAlloc(size);
printf("pVoid == 0x%x\n", pVoid);
memset(pVoid, 0xff, size);
CoTaskMemFree(pVoid);
return 0;
}
CoTaskMemFree를 호출하는 코드를 보면,
...[생략]...
00835DF4 8B F4 mov esi,esp
00835DF6 8B 45 E8 mov eax,dword ptr [pVoid1]
00835DF9 50 push eax
00835DFA FF 15 58 D1 83 00 call dword ptr [__imp__CoTaskMemFree@4 (083D158h)]
00835E00 3B F4 cmp esi,esp
00835E02 E8 74 B4 FF FF call __RTC_CheckEsp (083127Bh)
...[생략]...
--- onecore\com\combase\class\memapi.cxx ---------------------------------------
7623F800 8B FF mov edi,edi
7623F802 55 push ebp
7623F803 8B EC mov ebp,esp
7623F805 A1 E4 4E 39 76 mov eax,dword ptr [g_CMalloc (76394EE4h)] // 전역 IMalloc 인스턴스를 eax에 저장
7623F80A 56 push esi // non-volatile 레지스터 값이므로 사용 전 push
7623F80B 8B 70 14 mov esi,dword ptr [eax+14h] // esi == IMalloc 인터페이스의 6번째 메서드 포인터
7623F80E 81 FE 20 F5 20 76 cmp esi,offset CRetailMalloc_Free (7620F520h)
7623F814 0F 85 C9 75 07 00 jne `Microsoft::WRL::Module<1,Microsoft::WRL::Details::DefaultModule<1> >::Create'::`2'::`dynamic atexit destructor for 'moduleSingleton''+3CC3h (762B6DE3h)
7623F81A 8B 45 08 mov eax,dword ptr [ebp + 8] // ebp + 8 == 첫 번째 인자 pVoid1
7623F81D 85 C0 test eax,eax
7623F81F 74 0F je CoTaskMemFree+30h (7623F830h)
7623F821 50 push eax
7623F822 6A 00 push 0
7623F824 FF 35 0C 50 39 76 push dword ptr [g_hHeap (7639500Ch)]
7623F82A FF 15 94 72 39 76 call dword ptr [__imp__HeapFree@12 (76397294h)]
7623F830 5E pop esi
7623F831 5D pop ebp
7623F832 C2 04 00 ret 4
내부적으로 이미 생성해 두었던 IMalloc 전역 인스턴스(g_CMalloc)의,
g_CMalloc {lpVtbl=0x7616d158 {combase.dll!IMallocVtbl CRetailMallocVtbl} {QueryInterface=0x76315f80 {combase.dll!CMalloc_QueryInterface(IMalloc *, const _GUID &, void * *)} ...} } CMalloc
6번째 메서드가,
0x7616D158 76315f80 (QueryInterface)
0x7616D15C 7628e8a0 (AddRef)
0x7616D160 7628e8a0 (Release)
0x7616D164 7620f260 (IMalloc::Alloc)
0x7616D168 762781b0 (IMalloc::DidAlloc)
0x7616D16C 7620f520 (IMalloc::Free) <== eax + 14h
0x7616D170 7626e3a0
0x7616D174 76290f30
0x7616D178 76315fc0
0x7616D17C 00000000
0x7616D180 00000001
CRetailMalloc_Free 함수와 동일한 주소인지 체크한 후 맞는다면, HeapFree를 다음과 같은 인자로 호출합니다.
HeapFree(g_hHeap, 0, pVoid);
여기서 g_hHeap은 Win32 프로세스가 생성하는 default Heap으로 Win32 API로는
GetProcessHeap으로 구할 수 있습니다.
HANDLE hDefaultHeap = GetProcessHeap();
printf("pVoid == 0x%x\n", hDefaultHeap);
이게 끝입니다. 이후부터는 HeapFree의 절차를 그대로 따릅니다. 따라서 실질적인 기준으로 본다면, g_CMalloc 관련 코드도 단순히 CRetailMalloc_Free와 비교하는 것일 뿐 정작 g_CMalloc::Free 메서드를 호출하는 것도 아니기 때문에 CoTaskMemFree는 곧바로 HeapFree를 호출하는 것과 별반 다르지 않습니다.
정리하면, CoTaskMemAlloc == HeapAlloc(g_hHeap, ...)으로, CoTaskMemFree == HeapFree(g_hHeap, ...)이라고 보면 됩니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]