Манипулирование результатом дизассемблирования



 

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

6.1 Имена и присваивание имён

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

Эти имена редко указывают на назначение области или переменной, а значит, не облегчают наше понимание программы. Благодаря IDA, Вы с легкостью измените все имена, в то время как IDA будет обрабатывать любые детали в изменениях ассемблерного кода. В большинстве случаев, изменение имени – это простая операция: клик на нужном имени подсветит его, после чего нужно нажать на клавишу N для открытия диалогового окна изменения имени. Есть другой вариант: щелчок правой клавишей мыши вызовет меню, в котором есть пункт – Rename.

Параметры и локальные переменные. Имена, связанные с переменными стека, являются самой простой формой именования в листинге ассемблерного кода, прежде всего потому, что они не связаны с определенным виртуальным адресом, а значит, они не могут появиться в окне Names. Как и в большинстве языков программирования, такие имена ограничены в возможностях внутри функции, которой принадлежит стековый фрейм. Таким образом, у каждой функции в программе должна быть переменная стека arg_0, но ни у одной функции не может быть больше чем одной переменной с именем arg_0.

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

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

Именованные области. Переименование именованной области или обозначение имени области, которая его ещё не имела, проходит немного по-другому, нежели переименование переменной стека. Процесс доступа к диалоговому окну идентичен (по горячей клавише N).

Параметр «Maximum length of new names» дублирует значение из одного из файлов конфигурации IDA (<IDADIR>/cfg/ida.cfg). Вы можете использовать имя переменной и длиннее 15-ти символов, правда тогда IDA предложит вам увеличить максимально допустимую длину имени. Согласившись на это изменение, вы подтвердите изменение максимальной длины в рамках текущей базы данных. Любые другие базы данных, которые Вы создадите в дальнейшем, будут использовать значение, содержащееся в конфигурационном файле.

Возможные атрибуты, которые связаны с именем области:

− Локальное имя (local name) ограничено возможностями текущей функции, таким образом реализована уникальность имен в пределах одной функции. Как и локальные переменные, две различные функции могут содержать два идентичных локальных имени, однако два идентичных локальных имени не могут принадлежать единственной функции. Именованные области, которые находятся вне границ функции, не определяются как локальные имена. Как и глобальные переменные, они включают в себя имена функций.

− Включениевсписокимен (including in names list). Выбирая эту опцию, вы добавляете введенное имя в окно Names, которое может облегчить поиск данного имени, когда Вы захотите к нему вернуться. Имена, сгенерированные автоматически (фиктивные), по умолчанию никогда не включаются в окно Names.

− Общедоступное имя (public name). Обычно – это имя, которое экспортируется двоичным файлом как общая библиотека. Синтаксические анализаторы IDA обычно обнаруживают такие имена при парсинге заголовков во время начальной загрузки в базу данных. Выбирая этот атрибут, вы заставляете обработчик определять символ как public name.

− Автоматически сгенерированное имя (Autogenerated name). Этот атрибут не оказывает никакого видимого эффекта на дизассемблирование.

− Слабое имя (weak name). Слабый символ – специализированная форма общедоступного символа, использующегося только когда никакой общедоступный символ того же имени не находится для переопределения.

− Create name anyway. Для областей вне функции (в глобальной видимости) нельзя давать одинаковые имена.

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

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

Имена регистров. IDA позволяет переименовывать регистры в пределах границ функции. Переименование регистра работает почти такие же, как и переименование в любой другой области. Используйте горячую клавишу N, или щелкните правой кнопкой по имени регистра, и выберите Rename, чтобы открыть диалоговое окно. Когда Вы переименовываете регистр, Вы, в действительности, задаете псевдоним, по которому можно обратиться к регистру для текущей функции. IDA выполнит замену всех экземпляров имени регистра новым псевдонимом. Невозможно переименовать регистр, используемый в коде, который не принадлежит функции.

 

Комментарии в IDA

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

IDA предлагает несколько различных стилей комментариев, каждый из которых служит для определенной цели. Комментарии могут быть связаны с любой строкой ассемблерного кода с помощью опции Edit ► Comments. К функции комментариев IDA можно обратиться через горячие клавиши или контекстные меню.

Большинство комментариев IDA снабжено префиксом «точка с запятой», чтобы указать, что остаток строки нужно считать комментарием. Это подобно стилям комментариев, используемых многими ассемблерами, и приравнивается к # - во многих языках сценариев или // - в C++.

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

Повторяющиеся комментарии. Этот тип комментариев примечателен тем, что, будучи поставлен однажды, он может автоматически появляться во многих местах в ассемблерном коде. Свойство повторяющихся комментариев связано с понятием перекрестных ссылок. Когда одна программная область обращается ко второй, которая содержит повторяющийся комментарий, тогда комментарий, связанный со второй областью, отражен в первой. По умолчанию отраженный комментарий выделен серым. Горячая клавиша для таких комментариев - точка с запятой (;).

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

«Следующие» и «предыдущие» строки. Данный тип комментариев выглядит как целая строка, которая появляется до (anterior) и после (posterior) ассемблерной строки. Такие комментарии цветом никак не отмечены и встречаются только в IDA.

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

 

Первичное преобразование кода

Преобразование кода IDA включает следующие операции:

− Преобразование данных в код.

− Преобразование кода в данные.

− Преобразование последовательности инструкций в функцию.

− Изменение начального и конечного адреса существующих функций.

− Изменение формата вывода операндов.

Множество факторов влияют на степень использования этих операций, включая Ваше личное мнение.

Параметры отображения кода. Каждая строка дизассемблированного кода состоит из частей, которые IDA называет «частями дизассемблированных строк». В такой строке всегда присутствуют метки, мнемоника и операнд. Вы можете выбрать дополнительные параметры для каждой дизассемблированной строки, используя Опции ► Общие (Option►General) на вкладке Disassembly.

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

Префиксы строки section: address – это часть каждой дизассемблированной строки. Если убрать галочку с этой опции, то из каждой строки дизассемблирования будет удален префикс.

Выбор Stack Pointer позволит видеть на экране относительное изменение по мере выполнения каждой функции. Это может быть полезно в распознавании несоответствий в условных обозначениях или необычных манипуляций с указателем на вершину стека.

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

Простые и повторяющиеся комментарии. Отмена любой из этих опций запрещает отображать определенный тип комментария.

Автоматические комментарии. IDA может иногда напомнить, как именно выполняются некоторые команды, используя для этого автоматические комментарии. Для таких тривиальных инструкций, как x86 mov, комментарии естественно не добавляются. Комментарии пользователей всегда приоритетнее автоматических; поэтому, если Вы хотите видеть автоматический комментарий IDA для строки, Вам придется удалить все собственные комментарии (обычные или повторяющиеся).

Некорректная команда, маркер <BAD>. IDA может отмечать команды, корректные для процессора, но не распознаваемые некоторыми компиляторами. Недокументированные (в противоположность недопустимым) команды ЦП также могут попасть в эту категорию. В таких случаях IDA дизассемблирует инструкцию как последовательность байтов данных и выведет на экран недокументированную команду как комментарий с предисловием <BAD>.

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

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

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

Очень часто в своем исходном коде программисты используют именованные константы. Такие константы могут быть результатом #define операторов (или их эквивалента), или могут принадлежать ряду исчисляемых констант. К сожалению, к тому времени, когда компилятор заканчивает с исходным кодом, уже невозможно определить, использовалась ли в исходнике символьная или числовая константа. IDA поддерживает большой каталог констант, связанных со многими библиотеками, такими как стандартная библиотека C или API Windows. Этот каталог доступен в опциях символьной константы – Use standard symbolic constant в меню любой константы.

 

Управление функциями

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

Создание новых функций. Новые функции могут быть созданы существующими командами, которые уже не принадлежат функции, или из байтов необработанных данных, которые не были определены IDA (такими как двойные слова или строки). Для создания функции, поместите курсор в первый байт или команду, которая будет включена в новую функцию, и выберите Edit ► Functions ► Create Function. IDA пытается преобразовать данные, чтобы кодировать их в случае необходимости. Затем сканирует следующие строки, чтобы проанализировать структуру функции и найти оператор возврата. Если IDA сможет определить местоположение конца функции, она генерирует новое имя функции, анализирует стековый фрейм, и реструктурирует код в теле функции. Если же она не смогла определить местоположение конца функции или встретилась с какими-либо запрещенными командами, то операция прерывается.

Удаление Функций. Удалить существующую функцию вы можете, нажав на Edit ► Functions ► Delete Function, если думаете, что IDA допустила ошибку в анализе.

Функциональные Блоки. Функциональные блоки обычно находятся в коде, сгенерированном компилятором Microsoft Visual C++. Блоки - результат перемещения компилятором участков кода, которые реже всего выполняются, чтобы уплотнить часто выполняемые блоки в страницах памяти, которые вряд ли будут выгружены.

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

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

Вы создаете новые функциональные блоки, выбирая диапазон адресов, принадлежащих блоку, который не должен быть частью другой уже существующей функции, выбрав для этого Edit ► Functions ► Append Function Tail. Далее Вас попросят выбрать родительскую функцию из списка всех определенных функций.

В листинге ассемблерного кода функциональные блоки упоминаются как function chunks, а в меню IDA блоки функций названы function tails.

Для удаления функционального блока, подведите курсор к любой строке блока и выберите Edit ► Functions ► Remove Function Tail. Далее Вас попросят подтвердить своё решение.

Вы можете сделать так, чтобы IDA не создала функциональные блоки, убрав галочку с Create functional tails loader при загрузке файла в IDA. Эта одна из опций загрузчика, доступная через Опции Ядра(Kernel options) в начальном диалоговом окне загрузки файла. Если Вы отключаете Function Tails, Вы можете заметить, что теперь там содержатся переходы к областям вне границ функции. IDA выделяет такие переходы красным цветом и стрелками в окнах на левой стороне дизассемблирования. В графическом представлении для соответствующей функции не выведены на экран цели таких переходов.

Функциональные параметры. В IDA у функций есть много параметров:

Имя функции - альтернативный путь для смены имени функции.

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

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

Область локальных переменных - представляет собой число байтов в стеке, выделенных для локальных переменных функции.

Сохраненные регистры (Saved registers) - число байтов, используемых, чтобы сохранить значение регистров от имени вызывающей стороны. IDA полагает, что сохраненная область регистра лежит выше сохраненного обратного адреса и ниже любых локальных переменных функции.

Очищенные(purged) байты - очищенные байты показывают число байтов параметров, которые функция удаляет из стека, когда возвращала значение.

Допустимая ошибка (корректировка) указателя фрейма. В некоторых случаях компиляторы могут скорректировать указатель на фрейм функции, чтобы указать примерно в середину области локальной переменной, а не в сохраненное значение указателя фрейма у основания области локальной переменной. Это расстояние от скорректированного указателя фрейма до сохраненного указателя фрейма называют допустимой ошибкой указателя фрейма. В большинстве случаев любая допустимая ошибка указателя фрейма вычисляется автоматически при анализе. Компиляторы используют допустимую ошибку стекового фрейма для оптимизации скорости. Назначение допустимой ошибки заключается в том, чтобы сохранить как можно больше переменных стекового фрейма в пределах досягаемости введенного смещения 1 байта (-128. + 127) от указателя фрейма.

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

Невозвращение: функция не возвращается к ее вызывающей стороне.

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

Библиотека func отмечает функцию как библиотечный код. Библиотечная функция отмечается в коде цветом, определенным только для таких функций, чтобы отличать её от обычных функции,

Статический func показывает только список всех статических параметров.

Фрейм, базирующийся на BP(BP based frame показывает, что функция использует указатель фрейма. Для фреймов, основанных на указателях, ссылки на память, которые используют указатели фреймов, отформатированы так, чтобы использовать символьные имена переменной стека, а не числовые смещения.

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

Корректировка Указателя вершины стека. Если IDA не удается определить, изменяется ли указатель на команду вершины стека, вам придется вручную подкорректировать указатель на вершину стека.

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

 

Преобразование данных для кодирования

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

Независимо от причины, по которой Вы хотите переформатировать свой код ассемблера, это сделать довольно легко. Первым делом удалите свое текущее форматирование (кода или данных). Можно убрать переопределение функции, кода или данных, щелкнув правой кнопкой по элементу, который Вы хотите переопределить и выбрать undefine (также Edit ► Undefine или горячая клавиша U) в выпадающем меню. Такое переопределение элемента заставляет базовые байты быть переформатированными как список необработанных значений. Чтобы переопределить большие области, используйте метод буксировки. К примеру:

.text:004013E0 sub_4013E0 proc near

.text:004013E0 push ebp

.text:004013E1 mov ebp, esp

.text:004013E3 pop ebp

.text:004013E4 retn

.text:004013E4 sub_4013E0 endp

 

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

.text:004013E0 unk_4013E0 db 55h ; U

.text:004013E1 db 89h ; ë

.text:004013E2 db 0E5h ; s

.text:004013E3 db 5Dh ; ]

.text:004013E4 db 0C3h ; +

 

Чтобы дизассемблировать последовательность неопределенных байтов, щелкните правой кнопкой по первому нужному байту, и выберите Код (также Edit ► Code или горячая клавиша C). Это заставляет IDA дизассемблировать все байты, пока она не встречается с определенным элементом или запрещенной командой. Чтобы преобразовать большие области, используйте метод буксировки.

Преобразование кода в данные происходит немного сложнее. Во-первых, невозможно преобразовать код в данные, используя контекстное меню. Можно использовать только Edit ► Data или горячую клавишу D. Для объемного преобразования команд в данные легче всего сначала выполнить переопределение всех команд, которые Вы хотите преобразовать в данные, а затем форматировать уже данные.

 


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

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






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