Анимация
JavaScript
|
Главная Библионтека .MODEL TINY .CODE ORG 100h Start: sO DB Hello, Sailor!,0Dh,0Ah,$ Print:
END Start Первые четыре строки любой дизассемблер разберет без труда. Но вот дальше начнутся сложности. Как узнать, что следом идет текстовая строка, а не исполняемый код? Если же дизассемблировать как код из соображений одной лишь надежды на это, то полуученый результат станет похожим на бред и вся программа окажется дизассемблированной неправильно. Поэтому приходится эмулировать ее исполнение и отслеживать все косвенные и непосредственные переходы. Если ассемблер сумеет распознать вызов подпрограммы, то ссылку на строку он восстановит с куда большей легкостью. Однако, тут скрывается один подводный камень. Эмуляция (даже частичная) требует больших накладных расходов, и если каждый раз ее выполнять «налету», то никаких вычислительных ресурсов не хватит для нормальной работы! Поэтому приходится прибегать к компиляции. Да, именно к компиляции. Ведь компиляция это только перевод с одного языка в другой, а не обязательно в машинные коды. В данном случае, как раз запутанный язык ассемблера (а точнее одних лишь ссылок) преобразуется к максимально производительно и компактной форме записи. Или другими словами можно сказать, что перекрестные ссылки - это сохраненный результат работы эмулятора. Кроме того, если выполнение программы однонаправлено, то есть часто невозможно сказать, выполнение какой инструкции предшествовало текущей, то перекрестные ссылки предоставляют такую возможность! Можно начать изучение программы с любой точки, свободно двигаясь по ходу ее исполнения как взад, так и вперед. Это, в самом деле, очень удобно. Ведь в большинстве случаев не требуется изучить всю программу целиком, а только один из ее компонентов. Например, тот, что отвечает за взаимодействие с интересующим вас форматом файла. Предположим, что в заголовке находится некая сигнатура, которая проверятся исследуемой программой и находится в дизассемблируемом листинге в «прямом виде». Тогда можно по перекрестным ссылкам, перейти на процедуру загрузки файла и начать ее изучение. И это не единственный пример. Перекрестные ссыпки активно используются при дизассемблировании программ любой сложности и относятся к незаменимым компонентам дизассемблера. Однако, как нетрудно догадаться, что гарантировано можно отслеживать только непосредственные ссылки, такие как CALL 0x666, а уже MOV DX,0x777 может быть как смещением, так и константой, а про инструкции типа CALL BX говорить и вовсе не приходится - для вычисления значения регистра BX потребуется весьма не хилый эмулятор процессора. Поэтому не удивительно, что большинство ассемблеров отслеживало перекрестные ссылки из рук вон плохо. Даже первые версии IDA не были совершенны в этом плане. То есть поддержка перекрестных ссылок имелась, но их созданием приходилось заниматься человеку (ведь IDA изначально планировалась как интерактивная среда!), а не машине. Но с течением времени ситуация изменялась и интеллектуальные механизмы IDA улучшались. С версии 3.7 она уже значительно превосходила в этом отношении все остальные существующие в мире ассемблеры, включая SOURCER, и продолжала совершенствоваться! ALMA MATER Из предыдущей главы можно сделать вывод, что реально поддержка того, что подразумевается под термином «перекрестные ссылки» состоит как минимум из механизма отслеживания перекрестных ссылок и механизма работы с перекрестными ссылками, сохраненными в некотором внутреннем формате дизассемблера. Устройство и способности интеллектуального анализатора IDA тема для отдельного разговора, здесь же будет говориться исключительно о работе с уже созданными перекрестными ссылками. С первого взгляда возможно будет даже не понятно, о чем идет речь. Если перекрестная ссылка уже создана, то какие могут быть проблемы? На самом деле все не так просто. Перекрестная ссылка может быть создана (и при том не в единственном числе), а может быть, и нет. Как узнать это наверняка? И как получить адрес, на который перекрестная ссылка ссылается? Вот для этого и предусмотрено почти два десятка функций, поддерживающих работоспособность IDA. Все они ниже будут подробно рассмотрены, но сначала рассмотрим механизмы взаимодействия с перекрестными ссылками, не углубляясь в детали. Итак, любая перекрестная ссылка состоит из источника и приемника. В контекстной помощи IDA источник обозначается как from, а приемник - как to. Источником называется операнд, ссылающийся на примем ник, но не наоборот! Разберем в свете этого приведенный выше пример:
"Hello, Sailor!",0Dh,0Ah,$ ; 10. END Start Так в строке 8 должна быть создан источник перекрестной ссылки, а в строке 11 -приемник. Часто вызывает путаницу, что IDA создает комментарий к перекрестной ссылке только возле приемника. Это, разумеется, правильно, потому что источник в комментариях не нуждается - в большинстве случаев очевидно на что ссылается операнд (хотя в случае инструкций подобных CALL BX этого сказать нельзя), а вот по виду приемника источник установить невозможно. Вот IDA и отображает его в виде комментария: seg000:0100 public start seg000:0100 start proc near seg000:0100 seg000:0102 seg000:0105 seg000:0105 seg000:0107 seg000:0107 seg000:0107 seg000:0107 seg000:0108 XREF: start+2o seg000:0108 seg000 mov mov dx, ah, 9 offset aHelloSailor start retn endp aHelloSailor db Hello, Sailor!,0Dh,0Ah,$ ends DATA Обратите внимание, что IDA ничем не выделила строку 0x102 - создается иллюзия, что здесь ничего нет. Но на самом деле именно с этим адресом и связана перекрестная ссылка, а точнее, ее источник. Практически для всех манипуляций с перекрестными ссылками нужно знать пару значений - линейные адреса источника и приемника. Впрочем, есть и такие функции, что возвращают источник (источники) для указанного адреса приемника. Но об них поговорим позднее, а пока остановимся на том факте, что одновременно по одному и тому же адресу может располагаться несколько как приемников, так и источников. Начнем в первого, как более простого для понимания. g000: g000: g000 seg0 seg0 seg0 g000 g000 g000 g000 seg000 seg000 seg000 seg000 g000 g000 g000 g000 seg000 seg000 seg000 seg000 g000 g000 g000 seg000 seg000 -000101 0100 0100 0100 0100 0100 0100 0103 0106 0109 010C 010D 010D; 010D 010D 010D Print 010D 010D 010E 010F 0110 0112 0112 0114 0114 0114 org 100h assume es:nothing public push call push call retn ss:nothing, ds:seg000, fs:nothing, gs:nothing start ; "Hello, World!\r\n$" offset aHelloWorld Print offset aHelloSailor ; "Hello, Sailor!\r\n$" S U B R O U T I N E proc near pop pop push mov int retn endp ; sp ; CODE XREF: seg000:0103p ; seg000:0109p ; DOS - PRINT STRING ; DS:DX -> string terminated by "$" 0114 ; -----------0115 aHelloWorld 0125 aHelloSailor 0125 seg000 0D: sub 0 10D db Hello, World!,0Dh,0Ah,$ ; DATA XREF: db Hello, Sailor!,0Dh,0Ah,$ ; DATA XREF: ends eg000:0100o seg000:0106o В этом примере процедура Print вызывалась из двух точек кода, о чем свидетельствуют две перекрестные ссылки, проставленные IDA как комментарии. Следовательно, один приемник может иметь и более одного источника. Что бы изучить вызывающий эту процедуру код достаточно только подвести курсор в границы адреса, указанного в перекрестной ссылке и нажать Enter и при желании возвратиться назад по <Esc>. 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 |