|
РАССЫЛКА САЙТА
RSDN.RU |
Здравствуйте, уважаемые
подписчики! Known IUnknown Автор: Павел БлудовДемонстрационное приложение (COM-объект + console) IKnown test (25kb) Демонстрационное приложение (WTL MDI) PdbView (26kb) Просмотр интерфейсовОдной из основ COM-программирования является интерфейс IUnknown. Метод QueryInterface() этого интерфейса позволяет получать все прочие интерфейсы этого объекта. К сожалению, это подразумевает, что вызывающая сторона отлично осведомлена о возможных интерфейсах объекта и даже знает все GUID'ы этих интерфейсов. А если нет? Если возникает необходимость просто просмотреть все интерфейсы, в научно-познавательных целях? Тут есть два пути: узнать у разработчика и перебрать все возможные варианты GUID'ов интерфейсов. Увы, скорости нынешних процессоров недостаточно для того, чтобы перебрать все возможные 2 в 128-ой степени вариантов, и хорошо бы как-то уменьшить размер перебора. Давайте поищем большие скопления GUID'ов и отправим их все в IUnknown::QueryInterface(). РеестрОдно из таких скоплений это реестр Windows. Огромное количество интерфейсов прописано в HKEY_CLASSES_ROOT\Interface . CRegKey key; if(ERROR_SUCCESS == key.Open(HKEY_CLASSES_ROOT, _T("Interface"))) { TCHAR szInterface[MAX_PATH]; TCHAR szIID[64]; for (DWORD dwIndex = 0; ERROR_SUCCESS = = ::RegEnumKey(key, dwIndex, szIID, < br > sizeof(szIID) / sizeof(szIID[0])); ++dwIndex) { LONG cb = MAX_PATH; if (ERROR_SUCCESS != ::RegQueryValue(key, szIID, szInterface, &cb)) szInterface[0] = _T('\x0'); // TODO } } Такой способ использует DumpQIQS от Andrew Nosenko. Этот список дает приемлемые результаты, но, к сожалению, далеко не полон. Библиотечные (.lib и .obj) файлыДовольно большое количество GUID'ов недокументированных интерфейсов находится в .lib файлах uuid.lib, dxguid.lib, ADSIid.Lib и им подобным. Формат этих файлов хорошо известен, и я не буду вдаваться в технические детали, скажу лишь, что для нас представляют интерес только объекты класса IMAGE_SYM_CLASS_EXTERNAL размером 16 байт, т.е. sizeof(GUID). Пример IKnown test содержит целиком код для разбора .lib файлов на .obj и поиск GUID'ов в .obj файлах. Отладочные файлы (.pdb)Гарантировано полный список всех интерфейсов, реализуемых объектами какого-либо модуля находится в его отладочном файле. Увы, раздобыть .pdb файл нужного компонента ничуть не проще, чем его исходный код. Проще все-же написать разработчику и прямо спросить: "А нету ли у вас интерфейса, реализующего то-то и то-то?". Радостным исключением из этого правила является Microsoft Windows NT. Полный набор .pdb файлов этой операционной системы (а она теперь включает Microsoft Internet Explorer и DirectX) доступен для бесплатной загрузки с Web-сервера Майкрософт под названием Windows Customer Support Diagnostics или через подписку MSDN. Стоит только заполучить в свои руки отладочный файл интересующиего нас модуля, как проблема перебора всех GUID'ов интерфейсов решается и решается на 100%. Для этого нам нужно:
BOOL CALLBACK EnumSymbolsCallback( LPSTR szSymbolName, ULONG SymbolAddress, ULONG SymbolSize, PVOID pUserContext ) { // Обычно, GUID'ы объявляют как extern "C" так
Объект IKnown из тестового приложения реализует все три способа. Пример использования: pKnown->LoadInterfacesFromRegistry(); pKnown->LoadInterfacesFromLibrary(OLESTR("Uuid.Lib")); // Загружаем символы для модуля, в котором находится реализация // метода QueryInterface этого объекта DWORD_PTR *pVTable = ((DWORD_PTR **)pUnk.p)[0]; pKnown->LoadInterfacesFromDebugSymbols( pKnown->GetPointerHModule(LPVOID(pVTable[0]))); pKnown->DumpUnknown(pUnk, PrintCallback, LPVOID(pKnown)); Восстановление интерфейсаНу хорошо, выяснили мы, какие интерфейсы есть у объекта. А толку-то? Что теперь делать с этим IID_OurInterfaceForInternalUseOnly? Где можно узнать про этот пресловутый IOurInterfaceForInternalUseOnly? Да все там же! В тех самых файлах с отладочной информацией. Чтобы понять, как это возможно, нужно сначала разобраться, что же такое COM-интерфейс и как его реализуют нынешние компиляторы. Итак, COM-интерфейс это абстрактный базовый клас, все методы которого являются виртуальными, т.е. вызов этих методов осуществляется через специальную таблицу указателей на функции, известную как vftable. Например, вызов IUnknown::QueryInterface() преобразуется компилятором в push eax // положить в стек второй параметр push esi // положить в стек первый параметр mov ecx,dword ptr [ebp+0Ch] // загрузить vftable в ECX mov edx,dword ptr [ecx] // получить в EDX адрес Таким образом, наша задача сводится к поиску таких таблиц и вычислению имен каждого из методов. Для этого нужно перебрать все отладочные символы и отобрать среди них все те, чьи имена выглядят как const <имя_класса>::'vftable' или const <имя_класса>::'vftable' {for 'имя_базового_класса'}. Дальше мы можем просто трактовать область памяти, адресуемую этим символом как массив указателей на функции. BOOL CALLBACK EnumSymbolsCallback( LPSTR szSymbolName, ULONG dwSymbolAddress, ULONG dwSymbolSize, PVOID pUserContext ) { if (szSymbolName LIKE "const *::`vftable'*for `*`") { LPDWORD pdwMember = LPDWORD(dwSymbolAddress); PIMAGEHLP_SYMBOL pSymbol = (PIMAGEHLP_SYMBOL)alloca( sizeof(IMAGEHLP_SYMBOL) + MAX_PATH); if (pSymbol) { DWORD dwDisplacement = 0; BOOL bOk; do { pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL); pSymbol->MaxNameLength = MAX_PATH; bOk = ::SymGetSymFromAddr(::GetCurrentProcess(), *pdwMember, &dwDisplacement, pSymbol); if (bOk && 0 == dwDisplacement) { // TODO } } while(bOk); } } Тестовое приложениие PdbView пытается восстановить все классы заданного модуля если имеется соответствующий .pdb файл. Использованные статьи и литератураПРЕДУПРЕЖДЕНИЕ |