45824 (665168), страница 3

Файл №665168 45824 (Методы перехвата API-вызовов в Win32) 3 страница45824 (665168) страница 32016-07-31СтудИзба
Просмтор этого файла доступен только зарегистрированным пользователям. Но у нас супер быстрая регистрация: достаточно только электронной почты!

Текст из файла (страница 3)

После подключения DLL к потоку происходит инициализация всех переменных (каждый процесс, к которому подключается DLL, имеет копии всех глобальных и статических переменных, описанных в ней). Из глобальных переменных у нас есть 6 экземпляров класса CAPIHook (для GetDriveTypeA в DT2Lib.cpp и для LoadLibraryA, LoadLibraryW, LoadLibraryExA, LoadLibraryExW и GetProcAddress – в APIHook.cpp). Таким образом, при подключении DLL происходит шестикратный вызов конструктора класса CAPIHook, перехватывающего вышеперечисленные функции в текущем (то есть в том, к которому только что произошло подключение) процессе.

При завершении процесса внедрённая DLL отключается. При этом происходит вызов деструктора CAPIHook для всех экземпляров класса.

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

int WINAPI Hook_GetDriveTypeA(PCSTR lpRootPathName)

{

//Вызовем оригинальную функцию

int Result = ((PGetDriveTypeA)(PROC) g_GetDriveTypeA)(lpRootPathName);

if (Result > DRIVE_NO_ROOT_DIR)

{

int Drive = toupper(*lpRootPathName);

if (Drive >= 'A' && Drive<='Z')

{

Drive -= 'A'; //Индекс в массиве Drives

//Если этот диск переопределен, вернём значение из массива

if (Drives[Drive] < 0xFF)

Result = Drives[Drive];

}

}

return Result;

}

Функция Hook_GetDriveTypeA сначала вызывает оригинальную GetDriveTypeA. Затем, если возвращаемое значение больше DRIVE_NO_ROOT_DIR (то есть функции был передан корректный аргумент, и она выполнилась без ошибок), то проверяется, переопределен ли диск, тип которого запрашивается. Информация о значениях перехватываемых функций в данном случае хранится в реализованном мной массиве BYTE Drives[26], что позволяет реализовать перехват 26 дисков, от A: до Z:. В этом массиве хранятся значения, возвращаемые функцией GetDriveTypeA для каждого из дисков. Итак, если значение элемента массива, соответствующего аргументу GetDriveTypeA равно 0xFF, то значение возвращается без изменений, в противном случае возвращается значение из массива. Запись значений в этот массив реализуется в DriveType2.cpp.

СОВЕТ

Если вы хотите, чтобы эта программа полноценно работала в WinNT, следует также перехватить функцию GetDriveTypeW.

Ещё одна реализация данного метода описана в статье «Перехват API-функций в Windows NT/2000/XP», автор Тихомиров В. А., публиковалась в RSDN Magazine #1 (будьте осторожны, там та же ошибка, что и у Джеффри Рихтера).

ПРИМЕЧАНИЕ

У этого метода есть ещё один существенный недостаток: некоторые коммерческие программы (например, популярный файловый менеджер Total Commander, упакованный ASPack) используют различные системы защиты (ASProtect, VBox и т. д.), шифрующие таблицу импорта защищаемого приложения. С такими программами этот метод не работает.

Глобальный перехват может быть реализован и с помощью Detours (только в WinNT). А так как методов внедрения DLL известно несколько, то различных вариантов реализации глобального перехвата можно предложить довольно много.

Глобальный перехват методом подмены кода в DLL

Данный метод можно реализовать двумя способами: непосредственной правкой кода DLL, в которой расположена целевая функция, или подменой этой DLL другой, экспортирующей тот же набор функций. Второй способ известен под названием «Подмена с использованием оберток (wrappers)».

Первый способ позволяет реализовывать только сравнительно небольшие по размеру функции-перехватчики, так как код необходимо внедрять в свободные участки DLL – в основном в межсекционное пространство. Другой недостаток – код необходимо писать на ассемблере. Общая идеология работы этого метода та же, что и в Detours. В код целевой функции внедряется команда jmp к функции-перехватчику. Байты, скопированные «из-под» jmp’а, перемещаются в перехватчик (так как перехватчик всё равно пишется на ассемблере, в этом случае его проще сразу совместить с функцией-трамплином). Вот пример реализации этого метода.

В каталоге DriveType0 находится файл kernel32.dll, в котором я сделал следующие исправления (при помощи hiew32.exe):

По адресу 4E02 – локальный адрес .0BFF74E02 (это конец функции GetDriveTypeA) я поместил команду jmp .0BFF71080 – на первое попавшееся свободное место (в исполняемых файлах всегда много свободного места – обычно в концах секций).

По адресу .0BFF71080 (глобальный адрес 1080) я поместил следующий код:

.BFF71080: 3C03 cmp al,003 ;Возвращаем DRIVE_FIXED ?

.BFF71082: 750E jne .0BFF71092

.BFF71084: B402 mov ah,002 ;Да.

.BFF71086: CD16 int 016 ;/Проверим состояние ScrollLock

.BFF71088: 2410 and al,010 ;

.BFF7108A: 7404 je .0BFF71090 ;\Светодиод горит ?

.BFF7108C: B005 mov al,005 ;Да. Возвращаем DRIVE_CDROM

.BFF7108E: EB02 jmps .0BFF71092 ;На возврат

.BFF71090: B003 mov al,003

.BFF71092: 5F pop edi ;/Возврат из GetDriveTypeA

.BFF71093: 5E pop esi ; (кусок кода, скопированный

.BFF71094: 5B pop ebx ; из .0BFF74E02 - .BFF74E06)

.BFF71095: C9 leave ;

.BFF71096: C20400 retn 00004 ;\

Таким образом, когда светодиод ScrollLock не горит, функция GetDriveTypeA работает как обычно, а если горит – то для всех Windows-приложений все локальные диски (у меня это С:\ и D:\) превращаются в CD-ROMы.

ПРИМЕЧАНИЕ

Чтобы всё это заработало, необходимо заменить файл C:\Windows\System\kernel32.dll на файл DriveType0\kernel32.dll. Сделать это можно, только загрузив компьютер в режиме MS-DOS, так как kernel32.dll – одна из системных библиотек Windows. Данный пример реализован для Windows 98. Поскольку системные библиотеки меняются в зависимости от версии Windows (и даже от номера билда), то в других операционных системах этот пример работать не будет (его нужно реализовывать для каждой версии kernel32.dll заново).

Этот способ перехвата – один из самых мощных. Однако в коммерческих продуктах его использовать не удастся, так как он, очевидно, нарушает практически любое лицензионное соглашение.

Другой способ реализации этого метода – использование оберток (wrappers). Суть его в создании собственной DLL с тем же набором экспортируемых функций, что и оригинальная. В качестве примера могу привести следующий вариант реализации вышеприведённого примера:

Системную библиотеку Kernel32.dll переименовываем в kernel31.dll :).

Создаём библиотеку с именем Kernel32.dll, в которой реализована одна функция – GetDriveTypeA (это будет функция-перехватчик), а все остальные функции переадресуем к kernel31.dll (благо компилятор Visual C++ поддерживает переадресацию функций DLL).

Полученную библиотеку помещаем в системный каталог.

При этом функция-перехватчик может вызывать оригинальную функцию из kernel31.dll.

Основным недостатком данного способа является то, что он не годится для DLL, экспортирующих переменные.

Глобальный перехват методом подмены кода DLL в памяти (только Win9X)

Идея данного метода заключается в следующем: в Win9X системные DLL загружаются в общую для всех процессов область памяти (в третий гигабайт). Поэтому если бы удалось произвести реализацию Detours под Win9X, то изменения коснулись бы всех процессов (то есть случился бы глобальный перехват). Ситуация осложняется тем, что запись в область системных DLL в Win9X возможна или из режима ядра, или недокументированными средствами. Кроме того, в момент записи необходимо остановить все потоки, которые могут вызывать целевую функцию. Это можно сделать при помощи SuspendThread. Однако эта функция требует в качестве аргумента handle потока, а используемые для перечисления потоков функции Thread32First/Thread32Next возвращают ThreadID. Функция OpenThread, которая позволяет получить handle из ThreadID, реализована только начиная с Windows ME. По этой причине в общем виде документированными средствами из режима пользователя данный метод в Win9X нереализуем. Однако есть другой способ. Обе обозначенные проблемы (запись в область системных DLL и останов всех пользовательских потоков в системе) могут быть решены, если для перехвата использовать драйвер. Драйвер работает в режиме ядра, поэтому из него можно производить запись по любым доступным адресам. А если на момент установки/снятия перехвата поднять IRQL (уровень запроса на прерывание) до DISPATCH_LEVEL, то прервать поток сможет только процедура обработки аппаратного прерывания (откуда вызывать пользовательские системные функции нельзя). Кроме того, применение драйвера позволяет решить ещё одну проблему. Функция-перехватчик и функция-трамплин должны располагаться в области памяти, общей для всех процессов (в старших 2 гигабайтах). Конечно, можно было бы создать файл, отображаемый в память (MMF – они в Win9X размещаются в третьем гигабайте), и поместить код этих функций туда, или разместить их в отдельной DLL с ImageBase в третьем гигабайте, но проще реализовать их непосредственно в драйвере (драйверы в Win9X размещаются в четвёртом гигабайте – в разделе кода и данных режима ядра).

Рассмотрим пример, реализующий описанный метод. Для осуществления перехвата и размещения функции-трамплина и функции-перехватчика я написал WDM-драйвер (с использованием Visual C++ 6.0, Windows 2000 DDK и Compuware DriverStudio 2.7), а также программу для взаимодействия с ним. Программа и драйвер расположены в каталоге DriveType1 (там же – инструкции по установке).

Пример DriveType1 состоит из двух частей – драйвера DTDrv.sys и установочного скрипта DTDrv.inf, а также программы DriveType.exe.

DriveType.exe компилируется из одного модуля DriveType.cpp, в котором реализованы пользовательский интерфейс и интерфейс с драйвером. Интерфейс с драйвером реализуется через функции CreateFile (открытие драйвера), DeviceIoControl (операции ввода-вывода) и CloseHandle (закрытие драйвера). Реализованы четыре команды, вызываемые через DeviceIoControl – перехват функции GetDriveTypeA, снятие перехвата, установка возвращаемого значения функцией перехвата для каждого из дисков A: .. Z:, чтение текущего состояния перехвата.

Ну а вся работа по перехвату делается в драйвере, за исключением того, что адрес функции GetDriveTypeA определяется также в DriveType.cpp и присылается в качестве параметра в команде перехвата. После получения этого адреса функция DTDRV_IOCTL_HOOK_Handler (из модуля DTDrvDevice.cpp) реализует перехват рассмотренным выше способом. Функция DTDRV_IOCTL_UNHOOK_Handler снимает перехват, функция DTDRV_IOCTL_SETDRIVE_Handler устанавливает значение, возвращаемое перехватчиком, функция DTDRV_IOCTL_GETSTATE_Handler возвращает значения перехвата и флаг перехвата.

Основные переменные, используемые DriveType.cpp:

unsigned char IsHook = false; //Флаг перехвата

unsigned char Drives[26]; //Значения перехвата

PUCHAR GDT; //Адрес GetDriveTypeA

В Drives[26] хранятся значения, возвращаемые функцией MGetDriveType для дисков A: .. Z: (=0xFF, если информация о диске не переопределена).

Итак, функция-трамплин NewGetDriveType будет выглядеть следующим образом:

__declspec(naked) unsigned int NewGetDriveType(LPSTR Path)

{

_asm

{

nop //Здесь будут первые 5 байт из функции GetDriveTypeA

nop //(в Win98 3 команды ассемблера)

nop

nop

nop

jmp $ //А здесь - переход к GetDriveTypeA + 5

}

}

Изначально эта функция «пустая», так как весь её код пишется во время перехвата функцией DTDRV_IOCTL_HOOK_Handler, которая, если оперировать терминами Detours, реализует динамический перехват.

ПРИМЕЧАНИЕ

Код этой функции изначально может быть любым, но он должен занимать по крайней мере 10 байт (чтобы уместились 5 байт из заголовка GetDriveTypeA и 5 байт – команда jmp).

Собственно функция-перехватчик MGetDriveType реализована в моём примере так:

unsigned int MGetDriveType(LPSTR Path) //Это – функция-перехватчик

{

unsigned int res = NewGetDriveType(Path); //Вызовем старый GetDriveTypeA

unsigned char Letter = *Path;

if (Letter >= 'a' && Letter <= 'z') Letter- = 'a' - 'A'; //Заглавные

if (Letter >= 'A' && Letter <= 'Z')

{

unsigned char NewRes = Drives[Letter - 'A'];

if (NewRes < 0xFF) res = NewRes; //Если диск переназначен, вернём значение из таблицы

}

return res;

}

Сначала вызывается функция-трамплин NewGetDriveType, которая фактически выполняет код оригинальной GetDriveTypeA (сначала выполняются первые 5 байт – это 3 команды ассемблера, затем – всё остальное). После этого определяется буква диска. Преобразование буквы в верхний регистр осуществляется вручную. Далее, если данный диск перехвачен, возвращается значение из массива Drives, в противном случае – то, которое вернула NewGetDriveType.

Перехват реализован в функции DTDRV_IOCTL_HOOK_Handler следующим образом:

NTSTATUS DTDrvDev::DTDRV_IOCTL_HOOK_Handler(KIrp I)

{

NTSTATUS status = STATUS_SUCCESS;

I.Information() = 0;

if (IsHook)

return status;

#pragma pack(push, 1) //Включим выравнивание по границе байта

struct

{

UCHAR Byte0;

ULONG Byte1_4;

} Patch = {0xE9}; //Код инструкции jmp

#pragma pack(pop) //Вернём выравнивание по умолчанию

KIRQL oldirql;

KeRaiseIrql(DISPATCH_LEVEL, &oldirql); //Поднимем IRQL до DISPATCH_LEVEL

GDT = (PUCHAR)*(PULONG)I.IoctlBuffer(); //GDT = Адрес GetDriveTypeA

RtlCopyMemory(NewGetDriveType, GDT, 5); //Заголовок NewGetDriveType

Patch.Byte1_4 = (ULONG)GDT - (ULONG)NewGetDriveType - 5;

RtlCopyMemory((PVOID)((ULONG)NewGetDriveType + 5), &Patch, 5); //jmp GetDriveTypeA + 5

Patch.Byte1_4 = (ULONG)MGetDriveType - (ULONG)GDT - 5;

RtlCopyMemory(GDT, &Patch, 5); //jmp MGetDriveType

IsHook = true;

KeLowerIrql(oldirql); //Вернём IRQL обратно

return status;

}

Если функция GetDriveTypeA ещё не перехвачена (IsHook=false), то:

Определяется адрес функции GetDriveTypeA (он присылается в качестве параметра);

По адресу NewGetDriveType копируются 5 байт из начала GetDriveTypeA;

За ними вставляется байт 0xE9 (код команды jmp) и смещение до точки GetDriveTypeA + 5;

По адресу GetDriveTypeA вставляется 0xE9 и смещение до точки MGetDriveType;

Флаг перехвата IsHook устанавливается в true.

Функция снятия перехвата возвращает всё на свои места:

NTSTATUS DTDrvDev::DTDRV_IOCTL_UNHOOK_Handler(KIrp I)

{

NTSTATUS status = STATUS_SUCCESS;

I.Information() = 0;

if (!IsHook) return status;

KIRQL oldirql;

KeRaiseIrql(DISPATCH_LEVEL, &oldirql); //Поднимем IRQL до DISPATCH_LEVEL

RtlCopyMemory(GDT, NewGetDriveType, 5); //Вернём заголовок GetDriveTypeA на место

IsHook = false;

KeLowerIrql(oldirql); //Вернём IRQL обратно

return status;

}

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

Заключение

Итак, перехват API-вызовов – вещь, хотя и достаточно сложная, но все-таки реализуемая (причём различными способами). Методы перехвата различны и часто не переносимы из одной версии Windows в другую.

Microsoft старается сохранять совместимость программного обеспечения со старыми версиями Windows, но получается это далеко не всегда, и аспекты программирования, настолько приближённые к низкоуровневому системному программированию, очень сильно различаются для разных версий Windows. Поэтому часто приходится жертвовать эффективностью в ущерб универсальности – и наоборот.

Список литературы

Для подготовки данной работы были использованы материалы с сайта http://www.rsdn.ru/

Характеристики

Тип файла
Документ
Размер
162,44 Kb
Тип материала
Учебное заведение
Неизвестно

Список файлов реферата

Свежие статьи
Популярно сейчас
Почему делать на заказ в разы дороже, чем купить готовую учебную работу на СтудИзбе? Наши учебные работы продаются каждый год, тогда как большинство заказов выполняются с нуля. Найдите подходящий учебный материал на СтудИзбе!
Ответы на популярные вопросы
Да! Наши авторы собирают и выкладывают те работы, которые сдаются в Вашем учебном заведении ежегодно и уже проверены преподавателями.
Да! У нас любой человек может выложить любую учебную работу и зарабатывать на её продажах! Но каждый учебный материал публикуется только после тщательной проверки администрацией.
Вернём деньги! А если быть более точными, то автору даётся немного времени на исправление, а если не исправит или выйдет время, то вернём деньги в полном объёме!
Да! На равне с готовыми студенческими работами у нас продаются услуги. Цены на услуги видны сразу, то есть Вам нужно только указать параметры и сразу можно оплачивать.
Отзывы студентов
Ставлю 10/10
Все нравится, очень удобный сайт, помогает в учебе. Кроме этого, можно заработать самому, выставляя готовые учебные материалы на продажу здесь. Рейтинги и отзывы на преподавателей очень помогают сориентироваться в начале нового семестра. Спасибо за такую функцию. Ставлю максимальную оценку.
Лучшая платформа для успешной сдачи сессии
Познакомился со СтудИзбой благодаря своему другу, очень нравится интерфейс, количество доступных файлов, цена, в общем, все прекрасно. Даже сам продаю какие-то свои работы.
Студизба ван лав ❤
Очень офигенный сайт для студентов. Много полезных учебных материалов. Пользуюсь студизбой с октября 2021 года. Серьёзных нареканий нет. Хотелось бы, что бы ввели подписочную модель и сделали материалы дешевле 300 рублей в рамках подписки бесплатными.
Отличный сайт
Лично меня всё устраивает - и покупка, и продажа; и цены, и возможность предпросмотра куска файла, и обилие бесплатных файлов (в подборках по авторам, читай, ВУЗам и факультетам). Есть определённые баги, но всё решаемо, да и администраторы реагируют в течение суток.
Маленький отзыв о большом помощнике!
Студизба спасает в те моменты, когда сроки горят, а работ накопилось достаточно. Довольно удобный сайт с простой навигацией и огромным количеством материалов.
Студ. Изба как крупнейший сборник работ для студентов
Тут дофига бывает всего полезного. Печально, что бывают предметы по которым даже одного бесплатного решения нет, но это скорее вопрос к студентам. В остальном всё здорово.
Спасательный островок
Если уже не успеваешь разобраться или застрял на каком-то задание поможет тебе быстро и недорого решить твою проблему.
Всё и так отлично
Всё очень удобно. Особенно круто, что есть система бонусов и можно выводить остатки денег. Очень много качественных бесплатных файлов.
Отзыв о системе "Студизба"
Отличная платформа для распространения работ, востребованных студентами. Хорошо налаженная и качественная работа сайта, огромная база заданий и аудитория.
Отличный помощник
Отличный сайт с кучей полезных файлов, позволяющий найти много методичек / учебников / отзывов о вузах и преподователях.
Отлично помогает студентам в любой момент для решения трудных и незамедлительных задач
Хотелось бы больше конкретной информации о преподавателях. А так в принципе хороший сайт, всегда им пользуюсь и ни разу не было желания прекратить. Хороший сайт для помощи студентам, удобный и приятный интерфейс. Из недостатков можно выделить только отсутствия небольшого количества файлов.
Спасибо за шикарный сайт
Великолепный сайт на котором студент за не большие деньги может найти помощь с дз, проектами курсовыми, лабораторными, а также узнать отзывы на преподавателей и бесплатно скачать пособия.
Популярные преподаватели
Добавляйте материалы
и зарабатывайте!
Продажи идут автоматически
7027
Авторов
на СтудИзбе
260
Средний доход
с одного платного файла
Обучение Подробнее