Обратная инженерия Антон Hex Кукоба (xtin@ua.fm) Спецвыпуск: Хакер, номер #057, стр. 057-012-4 Пример подхода снизу. Есть программа, работающая с драйвером, в котором и реализована функциональность. Нужно узнать, как можно работать с драйвером без программы. Или, например, нужно декомпилировать алгоритм работы драйвера для реализации в ring 3. Тут сразу видно, что есть точка старта внизу. Эта точка - обмен данными между драйвером и программой, то есть функция DeviceIoControl(). Анализ начинается с перехвата управляющей программы. Смотрим, какие IOCTL-коды нужны каждой из функциональностей. После этого дизассемблируем драйвер и исследуем код, обрабатывающий каждый из нужных IOCTL-кодов. Далее снова анализируем программу для определения параметров, передаваемых в драйвер, и т.д. Для получения наилучшего эффекта оба подхода нужно комбинировать, выбирая тот или иной в зависимости от текущей подзадачи. Оформление disasm-листинга Внутренней информации часто бывает мало. Функции низкого уровня могут быть чисто алгоритмического типа, без вызовов API. А может попасться release-вариант, где все вылизали и не оставили вообще никаких намеков. В таком случае придется придумывать свой подход для именования переменных/функций/структур. Единого стандарта тут нет, и я сам еще не до конца для себя решил, как их называть. Пока при именовании я ввожу подфункции и надфункции. С функциями, найденными по ходу реверсинга, я поступаю по следующему принципу: 1) Если встречаю функцию, которая полностью реализует какую-то функциональность, я даю ей имя этой функциональности. Пример: FormatDiskC, DoDecrypt и т.д. 2) Если внутри именованной функции встречаю вызов неименованной функции, которая выполняет конкретно реализацию функциональности, то даю ей имя с подчеркиванием в конце. Это подфункция. Пример: FormatDiskC_, DoDecrypt_. Количество подчеркиваний для меня является показателем уровня, на котором подфункция находится. Так, FormatDiskC - нулевой уровень, FormatDiskC_ - первый, FormatDiskC__ - второй. 3) Если встречаю функцию, которая вызывает функцию нулевого уровня, но не делает ничего кроме этого (является переходником для преобразования типов), то добавляю подчеркивание перед именем. Это надфункция. Пример: _FormatDiskC, _DoDecrypt. Имена методов объектов, как обычно, Object::Method. Давать имена переменным часто бывает довольно сложно. Приходится переименовывать их по три-четыре раза, если попадется какая-нибудь временная переменная. Но главной проблемой всегда было именование копий переменных. К примеру, в функцию передается указатель, и дальше, чтобы не испортить его значение, он копируется в какую-то переменную. То есть сначала именуются аргументы, а дальше приходится именовать копии аргументов в локальных переменных. Локальным переменным - копиям аргументов - я даю имена с подчеркиванием в конце. То есть аргумент функции имеет чистое имя, а у локальных переменных в именах есть подчеркивание. Еще при именовании аргументов я стараюсь выделять входные и выходные параметры, дописывая им суффиксы _in или _out. Обязательно ставлю префикс "p", если передается указатель. Называть структуры и классы сложно, имена часто приходится придумывать в зависимости от данных, которые они хранят в себе, но большей частью приходится проявлять чудеса воображения. Подход типа Struct1, Struct2... при появлении десяти и более структур запутывает напрочь. Иногда мне хочется, подобно астрономам, использовать кодовые имена. Но это неэффективно, потому что имя функции/структуры должно нести информацию о том, что она содержит и что делает. |