Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > ХуКи поставить на процу


Автор: Гость_Виталь 25.9.2004, 11:07
Народ, Доброго времени суток!
Видел статьи (в.т.ч. на uinc.ru/статьи) о том, как наложить болт (в смысле хук) на процедуру -
т.е. есть в длл, загруженной в памяти некая процедура zz(x:string):integer;
Надо, чтоб при вызове ее из любой другой программы управление передавалось на мою прогу на что-то типа
Код

begin
...
       if x='stroka' then result:=-1
       else         //вызов функции-оригинала
...
end;

но все проимеры на сях - я не шарю.
Может, у кого есть на делфи?

Автор: S.A.P. 25.9.2004, 12:41
Кидай пример на сях сюда, а то подключиться к uint.ru не могу.
Тоже интересно, как такое rolleyes.gif

Автор: Виталь 26.9.2004, 11:29
Ё Низя файл присоединить...
Цитата
Перехват вызовов функций

Введение
  В этой статье продемонстрирован один из способов перехвата вызовов функций. Перехватываемые функции не обязательно должны быть экспортируемыми из DLL. Единственное требование к ним - они должны использовать соглашение о вызове stdcall (стандартный способ вызова функций в Win32).
  stdcall выбран лишь потому, что он наиболее распространён в Win32. Каких-то принципиальных сложностей с другими типами вызова нет, и ловушки для них можно реализовать аналогично приведённой в данной статье.
  Перехват вызовов функций может использоваться для отладки своих приложений или для исследования чужих. При исследовании чужих приложений, например, можно определять протоколы взаимодействия нескольких модулей друг с другом (в том числе и взаимодействие по сети - перехватив функции socket, send, recv и другие). Ещё одно применение перехвата функций - изменение работы приложения: перехватив функцию, вы можете изменять передаваемые аргументы и возвращаемый результат, или вообще не вызывать "правильную" функцию.

Механизм перехвата
  Для перехвата вызова производится изменение кода уже загруженного в память модуля. Изменяются первые 5 байт перехватываемой функции (вставляется jmp hook).
  Такой способ позволяет перехватывать даже функции, которые не импортируются явно через таблицу импорта, а вызываются по адресу, полученному, например, через GetProcAddress() или просто по фиксированному адресу.
  Функция-ловушка должна находиться в адресном пространстве того же процесса, в котором перехватывается вызов. Она может быть внедрена туда несколькими способами. Например, через SetWindowHookEx() или прямой записью кода в память процесса.
  Минусом этого способа перехвата является невозможность перезаписи памяти в Windows 9x в диапазоне адресов от 0x80000000 до 0xBFFFFFFF. По этим адресам располагаются некоторые системные DLL (например, kernel32.dll).
  Ещё одним минусом является то, что ловушка ставится в уже запущенном процессе, а некоторые (возможно важные) шаги в программе уже сделаны.
  Оба недостатка можно обойти, если вставить код ловушки не в работающий процесс, а в файл интересуемого модуля.

Подробнее о функции-ловушке
  Функция-ловушка должна в первую очередь сохранить адрес возврата и положение аргументов. Произведя какие-то свои задачи, функция-ловушка может вызвать "правильную" функцию с какими-то аргументами или не вызывать её вообще.
  Так как заранее может быть не известно, по каким адресам расположится код ловушки, в ней не следует использовать абсолютных адресов. Т.е. ловушка сама узнаёт свой адрес по регистру eip и относительно него адресует свои структуры данных. Альтернативным вариантом является использование абсолютных адресов, но в этом случае потребуется настройка кода ловушки программой, которая её внедряет.
  Чтобы не нарушить работу программы, функция должна перед возвратом восстановить регистры (все регистры общего назначения, кроме eax, ecx) и очистить стек от адреса возврата и аргументов (stdcall подразумевает, что функция сама удаляет аргументы, которые ей передали).
  Для вызова "правильной" функции сначала необходимо вернуть на место старые 5 байт, чтобы избежать повторных вызовов ловушки из самой себя и зацикливания. После возврата из "правильной" функции 5 байт должны быть опять переписаны, чтобы при следующих вызовах управление передавалось в функцию-ловушку.
  Сложность принципиального характера проявляется при использовании таких ловушек в многопотоковом приложении. При одновременном вызове ловушки из разных потоков может возникнуть ситуация, когда один поток вернул старые 5 байт, а второй в этот момент вызвал функцию. В результате второй поток не попадает в ловушку, а вызывает только "правильную" функцию.
  Если функция-ловушка небольшая, то не трудно найти для неё место в памяти процесса. Варианты:

DOS часть заголовка модуля. В структуре IMAGE_DOS_HEADER (располагается непосредственно по адресу image base, который для exe-файлов обычно равен 0x400000) для Windows важны только поля e_magic (первое) и e_lfanew (последнее). Соответственно всё, что между ними (58 байт), может использоваться ловушкой. Ещё одна неиспользуемая область - dos stub (код, который работает при запуске программы в режиме DOS и выдаёт сообщение "This program can’t be run in DOS mode."). Эта область начинается сразу после IMAGE_DOS_HEADER и заканчивается там, где начинается IMAGE_NT_HEADERS. Обычно её размер составляет около 200 байт.

Relocation table. Область данных, на которую ссылается IMAGE_DIRECTORY_ENTRY_BASERELOC. После загрузки модуля она не нужна. Её размер обычно достигает нескольких килобайт. В exe-файлах её обычно нет, но она почти всегда есть в dll.

Неиспользуемые области, выравнивающие секции на соответствующие границы. Их не трудно найти, просмотрев все структуры IMAGE_SECTION_HEADER.

Если подумать, наверно можно найти ещё доступные для безопасной перезаписи области. Если же функция-ловушка большая, то проще всего её внедрить, используя SetWindowsHookEx(). Кстати, в этом случае не придётся настраивать абсолютные адреса - это сделает Windows при загрузке dll.

Практика
  К данной статье прилагаются исходники, реализующие вариант ловушки. Для большинства случаев его окажется достаточно и вам не придется разбираться в низкоуровневых механизмах. Ловушка реализована в виде класса C++. Она выполняет всю необходимую работу, связанную с получением аргументов и восстановлением стека при возврате. Для использования ловушки вы должны реализовать свою функцию, которая будет вызываться из ловушки.

Вот объявление требуемой функции:

typedef unsigned (_stdcall *YOURPROC)(unsigned param, unsigned* arg1,
unsigned* countOfArgs);
Значение, которое вернёт ваша функция, будет возвращено как результат функции, которая перехватывается.

Описание аргументов функции:
param - произвольное значение, которое вы можете передать при установке ловушки. Это может быть, например, указатель на структуру данных.
arg1 - указатель на первый аргумент, переданный основной программой. Чтобы получить значение первого и последующих аргументов нужно просто подставлять индексы: arg1[0], arg1[1], arg1[2], ...
countOfArgs - по этому адресу ваша функция должна сохранить число аргументов, которое имеет перехватываемая функция. Функция-ловушка реализована универсально, и поэтому она не может знать, сколько аргументов передано. Но зато она может быть использована на функциях с любым количеством аргументов.

Объявление класса:

class CAPIHook
{
public:

    CAPIHook(void);

    ~CAPIHook();

    bool Attach(void* process, void* addrOfAPI, void* addrOfHook,
        YOURPROC addrOfYourProc, unsigned param);

    bool Detach(void);

    unsigned GetStatus(void);

private:

    ...
};
Для установки ловушки необходимо вызвать CAPIHook::Attach().
Описание аргументов:
process - HANDLE процесса, в котором требуется перехватить вызов. HANDLE должен иметь доступ к процессу
PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION.
Все аргументы-указатели, переданные данной функции должны указывать на область памяти в том процессе, в котором перехватывается функция.
addrOfAPI - адрес перехватываемой функции.
addrOfHook - адрес, по которому будет прописана базовая часть ловушки, реализованная в CAPIHook. Размер ловушки, реализованной в CAPIHook, составляет 80 байт.
addrOfYourProc - адрес вашей функции в адресном пространстве исследуемого процесса. Способ внедрения этой функции остаётся на ваше усмотрение.
param - произвольный 32-битный параметр, который будет передан вашей функции в качестве одного из аргументов.

Для снятия ловушки необходимо вызвать CAPIHook::Detach().

Если при выполнении Attach() или Detach() произойдёт ошибка, то они вернут значение false (иначе - true). Более подробную информацию об ошибке можно получить функцией
GetStatus(), которая возвращает стандартный код ошибки Win32.

В качестве примера использования CAPIHook реализован перехват MessageBox() в Notepad.exe. Ловушка убирает первую букву в заглавии сообщений, выдываемых в Notepad. Так как MessageBox() находится в user32.dll, этот пример не будет работать в Windows 9x. Пример был проверен в Windows 2000.

Здесь приводится только информация о перехвате вызовов функций. О внедрении своих функций в адресное пространство других процессов существуют другие статьи. Для интересующихся, будет полезно посмотреть один из способов внедрения DLL в чужой процесс здесь.

Приложение: Скачать можно здесь. (Скачано 3911 раз)


Идея написать статью родилась после обсуждения проблем, связанных с перехватом функций, на форуме [Programming] сайта www.BugTraq.ru. Сама идея вставлять jmp hook в начало перехватываемой функции принадлежит NeuronViking.
За заслуги перед отечеством smile.gif большое спасибо:
NeuronViking,
Heromantor - принимавшему участие в обсуждении перехватов вызовов функций,
Dr.Golova - за дельные замечания по статье.

[c]Copyright 2001, ggg, [email protected]
Статья написана специально для UInC (http://www.uinc.ru).



Добавлено @ 11:33
кстати, все работает.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)