Предисловие. Немного философии 3
Введение 11
Заголовки исполняемого (РЕ) файла 14
Заголовок DOS 14
Заголовки PE-файла 23
«Обязательный» заголовок PE-файла 24
«Необязательный» заголовок РЕ-файла 28
Таблица разделов 56
Разделы 72
Раздел экспорта 76
Раздел импорта 91
Раздел ресурсов 112
Окно диалога 119
Информация о версии программы 174
Ресурс типа меню 210
Строковые ресурсы. 222
Формат метаданных, используемых платформой .NET 225
Приложения 266

Немного философии

Замысел этой книги возник у меня достаточно давно, но, как говорится, все руки не доходили. И как только появилось время, необходимое для написания книги, я постарался не упустить такую возможность. Результат – перед вами.
В последнее время наметилась следующая тенденция: сейчас для того, чтобы выйти на рынок программных средств и занять в нем свою нишу, фирма, а соответственно, и программисты должны делать продукт как можно быстрее. При этом, естественно, достаточно часто вопросы эффективности, быстродействия, минимизации размера используемой памяти и тому подобные во внимание просто не принимаются. Спрашивается: зачем думать о таких «мелочах», если современные компьютеры достаточно мощные и «переваривают» почти любые объемы. Подумаешь, мегабайт памяти – туда, мегабайт – сюда…
Кроме того, большое влияние на квалификацию программистов оказывают многочисленные средства быстрой разработки, бурное развитие которых наблюдается в последнее время. Средства, предназначенные для повышения производительности труда квалифицированных программистов, заняли на рынке совершенно другое место. Достаточно часто эти средства используются программистами низкой квалификации для того, чтобы в кратчайшее время создать работающую программу, прикладывая при этом минимум усилий.
Более того, средства быстрой разработки позволяют программисту скрыть недостаток квалификации, ибо вся черновая работа делается без участия программиста. Вместо того чтобы овладеть необходимым для профессиональной деятельности минимумом, можно недостаток квалификации скрыть посредством использования программы, которая все сделает сама. Таким образом, средства быстрой разработки превратились в средства создания неэффективных программ неквалифицированными программистами. Что поделаешь, рынок диктует свои условия…
Соответственно, такой подход приводит к тому, что достаточно часто у программистов появляется завышенная самооценка. Раз я могу «накропать» программу за неделю, значит, я – великий программист, все умею, все могу. Зачем мне чему-то учиться, я (при помощи конкретного средства) всегда сделаю то, что хочу! Появилось даже расхожее определение – «программист, пишущий мышкой»… Но стоит у подобных, с позволения сказать, программистов, забрать средство быстрой разработки, как они становятся беспомощными. Это связано стем, что они являются программистами на конкретном средстве! Другими словами, они являются пользователями конкретного средства… А пользователи и программисты – это совершенно различные классы людей, использующих компьютер в своей профессиональной деятельности. И если пользователю необходимо знать только порядок использования и взаимодействие частей одной или нескольких программ (WinWord, бухгалтерская или банковская программы, программа обработки изображений и т. д.), то программист помимо всего прочего должен, почти обязан понимать то, как функционирует компьютер, на основе каких принципов построена операционная система, понимать принципы организации данных и быть в состоянии написать эффективную программу, решающую поставленную перед программистом задачу. В том случае, если программист является профессионалом, то использование средств быстрой разработки только поможет ему, позволив сократить время, необходимое для разработки программы, и минимизировать усилия, необходимые для разработки таковой.
Поводом, подвигнувшим меня на написание этой книги, явился вопрос, заданный на одном из многочисленных форумов по программированию. Вопрос, судя по всему, был задан программистом с явно завышенной самооценкой. Смысл вопроса можно свести к следующему. Да, на свете есть хорошие дизассемблеры, в частности IDA Pro. Но я в ближайшем будущем напишу настолько «крутой» дизассемблер, что IDA Pro моему дизассемблеру и в подметки годиться не будет. Мне для этого только одной мелочи не хватает. Не будет ли любезен многоуважаемый All сообщить мне смещение в исполняемом файле, с которого необходимо начинать дизассемблирование? Что можно сказать об авторе этого сообщения и его потенциальном продукте?
Теперь мне бы хотелось сказать еще об одной тенденции, наметившейся в последнее время. Чуть выше был упомянут дизассемблер. Вторая наметившаяся тенденция связана с дизассемблерами и отладчиками. Дело в том, что сейчас для очень многих молодых «программистов» смыслом профессии стало не создание новых программ и не получение необходимых для этого знаний, а взлом. Неважно что, лишь бы взломать! Снять пароль с программы, взломать сайт, показать всем, какой я «крутой»… Многочисленные руководства по подобным взломам рекомендуют в таких случаях «поставить бряк» на такой-то функции и дождаться «всплытия» отладчика. Да, наверное, за несколько минут ТАКИМ образом можно заставить программу неверно реагировать на введенный пароль или вообще обходить участок кода, обеспечивающий ввод пароля.
А теперь, уважаемый читатель, вспомните сколько программ, находящихся на дисках известного происхождения, «падает» через несколько минут работы. Почему так происходит? Вспомним «руководства» по защите программ, которые легко можно найти в Интернете. Одной из первых рекомендаций, которую можно прочесть в них, является рекомендация «размазывать» защиту по всей программе и не реагировать на неверно введенный пароль непосредственно после его ввода. А можно ли понять логику защиты, не понимая того, что происходит в программе и ориентируясь ТОЛЬКО на «всплывающий» отладчик? Наверное, нет. Ни о какой квалификации «крутых хакеров» не приходится говорить…
Я лично никоим образом не отвергаю идею взлома как таковую. Но! Если я считаю возможным взломать программу, которая работает у меня на компьютере, то я никогда не буду взламывать чужой сайт, какие бы интересные программы на нем не находились. Для меня это сродни проникновению в чужое жилище. С другой стороны, взлом программ я допускаю только ради двух целей.
Цель первая – интеллектуальный спорт, или, как еще говорят, гимнастика ума. Известно, что нет программ, которые не могут быть взломаны, но есть хорошие и плохие защиты. Мне доставляет удовольствие посоревноваться с автором защиты. Если у меня получается запустить программу через несколько минут или даже часов после начала работы с ней, то я считаю, что защита плохая. Если же программу через несколько часов запустить не удается, то тогда можно говорить о хорошей защите.
Цель вторая – это самообразование. Иногда в хорошо написанных программах есть настоящие жемчужины. Хотя, как ни странно, авторы этих программ обычно не говорят о том, что их программу нельзя дизассемблировать и прогонять под отладчиком. Вероятно, квалификация их достаточна для того, что они не боятся потерять свою ценность после того, как кто-то разберется в придуманном ими алгоритме. Как бы то ни было, главное в таких случаях для меня – это восстановить ход мысли автора, постараться воспроизвести ход его рассуждений при разработке той или иной программы. Здесь, естественно, не обойтись без некоторых программных инструментов.
В данном случае мне бы категорически не хотелось использовать термин «боевой софт», получивший в последнее время широкое распространение. То, о чем я буду говорить, можно сравнить с отверткой. Отверткой можно убить человека, но для этого ли она предназначена? Подчеркиваю, что то, о чем я буду говорить ниже, имеет своей целью ТОЛЬКО образование и интеллектуальную гимнастику. Не более. Мне бы очень не хотелось, чтобы описанное здесь использовалось бы с другими целями.
Продолжим разговор об инструментах. Естественно, в арсенале исследователя программ (я избегаю слова «хакер», получившего в последнее время негативный оттенок) должны быть некоторые инструменты. Наверное, первыми из них должны быть обычные просмотрщики файлов, аналоги вызываемых при нажатии клавиш F3 в Norton Commander’е и Far’е. При внимательном просмотре они способны дать исследователю достаточно много информации. Например, взглянув на содержимое файла, можно с достаточно большой вероятностью сказать, является ли этот файл исполняемым, предназначен ли он для работы в 16- или 32-разрядной Windows. Если глаз достаточно наметан, то можно даже предположить, каким компилятором скомпилирован этот файл. Можно увидеть разделы файла, импортируемые им функции и многое другое. Скажу больше. Имея только 16-ричный дамп, часто можно сделать достаточно обоснованное предположение о назначении программы. Естественно, 16-ричные просмотрщики являются средством статического анализа программ. Если программист использует хоть какую-то минимальную хитрость для того, чтобы видоизменить внешний вид программы, скажем, при помощи операции XOR изменяет один бит в каждом байте, то тогда анализ программы при помощи 16-ричного дампа становится практически неразрешимым.
Затем, вероятно, приходит очередь всевозможных мониторов и профилировщиков. Мониторы перехватывают обращение программ к различным объектам, например, к реестру или определенным файлам, и выводят на отображение аргументы, с которыми это обращение происходит, а также результаты этих обращений. Кстати, очень часто после работы мониторов при взломе дальнейших исследований уже не требуется. Однако это ненамного лучше того, чем ждать «всплытия» отладчика. В лучшем случае это позволяет сделать какие-то выводы о том, какие ветви реестра или какие файлы используются для хранения информации.
Профилировщики дают возможность отследить поведение системы на несколько более глубоком уровне. Например, профилировщик, о котором мы поговорим позже, перехватывает вызовы импортируемых функций, после чего сбрасывает в файл или выдает на отображение как аргументы, с которыми эти функции вызываются, так и результаты их работы. Вот здесь уже можно делать достаточно обоснованные предположения не только о том, что используется в программе, но и каким образом это «что-то» работает.
Но и профилировщики не дают полного представления о логике программы. Для того чтобы полностью восстановить логику программы, необходим дизассемблер. Дизассемблер – это инструмент, который машинный код представляет в виде более удобных для понимания человеком ассемблерных инструкций.
Большинство дизассемблеров не ограничиваются простым перекодированием машинного кода в ассемблер. Например, хороший дизассемблер вместо символических адресов импортируемых функций показывает их имена, пытается сделать какие-то предположения о содержимом регистров процессора, и так далее. К примеру, широко известный дизассемблер Sourcer комментирует сгенерированный им код, что позволяет намного реже обращаться к документации.
Однако дизассемблер очень легко заставить нести полную чушь. Для этого достаточно всего-навсего слегка видоизменить код или нарушить его «синхронизацию». Для того чтобы выяснить, каким образом работает программа, необходим инструмент, который дает возможность осуществить прохождение программы в пошаговом режиме и посмотреть, какие регистры и участки памяти используются. Для восстановления логики программы отладчик незаменим. При этом в зависимости от целей, которые ставит перед собой исследователь программы, необходимо использовать либо отладчик уровня ядра, либо отладчик пользовательского уровня, позволяющий отлаживать пользовательские программы. Лучше всего, наверное, в своем арсенале иметь отладчики обоих типов.
При перечислении инструментов исследователя программ я не упомянул об одном из возможных инструментов. Этот инструмент придуман лично мною. Я не защищал его патентом, но аналогов этого инструмента пока не видел. Он занимает промежуточное положение между 16-ричным просмотрщиком и дизассемблером, однако, с точки зрения понимания функционирования и работы системы он является уникальным. Этот инструмент позволяет создать карту файла, то есть показать структуру файла с привязкой к смещению относительно начала файла и используемым при этом структурам, описанным в заголовочных файлах Windows SDK и документации в MSDN. Главное, что позволяет сделать этот инструмент, – это ПОНЯТЬ структуру РЕ-файла и ответить на вопросы «Каким образом я могу извлечь ту или иную информацию из недр РЕ-файла?» и «Что находится в файле по такому-то смещению?» Пример отображения файла таким просмотрщиком приведен на рис. 1.
Из рисунка видно, что анализ программы завершен анализом ресурсов (нижняя часть рисунка, закладка «Log»). Кроме этого, видно, что файл состоит из заголовка DOS, заголовка NT (обычно его называют заголовком РЕ-файла), таблицы секций (разделов) и непосредственно разделов. Можно понять, что таблица секций, к примеру, находится по смещению 0х178 относительно начала файла, а раздел импорта – по смещению 0х80а00. Нетрудно догадаться, что раздел (секция) ресурсов состоит, в частности, из структур типа IMAGE_RESOURCE_DIRECTORY.
Моим глубоким убеждением является то, что анализ программы должен начинаться не с «потрошения» ее при помощи отладчика и дизассемблера, а с помощью 16-ричного просмотрщика или инструмента, придуманного мною. Предварительная работа над файлом при использовании этих инструментов может принести большую пользу. Как ни странно, ни подобные инструменты, ни работа с ними нигде не описаны. Несколько статей Мэтта Питтрека о структуре РЕ-файлов, естественно, проблемы не решают.
Следует отметить, что для меня описанное в этой книге было настоящим исследованием. Основные трудности в этом исследовании связаны с огромным количеством ошибок и недомолвок в документации и заголовочных файлах. Из-за этого приходилось идти почти на ощупь, набивая шишки и выясняя все опытным путем. Другими словами, эта книга не является повторением переписанного в документации. И я надеюсь, что досконально исследовал структуру РЕ-файла. В своем исследовании я никоим образом не опирался на статьи Мэтта Питтрека. Главными источниками информации для меня были заголовочные файлы Windows SDK и в меньшей степени MSDN. Дело в том, что многие структуры, в частности все то, что качается ресурсов версии, не соответствуют ни одной из приведенных в заголовочных файлах структур. Первоначальное понятие о том, чему же соответствуют те или иные данные, можно найти только в MSDN. Кстати, и в MSDN, к большому сожалению, полным-полно ошибок.
Итак, как читатель уже догадался, предлагаемая ему книга рассказывает о том, какое исследование программы может и, наверное, должно быть предпринято до того, как приступить к обработке таковой при помощи дизассемблера и отладчика. В этой книге рассказывется обо всех структурах, включенных в заголовочные файлы Windows SDK и описанные в MSDN, которые соответствуют тем или иным частям исполняемого файла. В ходе рассмотрения мы напишем программу, которая позволит просмотреть содержимое исполняемого файла, познакомимся с некоторыми инструментами, которые могут быть использованы не только в программе просмотра исполняемого файла, но и в других программах.
Полное описание работы программы приведено в приложении A. Там же приведен исходный текст тех частей программы, которые не участвуют непосредственно в анализе исполняемого файла.