Охота на ведьм Николай «GorluM» Андреев Хакер, номер #078, стр. 078-104-3 // хэндл = указатель на DOS-заголовок PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) hModule; // получаю указатель на PE-заголовок, // суммируя хэндл и смещение в DOS-заголовке // MakePtr — это просто сумма с кастом PIMAGE_NT_HEADERS pNTHeaders = MakePtr(PIMAGE_NT_HEADERS, hModule, pDosHeader->e_lfanew); // и из OptionalHeader я уже получаю виртуальный // адрес нашей таблицы PIMAGE_IMPORT_DESCRIPTOR pImportDesc = MakePtr (PIMAGE_IMPORT_DESCRIPTOR, hModule, pNTHeaders-> OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); Если ты разобрался с форматом PE, то уже должен понять, что pImportDesc — это указатель на первую структуру IMAGE_IMPORT_DESCRIPTOR в таблице импорта. Следом за первой может следовать вторая, третья и т.п. Каждая структура соответствует какой-нибудь статически прилинкованной библиотеке (к примеру, kernel32.dll) и содержит в себе несколько очень важных для нашей дальнейшей работы параметров: смещение имени модуля (Name), смещение таблицы адресов имен функций (OriginalFirstThunk) и смещение таблицы адресов функций (FirstThunk). Как раз эти две таблицы и играют главную роль в определении перехвата. Мы берем элемент из первой таблицы, имя функции вместе с именем модуля из структуры IMAGE_IMPORT_DESCRIPTOR и передаем в параметрах нашей GetProcAddressEx. Возвращенное значение, то есть подлинный адрес функции с таким-то именем, мы сравниваем с соответствующим элементом второй таблицы, проверяемым адресом. Если вдруг они не совпали, то знай — имеет место быть перехват. В коде вся нехитрая проверка таблицы выглядит так: [проверка подлинности таблицы импорта] while (pImportDesc->Name) { // имя модуля char *szModName = MakePtr(LPSTR, hModule, pImportDesc->Name); // указатель на массив указателей на имена функций… во как PIMAGE_THUNK_DATA pNamesTable = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->OriginalFirstThunk); // то же самое, только с адресами PIMAGE_THUNK_DATA pThunk = MakePtr(PIMAGE_THUNK_DATA, hModule, pImportDesc->FirstThunk); while (pThunk->u1.Function) { // проверяем, задана ли функция именем или не ординатой if (!(pNamesTable->u1.Ordinal&0x80000000)) { // получаем имя функции PIMAGE_IMPORT_BY_NAME pName = MakePtr(PIMAGE_IMPORT_BY_NAME, hModule, pNamesTable->u1.AddressOfData); // оригинальный адрес DWORD f = (DWORD)GetProcAddressEx(GetModuleHandle(szModName), (PSTR)&pName->Name); if (f != pThunk->u1.Function) // сравниваем Error((PSTR)&pName->Name, pThunk->u1.Function); } else { DWORD f = (DWORD)GetProcAddressEx(GetModuleHandle(szModName), MAKEINTRESOURCE(((pNamesTable->u1.Ordinal)<<16)>>16)); if (f != pThunk->u1.Function) // сравниваем Error((PSTR)&pName->Name, pThunk->u1.Function); } pNamesTable++; pThunk++; } pImportDesc++; } Функция Error должна отчаянно орать и ругаться, что, мол, обнаружен перехват такой-то API. Но одним только выводом имени API ограничиваться нельзя — оно нам никак не поможет засечь перехватчик. Для того чтобы иметь возможность уничтожить зверя, нам нужно узнать, в каком модуле живет stealth, а не кого перехватывает. Тут нам пригодится функция, написанная когда-то господином Рихтером и позволяющая узнать, какому модулю принадлежит тот или иной адрес: |