Анимация
JavaScript


Главная  Библионтека 

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

#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:002E PROGRAM

proc

near

seg000:002E

call

@ SystemInit$qv ;

SystemInit(void)

seg000:0033

call

sub 5 D

seg000:0038

push

seg000:0039

bp, sp

seg000:003B

ax, ax

seg000:003D

call

@ StackCheck$q4Word ; Stack

overflow check (AX)

seg000:0042

call

sub 0 6

seg000:0045

seg000:0046

ax, ax

seg000:0048

call

@Halt$q4Word ; Halt(Word)

seg000:0048 PROGRAM

endp

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

Код, сгенерированный компилятором одинаково хорошо похож, как на процедуру, так и на функцию:

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