- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
if (::IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) || IMAGE_DOS_SIGNATURE != pDosHeader->e_magic) {
return E_INVALIDARG;
}
PIMAGE_NT_HEADERS pNTHeaders =
MakePtr(PIMAGE_NT_HEADERS, hModule, pDosHeader->e_lfanew);
if (::IsBadReadPtr(pNTHeaders, sizeof(IMAGE_NT_HEADERS)) || IMAGE_NT_SIGNATURE != pNTHeaders->Signature) {
return E_INVALIDARG;
}
HRESULT hr = E_UNEXPECTED;
IMAGE_DATA_DIRECTORY& expDir =
pNTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
PIMAGE_EXPORT_DIRECTORY pExpDir =
MakePtr(PIMAGE_EXPORT_DIRECTORY, hModule, expDir.VirtualAddress);
LPDWORD pdwAddrs = MakePtr(LPDWORD, hModule, pExpDir->AddressOfFunctions);
LPWORD pdwOrd = MakePtr(LPWORD, hModule, pExpDir->AddressOfNameOrdinals);
DWORD dwAddrIndex = -1;
if (IS_INTRESOURCE(szEntry)) {
// By ordinal
dwAddrIndex = WORD(szEntry) - pExpDir->Base;
hr = S_OK;
} else {
// By name
LPDWORD pdwNames = MakePtr(LPDWORD, hModule, pExpDir->AddressOfNames);
for (DWORD iName = 0; iName < pExpDir->NumberOfNames; iName++) {
if (0 == ::lstrcmpiA(MakePtr(LPSTR, hModule, pdwNames[iName]), szEntry)) {
dwAddrIndex = pdwOrd[iName];
hr = S_OK;
break;
}
}
}
if (SUCCEEDED(hr)) {
if (pdwAddrs[dwAddrIndex] >= expDir.VirtualAddress && pdwAddrs[dwAddrIndex] < expDir.VirtualAddress + expDir.Size) {
// We have a redirection
LPSTR azRedir = MakePtr(LPSTR, hModule, pdwAddrs[dwAddrIndex]);
ATLASSERT(!IsBadStringPtrA(azRedir, -1));
LPSTR azDot = strchr(azRedir, '.');
int nLen = azDot - azRedir;
LPSTR azModule = (LPSTR)alloca(nLen);
memcpy(azModule, azRedir, nLen);
azModule[nLen] = 'x0';
// Try to patch redirected function
return ApiHijackExports(
::GetModuleHandle(azModule), azDot + 1, pHijacker, ppOrig);
}
if (ppOrig)
*ppOrig = MakePtr(LPVOID, hModule, pdwAddrs[dwAddrIndex]);
DWORD dwOffset = DWORD_PTR(pHijacker) - DWORD_PTR(hModule);
// write to write-protected memory
hr = WriteProtectedMemory(pdwAddrs + dwAddrIndex, &dwOffset, sizeof(LPVOID));
}
return hr;
}
Имейте в виду, под Windows9x нельзя честно подменить экспорты для разделяемых библиотек, таких как user32.dll, kernel32.dll и gdi32.dll. Это связано с тем, что область памяти начиная с адреса 7FC00000h и выше совместно используестя всеми процессами в системе, и модификация сказалась бы на каждом из них. А это нежелательно, поскольку память, занимаемая нашей функцией-перехватчиком, наоборот, принадлежит только нашему процессу. Во всех остальных процессах в системе ::GetProcAddress(), после подмены таблицы экспорта, вернула бы неправильный указатель. Тем не менее, если нельзя, но очень хочется, то можно. Для этого нам придется вручную создать новый дескриптор в GDT (вот тут-то у Windows9x проблем не возникает) и используя этот дескриптор произвести необходимые изменения. Но будьте готовы к тому, что понадобится написать свою разделяемую библиотеку, установить ее в системе и проверять ID процесса при каждом обращении. Рабочий пример есть на internals.com.
Модификация самого обработчикаЭти два способа работают в 99% случаев. Последний процент – это подмена функции, вызываемой внутри чужого модуля, т.е. когда и вызаваемая и вызывающая процедура находятся в одном и том же, да к тому же чужом, модуле. В этом случае, вызов будет сделан напрямик, а не через таблицы импорта/экспорта. Тут уже ничего сделать нельзя. Почти. Можно изменить саму функцию-обработчик, с тем чтобы перенаправить вызовы в нашу собственную. Делается это довольно просто: в начало исходного обработчика прописывается команда безусловного перехода на нашу процедуру, а если нужно вызвать оригинал, то нужно просто сохранить первые 5 байт затертых командой перехода, добавить после них опять-таки команду безусловного перехода на изначальный код +5 байт. Разумется, эти пять байт кода не дожны содержать команд перехода или вызова. Кроме того, может понадобиться больше чем 5 байт, ведь команда перехода посреди длинной инструкции работать не будет. Это случается крайне редко. Обычно код функции, как его генерит компилятор для I86 выглядит примерно так: инициализация стека, загрузка в регистры параметров функции, их проверка и переход в случае неудовлетворительных результатов. Этого вполне хватает чтобы вставить наш маленький перехватчик. Но бывает и так:
CSomeClass::Release:
FF152410E475 call dword ptr [InterlockedDecrement]
85C0 test eax,eax
Или даже
CSomeClass::NonImplemented:
C20400 ret 4
Что, впрочем, можно распознать и вернуть код ошибки если инструкции ret, jmp или call встретится слишком рано. Но вот такой случай распознать не получится:
SomeFunction:
33C0 xor eax,eax
SomeFunction2:
55 push ebp
8BEC mov ebp,esp
Иными словами, модификация SomeFunction приведет к неизвестным изменениям в SomeFunction2, и, возможно, краху всей системы.
Все это сильно усложняет нам задачу. Нужно дизассемблировать эти байты и проверить каждую инструкцию. Чтобы немного облегчить нам жизнь, фирма Майкрософт разработала специальный SDK для такого рода трюков: Microsoft Detours. С этим SDK задача подмены чужой функции реализуется удивительно просто:
#include <detours.h>
DetourFunction(PBYTE(::MessageBeep), PBYTE(MyMessageBeep));
После чего все вызовы ::MessageBeep(), откуда бы они не были произведены, окажутся вызовами нашей MyMessageBeep(). Что и требовалось.
Модификация самого обработчика 2Довольно оригинальный вариант предыдущего способа был предложен Дмитрием Крупорницким: первая инструкция перехватываемой функции заменяется инструкцией прерывания INT 3. Далее процедура обработки необработанных исключений (unhandled exception handler) подменяет регистр EIP на адрес нашей функции-перехватчика.
static DWORD_PTR m_dwFunction;
static LONG WINAPI MyUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ContextRecord->Eip != m_dwFunction)
return EXCEPTION_CONTINUE_SEARCH;
// Continue execution from MyMessageBeep
pExceptionInfo->ContextRecord->Eip = (DWORD_PTR)MyMessageBeep;
return EXCEPTION_CONTINUE_EXECUTION;
}
LRESULT CMainDlg::OnMethod5(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/) {
m_dwFunction =
(DWORD_PTR)::GetProcAddress(::GetModuleHandle("USER32.dll"), "MessageBeep");
BYTE nSavedByte = *(LPBYTE)m_dwFunction;
LPTOP_LEVEL_EXCEPTION_FILTER pOldFilter =
::SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
const BYTE nInt3 = 0xCC;
// Inject int 3
HRESULT hr = WriteProtectedMemory(LPVOID(m_dwFunction), &nInt3, sizeof(const BYTE));
if (SUCCEEDED(hr)) {
::MessageBeep(m_uType);
// Restore function
hr = WriteProtectedMemory(LPVOID(m_dwFunction), &nSavedByte, sizeof(BYTE));
}
::SetUnhandledExceptionFilter(pOldFilter);
return 0;
}
Недостатком такого способа является его непредсказуемость. Кто угодно может зарегистрировать свой обработчик исключений и поломать нам логику. Более того, инструкции (…)/(), часто встречающиеся в программах, могут перехватить управление и не дать нашему обработчику шанса.
Подмена с использованием оберток(wrappers)Еще один способ, о котором я хотел бы упомянуть. Он заключается в написании собственной динамической библиотеки с тем же именем и набором экспортимых функций. Такая библиотека кладется на место ориганальной, запускается использующий эту библиотеку процесс, тот находит нашу DLL и ничего не подозревая ее использует. А если переименовать оригинальную DLL и положить рядом, то можно даже переадресовывать часть вызовов в оригинальную библиотеку, а часть оставлять себе. К сожалению, это будет работать с экспортируемыми функциями, но не с экспортируемыми переменными. Пример такого рода обертки можно найти у Алексея Остапенко.
А вот с COM-объектами и интерфейсами обертки работают как нельзя лучше. Для этого создается другой COM-объект, реализующий нужный нам интерфейс, к нему создатся аггрегированный оригинальный com-объект, а перехватчик отдается тем кто с ним будет в дальнейшем работать. Если оригинальный COM-объект не поддерживает аггрегацию, то придется реализовать все его интерфейсы, и если у него нету никаких внутренних (недокументированных) интерфейсов, то, возможно, все и заработает.
Все это касается подмены API текущего процесса. Если понадобится перехватить вызов в чужом процессе, то следует выбрать любой из приведенных здесь методов, поместить их в DLL и закинуть ее в чужой процесс. Это тема для отдельного разговора и здесь рассматриваться не будет.
Использованные статьи и литератураФорматы PE и COFF файлов
Расширение MSGINA – это просто
API Spying Techniques for Windows 9x, NT and 2000
APIHijack
EliCZ ApiHooks
Контроль вызова API функций в среде систем Windows '95, Windows '98 и Windows NT
Это все на сегодня. Пока!
Алекс Jenter [email protected] Duisburg, 2001. Публикуемые в рассылке материалы принадлежат сайту RSDN.Программирование на Visual C++