HRESULT GetImageInfoFromPEHeader( HANDLE hProcess, void* dllBase, uint16_t& machine, uint32_t& size, Address& prefBase ) { IMAGE_DOS_HEADER dosHeader = { 0 }; SIZE_T cActual = 0; if ( !::ReadProcessMemory( hProcess, dllBase, &dosHeader, sizeof dosHeader, &cActual ) ) { _ASSERT( !"Failed to read IMAGE_DOS_HEADER from loaded module" ); return GetLastHr(); } IMAGE_NT_HEADERS ntHeaders = { 0 }; void* ntHeadersAddr = (void*) ((DWORD_PTR) dosHeader.e_lfanew + (DWORD_PTR) dllBase); if ( !::ReadProcessMemory( hProcess, ntHeadersAddr, &ntHeaders, sizeof ntHeaders, &cActual ) ) { _ASSERT( !"Failed to read IMAGE_NT_HEADERS from loaded module" ); return GetLastHr(); } // These fields line up for 32 and 64-bit IMAGE_NT_HEADERS; make sure of it // otherwise, we would have had to check fileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE C_ASSERT( &((IMAGE_NT_HEADERS32*) 0)->OptionalHeader.SizeOfImage == &((IMAGE_NT_HEADERS64*) 0)->OptionalHeader.SizeOfImage ); C_ASSERT( &((IMAGE_NT_HEADERS32*) 0)->FileHeader.Machine == &((IMAGE_NT_HEADERS64*) 0)->FileHeader.Machine ); machine = ntHeaders.FileHeader.Machine; size = ntHeaders.OptionalHeader.SizeOfImage; prefBase = ntHeaders.OptionalHeader.ImageBase; return S_OK; }
HRESULT DebuggerProxy::Init( IEventCallback* callback ) { _ASSERT( callback != NULL ); if ( (callback == NULL ) ) return E_INVALIDARG; if ( (mCallback != NULL ) ) return E_ALREADY_INIT; HandlePtr hReadyEvent; HandlePtr hCommandEvent; HandlePtr hResultEvent; hReadyEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( hReadyEvent.IsEmpty() ) return GetLastHr(); hCommandEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( hCommandEvent.IsEmpty() ) return GetLastHr(); hResultEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( hResultEvent.IsEmpty() ) return GetLastHr(); mhReadyEvent = hReadyEvent.Detach(); mhCommandEvent = hCommandEvent.Detach(); mhResultEvent = hResultEvent.Detach(); mCallback = callback; mCallback->AddRef(); return S_OK; }
HRESULT DebuggerProxy::InvokeCommand( CommandFunctor& cmd ) { if ( mWorkerTid == GetCurrentThreadId() ) { // since we're on the poll thread, we can run the command directly cmd.Run(); } else { GuardedArea area( mCommandGuard ); mCurCommand = &cmd; ResetEvent( mhResultEvent ); SetEvent( mhCommandEvent ); DWORD waitRet = 0; HANDLE handles[2] = { mhResultEvent, mhThread }; waitRet = WaitForMultipleObjects( _countof( handles ), handles, FALSE, INFINITE ); mCurCommand = NULL; if ( waitRet == WAIT_FAILED ) return GetLastHr(); if ( waitRet == WAIT_OBJECT_0 + 1 ) return CO_E_REMOTE_COMMUNICATION_FAILURE; } return S_OK; }
HRESULT ImageDebugContainer::LockDebugSection( BYTE*& bytes, DWORD& size ) { HRESULT hr = S_OK; HandlePtr hMapping; DWORD viewAlignedAddr = 0; DWORD viewDiff = 0; MappedPtr view; if ( mImage.get() != NULL ) { hr = mImage->GetMappingHandle( hMapping.Ref() ); } else if ( mDbg.get() != NULL ) { hr = mDbg->GetMappingHandle( hMapping.Ref() ); } else hr = E_FAIL; if ( FAILED( hr ) ) return hr; AlignAddress( mDebugDir.PointerToRawData, viewAlignedAddr, viewDiff ); view = MapViewOfFile( hMapping, FILE_MAP_READ, 0, viewAlignedAddr, mDebugDir.SizeOfData + viewDiff ); if ( view.IsEmpty() ) return GetLastHr(); bytes = (BYTE*) view.Detach() + viewDiff; size = mDebugDir.SizeOfData; return S_OK; }
HRESULT ImageDebugContainer::UnlockDebugSection( BYTE* bytes ) { BOOL bRet = UnmapViewOfFile( bytes ); if ( !bRet ) return GetLastHr(); return S_OK; }
HRESULT DebuggerProxy::Start() { _ASSERT( mCallback != NULL ); if ( (mCallback == NULL ) ) return E_UNEXPECTED; if ( mhThread != NULL ) return E_UNEXPECTED; HandlePtr hThread; hThread = (HANDLE) _beginthreadex( NULL, 0, (CrtThreadProc) DebugPollProc, this, 0, NULL ); if ( hThread.IsEmpty() ) return GetLastHr(); HANDLE waitObjs[2] = { mhReadyEvent, hThread.Get() }; DWORD waitRet = 0; // TODO: on error, thread will be shutdown from Shutdown method waitRet = WaitForMultipleObjects( _countof( waitObjs ), waitObjs, FALSE, INFINITE ); if ( waitRet == WAIT_FAILED ) return GetLastHr(); if ( waitRet == WAIT_OBJECT_0 + 1 ) { DWORD exitCode = (DWORD) E_FAIL; // the thread ended because of an error, let's get the return exit code GetExitCodeThread( hThread.Get(), &exitCode ); return (HRESULT) exitCode; } _ASSERT( waitRet == WAIT_OBJECT_0 ); mhThread = hThread.Detach(); return S_OK; }
HRESULT ImageDebugContainer::LoadExe( const wchar_t* filename, ILoadCallback* callback ) { HRESULT hr = S_OK; auto_ptr<ImageFile> image( new ImageFile() ); DataDirInfo dirInfo = { 0 }; HandlePtr hMapping; DWORD viewAlignedAddr = 0; DWORD viewDiff = 0; MappedPtr view; DWORD debugDirCount = 0; IMAGE_DEBUG_DIRECTORY* debugDir = NULL; if ( image.get() == NULL ) return E_OUTOFMEMORY; hr = image->LoadFile( filename ); if ( FAILED( hr ) ) return hr; if ( !image->FindDataDirectoryData( IMAGE_DIRECTORY_ENTRY_DEBUG, dirInfo ) ) return E_FAIL; hr = image->GetMappingHandle( hMapping.Ref() ); if ( FAILED( hr ) ) return hr; AlignAddress( dirInfo.FileOffset, viewAlignedAddr, viewDiff ); view = MapViewOfFile( hMapping, FILE_MAP_READ, 0, viewAlignedAddr, dirInfo.Size + viewDiff ); if ( view.IsEmpty() ) return GetLastHr(); debugDir = (IMAGE_DEBUG_DIRECTORY*) ((BYTE*) view.Get() + viewDiff); if ( callback != NULL ) callback->NotifyDebugDir( true, dirInfo.Size, (BYTE*) debugDir ); for ( debugDirCount = dirInfo.Size / sizeof *debugDir; debugDirCount > 0; debugDirCount--, debugDir++ ) { if ( (debugDir->Type == IMAGE_DEBUG_TYPE_CODEVIEW) || (debugDir->Type == IMAGE_DEBUG_TYPE_MISC) ) break; } if ( debugDirCount == 0 ) return HRESULT_FROM_WIN32( ERROR_NOT_FOUND ); if ( debugDir->Type == IMAGE_DEBUG_TYPE_MISC ) return LoadDbg( image.get(), debugDir, callback ); mDebugDir = *debugDir; mImage.reset( image.release() ); return S_OK; }
HRESULT DebuggerProxy::CheckMessage() { HRESULT hr = S_OK; DWORD waitRet = 0; waitRet = WaitForSingleObject( mhCommandEvent, CommandTimeoutMillis ); if ( waitRet == WAIT_FAILED ) return GetLastHr(); if ( waitRet == WAIT_TIMEOUT ) return S_OK; hr = ProcessCommand( mCurCommand ); if ( FAILED( hr ) ) return hr; ResetEvent( mhCommandEvent ); SetEvent( mhResultEvent ); return S_OK; }
HRESULT ReadMemory( HANDLE hProcess, UINT_PTR address, SIZE_T length, SIZE_T& lengthRead, SIZE_T& lengthUnreadable, uint8_t* buffer ) { _ASSERT( hProcess != NULL ); _ASSERT( buffer != NULL ); _ASSERT( length < limit_max( length ) ); HRESULT hr = S_OK; BOOL bRet = FALSE; MEMORY_BASIC_INFORMATION memInfo = { 0 }; SIZE_T sizeRet = 0; SIZE_T lenReadable = 0; SIZE_T lenUnreadable = 0; UINT_PTR nextAddr = address; while ( ((lenReadable + lenUnreadable) < length) && (nextAddr >= address) ) { bool readable = true; sizeRet = VirtualQueryEx( hProcess, (void*) nextAddr, &memInfo, sizeof memInfo ); if ( sizeRet == 0 ) return HRESULT_FROM_WIN32( ERROR_PARTIAL_COPY ); if ( (memInfo.State != MEM_COMMIT) || (memInfo.Protect == 0) || ((memInfo.Protect & PAGE_NOACCESS) != 0) ) readable = false; SIZE_T curSize = memInfo.RegionSize - (nextAddr - (UINT_PTR) memInfo.BaseAddress); if ( readable ) { // we went from (readable to) unreadable to readable, // this last readable won't be returned, so we finished if ( lenUnreadable > 0 ) break; lenReadable += curSize; } else { lenUnreadable += curSize; } nextAddr = (UINT_PTR) memInfo.BaseAddress + memInfo.RegionSize; } // cap the length to read to the length the user asked for SIZE_T lenToRead = (lenReadable > length) ? length : lenReadable; bRet = ::ReadProcessMemory( hProcess, (const void*) address, buffer, lenToRead, &lengthRead ); if ( !bRet ) return GetLastHr(); lengthUnreadable = (lenUnreadable > (length - lengthRead)) ? length - lengthRead : lenUnreadable; _ASSERT( lengthRead <= length ); _ASSERT( lengthUnreadable <= length ); _ASSERT( (lengthRead + lengthUnreadable) <= length ); return hr; }
HRESULT ImageDebugContainer::LoadDbg( BinImage::ImageFile* image, const IMAGE_DEBUG_DIRECTORY* miscDebugDir, ILoadCallback* callback ) { _ASSERT( image != NULL ); _ASSERT( miscDebugDir != NULL ); HRESULT hr = S_OK; HandlePtr hMapping; DWORD viewAlignedAddr = 0; DWORD viewDiff = 0; MappedPtr view; IMAGE_DEBUG_MISC* misc = NULL; UniquePtr<wchar_t[]> strBuf; DWORD dataLen = 0; if ( miscDebugDir->SizeOfData < sizeof( IMAGE_DEBUG_MISC ) ) return E_FAIL; hr = image->GetMappingHandle( hMapping.Ref() ); if ( FAILED( hr ) ) return hr; AlignAddress( miscDebugDir->PointerToRawData, viewAlignedAddr, viewDiff ); view = MapViewOfFile( hMapping, FILE_MAP_READ, 0, viewAlignedAddr, miscDebugDir->SizeOfData + viewDiff ); if ( view.IsEmpty() ) return GetLastHr(); misc = (IMAGE_DEBUG_MISC*) view.Get(); dataLen = misc->Length - offsetof( IMAGE_DEBUG_MISC, Data ); if ( misc->DataType != IMAGE_DEBUG_MISC_EXENAME ) return E_FAIL; // I don't know that the string in data is definitely \0 terminated, so we force it if ( misc->Unicode ) { size_t charCount = wcsnlen( (wchar_t*) misc->Data, dataLen / sizeof( wchar_t ) ); // TODO: why were we calling wcsnlen twice? //charCount = wcsnlen( (wchar_t*) misc->Data, charCount ); strBuf.Attach( new wchar_t[ charCount + 1 ] ); if ( strBuf.Get() == NULL ) return E_OUTOFMEMORY; // adds terminator wcsncpy_s( strBuf.Get(), charCount + 1, (wchar_t*) misc->Data, charCount ); } else { size_t charCount = strnlen( (char*) misc->Data, dataLen ); int nChars = 0; nChars = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, (char*) misc->Data, charCount, NULL, 0 ); if ( nChars == 0 ) return GetLastHr(); strBuf.Attach( new wchar_t[ nChars + 1 ] ); if ( strBuf.Get() == NULL ) return E_OUTOFMEMORY; nChars = MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS, (char*) misc->Data, charCount, strBuf.Get(), nChars + 1 ); if ( nChars == 0 ) return GetLastHr(); strBuf[nChars] = L'\0'; } hr = LoadDbg( strBuf.Get(), callback ); return hr; }