Анимация
JavaScript
|
Главная Библионтека фактически происходит именно так - или другими словами - текущая инструкция передает, или может передавать, управление следующей). Вот в этих случаях и создается ссылка Ordinary flow "Скрытой" она объявлена по двум причинам. Первая из них очевидна - какой смысл захламнять текст лишней, никому не нужной информацией? Впрочем, IDA все же выделяет перекрестные ссылки на следующую команду. Точнее выделяет их отсутствие. Сплошная черта (в нашем примере в строке 0x21) как раз и говорит об том, что ссылка на следующую команду в данном месте отсутствует. Вторая причина кроется в оптимизации. Если все остальные ссылки хранятся в bTree, которой обеспечивает не самый быстрый доступ к данным, то ссылки на следующую команду содержаться в флагах (смотри описании виртуальной памяти), что значительно ускоряет работу с ними. А поскольку IDA очень интенсивно использует их, то выигрыш в скорости весьма существенен. Таким образом, Ordinary flow можно рассматривать как отдельный, самостоятельный тип ссылок, а с другой стороны, как частный случай раздновидности ссылок на код. Предоставленные в распоряжение пользователя функции большей частью скрывают эти различия, но и то часть команд приходится выполнять с огорками, о чем и сказано в их описании. Как будет показано ниже, IDA поддерживает еще и «уточняющий» тип - скажем Jump, call или offset. Но на самом деле это всего лишь флаг, или атрибут перекрестной ссылки и играет только информационную роль, и никакого другого влияния не оказывает. С другой стороны два типа перекрестных ссылок на код и данные можно рассматривать вместе, поскольку операции с ними производятся аналогично, не зависимо от типа, но разными наборами функций. Ссылки на следующую инструкцию при этом лучше не модифицировать без особой на то нужны, обращаясь к ним только на чтение, хотя и запись так же доступна. Итак, рассмотрим работу со списком, о котором мы говорили выше. Как было сказано ранее, вся архитектура IDA базируется на линейных адресах и связанных с ними объектах и элементах. Но если с каждым линейным адресом мог был связан только один комментарий каждого типа и только одно имя, то источников и приемников у каждого адреса может быть сколько угодно. Причем один и тот же адрес может быть одновременно как источником одной перекрестной ссылки, так и приемником другой. Например: seg000:000C seg000:000E seg000:0010 seg000:0012 seg000:0015 seg000:0015 seg000:0015 seg000:0017 seg000:0017 seg000:0017 loc 0 17
; CODE XREF: seg00 ds:word 0 1DA, ax seg000:01DA*word 0 1DA dw 0 ; DATA XREF: seg000:0017w Поэтому необходимо говорить о двух независимых списках - приемников и источников, да еще отдельных для каждого типа ссылок - для кода и для данных. Возникает вопрос, - а куда входят ссылки на следующую инструкцию? Ответ - они вообще не входят в упомянутый выше список, так как физически хранятся отдельно. Но некоторые функции IDA эмулируют их присутствие в списке ссылок типа «код», однако, это приводит часто к путанице и запутывает понимание пользователя. Поэтому будем все же считать, что инструкции на следующую команду как бы сами по себе. Это значительно упрощает понимание. Таким образом, перейдем к рассмотрению организации этого списка и работы с ним (поскольку не зависимо от хранимых данных, - работа со всеми списками идентична). Но при ближайшем рассмотрении (и залезании с позволения так сказать интимную область IDA) никакого списка нет, а есть только двоичное дерево, в узлах которого и хранятся перекрытые ссылки в виде (from, to). При необходимости IDA просматривает дерево и извлекает все элементы, адреса которых совпадают с запрошенным. Но, увы, доступа к Btree IDA не предоставляет, но дает функции, работающие с перекрестными ссылками исключительно, как с односвязным списком. То есть это Rfirst (получение первого элемента) и Rnext (получение всех последующих элементов). При этом работа идет не с индексами (которых попросту нет), а исключительно со значениями элементов списка. Таким образом, Rnext принимает линейный адрес и, просматривая двоичное дерево, выдает следующую за ним перекрестную ссылку указанного типа или -1, когда список исчерпан. Таким образом, Rnext(0) с первого взгляда равносильна Rfist, которая становится попросту не нужна. На самом деле все немного запутаннее. И понять это автор смог только после того, как связался с разработчиком IDA и обратился к нему за разъяснениями. На самом деле Rnext никогда не возвращает ссылок на следующую инструкцию. Они хранятся отдельно и поэтому выпадают из поля зрения Rnext. Но вот Rfist действует иначе. Она проверяет - существует ли ссылка на следующую инструкцию и если да, то возвращает в первую очередь ее. В противном случае - первый элемент списка. А теперь вообразим себе следующую ситуацию. Пусть у нас имеется следующий код: seg000:0000 seg000:2864p seg000:0000 seg000:2864 push ax ; CODE XREF: ; приемник call bx ; источник seg000:2869 loc 0 286 seg000:2864p seg000:2869 seg000:2892 loc 0 2892: seg000:2864p seg000:2892 seg000:2892 ; CODE XREF: inc si ; приемник ; CODE XREF: ; приемник cmp byte ptr [si], 22h ; Что будет если попытаться просмотреть список приемников в строке 0x2864? По логике Rfirs должна возвратить адрес ссылки на следующую команду, то есть 0x2869. Если теперь передать его Rnext то, по логике она должна будет возвратить следующий на ним приемник, то есть 0x2892, а приемник по адресу 0х0 окажется «вне поля зрения». Так ли это? На самом деле нет! Rnext сперва проверяет - является ли переданный ей адрес ссылкой на следующую команду, и если да, то начинает просмотр с начала списка! Однако, Функции xfirst0 ведут себя иначе и не выполняют этой дополнительной проверки, в остальном же они ничем другим не отличаются от своих собратьев. Зачем знать все эти подробности? Да просто работать программно с перекрестными ссылками придется намного чаще, чем бы этого хотелось. Дело в том, что IDA может отображать список приемников, как в виде комментариев, так и в окне списка (подробности смотри выше в главе ALMA MATER), но вот никак не отображает источники, считая что «они и так очевидны». Но это на самом деле не так, в случае с командами по типу "CALL BX" - и возникает естественная потребность посмотреть, - а куда же передается управление. Конечно, IDA не отслеживает значение регистра BX автоматически и не создает в этом месте перекрестные ссылки, но вот человек это сделать очень даже может. А вот оставшуюся мелочь - перейти по требуемому адресу (или посмотреть их список) интерактивно решить, видимо, невозможно. Поэтому приходится прибегать к языку скриптов и самостоятельно просматривать список значений. Кстати, для облегчения навигации по файлу его можно добавить в комментарий к инструкции. Это, по-видимому будет наилучшим решением. Подробнее об архитектуре перекрестных ссылок рассказано в описании функций, которые приведены ниже. ХРАНЕНИЕ ПЕРЕКРЕСТНЫХ ССЫЛОК Как хранятся перекрестные ссылки внутри IDA скрыто от пользователя. Достоверно известно лишь то, что хранятся они достаточно эффективно. А детали реализации недокументированны и могут меняться от версии к версии. Однако, значение того, как физически хранятся перекрестные ссылки помогает лучше понять их структуру и работу с ними. На самом деле перекрестная ссылка представляет собой два объекта. Первый из них - источник, который «одним концом» ассоциирован с линейным адресом, по которому он расположен, а другим указывает на адрес приемника. Аналогично и с приемником. Он так же состоит из двух концов. То есть перекрестная ссылка это «двуполый» объект и этим и объясняться идентичный набор функций для работы с ее источником и приемников, а во-вторых, скорость доступа к источнику и приемнику одинакова. Это было бы не так, если бы в узле дерева хранилась структура (from, to) и тогда бы для поиска каждого приемника пришлось бы просматривать все дерево Это не относится к ссылкам на следующую команду, которые хранятся во флагах и организованы совсем по-другому. На каждую ссылку расходуется всего один бит(!). Если он установлен, то, следовательно, ссылка на следующую команду присутствует и наоборот. Адрес же ссылки определяется длиной инструкции, которая численно совпадает с длиной объекта. Подробнее об этом можно прочитать в главе «Объекты в IDA» Однако, необходимо еще раз уточнить, что все эти подробности могут и не соответствовать действительности в какой-то конкретной версии IDA. Кроме того, с течением времени алгоритмы могут быть пересмотрены и изменены на другие, более эффективные. Возможно, даже, что изменения затронут и встроенный язык IDA, что происходит, прямо скажем, регулярно. Поэтому рекомендуется пользоваться своими функциями -обертками, что уже не раз упоминалась в данной книге. Дело в том, что переписать библиотеку своих функций намного проще, чем изменить все скрипты. К тому же библиотеку легко разместить в любом включаемом файле в IDA и тогда скрипты окажутся переносимыми, в противном же случае их придется редактировать каждый раз заново. А вообще, если бы язык скриптов был бы на порядок популярнее, то автор бы не позволил себе такую роскошь как менять прототипы встроенных функций без сохранения обратной совместимости. МЕТОДЫ Функция I Назначение 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [ 67 ] 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |