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

Жажда скорости

Крис Касперски

Хакер, номер #095, стр. 095-130-5


MOV CR4, EAX

…

Следующий код обеспечивает взаимодействие драйвера с прикладной программой через API-функцию DeviceIOControl, передающую в IOCTL-коде номер события, за которым необходимо вести мониторинг. По соображениям наглядности здесь используется всего лишь один счетчик производительности, управляемый MSR-регистром PerfEvtSel0.

IRP_MJ_DEVICE_CONTROL: ; // процедура обработки IOCTL-запросов

; // настраиваем регистр perfevtsel0 для мониторинга нужных событий

XOR EDX, EDX

MOV EAX, pisl->Parameters.DeviceIoControl.IoControlCode ; //номер события

TEST EAX, EAX ; // если код события равен нулю

JZ wrt ; // то вырубаем счетчик

OR EAX, 10000h ; // делаем счетчик доступным

; // с прикладного уровня

OR EAX, 400000h ; // пускаем счетчик

wrt:

MOV ECX,0x186 ; // выбираем MSR-регистр PERFEVTSEL0

WRMSR

При деинициализации драйвера крайне желательно запретить доступ к команде RDPMC с прикладного уровня и остановить все ранее запущенные счетчики производительности, сбросив флажок Enable Counter в MSR-регистрах PerfEvtSel0/PerfEvtSel1 (код, приведенный ниже, останавливает только PerfEvtSel0):

DriverUnload: ; // процедура деинициализации драйвера

…

; // сбрасываем бит PCE регистра CR4 для запрета чтения

; // счетчика производительности с пользовательского уровня

MOV EAX, CR4

MOV ECX, 100h

NOT ECX ; // запрещаем доступ к RDPMC с прикладного уровня

AND EAX, ECX

MOV CR4, EAX

; // останавливаем счетчик производительности

XOR EDX, EDX

XOR EAX, EAX

MOV ECX, 186h

WRMSR

…

Прикладная программа первым делом должна загрузить драйвер (пусть для определенности он будет называться 996.SYS), открыв его с помощью функции CreateFile. При этом управление получит процедура инициализации, открывающая доступ к машинной команде RDPMC. Но сами счетчики производительности еще не заданы, так что читать, собственно говоря, нечего и незачем.

Нет никакой необходимости писать загрузку драйвера на ассемблере, лучше всего воспользоваться для этой цели языком Си:

// определения необходимых констант

#define PrefCtrl0 0x0000

#define DCU_MISS_OUTSTANDING 0x0048

// дескриптор драйвера 996

static HANDLE _996_handle = INVALID_HANDLE_VALUE;

int _996_init()

{

if (_996_handle == INVALID_HANDLE_VALUE)

{

_996_handle = CreateFile("\\\\.\\996",GENERIC_READ,

FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

if (_996_handle == INVALID_HANDLE_VALUE) return 0;

} return 1;

}

То же самое относится и к функции, вызывающей DeviceIoControl и передающей ей код интересующего нас события. На языке Си она выглядит гораздо нагляднее:

int _996_select(int xCode, int REG)

{

DWORD x;

if (REG != PrefCtrl0) return 0;

// если программист забыл загрузить драйвер,

// данная функция делает это самостоятельно

if (_996_handle == INVALID_HANDLE_VALUE) _996_init();

// если загрузка драйвера провалилась, сваливаем отсюда

if (_996_handle == INVALID_HANDLE_VALUE) return 0;

return DeviceIoControl(_996_handle, xCode, &x,0, &x, 0, &x, 0);

}

Назад на стр. 095-130-4  Содержание  Вперед на стр. 095-130-6