В следующих нескольких главах мы рассмотрим типы модулей, которые можно создать с помощью IDA SDK. Мы также обсудим новые функции (начиная с IDA 5.7), которые позволяют разрабатывать такие же типы модулей с использованием одного из языков сценариев IDA. Независимо от того, собираетесь ли вы когда-либо создавать свои собственные подключаемые модули или нет, базовое понимание подключаемых модулей значительно улучшит ваш опыт использования IDA, поскольку, возможно, большая часть стороннего программного обеспечения, разработанного для использования с IDA, распространяется в виде плагинов. В этой главе мы начнем изучение модулей IDA с обсуждения назначения подключаемых модулей IDA, а также способов их создания, установки и настройки.
Плагины, вероятно, лучше всего описать как скомпилированные, хотя и более мощные, эквиваленты сценариев IDA. Плагины обычно связаны с горячей клавишей и/или пунктом меню и обычно доступны только после открытия базы данных. Отдельные подключаемые модули могут иметь общее назначение и быть полезными для самых разных типов двоичных файлов и архитектур процессоров, или они могут быть очень специализированными, предназначенными для использования только с определенным форматом файла или типом процессора. Во всех случаях, благодаря тому, что плагины являются скомпилированными модулями, они имеют полный доступ к IDA API и, как правило, могут выполнять гораздо более сложные задачи, чем вы когда-либо могли надеяться выполнить, используя только сценарии.
Написание плагина
Все модули IDA, включая подключаемые модули, реализованы как компоненты общей библиотеки, соответствующие платформе, на которой ожидается выполнение подключаемого модуля. В соответствии с модульной архитектурой IDA модули не обязаны экспортировать какие-либо функции. Вместо этого каждый тип модуля должен экспортировать переменную определенного класса. В случае подключаемых модулей этот класс называется plugin_t и определяется в файле loader.hpp пакета SDK.
РАЗВИВАЮЩИЙСЯ API IDA
Начиная с SDK 4.9, Hex-Rays пытается свести к минимуму изменения существующих функций API между выпусками IDA. Одним из результатов этой политики является то, что бинарные подключаемые модули из более старой версии IDA часто могут быть скопированы непосредственно в более новые установки IDA и продолжать работать должным образом. Тем не менее, API IDA расширялся с каждым новым выпуском, добавляя новые функции и новые опции, чтобы воспользоваться преимуществами постоянно расширяющегося списка возможностей IDA. По мере развития SDK Hex-Rays решила отказаться от некоторых функций API. Когда функция (или любой другой символ) устарела, Hex-Rays перемещает ее в блок кода, ограниченный проверкой макроса NO_OBSOLETE_FUNCS. Если вы хотите убедиться, что ваши подключаемые модули (или другие модули) не используют какие-либо устаревшие функции, вы должны определить NO_OBSOLETE_FUNCS до включения каких-либо заголовочных файлов SDK.
Чтобы понять, как создать подключаемый модуль, вы должны сначала понять класс plugin_t и поля данных его компонентов (класс не имеет функций-членов). Здесь показан макет класса plugin_t с комментариями, взятыми из loader.hpp:
Каждый подключаемый модуль должен экспортировать объект plugin_t с именем PLUGIN. Экспорт вашего объекта PLUGIN обрабатывается loader.hpp, что оставляет за вами ответственность за объявление и инициализацию фактического объекта. Поскольку успешное создание подключаемого модуля зависит от правильной инициализации этого объекта, мы опишем здесь назначение каждого члена. Обратите внимание, что даже если вы предпочитаете использовать преимущества новых подключаемых модулей сценариев IDA, вам все равно необходимо ознакомиться с каждым из этих полей, поскольку они также используются в подключаемых модулях сценариев.
version
Этот элемент указывает номер версии API, который использовался для создания подключаемого модуля. Обычно это константа IDP_INTERFACE_VERSION, объявленная в idp.hpp. Значение этой константы не изменилось с тех пор, как API был стандартизирован с помощью SDK версии 4.9. Первоначальное назначение этого поля состояло в том, чтобы предотвратить загрузку подключаемых модулей, созданных с помощью более ранних версий SDK, в версии IDA, созданные с использованием более новых версий SDK.
flags
Это поле содержит различные флаги, указывающие, как IDA должна относиться к подключаемому модулю в различных ситуациях. Флаги устанавливаются с помощью побитовой комбинации констант PLUGIN_XXX, определенных в loader.hpp. Для многих плагинов будет достаточно присвоить этому полю ноль. Пожалуйста, обратитесь к loader.hpp за значениями каждого бита флага.
Init
Это первый из трех указателей на функции, содержащихся в классе plugin_t. Этот конкретный элемент является указателем на функцию инициализации подключаемого модуля. Функция не принимает параметров и возвращает целое число. IDA вызывает эту функцию, чтобы дать вашему плагину шанс загрузиться. Инициализация подключаемых модулей обсуждается в разделе «Инициализация подключаемых модулей» на стр. 320.
term
Этот элемент является еще одним указателем на функцию. IDA вызывает связанную функцию, когда ваш подключаемый модуль выгружается. Функция не принимает аргументов и не возвращает значения. Эта функция предназначена для выполнения любых задач по очистке (освобождение памяти, закрытие дескрипторов, сохранение состояния и т. д.), требуемых вашим подключаемым модулем, до того, как IDA выгрузит его. Это поле может быть установлено в NULL, если у вас нет действий, которые нужно выполнить, когда ваш подключаемый модуль выгружается.
run
Этот элемент указывает на функцию, которая должна вызываться всякий раз, когда пользователь активирует (через горячую клавишу, пункт меню или вызов скрипта) ваш подключаемый модуль. Эта функция является сердцем любого подключаемого модуля, поскольку именно здесь определяется поведение, которое пользователи связывают с подключаемым модулем. Это функция, которая больше всего похожа на сценарии поведения. Функция получает один целочисленный параметр (обсуждается позже в разделе «Выполнение подключаемого модуля» на стр. 322) и ничего не возвращает.
comment
Этот член является указателем на строку символов, которая служит комментарием для подключаемого модуля. Он не используется непосредственно IDA и может быть безопасно установлен в NULL.
help
Этот элемент является указателем на строку символов, которая служит многострочной строкой справки. Он не используется непосредственно IDA и может быть безопасно установлен в NULL.
wanted_name
Этот элемент является указателем на строку символов, содержащую имя подключаемого модуля. Когда плагин загружается, эта строка добавляется в меню Правка->Плагины как средство активации плагина. Не требуется, чтобы имя было уникальным среди загруженных подключаемых модулей, хотя это трудно определить, какой из двух плагинов с одинаковыми именами будет активирован при выборе имени из меню.
wanted_hotkey
Этот элемент является указателем на строку символов, содержащую имя горячей клавиши (например, «Alt-F8»), которую IDA попытается связать с подключаемым модулем. Здесь снова нет необходимости в том, чтобы это значение было уникальным среди загруженных подключаемых модулей; Однако; если значение не уникально, горячая клавиша будет связана с последним подключаемым модулем, запросившим ее. В разделе «Настройка подключаемых модулей» на стр. 330 обсуждается, как пользователи могут переопределить значение Wanted_hotkey.
Здесь показан пример инициализации объекта plugin_t:
Указатели функций, включенные в класс plugin_t, позволяют IDA находить необходимые функции в вашем подключаемом модуле, не требуя от вас экспорта этих функций или выбора конкретных имен для этих функций.
Жизненный цикл плагина
Типичный сеанс IDA начинается с запуска самого приложения IDA и продолжается загрузкой и анализом нового двоичного файла или существующей базы данных, прежде чем остановиться и дождаться взаимодействия с пользователем. Во время этого процесса есть три различных момента, в которых IDA предлагает подключаемым модулям возможность загрузки:
1. Плагин может загружаться сразу после запуска IDA, независимо от того, загружается база данных или нет. Загрузка таким образом контролируется наличием бита PLUGIN_FIX в PLUGIN.flags.
2. Плагин может загружаться сразу после модуля процессора и оставаться загруженным до тех пор, пока модуль процессора не будет выгружен. Связывание подключаемого модуля с процессорным модулем управляется битом PLUGIN_PROC в PLUGIN.flags.
3. В отсутствие только что упомянутых флагов IDA предлагает подключаемым модулям возможность загрузки каждый раз, когда база данных открывается в IDA.
IDA предлагает подключаемым модулям возможность загрузки путем вызова PLUGIN.init. При вызове функция инициализации должна определить, предназначен ли подключаемый модуль для загрузки с учетом текущего состояния IDA. Значение текущего состояния зависит от того, какая из трех предыдущих ситуаций применима при загрузке подключаемого модуля. Примеры состояний, которые могут быть интересны подключаемому модулю, включают тип входного файла (плагин может быть разработан специально для использования с PE-файлами, например) и тип процессора (плагин может быть разработан исключительно для использования с двоичными файлами x86).
Чтобы сообщить IDA о своих желаниях, PLUGIN.init должен вернуть одно из следующих значений, определенных в loader.hpp.
PLUGIN_SKIP Возврат этого значения означает, что подключаемый модуль не следует загружать.
PLUGIN_OK Возвращая это значение, IDA дает указание сделать подключаемый модуль доступным для использования с текущей базой данных. IDA загружает подключаемый модуль, когда пользователь активирует подключаемый модуль с помощью действия меню или горячей клавиши.
PLUGIN_KEEP Возврат этого значения дает IDA указание сделать подключаемый модуль доступным для использования с текущей базой данных и оставить подключаемый модуль загруженным в памяти.
После загрузки плагина его можно активировать одним из двух способов. Наиболее частый метод активации подключаемого модуля — по указанию пользователя в ответ на выбор меню или активацию горячей клавиши. Каждый раз, когда подключаемый модуль активируется таким образом, IDA передает управление подключаемому модулю, вызывая PLUGIN.run. Альтернативный метод активации подключаемого модуля заключается в подключении подключаемого модуля к системе уведомления о событиях IDA. В таких случаях подключаемый модуль должен проявлять интерес к одному или нескольким типам событий IDA и регистрировать функцию обратного вызова, которая будет вызываться IDA при возникновении интересующего события.
Когда приходит время для выгрузки подключаемого модуля, IDA вызывает PLUGIN.term (при условии, что он не равен NULL). Обстоятельства, при которых подключаемый модуль выгружается, зависят от битов, установленных в PLUGIN.flags. Плагины, для которых не указаны флаговые биты, загружаются в соответствии со значением, возвращаемым PLUGIN.init. Плагины этих типов выгружаются, когда закрывается база данных, для которой они были загружены.
Когда подключаемый модуль указывает бит флага PLUGIN_UNL, подключаемый модуль выгружается после каждого вызова PLUGIN.run. Такие плагины необходимо перезагружать (что приводит к вызову PLUGIN.init) для каждой последующей активации. Плагины, у которых указан бит флага PLUGIN_PROC, выгружаются, когда модуль процессора, для которого они были загружены. Модули процессора выгружаются всякий раз, когда база данных закрывается. Наконец, подключаемые модули, в которых указан бит флага PLUGIN_FIX, выгружаются только после завершения работы самой IDA.
Инициализация плагина
Плагины инициализируются в два этапа. Статическая инициализация плагинов происходит во время компиляции, а динамическая инициализация происходит во время загрузки посредством действий, выполняемых в PLUGIN.init. Как обсуждалось ранее, поле PLUGIN.flags, которое инициализируется во время компиляции, определяет несколько вариантов поведения подключаемого модуля.
При запуске IDA проверяется поле PLUGIN.flags каждого подключаемого модуля в <IDADIR>/plugins. В этот момент IDA вызывает PLUGIN.init для каждого подключаемого модуля, в котором указан флаг PLUGIN_FIX. Плагины PLUGIN_FIX загружаются перед любым другим модулем IDA и, следовательно, имеют возможность получать уведомления о любом событии, которое может генерировать IDA, включая уведомления, генерируемые модулями загрузчика и процессорными модулями. Функция PLUGIN.init для таких подключаемых модулей обычно должна возвращать либо PLUGIN_OK, либо PLUGIN_KEEP, потому что мало смысла запрашивать ее загрузку при запуске только для того, чтобы возвращать PLUGIN_SKIP в PLUGIN.init.
Однако, если ваш подключаемый модуль предназначен для выполнения одноразовой задачи инициализации при запуске IDA, вы можете выполнить эту задачу в функции инициализации подключаемого модуля и вернуть PLUGIN_SKIP, чтобы указать, что подключаемый модуль больше не нужен.
Каждый раз, когда загружается процессорный модуль, IDA выбирает флаг PLUGIN_PROC в каждом доступном подключаемом модуле и вызывает PLUGIN.init для каждого подключаемого модуля, в котором установлен PLUGIN_PROC. Флаг PLUGIN_PROC позволяет создавать подключаемые модули, которые реагируют на уведомления, генерируемые процессорными модулями, и тем самым дополняют поведение этих модулей. Функция PLUGIN.init для таких модулей имеет доступ к глобальному объекту CPU_t, ph, который может быть проверен и использован для определения того, следует ли пропустить или сохранить подключаемый модуль. Например, подключаемый модуль, разработанный специально для использования с процессорным модулем MIPS, вероятно, должен возвращать PLUGIN_SKIP, если загружается процессорный модуль x86, как показано здесь:
Наконец, каждый раз при загрузке или создании базы данных вызывается функция PLUGIN.init для каждого еще не загруженного подключаемого модуля, чтобы определить, следует ли загружать подключаемый модуль или нет. На этом этапе каждый подключаемый модуль может использовать любое количество критериев, чтобы определить, должна ли IDA сохранять его или нет. Примеры специализированных подключаемых модулей включают те, которые предлагают поведение, специфичное для определенных типов файлов (ELF, PE, Mach-O и т. д.), типов процессоров или типов компиляторов.
Независимо от причины, когда подключаемый модуль решает вернуть PLUGIN_OK (или PLUGIN_KEEP), функция PLUGIN.init также должна позаботиться о любых однократных действиях по инициализации, необходимых для обеспечения правильной работы подключаемого модуля, когда он в итоге активируется. Любые ресурсы, запрошенные PLUGIN.init, должны быть освобождены в PLUGIN.term. Основное различие между PLUGIN_OK и PLUGIN_KEEP заключается в том, что PLUGIN_KEEP предотвращает повторную загрузку и выгрузку подключаемого модуля и, таким образом, снижает потребность в выделении, освобождении и перераспределении ресурсов, которые могут потребоваться, когда подключаемый модуль указывает PLUGIN_OK. Как правило, PLUGIN.init должен возвращать PLUGIN_KEEP, когда будущие вызовы подключаемого модуля могут зависеть от состояний, накопленных во время предыдущих вызовов подключаемого модуля. Обходной путь для этого заключается в том, чтобы подключаемые модули сохраняли любую информацию о состоянии в открытой базе данных IDA, используя механизм постоянного хранения, такой как сетевые узлы. Используя такой метод, последующие вызовы подключаемого модуля могут находить и использовать данные, сохраненные при более ранних вызовах подключаемого модуля. Преимущество этого метода в том, что он обеспечивает постоянное хранилище не только при вызовах подключаемого модуля, но и при сеансах IDA.
Для подключаемых модулей, в которых каждый вызов полностью независим от любых предыдущих вызовов, PLUGIN.init часто подходит для возврата PLUGIN_OK, преимущество которого заключается в уменьшении объема памяти IDA за счет сохранения меньшего количества модулей, загруженных в память в любой момент времени.
Уведомление о событии
В то время как плагины довольно часто активируются непосредственно пользователем через выбор меню (Правка -> Плагины) или с помощью горячей клавиши, возможности уведомления о событиях IDA предлагают альтернативные средства активации плагинов.
Если вы хотите, чтобы ваши подключаемые модули уведомлялись об определенных событиях, происходящих в IDA, вы должны зарегистрировать функцию обратного вызова, чтобы выражать интерес к определенным типам событий. Функция hook_to_notification_point используется для информирования IDA (1) о том, что вас интересует определенный класс событий и (2) о том, что IDA должна вызывать указанную вами функцию каждый раз, когда происходит событие в указанном классе. Здесь показан пример использования hook_to_notification_point для регистрации интереса к событиям базы данных:
Существуют четыре широкие категории уведомлений: уведомления процессора (idp_notify в idp.hpp, HT_IDP), уведомления пользовательского интерфейса (ui_notification_t в kernwin.hpp, HT_UI), события отладчика (dbg_notification_t в dbg.hpp, HT_DBG) и события базы данных (idp_event_t в idp.hpp, HT_IDB). В каждой категории событий есть ряд отдельных кодов уведомлений, которые представляют определенные события, о которых вы будете получать уведомления. Примеры уведомлений базы данных (HT_IDB) включают idb_event::byte_patched, чтобы указать, что байт базы данных был исправлен, и idb_event::cmt_changed, чтобы указать, что обычный или повторяющийся комментарий был изменен. Каждый раз, когда происходит событие, IDA вызывает каждую зарегистрированную функцию обратного вызова, передавая определенный код уведомления о событии и любые дополнительные параметры, относящиеся к коду уведомления. Параметры, предоставляемые для каждого кода уведомления, подробно описаны в файлах заголовков SDK, которые определяют каждый код уведомления.
Продолжая предыдущий пример, мы могли бы определить функцию обратного вызова для обработки событий базы данных следующим образом:
Этот конкретный пример распознает только сообщение уведомления byte_patched, для которого он печатает адрес исправленного байта, новое значение байта и исходное значение байта. Функции обратного вызова уведомления используют список переменных аргументов C++, va_list, для обеспечения доступа к переменному количеству аргументов, в зависимости от того, какой код уведомления отправляется функции. Количество и тип аргументов, предоставляемых для каждого кода уведомления, указываются в файлах заголовков, в которых определяется каждый код уведомления. Код уведомления byte_patched определен в loader.hpp для получения одного аргумента типа ea_t в его va_list. Макрос C++ va_arg следует использовать для извлечения последовательных аргументов из списка va_list. Адрес исправленного байта извлекается из списка va_list{1}в предыдущем примере.
Здесь показан пример отсоединения от событий уведомления базы данных:
Все подключаемые модули с хорошим поведением должны отключать любые уведомления всякий раз, когда подключаемый модуль выгружается. Это одно из предполагаемых назначений функции PLUGIN.term. Неспособность отключить все ваши активные уведомления почти наверняка приведет к сбою IDA вскоре после выгрузки вашего подключаемого модуля.
Выполнение плагина
До сих пор мы обсуждали несколько случаев, когда IDA вызывает функции, принадлежащие подключаемому модулю. Операции загрузки и выгрузки плагина приводят к вызову PLUGIN.init и PLUGIN.term соответственно. Активация пользовательского плагина через меню Правка->Плагины или связанная с ним горячая клавиша приводит к вызову run. Наконец, функции обратного вызова, зарегистрированные подключаемым модулем, могут вызываться в ответ на различные события, происходящие в IDA.
Независимо от того, как выполняется плагин, важно понимать несколько важных фактов. Функции подключаемых модулей вызываются из основного цикла обработки событий IDA. Во время выполнения подключаемого модуля IDA не может обрабатывать события, включая поставленные в очередь задачи анализа или обновления пользовательского интерфейса. Поэтому важно, чтобы ваш плагин выполнял свою задачу как можно быстрее и возвращал управление IDA. В противном случае IDA будет полностью невосприимчива, и восстановить управление будет невозможно. Другими словами, когда ваш подключаемый модуль запущен, нет простого способа выйти из него. Вы должны либо дождаться завершения работы надстройки, либо завершить процесс IDA. В последнем случае у вас, скорее всего, будет открытая база данных, которая может быть повреждена, а может и не быть повреждена, и IDA может или не может ее восстановить. SDK предлагает три функции, которые можно использовать для решения этой проблемы. Функция show_wait_box может быть вызвана для отображения диалогового окна с сообщением Please wait . . . вместе с кнопкой Отмена. Вы можете периодически проверять, нажимал ли пользователь кнопку Cancel modific, вызывая функцию wasBreak. Преимущество этого подхода заключается в том, что при вызове wasBreak IDA воспользуется возможностью обновить свой пользовательский интерфейс, и это позволит вашему подключаемому модулю решить, следует ли ему остановить выполняемую обработку. В любом случае вы должны вызвать hide_wait_box, чтобы удалить диалоговое окно ожидания с экрана.
Не пытайтесь проявить творческий подход в своих подключаемых модулях, заставляя функцию PLUGIN.run создавать новый поток для обработки обработки внутри вашего подключаемого модуля. IDA не является потокобезопасной. Нет ни механизмов блокировки для синхронизации доступа ко многим глобальным переменным, используемым IDA, ни механизмов блокировки, обеспечивающих атомарность транзакций базы данных. Другими словами, если вы создали новый поток и использовали функции SDK для изменения базы данных из этого потока, вы можете повредить базу данных, поскольку IDA может находиться в середине своего владения базой данных, что конфликтует с вашей попыткой изменения.
Учитывая эти ограничения, для большинства подключаемых модулей основная часть работы, выполняемой подключаемым модулем, будет реализована в PLUGIN.run. Опираясь на наш предварительно инициализированный объект PLUGIN, минимальная (и скучная) реализация PLUGIN.run может выглядеть следующим образом:
Каждый подключаемый модуль имеет в своем распоряжении API C++ и IDA. Дополнительные возможности доступны при связывании подключаемого модуля с соответствующими библиотеками для конкретной платформы. Например, полный Windows API доступен для подключаемых модулей, разработанных для работы с версиями IDA для Windows. Чтобы сделать что-то более интересное, чем вывод сообщения в окно вывода, вам нужно понять, как выполнить желаемую задачу, используя доступные функции из IDA SDK. Взяв, к примеру, код из листинга 16.6, мы могли бы разработать следующую функцию:
Используя эту функцию, у нас теперь есть ядро подключаемого модуля, которое выводит информацию о фрейме стека для текущей выбранной функции каждый раз, когда подключаемый модуль активируется.
Создание ваших плагинов
В Windows плагины — это допустимые DLL-файлы (которые используют расширение .plw или .p64), а в Linux и Mac плагин — это допустимый общий объектный файл (который использует .plx/.plx64 или расширение .pmc/.pmc64 соответственно). Создание подключаемых модулей может оказаться непростой задачей, потому что вы должны правильно задать все параметры сборки, иначе процесс сборки почти наверняка завершится неудачно. SDK содержит несколько примеров подключаемых модулей, каждый из которых содержит собственный make-файл. Все make-файлы были созданы с учетом средств сборки Borland для Windows. Это создает некоторые проблемы, когда вы хотите использовать другую цепочку инструментов или другую платформу. В файлах install_xxx.txt, включенных в SDK, обсуждается использование <SDKDIR>/bin/idamake.pl для создания подключаемых модулей с помощью GNU make и gcc. Цель idamake.pl состоит в том, чтобы сгенерировать make-файл в стиле GNU make из make-файлов в стиле Borland, а затем вызвать GNU make для сборки подключаемого модуля.
Мы предпочитаем создавать плагины с помощью упрощенных make-файлов с инструментами GNU (через MinGW в Windows). Упрощенный make-файл в листинге 17-1 можно легко адаптировать к вашим собственным проектам подключаемых модулей:
Предыдущий make-файл использует команду uname для определения платформы, на которой он работает, и соответствующим образом настраивает некоторые флаги сборки. В проект подключаемого модуля можно добавить дополнительные исходные файлы, добавив имена связанных объектных файлов к переменной $OBJS и в конец make-файла. Если вашему плагину требуются дополнительные библиотеки, вы должны указать имена библиотек в $EXTRALIBS. Переменная $IDA_SDK используется для указания местоположения <SDKDIR>, а $IDA_SDK может быть указан как абсолютный или относительный путь. В этом примере $IDA_SDK указывается как относительный путь, указывающий, что <SDKDIR> находится на два каталога выше каталога подключаемого модуля. Это соответствует размещению проектов подключаемых модулей в каталоге <SDKDIR>/plugins (в данном случае <SDKDIR>/plugins/idabook_plugin). Если вы решите разместить каталог проекта вашего подключаемого модуля в другом месте относительно <SDKDIR>, вы должны убедиться, что $IDA_SDK правильно ссылается на <SDKDIR>. Наконец, в предыдущем примере настроено сохранение успешно скомпилированных подключаемых модулей в <SDKDIR>/bin/plugins. Важно понимать, что успешная компиляция плагина не обязательно означает его установку. Мы рассмотрим установку плагинов в следующем разделе.
Использование Microsoft Visual C++ Express для создания модулей IDA обсуждается в файле install_visual.txt. Чтобы создать проект с нуля с помощью Visual Studio 2008, выполните следующие действия:
1. Выберите File->New->Project, чтобы открыть диалоговое окно New Project, показанное на рис. 17-1.
2. Укажите тип проекта Visual C++/Win32, выберите шаблон проекта Win32 и укажите имя и расположение вашего проекта. Обычно мы создаем новые проекты подключаемых модулей в каталоге <SDKDIR>/plugins, чтобы все наши подключаемые модули были сгруппированы вместе. Когда вы нажмете «ОК», появится мастер приложений Win32. Нажмите «Далее», чтобы перейти к шагу «Настройки приложения», а затем установите для параметра «Тип приложения» значение «DLL» и для дополнительных параметров значение «Пустой проект», прежде чем нажать «Готово», как показано на рис. 17-2.
3. После создания базовой структуры проекта необходимо настроить несколько дополнительных параметров. Доступ к свойствам проекта в Visual Studio 2008 осуществляется через Project->Properties, что вызывает диалоговое окно, показанное на рис. 17-3. Параметры конфигурации C/C++ становятся доступными только после добавления исходного файла в проект путем добавления и редактирования нового файла или добавления существующего файла.
Параметры, требующие изменения, распределены по всему разделу «Свойства конфигурации» в левой части диалогового окна. На рис. 17-3 показано, как задаются свойства в проекте. Для каждой категории свойств, выбранной в левой части диалогового окна, список настраиваемых свойств отображается в правой части диалогового окна. Обратите внимание, что категории свойств организованы в иерархическом порядке. Свойства редактируются с помощью элементов управления выбором файлов, элементов управления однострочным редактированием, элементов управления многострочным редактированием или элементов управления выбора раскрывающегося списка. В Табл. 17-1 подробно описаны свойства, которые необходимо отредактировать для создания проекта подключаемого модуля.
Обратите внимание, что Visual Studio позволяет указать отдельные параметры конфигурации для отладочной и выпускной версий проекта (см. рис. 17-3 в верхнем левом углу). Если вы намерены создавать отдельные отладочные и выпускные версии вашего подключаемого модуля, убедитесь, что вы изменили свойства в обеих конфигурациях. Кроме того, вы можете сэкономить время, выбрав «Все конфигурации» в раскрывающемся списке «Конфигурации» (в левом верхнем углу диалогового окна «Свойства»), и в этом случае ваши изменения свойств будут применены ко всем конфигурациям сборки.