|
|
РАССЫЛКА САЙТА
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 файл. Использованные статьи и литератураПРЕДУПРЕЖДЕНИЕ |