Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ida.final.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
6 Mб
Скачать

Функции #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 ; Attributes: bp-based frame

seg000:0006

seg000:0006 sub_0_6 proc near ; CODE XREF: PROGRAM+14p

seg000:0006 push bp

seg000:0007 mov bp, sp

seg000:0009 xor ax, ax

seg000:000B call @__StackCheck$q4Word ; Stack overflow check (AX)

seg000:0010 mov di, offset unk_E1_166

seg000:0013 push ds

seg000:0014 push di

seg000:0015 mov di, offset asc_0_0 ; "\x05Hello"

seg000:0018 push cs

seg000:0019 push di

seg000:001A xor ax, ax

seg000:001C push ax

seg000:001D call @Write$qm4Textm6String4Word ; Write(var f; s: String; width:

seg000:0022 call @WriteLn$qm4Text ; WriteLn(var f: Text)

seg000:0027 call @__IOCheck$qv ; Exit if error

seg000:002C pop bp

seg000:002D retn

seg000:002D sub_0_6 endp

seg000:002D

seg000:002E assume ss:seg004

seg000:002E PROGRAM proc near

seg000:002E call @__SystemInit$qv ; __SystemInit(void)

seg000:0033 call sub_5_D

seg000:0038 push bp

seg000:0039 mov bp, sp

seg000:003B xor ax, ax

seg000:003D call @__StackCheck$q4Word ; Stack overflow check (AX)

seg000:0042 call sub_0_6

seg000:0045 pop bp

seg000:0046 xor ax, ax

seg000:0048 call @Halt$q4Word ; Halt(Word)

seg000:0048 PROGRAM endp

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

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

seg000:0006 sub_0_6 proc near

seg000:0006 push bp

seg000:0007 mov bp, sp

seg000:0027 call @__IOCheck$qv

seg000:002C pop bp

seg000:002D retn

Поэтому при дизассемблировании принято не акцентировать внимания на подобных различиях и говорить о подпрограммах.

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

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

При этом в ассемблерном листинге функции оформляются в виде процедур.

seg000:0006 sub_0_6 proc near

seg000:002D sub_0_6 endp

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]