Анимация
JavaScript
|
Главная Библионтека #Definition Как только подпрограммы стали неотъемлемой конструкцией любого языка, возникли проблемы с их классификацией. Начала всему положил BASIC, в котором операторы сплошь и рядом спутаны с переменными, функции с операторами, а подпрограммы представляют наименее развитую конструкцию языка. Затем было предложено называть подпрограмму, не возвращающую результатов своей работы процедурой, а возвращающую функцией. Именно такая классификация и была использована в языке Pascal. Разумеется, было много путаницы. Мало того, что трудно представить себе процедуру, совсем ничего не возвращающую. По крайней мере она выводит что-то на экран, или в порты ввода-вывода, пускай даже меняет значения глобальных переменных -то есть все-таки что-то возвращает. Потом, любая процедура имеет доступ к аргументам, передаваемым по ссылке, и может их модифицировать, даже если она ничего не возвращает с точки зрения языка. Иными словами выражение: Resultant := MyProc (arg1, ard2); С точки зрения языка Pascal бессмысленно, процедура не может ничего возвращать. Если опуститься на уровень реализации компилятора Turbo-Pascal, то грубо говоря, функции возвращают результат своей работы в регистре AX, а процедуры оставляют его неопределенным. То есть другими словами, функцией называется то, что возвращает результат своей работы в регистре AX. В языке Си все совсем иначе. Не зависимо от того, возвращает что ни будь подпрограмма или нет, она называется функцией. Процедур в Си нет. Функция всегда что-то возвращает, по крайней мере бестиповое значение void, которые можно присвоить переменной. Не будем вспоминать сейчас те языки, где классификация подпрограмм еще более запутана или вовсе не развита. Сейчас же важно представить себе, как будет работать со всем этим IDA. Знает ли она что-нибудь об этих языковых конструкциях? Для ответа на этот вопрос необходимо уточнить, что нужно понимать под термином «знает». Прежде всего она, как и любой другой дизассемблер явно поддерживает только те конструкции, которые «понимает» целевой ассемблер. С другой стороны, автоматический анализатор способен распознавать различные языковые конструкции, которые могут напрямую и не поддерживаться ассемблером. Популярные ассемблеры MASM и TASM кроме низкоуровневой поддержки подпрограмм, обеспечивают ряд типовых операций с ними, такими как, например, передача аргументов через стек и обращения к ним, абстрагируясь от модели памяти (все вычисления ложатся на плечи ассемблера). Однако, эту конструкцию создатели языка почету-то окрестили процедурой, чем окончательно всех запутали. С одной стороны ассемблер не может автоматически присваивать переменным и регистрам результат, возвращенный процедурой, но с другой это еще никак не означает, что процедура не может ничего возвращать. Воистину, «называемое дао, не есть дао». Впрочем, само по себе такой расклад вещей достаточно приемлем - поскольку, ассемблер, как и любой другой язык, имеет свои соглашения и может совсем не интересоваться, как та же конструкция называется у других. Но ведь IDA это не просто дизассемблер, но и немного декомпилятор, то есть мотивом ее использования, зачастую служит необходимость получить хотя бы отдаленное представление об исходном тексте программы, пусть и в другой форме. Однако, поддерживать отдельные языковые конструкции было бы нецелесообразно, да и в ряде случаев и вовсе невозможно. Поэтому было принято решение остановиться на одной универсальной конструкции, которая бы с успехом подходила для любого языка. Названа же она была функцией. Но в ассемблере не существует такого понятия! Поэтому для выражения функции приходится пользоваться средствами ассемблера, в котором она (функция) называется процедурой, но ничем другими принципиально, кроме названия не отличается. Это иногда приводит к небольшой путанице. Так, например, если дизассемблировать программу, написанную на Turbo-Pascal, то IDA автоматически распознает все процедуры, но называться теперь они будут функциями, а выделяться в ассемблерном листинге ключевым словом PROC (сокращение от procedure) Пусть исходная программа выглядела так: Procedure MyProc; begin WriteLn(Hello); end; BEGIN MyProc; End. Тогда результат работы IDA может выглядеть следующим образом: seg000:0006 seg000:0006 seg000:0006 PROGRAM+14p seg000:0006 seg000:0007 seg000:0009 seg000:000B overflow che seg000:0010 seg000:0013 seg000:0014 seg000:0015 "\x05Hello" seg000:0018 seg000:0019 seg000:001A seg000:001C seg000:001D Write(var f; seg000:0022 WriteLn(var seg000:0027 error seg000:002C seg000:002D seg000:002D seg000:002D seg000:002E ; Attributes: bp-based frame sub 0 6 proc near ; CODE XREF: ck (AX) push call mov push push mov push push xor push call s: String; width: call f: Text) call pop retn endp sub 0 6 bp, sp ax, ax @ StackCheck$q4Word ; Stack di, offset unk E1 166 di, offset asc 0 0 ; cs ax, ax @Write$qm4Textm6String4Word ; @WriteLn$qm4Text ; @ IOCheck$qv ; Exit if assume ss:seg004
На самом же деле никакого противоречия тут нет. Компиляция однонаправленный процесс с потерями, поэтому можно забыть о существующих конструкциях в языке-источнике. Код, сгенерированный компилятором одинаково хорошо похож, как на процедуру, так и на функцию: seg000:0006 sub 0 6 seg000:0006 seg000:0007 proc near push bp mov bp, sp seg000:0027 seg000:002C seg000:002D call pop retn @ IOCheck$qv Поэтому при дизассемблировании принято не акцентировать внимания на подобных различиях и говорить о подпрограммах. Подпрограмма, оформленная особым образом, в IDA называется функцией. Но под этим понимается не только совокупность кода и данных, но и действия, которые над ними можно совершить. Чувствуете разницу? Функцию можно создать, дать ей имя, потом удалить ее, внутри функции IDA может отслеживать значение регистра указателя на верхушку стека и автоматически поддерживать локальные переменные. При этом в ассемблерном листинге функции оформляются в виде процедур. seg000:0006 sub 0 6 seg000:002D sub 0 6 proc near endp Противоречия не возникнет, есть понять, что в данном случае под процедурой является однин из возможных вариантов предстваления функции, средствами выбранного языка (в данном случае ассемблера MASM) Таким образом, мы будем говорить о функции не как о совокупности кода и данных, а именно как о методах взаимодействия с ней. Сводная таблица функций Имя функции Назначение 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 |