Download Source :- LeakMon_source.zip
Basic working principle
So, when if you select a process and press the "Inject" button, what it does is, It will inject the "HookDll.dll" to the traget process' address space. This is done with the help of CreateRemoteThread API. Injecting a DLL using CreateRemoteThread API is explained detailed in the article Three Ways to Inject Your Code into Another Process. Anyway in Injector.exe the following code part does the injection.
{
......
CString csPid = m_List.GetItemText( nSelected, 0 );
DWORD dwPID = _ttoi( csPid );
HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|
PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ,
FALSE, dwPID );
if( !hProcess)
{
AfxMessageBox( _T("Failed to open the process" ));
return;
}
HINSTANCE hLib = LoadLibrary( "Kernel32.dll" );
PROC pLoadLib = (PROC)GetProcAddress( hLib, "LoadLibraryA" );
void* pLibRemote = ::VirtualAllocEx( hProcess, NULL, csPath.GetLength(),
MEM_COMMIT, PAGE_READWRITE );
::WriteProcessMemory( hProcess, pLibRemote, (void*)csPath.operator LPCTSTR(),
csPath.GetLength(), NULL );
if( !CreateRemoteThread( hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pLoadLib, pLibRemote, 0, 0 ))
{
AfxMessageBox( "Create Remote thread Failed" );
:VirtualFreeEx( hProcess, pLibRemote,csPath.GetLength(), MEM_RELEASE );
}
}
{
HANDLE hThread = ::CreateThread( 0,0,DumpController, 0,0, 0 );
CloseHandle( hThread );
return CWinApp::InitInstance();
}
1. Select what kind of resource allocation you want to track
2. Select the path of the PDB's of the application
3. Stack depth
{
..................
SymInitialize(GetCurrentProcess(), (LPTSTR)csWholePath.operator LPCTSTR() , FALSE );
SymRegisterCallback64( GetCurrentProcess(),SymRegisterCallbackProc64,(ULONG64 )this );
..................
if( pSymRefreshModuleList )
{
pSymRefreshModuleList( GetCurrentProcess());
}
..................
}
BOOL CALLBACK ConfigDlg::SymRegisterCallbackProc64(HANDLE hProcess,
ULONG ActionCode,
ULONG64 CallbackData,
ULONG64 UserContext)
{
if( CBA_DEFERRED_SYMBOL_LOAD_START == ActionCode )
{
PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 pSybolLoadInfo =(PIMAGEHLP_DEFERRED_SYMBOL_LOAD64)CallbackData;
ConfigDlg* pDlg = (ConfigDlg*)UserContext;
CString csLoadtext = _T("Loading symbol for file: ");
csLoadtext += pSybolLoadInfo->FileName;
pDlg->m_ProgressDlg.SetDlgItemText( IDC_LOAD_INFO, csLoadtext );
}
// return false to indicate that we are not doing any symbol loading by ourself
return FALSE;
}
| Memory allocation and de-allocation functions | |||
|---|---|---|---|
| HeapAlloc | HeapFree | HeapReAlloc | VirtualAllocEx |
| VirtualFreeEx | GlobalAlloc | GlobalReAlloc | GlobalFree |
| LocalAlloc | LocalReAlloc | LocalFree | |
| GDI object creation and deletion functions | ||
|---|---|---|
| Bitmaps | ||
| LoadBitmapA | LoadBitmapW | LoadImageA |
| LoadImageW | CreateBitmap | CreateBitmapIndirect |
| CreateCompatibleBitmap | CreateDIBitmap | CreateDIBSection |
| CreateDiscardableBitmap | CopyImage | GetIconInfo |
| GetIconInfoExA | GetIconInfoExW | |
| Icons | ||
| CopyIcon | CreateIcon | CreateIconFromResource |
| CreateIconFromResourceEx | CreateIconIndirect | DestroyIcon |
| DuplicateIcon | ExtractAssociatedIconA | ExtractAssociatedIconW |
| ExtractAssociatedIconExA | ExtractAssociatedIconExW | ExtractIconA |
| ExtractIconW | ExtractIconExA | ExtractIconExW |
| LoadIconA | LoadIconW | PrivateExtractIconsA |
| PrivateExtractIconsW | ||
| Cursor | ||
| CreateCursor | DestroyCursor | LoadCursorA |
| LoadCursorW | LoadCursorFromFileA | LoadCursorFromFileW |
| Brush | ||
| CreateBrushIndirect | CreateSolidBrush | CreatePatternBrush |
| CreateDIBPatternBrush | CreateDIBPatternBrushPt | CreateHatchBrush |
| Device context | ||
| CreateCompatibleDC | CreateDCA | CreateDCW |
| CreateICA | CreateICW | GetDC |
| GetDCEx | GetWindowDC | ReleaseDC |
| DeleteDC | ||
| Font | ||
| CreateFontA | CreateFontW | CreateFontIndirectA |
| CreateFontIndirectW | ||
| Metafile | ||
| CreateMetaFileA | CreateMetaFileW | CreateEnhMetaFileA |
| CreateEnhMetaFileW | GetEnhMetaFileA | GetEnhMetaFileW |
| GetMetaFileA | GetMetaFileW | DeleteMetaFile |
| DeleteEnhMetaFile | CopyEnhMetaFileA | CopyEnhMetaFileW |
| CloseEnhMetaFile | CloseMetaFile | |
| Pen | ||
| CreatePen | CreatePenIndirect | ExtCreatePen |
| Region | ||
| PathToRegion | CreateEllipticRgn | CreateEllipticRgnIndirect |
| CreatePolygonRgn | CreatePolyPolygonRgn | CreateRectRgn |
| CreateRectRgnIndirect | CreateRoundRectRgn | ExtCreateRegion |
| Palette | ||
| CreateHalftonePalette | CreatePalette | |
| Common Function | ||
| DeleteObject | ||
| Handle creation and deletion functions | ||
|---|---|---|
| Synchronization objects | ||
| CreateEventA | CreateEventW | CreateEventExA |
| CreateEventExW | OpenEventA | OpenEventW |
| CreateMutexA | CreateMutexW | CreateMutexExA |
| CreateMutexExW | OpenMutexA | OpenMutexW |
| CreateSemaphoreA | CreateSemaphoreW | CreateSemaphoreExA |
| CreateSemaphoreExW | OpenSemaphoreA | OpenSemaphoreW |
| CreateWaitableTimerA | CreateWaitableTimerW | CreateWaitableTimerExA |
| CreateWaitableTimerExW | OpenWaitableTimerA | OpenWaitableTimerW |
| File function | ||
| CreateFileA | CreateFileW | CreateFileTransactedA |
| CreateFileTransactedW | FindFirstFileA | FindFirstFileW |
| FindFirstFileExA | FindFirstFileExW | FindFirstFileNameTransactedW |
| FindFirstFileNameW | FindFirstFileTransactedA | FindFirstFileTransactedW |
| FindFirstStreamTransactedW | FindFirstStreamW | FindClose |
| OpenFileById | ReOpenFile | CreateIoCompletionPort |
| Authorization function | ||
| CreateRestrictedToken | DuplicateToken | DuplicateTokenEx |
| OpenProcessToken | OpenThreadToken | |
| Directory management | ||
| FindFirstChangeNotificationA | FindFirstChangeNotificationW | FindCloseChangeNotification |
| File mapping | ||
| CreateMemoryResourceNotification | CreateFileMappingA | CreateFileMappingW |
| CreateFileMappingNumaA | CreateFileMappingNumaW | OpenFileMappingA |
| OpenFileMappingW | ||
| Memory | ||
| HeapCreate | HeapDestroy | GlobalAlloc |
| GlobalReAlloc | GlobalFree | LocalAlloc |
| LocalReAlloc | LocalFree | |
| Process and thread | ||
| CreateProcessA | CreateProcessW | CreateProcessAsUserA |
| CreateProcessAsUserW | CreateProcessWithLogonW | CreateProcessWithTokenW |
| OpenProcess | CreateThread | CreateRemoteThread |
| OpenThread | CreateJobObjectA | CreateJobObjectW |
| Mail slot | ||
| CreateMailslotA | CreateMailslotW | |
| pipe | ||
| CreatePipe | CreateNamedPipeA | CreateNamedPipeW |
| Registry | ||
| RegCreateKeyExA | RegCreateKeyExW | RegCreateKeyTransactedA |
| RegCreateKeyTransactedW | RegOpenCurrentUser | RegOpenKeyA |
| RegOpenKeyW | RegOpenKeyExA | RegOpenKeyExW |
| RegOpenKeyTransactedA | RegOpenKeyTransactedW | RegOpenUserClassesRoot |
| RegCreateKeyA | RegCreateKeyW | RegCloseKey |
| Common functions | ||
| DuplicateHandle | CloseHandle | |
Now lets see how the hooking works. For each of the function in the above table LeakMon have a dummy function ( Which is actually 199 dummy functions ). So after hooking, the application will be calling my dummy function instead of the original function. In the dummy function what it does is,
DWORD dwFlags,
SIZE_T dwBytes )
{
LPVOID lMem = pOrgHeapAlloc( hHeap, dwFlags, dwBytes );
CreateCallStack( lMem, dwBytes );
return lMem;
}
{
RemovCallStack( lpMem );
return pOrgHeapFree( hHeap, dwFlags, lpMem );
}
This is the only part which actually has different processing for 64 bit and 32 bit. In 32 bit applications the call stack is created using the StackWalk64 function. You can either check the code ( StackDump function ) or read this nice article Walking the callstack, to learn more about the StackWalk64 function.
The stack walking in x64 machine was bit difficult for me. I actually had no idea how to do it. Thanks to Ken Johnson for posting an example of stack walking in x64 machine which practically made me to port this application to 64 bit also.
Dumping the leaks
Open Source
