Типы данных и структуры данных



 

Самый простой метод для того, чтобы связать определенный тип данных с переменной - использование переменной в качестве параметра к функции, о которой мы хоть что-то знаем. Во время ее анализа IDA комментирует типы данных, основанные на использовании переменных в функции, для которой у IDA есть прототип. Когда возможно, IDA использует имя формального параметра, взятого от прототипа функции вместо того, чтобы генерировать фиктивное имя по умолчанию для переменной. Это можно заметить в следующем дизассемблировании вызова connect:

.text:004010F3 push 10h ; namelen

.text:004010F5 lea ecx, [ebp+name]

.text:004010F8 push ecx ; name

.text:004010F9 mov edx, [ebp+s]

.text:004010FF push edx ; s

.text:00401100 call connect

 

В нем мы можем видеть, что каждое нажатие было прокомментировано именем параметра (взятого из базы IDA о прототипе функции). Кроме того, две локальных переменные стека были названы по имени параметров, которым они соответствуют. В большинстве случаев эти имена будут намного более информативными, чем фиктивные имена, генерируемые IDA.

Недостаток IDA в том, что она распознает только параметры функции, которые помещены в стек.

Во многих случаях IDA будет знать прототип функции, и вы увидите это, наведя мышь на ее имя. Если о функции IDA ничего не известно, то лучше всего Вам обратиться к справочной литературе.

 

Использование распознавания структуры данных

Прежде, чем мы сможем обсудить функцию IDA для улучшения читабельности кода, который использует сложные типы данных, мы должны рассмотреть, на что похож такой код.

 

Доступ к элементам массива

Массивы - самая простая компонентная структура данных с точки зрения расположения памяти. Традиционно, массивы - непрерывные блоки памяти, которые содержат последовательные элементы одного типа данных. Размер массива легко вычислить – это произведение количества элементов на их размер.

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

Глобальные Массивы. Когда массив определен в пределах глобальной области данных программы (в пределах .data или раздела .bss, например), начальный адрес массива компилятор определяет во время компиляции. Фиксированный начальный адрес позволяет компилятору вычислить фиксированные адреса для любого элемента массива, к которому можно получить доступ, используя фиксированный индекс.

Дизассемблирование не даст нам точных данных о том, что в коде присутствует глобальный массив.

Использование значений индексных переменных приводит нас к началу массива, потому что базовый адрес будет показан, когда вычисленное смещение будет добавлено к нему, чтобы вычислить фактическое расположение массива.

Стековые Массивы. На практике компиляторы обрабатывают стековые массивы почти как и глобальные. Однако во время компиляции не известен адрес, в котором будет выделен массив, таким образом, компилятор не может вычислить адрес этого массива, в отличие от того, как это было бы сделано в глобальном массиве.

«Кучевые» (heap-allocated) Массивы. Кучевые массивы образуются, используя функцию динамического выделения памяти, такую как malloc (C) или new (C++). С точки зрения компилятора разница в том, что он должен генерировать все ссылки в массив, основанный на значении адреса, возвращенном из функции выделения памяти.

 

Доступ к элементам структуры

Главным отличительным признаком структур является то, что к полям данных в пределах структуры получают доступ по имени, а не по индексу, как это реализовано в массивах. К сожалению, имена полей преобразованы в числовые смещения компилятором. Таким образом, к тому времени, когда вы посмотрите на результат дизассемблирования, доступ к полю структуры становится удивительно похожим на доступ к элементам массива через постоянные индексы.

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

Глобальные Структуры. Как и с глобальными массивами, адрес глобальных структур известен во время компиляции. Это позволяет компилятору вычислять адрес каждого элемента структуры во время компиляции и избавляет от необходимости делать любые вычисления во время выполнения.

Стековые Структуры. Как и стековые массивы, стековые структуры трудно распознать, основываясь только на расположении стека.

В случае стековых массивов в результате дизассемблирования мы получим пять отдельных переменных, а не единственную, которая содержит пять разных полей.

«Кучевые» Структуры. Кучевые структуры, оказывается, намного более прозрачны относительно размера структуры и расположения ее полей. Когда кучевая структура выделена в программе, у компилятора нет никакого выбора, кроме как генерировать код, чтобы вычислить надлежащее смещение в структуре всякий раз, когда к полю требуется доступ. Для кучевой структуры единственная ссылка доступная компилятору, является указателем на начальный адрес структуры.

Массивы Структур. Предыдущие знания о массивах и структурах применяются точно также при контакте с вложенными типами.

 

Создание Структур IDA

Далее мы рассмотрим как средства IDA могут улучшить читабельность кода, который управляет структурами.

 

Ручное Определение Структуры

Всякий раз, когда Вы обнаруживаете, что программа управляет структурой данных, Вы должны решить, хотите ли Вы включить имена полей структуры в код ассемблера или Вы можете понять все числовые смещения, появляющиеся в коде. В некоторых случаях IDA может распознать использование структуры, как часть стандартной библиотеки C или API Windows.

Создание Новой Структуры (или Объединение). Когда программа использует структуру, о которой у IDA нет никакой информации, IDA предлагает определить состав структуры и включить в дизассемблирование новую. Создание структуры в IDA происходит в окне Structures. Никакая структура не может быть включена в дизассемблирование, пока она сначала не занесена в окне Structures. Любая структура, которая известна IDA и распознана программой, автоматически присутствует в окне Structures.

Первые четыре строки в окне Structures служат постоянным напоминанием об операциях, которые возможны в пределах окна. Основные операции, которые нам нужны - добавление, удаление, и редактирование структур. Добавление структуры инициируется, используя клавишу INSERT.

Чтобы создать новую структуру, Вы должны задать имя в поле имени Structure. Первые два флажка определяют, где будет новая структура выведена на экран и будет ли выведена вообще. Третий флажок, Create Union, определяет, будет ли структуру C или нет. Для структур размер вычисляется как сумма размеров каждого компонента, в то время как для объединений, размер вычисляется как размер самого большого компонента. «Добавить стандартную структуру» используется для доступа к списку всех известных IDA типов данных структур. Как только Вы определяете имя структуры и нажимаете OK, пустая структура создастся в окне Structures.

Редактирование Элементов структуры. Чтобы добавить поля к Вашей новой структуре, Вы должны использовать команды D, A, и звездочку (*). Первоначально, только команда D полезна, и, к сожалению, ее действие очень зависит от расположения курсора.

Чтобы добавить новое поле к структуре, расположите курсор на последнюю строку определения структуры (содержащий ends) и нажмите D. Это заставляет новое поле быть добавленным в конец структуры. Размер нового поля будет установлен согласно первому размеру, выбранному на карусели данных. Имя поля первоначально будет field_N, где N - числовое смещение от начала структуры к началу нового поля (field_0, например).

Если вам необходимо изменить размер поля, вы можете сделать это, убедившись, что курсор установлен на новом имени поля и выбрав правильный размер данных, затем нужно несколько раз нажать на клавишу D для переключения между типами данных на карусели данных. Также вы можете использовать Options ► Setup Data Types, чтобы определить размер, который не доступен на карусели данных. Если поле - массив, щелкните правой кнопкой по имени и выберите Array, чтобы открыть диалоговое окно спецификации массива.

Чтобы изменить название поля структуры, щелкните по имени поля и используйте горячую клавишу N, или щелкните правой кнопкой по имени и выберите Name; задайте новое имя для поля.

Стековые фреймы как специализированные структуры. Вы наверняка заметили, что определение структуры выглядит подобно подробному виду стекового фрейма. Это не случайно, поскольку IDA обрабатывает обоих одинаково.

 

Использование шаблонов структур

Есть два способа использовать объявление структуры в коде. Во-первых, Вы можете переформатировать ссылки в памяти, чтобы сделать их более читаемыми, преобразовывая числовые смещения структуры, такие как [ebx+8] в символьные ссылки, такие как [ebx+ch8_struct.field4]. Последняя форма предоставляет гораздо больше информации о том, на что ссылаются. Во-вторых, использование шаблонов структур можно для обеспечения дополнительных типов данных, которые могут быть применены к стеку и глобальным переменным.

IDA позволяет Вам переформатировать любую постоянную величину, используемую в операнде в эквивалентное символьное представление.

Как альтернатива форматированию отдельных ссылок памяти, стек и глобальные переменные могут быть отформатированы как все структуры. Чтобы отформатировать переменную стека как структуру, откройте подробное представление стекового фрейма, дважды щелкнув по нужной переменной, и затем Edit ► Struct Var (ALT-Q), чтобы вывести на экран список известных структур.

При выборе одной из имеющихся структур, содержащей в себе соответствующее количество байт в стеке структуры соответствующего типа, переформатируются все необходимые ссылки памяти в структуру ссылок.

Процедура форматирования глобальных переменных как структуры почти идентична используемой для переменных стека. Чтобы сделать это, выберите переменную или адрес, который отмечает начало структуры, и используйте Edit ► Struct Var (ALT-Q), чтобы выбрать соответствующий тип структуры.

 

Импортирование новых структур

IDA способна к парсингу индивидуальных C (не C++) объявлений данных, так же, как и всех типов файлов C, и автоматически созданных представлений структуры IDA.

Парсинг структур Си. Версия 5.2 IDA ввела подвид Local Types, доступный через View ► OpenSubviews ► Local Types, в окне Local Types представляется как список всех типов, которые были проанализированы в текущей базе данных. Для новых баз данных окно Local Types первоначально пусто, но предлагается возможность проанализировать новые типы через клавишу INSERT или опцию Insert в контекстном меню.

В течение парсинга нового типа на экран в окне сообщений IDA могут выводиться сообщения об ошибках. Если описание типа успешно проанализировано, тип и его объявление перечисляются в окне Local Types,

Типы данных, добавленные в окно Local Types, не сразу доступны через окно Structures. Вместо этого каждый новый тип добавляется к списку стандартных структур и должен быть импортирован в окно Structures.

Parsing C Header Files. Чтобы проанализировать эти файлы, используйте File ► Load File ► Parse C Header File, чтобы выбрать заголовок, который Вы хотите проанализировать. Любые сообщения об ошибках будут выведены на экран в окне сообщений IDA. В другом случае, если ошибок нет, Вы увидите сообщение об успешной компиляции.

IDA добавляет все структуры, которые были успешно проанализированы к ее списку стандартных структур (в конец списка, чтобы быть точным) доступных в текущей базе данных. Когда у новой структуры есть то же имя, что и у существующей структуры, она будет перезаписана с новым расположением структуры. Ни одна из новых структур не появится в окне Structures, пока Вы не добавите их явно.

 


Дата добавления: 2018-06-27; просмотров: 552; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!