Select language:

securelist.com Уровень опасности: 1
Детектируемые объекты
Спам и фишинг
Уязвимости и хакеры
Внутренние угрозы

Грамотная документация


Печать
Bookmark and Share
Закладки
Вячеслав Русаков
опубликовано 2 июл 2009, 16:35  MSK
Сюжеты: Антивирусные технологии
рейтинг постинга
0.6

Грамотная документация - залог здоровья и спокойствия программиста. Причем начинающего программиста, так как эксперт проблему решит намного быстрее. Да и не впервой ему - эксперту сталкиваться с плохой документацией, с программным кодом без комментариев и прочими неприятными вещами.

Одной из самых объемных и важных документаций для программистов является MSDN (Microsoft Developer Network). Для низкоуровневых программистов и программистов драйверов существует отдельная документация WDK (Windows Driver Kit), входящая в состав MSDN. В MSDN перечислены функции, параметры к ним, объяснения, примеры кода и многое другое.

Большая документация - сложный продукт. В любом сложном продукте есть недочеты, ошибки и недоработки. Встречаются они и в документации, ведь все мы люди, все мы ошибаемся. Недавно с такой ошибкой столкнулся и я.

Операционная система Windows предоставляет специальную функцию, которая позволяет отслеживать различные изменения в системном реестре указанного ключа. Она есть как в пользовательском пространстве (RegNotifyChangeKeyValue), так и в пространстве ядра (ZwNotifyChangeKey), там, где работает сама операционная система и драйверы. Этой функцией пользуются как антивирусные продукты, так и вредоносный код, который следит за изменениями своих вредоносных веток антивирусом (обычно это лечение веток реестра).

Прототип функции ZwNotifyChangeKey выглядит так:

NTSTATUS
ZwNotifyChangeKey(
  __in HANDLE KeyHandle,
  __in_opt HANDLE Event,
  __in_opt PIO_APC_ROUTINE ApcRoutine, // указатель на нашу функцию
  __in_opt PVOID ApcContext,
  __out PIO_STATUS_BLOCK IoStatusBlock,
  __in ULONG CompletionFilter,
  __in BOOLEAN WatchTree,
  __out_bcount_opt(BufferSize) PVOID Buffer,
  __in ULONG BufferSize,
  __in BOOLEAN Asynchronous
  );

Через параметр KeyHandle необходимо передать дескриптор открытого ключа, изменения которого мы хотим отслеживать. Параметр ApcRoutine - это указатель на нами реализованную функцию, которая будет вызываться в случае изменений в указанной ветке реестра.

Функция должна быть определена так:

typedef VOID (NTAPI *PIO_APC_ROUTINE) ( IN PVOID ApcContext, IN PIO_STATUS_BLOCK IoStatusBlock, IN ULONG Reserved )

Не суть важно, что тут за параметры, главное - то, что у функции три аргумента. Это важно.

В одном из драйверов мне понадобилась эта функция. Я написал весь сопутствующий код, использовал функцию, скомпилировал драйвер, положил в нужное место и запустил. Система упала в BSOD. 'Не в первый и не в последний раз', - подумал я и начал проверять параметры функции. Все было идеально, все было правильно, а BSOD повторялся каждый раз.

Google не смог мне помочь с данной проблемой и пришлось разбираться самому. Потратив некоторое количество часов, я выяснил, что в недрах операционной системы при определенных обстоятельствах (PreviousMode == KernelMode) указатель PIO_APC_ROUTINE трактуется совершенно по-другому, как указатель на структуру WORK_QUEUE_ITEM, в которой содержится указатель на нами определенную функцию. И этого не было в документации, эту информацию я даже не смог найти в сети.

Структура выглядит следующим образом:

typedef struct _WORK_QUEUE_ITEM {
    LIST_ENTRY List;
    PWORKER_THREAD_ROUTINE WorkerRoutine; // указатель на нашу функцию
    __volatile PVOID Parameter;
} WORK_QUEUE_ITEM, *PWORK_QUEUE_ITEM;

Функция должна быть определена так:

typedef VOID (*PWORKER_THREAD_ROUTINE)( IN PVOID Parameter )

И она принимает всего один параметр. И это важно.

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

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


Комментарии (5)

 
Umnik02 июл 2009, 17:24
2
Знакомые разработчики Миранды тоже несколько раз находили ошибки в MSDN.
Это ладно, а вот перевод MSDN... Не знаю, официальный он или как, но times перевели сначала как время, а ниже как разы. Второе было верным. К сожалению, пруфлинк потерян.
Вячеслав Русаков02 июл 2009, 19:00
3
Re:
А я русский даже смотреть не стал =)
Umnik02 июл 2009, 17:35
0
Собственно (верстка слетает, чего-то)
[17:16] +Umnik: FYR: я помню, ты тоже какую-то ошибку находил в MSDN
[17:25] %FYR: Umnik: да
[17:25] +Umnik: FYR: не помнишь, чего там было?
[17:25] %FYR: Umnik: например там про GetUpdateRgn и BeginPaint неверно написано.. на этом Дора строиться
[17:25] +Umnik: Объяснил %)
[17:28] %FYR: BeginPaint в PAINTSTRUCT возвращает ps.rcPaint это типо Specifies the upper left and lower right corners of the rectangle in which the painting is requested.
[17:29] %FYR: и типо это BoundingBox от update region тот же что вернула бы функция GetUpdateRgn
[17:29] %FYR: но если у окна есть WS_EX_LAYERED
[17:31] %FYR: То BeginPaint, скотина, возвращает тут 0,0,0,0. и валидирует этот регион
[17:31] %FYR: поэтому пришлось писать свои заменители BeginPaint/EndPaint
Станислав21 июл 2009, 12:44
0
Notify
А зачем нотифи юзать ? Она ведь только информирует, не так ли ?
Захученые ZwCreateKey и т.п. удобнее не так ли ? Ведь можно заблокировать вызывающий поток, узнать возвращяемое, получить инфу о вызывающем процессе\потоке.
Вячеслав Русаков28 июл 2009, 09:44
0
Re: Notify
Да, они только информируют и не позволяют изменить управление, однако, они полезны в некоторых случаях. Например, малвара может повесить нотифай на изменение своих ключей прямо в юзермоде и по срабатыванию вызывать процедуру восстановления. И не нужен драйвер, не нужны перехваты :) при условии, что не нужна информация о вызывающем потоке и т.п.

Логин:
Пароль
Запомнить