Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #78, ИЮНЬ 2005 г.

Охота на ведьм

Николай «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, а не кого перехватывает. Тут нам пригодится функция, написанная когда-то господином Рихтером и позволяющая узнать, какому модулю принадлежит тот или иной адрес:

Назад на стр. 078-104-2  Содержание  Вперед на стр. 078-104-4