Аннотация:
Это не единственная статья на тему перехвата API-вызовов. Необходимость в ней возникла вследствие того, что в других широко известных статьях и книгах есть небольшие ошибки, которые порой приводят к тому, что перехват не работает. Эта статья избавлена от указанных недостатков :)
Прочитал. Хорошая статья. Но не мешало бы добавить раздел, связанный с
перехватом на уровне 0 кольца (метод Руссиновича, т.е. перехват
обработчика int 2e). Пусть даже в виде компиляции из Шрайбера, например.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Прочитал. Хорошая статья. Но не мешало бы добавить раздел, связанный с PD>перехватом на уровне 0 кольца (метод Руссиновича, т.е. перехват PD>обработчика int 2e). Пусть даже в виде компиляции из Шрайбера, например.
Кто придумал, тот и вОда
Павел, мы (rsdn-team & редакция) будем только рады, если ты за это возьмёшься!
Здравствуйте, Игорь В. Филимонов, Вы писали:
ИВФ>Статья:
ИВФ>Авторы: ИВФ> Игорь В. Филимонов
ИВФ>Аннотация: ИВФ>Это не единственная статья на тему перехвата API-вызовов. Необходимость в ней возникла вследствие того, что в других широко известных статьях и книгах есть небольшие ошибки, которые порой приводят к тому, что перехват не работает. Эта статья избавлена от указанных недостатков
Насчет отсутствия OpenThread в Windows95, Windows98, WindowsNT. Всё реализуемо.
PD>Прочитал. Хорошая статья. Но не мешало бы добавить раздел, связанный с PD>перехватом на уровне 0 кольца (метод Руссиновича, т.е. перехват PD>обработчика int 2e). Пусть даже в виде компиляции из Шрайбера, например.
Ага, та же самая мысль возникла сразу по прочтении. Значит, действительно нужно
Кстати, ощущения после статьи двойственные.. С одной стороны, неплохо, тем более, что — что подобные вопросы часто на форуме WinAPI поднимаются. С другой — иногда попадаются совершенно удивительные с точки зрения русского языка фразы. Например:
Данный метод основан на следующем: если можно перехватить функцию из текущего процесса, то нужно выполнить код перехвата во всех процессах в системе.
О чем хотел сказать автор, я понял только после 2-го прочтения Да и то смутно — как относятся части если и то для меня осталось загадкой.
Наверное, лучше сказать было бы так:
Данный метод использует технику выполнения кода перехвата во всех процессах в системе.
Ну или что то в этом роде. Впрочем, в любом случае — статья полезная.
"... А в приложениях без очереди сообщений (например, консольных) этот способ внедрения вообще не работает."
Насколько я себе это представляю — если у потока нет очереди сообщений (message queue), то она автоматически сделается, при поступлении первого же сообщения потоку. По крайней мере в mdsn так написано. А если речь не о win32 приложении, то вроде как и перехватывать сабж не нужно? Так что это за приложения "без очереди сообщений".
...А отсюда наливаем, когда рецепт написан совсем неразборчиво...
Здравствуйте, Игорь В. Филимонов, Вы писали:
ИВФ>Статья:
ИВФ>Авторы: ИВФ> Игорь В. Филимонов
ИВФ>Аннотация: ИВФ>Это не единственная статья на тему перехвата API-вызовов. Необходимость в ней возникла вследствие того, что в других широко известных статьях и книгах есть небольшие ошибки, которые порой приводят к тому, что перехват не работает. Эта статья избавлена от указанных недостатков
--
В статье совершенно справедливо отмечается, что при перехвате управления путем непосредственной замены кода функции, необходимо:
----- ПРЕДУПРЕЖДЕНИЕ
На момент установки/снятия перехвата нужно останавливать все остальные потоки процесса,
в котором происходит перехват (или удостовериться, что они не могут вызывать перехватываемую функцию).
-----
Однако нужно не просто остановить потоки; но остановить их так, чтобы текущая выполняемая команда любого потока не оказалась внутри перехватываемого участка.
C уважением,
Геннадий Майко.
Re[2]: Методы перехвата API-вызовов в Win32
От:
Аноним
Дата:
21.09.04 04:12
Оценка:
Здравствуйте, Геннадий Майко, Вы писали:
ГМ>Однако нужно не просто остановить потоки; но остановить их так, чтобы текущая выполняемая команда любого потока не оказалась внутри перехватываемого участка.
А зачем? Пусть себе выполняется дальше. Даже если уже функция подменена, ничего страшного не произойдет. Адрес возврата в стеке ж не меняеться, так что функция благополучно завершит работу.
Здравствуйте, Аноним, Вы писали:
ГМ>>Однако нужно не просто остановить потоки; но остановить их так, чтобы текущая выполняемая команда любого потока не оказалась внутри перехватываемого участка.
А>А зачем? Пусть себе выполняется дальше. Даже если уже функция подменена, ничего страшного не произойдет. Адрес возврата в стеке ж не меняеться, так что функция благополучно завершит работу.
--
Возьмем, для примера, процессор семейства x86 и функцию, перехватываемый участок которой состоит из 5 однобайтных команд типа MOV R0, R1:
1: mov ax, bx
2: mov cx, dx
3: mov ebx, edx
4: mov ebp, sp
5: mov edi, esi
Предположим, что поток был остановлен после выполнении команды 3 и, следовательно, начнет выполнение с команды 4 после того, как возобновит работу. После замены этой последовательности команд на jmp DetourFunction, на месте байта команды 4 окажется байт из адреса DetourFunction, который будет интерпретирован возобновившим выполнение потоком как начало некоторой, в общем случае — другой команды.
Да действительно можно. Можно и в старшие 2 Гб писать.
Но — недокументированными методами. Возможно, об этом стоило упомянуть
(по-моему, в предварительных вариантах статьи об этом что-то было,
но потом я решил, что рассматривать реализацию недокументированными методами не стоит).
Вообще в Windows на самом деле можно многое из того, что нельзя.
Здравствуйте, Геннадий Майко, Вы писали:
ГМ>Возьмем, для примера, процессор семейства x86 и функцию, перехватываемый участок которой состоит из 5 однобайтных команд типа MOV R0, R1:
ГМ>1: mov ax, bx ГМ>2: mov cx, dx ГМ>3: mov ebx, edx ГМ>4: mov ebp, sp ГМ>5: mov edi, esi
ГМ>Предположим, что поток был остановлен после выполнении команды 3 и, следовательно, начнет выполнение с команды 4 после того, как возобновит работу. После замены этой последовательности команд на jmp DetourFunction, на месте байта команды 4 окажется байт из адреса DetourFunction, который будет интерпретирован возобновившим выполнение потоком как начало некоторой, в общем случае — другой команды.
Совершенно справедливо. Я это и подразумевал. Понятно, что в своём процессе мы худо-бедно это проконтролировать сможем, а вот при глобальном перехвате — остаётся надеятся на удачу (а то можно загробить чужой процесс).
Конечно, можно остановить чужой поток, проанализировать состояние регистра PC и если он внутри "критической секции" из пяти заменяемых байт, то ResumeThread и остановить его чуть позже. В принципе, это реализуемо, но неслолько усложняет реализацию (особенно в примере с драйвером).
Что-то оно не работает
Решил я попробовать тестовый пример DriveType2.
Менял типы дисков — в программке из архива все работает. Даже более того, она сохраняет все значения типов дисков даже после ее перезапуска — . Но нигде в системе я не обнаружил изменений. Написал тестовую прогу, которая вызывает метод DriveTypeA — и ничего у меня не перехватывается.
Что не так делаю?
Здравствуйте, Михаил, Вы писали:
М>Насколько я себе это представляю — если у потока нет очереди сообщений (message queue), то она автоматически сделается, при поступлении первого же сообщения потоку. По крайней мере в mdsn так написано. А если речь не о win32 приложении, то вроде как и перехватывать сабж не нужно? Так что это за приложения "без очереди сообщений".
Очередь сообщений создается потоком и никем другим. При чем только тогда, когда вызывается одна из функций мудуля user. Если посылать потоку сообщения, то очередь никогда не будет создана.
Здравствуйте, Myk, Вы писали:
Myk>Что-то оно не работает Myk>Решил я попробовать тестовый пример DriveType2. Myk>Менял типы дисков — в программке из архива все работает. Даже более того, она сохраняет все значения типов дисков даже после ее перезапуска — . Но нигде в системе я не обнаружил изменений. Написал тестовую прогу, которая вызывает метод DriveTypeA — и ничего у меня не перехватывается. Myk>Что не так делаю?
Здравствуйте, Myk, Вы писали:
Myk>>Что-то оно не работает Myk>>Решил я попробовать тестовый пример DriveType2. Myk>>Менял типы дисков — в программке из архива все работает. Даже более того, она сохраняет все значения типов дисков даже после ее перезапуска — . Но нигде в системе я не обнаружил изменений. Написал тестовую прогу, которая вызывает метод DriveTypeA — и ничего у меня не перехватывается. Myk>>Что не так делаю?
У меня-то точно всё работает.
Вопрос:
какая операционка и на чём написан пример.
Здравствуйте, Myk, Вы писали:
Myk>У вас всех работает?
Имеется три машины...
win2k sp4(точно не уверен), всё работает...
winXP sp2 — валится StackOverFlow в методе LoadLibraryExW( там грузится в процесс еще одна DT2lib.dll,
и дальше следующий вызов LoadLibraryExW приводит к зацикливанию..)
другая машина с winXP sp2 при попытке хуканья чего-либо из хукнутого метода LoadLibraryExW,
валится случайный набор запущеных приложений)
если не хукать LoadLibrary(все варианты) и GetProcAddress. то работает (но конечно не везде)
вот
Re: Методы перехвата API-вызовов в Win32
От:
Аноним
Дата:
04.10.04 15:23
Оценка:
Здравствуйте, Игорь В. Филимонов, Вы писали:
ИВФ>Статья:
ИВФ>Авторы: ИВФ> Игорь В. Филимонов
ИВФ>Аннотация: ИВФ>Это не единственная статья на тему перехвата API-вызовов. Необходимость в ней возникла вследствие того, что в других широко известных статьях и книгах есть небольшие ошибки, которые порой приводят к тому, что перехват не работает. Эта статья избавлена от указанных недостатков
Здравствуйте, lastwalrus, Вы писали:
L>Имеется три машины... L>win2k sp4(точно не уверен), всё работает... L>winXP sp2 — валится StackOverFlow в методе LoadLibraryExW( там грузится в процесс еще одна DT2lib.dll, L>и дальше следующий вызов LoadLibraryExW приводит к зацикливанию..)
В коде примера ошибка. Он не дружит с повторным вызовом LoadLibrary
для Dll перехватчика. Вот работоспособный вариант.
void CAPIHook::FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags)
{
// If a new module is loaded, hook the hooked functionsif ((hmod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0)) {
const HMODULE hmodThisMod = ModuleFromAddress(ReplaceIATEntryInAllMods); // Добавленоfor (CAPIHook* p = sm_pHead; p != NULL; p = p->m_pNext) {
if (p->m_fExcludeAPIHookMod && hmodThisMod == hmod) continue; // Добавлено
ReplaceIATEntryInOneMod(p->m_pszCalleeModName, p->m_pfnOrig, p->m_pfnHook, hmod);
}
}
}
не знаю, может я самый настоящий валенок, но после компилирования DriveType2 на Windows XP,
и заменой последнего аргумента в DT2_HookAllApps на thread id процесса у которого я хочу
перехватывать API, SetWindowsHookEx (внутри ентой DT2_HookAllApps) постоянно возвращает
NULL, с GetLastError
Прочитал Вашу статью, попробовал, собрал — и о чудо —
программа работает.
Пошёл дальше — попробовал перехватить дпугие API-функции —
и опять — сработало.
Но вот моя проблема: решил вместо GUI-вой программы, написать
консольное приложение, а именно — прогу в формате "службы Windows NT".
Сама моя служба работает, а вот если из этой службы вызвать функцию
из DLL-ки установки хука, то хук ставится, но ведёт себя странно, т.е.
SetWindowsHookEx возвращает не NULL, но хукирующая функция то ли не вызывается,
то ли вызывается, но очень очень редко. Перехват API-функция естесственно, тоже
не срабатывает.
Вопрос: с чем это может быть связано? Может быть нельзя функцию установки хука
вызывать из консольного приложения или службы Windows ?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Игорь В. Филимонов.
А>Прочитал Вашу статью, попробовал, собрал — и о чудо - А>программа работает. А>Пошёл дальше — попробовал перехватить дпугие API-функции - А>и опять — сработало. А>Но вот моя проблема: решил вместо GUI-вой программы, написать А>консольное приложение, а именно — прогу в формате "службы Windows NT". А>Сама моя служба работает, а вот если из этой службы вызвать функцию А>из DLL-ки установки хука, то хук ставится, но ведёт себя странно, т.е. А>SetWindowsHookEx возвращает не NULL, но хукирующая функция то ли не вызывается, А>то ли вызывается, но очень очень редко. Перехват API-функция естесственно, тоже А>не срабатывает. А>Вопрос: с чем это может быть связано? Может быть нельзя функцию установки хука А>вызывать из консольного приложения или службы Windows ?
Служба (если не стоит флаг разрешения взаимодействия с пользователем) запускается на другой оконной станции и десктопе (объекты Window Station и Desktop, см MSDN), а хуки действуют только в пределах десктопа. Кстати, это действует и обратно, т.е., поставив хук в обычном приложении нельзя внедриться в службу и перехватить там функцию.
Ограничение технологи, ничего не поделаешь... Но из обычного консольного приложения должно работать.
Здравствуйте,
Поместил пример из статьи класса CAPIHook в длл, экспортнул функцию установки хука. И пытаюсь перехватывать, вызывая функцию установки хука из приложения. Работает не всегда. Например, MessageBoxA — не перехватывает, а MessageBoxW — перехватывает. В чем может быть проблема?
Здравствуйте, SPeller, Вы писали:
SP>Поместил пример из статьи класса CAPIHook в длл, экспортнул функцию установки хука. И пытаюсь перехватывать, вызывая функцию установки хука из приложения. Работает не всегда. Например, MessageBoxA — не перехватывает, а MessageBoxW — перехватывает. В чем может быть проблема?
Какие-нибудь подробности?
Какая ОС, как именно проверялось?
SH>Какие-нибудь подробности? SH>Какая ОС, как именно проверялось?
Ось ХП СП3. Вызов из Дельфи 2009 при создании формы. При установке хука на MessageBoxA он вроде устанавливается, возвращает адрес оригинала, но при вызове функции функция перехватчик не отрабатывает, вызов идет в оригинальную функцию.
Здравствуйте, SPeller, Вы писали:
SP>Ось ХП СП3. Вызов из Дельфи 2009 при создании формы. При установке хука на MessageBoxA он вроде устанавливается, возвращает адрес оригинала, но при вызове функции функция перехватчик не отрабатывает, вызов идет в оригинальную функцию.
Я бы предположил, что хитрый дельфи вызывает MessageBoxW в любом случае. Для проверки нужно перехватить обе и посмотреть, среагирует ли хоть один.
Другой вариант -- перейти на метод перехвата, исправляющий код функции (там, где Detours используется).
Я нашел причину Дело в том же, о чем пишется в статье про дельфийский компилятор. Только деле еще красивее. MessageBoxA имеет 3 точки импорта. Закомментировал строку
return; // We did it, get out
в методе CAPIHook::ReplaceIATEntryInOneMod и всё заработало Ну, не умеет дельфийский компилятор объединять импорты.