КулЛиб - Классная библиотека! Скачать книги бесплатно 

Образ мышления – дизассемблер IDA Pro. Том I [Крис Касперски] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
ОБРАЗ МЫШЛЕНИЯ – ДИЗАССЕМБЛЕР
IDA Pro
ТОМ I
ОПИСАНИЕ ФУНКЦИЙ ВСТРОЕННОГО ЯЗЫКА
IDA Pro
Аннотация
Подробный справочник по функциям встроенного языка, интерфейсу и архитектуре
дизассемблера IDA Pro 4.01 с уточнением особенностей младших версий.
Показывает приемы эффективного использования IDA Pro для исследования
зашифрованного кода, π-кода, самомодифицирующегося кода и кода, защищенного
антиотладочными приемами.
Ориентирован на системных программистов средней и высокой квалификации в
совершенстве владеющих языком ассемблера микропроцессоров серии Intel 80x86 и
работающих с операционными системами фирмы Microsoft.

Введение. Об этой книге
Цель этой книги – частично компенсировать информационный голод, окутывающий
один из популярнейших дизассемблеров современности – IDA Pro. Сведения,
содержащиеся в документации, подавляемой вместе с этим продуктом, весьма
отрывочные и безнадежно устаревшие.
Самостоятельное же освоение IDA Pro требует значительных усилий, длительного
времени и постоянных консультаций с ее разработчиками. Появлению этой книги
предшествовал большой объем работы, проделанной автором. Первоначально когда
замышлялось написать книгу, обобщающую достижения современного реинженеринга,
планировалось скоромное издание страниц максимум в пятьсот, но в течение работы над
проектом выяснилось: описание одних лишь функций встроенного языка IDA Pro
значительно превышает этот объем. Поэтому весь материал пришлось разбить на три
тома – «Описание функций встроенного языка IDA Pro», «Приемы эффективной работы с
IDA Pro» и «Технологии дизассемблирования».
Функции ядра IDA находят применение не только во встроенном языке, – сама IDA
активно использует их для дизассемблирования, а большинство пунктов меню
эквивалентны соответствующим функциям ядра. Поэтому, любую операцию можно
выполнить не только последовательными нажатиями «горячих» клавиш, но и
совокупностью команд встроенного языка.
Эта книга так же рассказывает и об архитектуре, затрагивая вопросы внутреннего
устройства IDA Pro без понимания которых полноценная работа с дизассемблера
невозможна.
ОБРАЩЕНИЕ АВТОРА К ЧИТАТЕЛЮ: когда писались первые строки этой
книги ее автор еще не обладал тем опытом, который необходим для
написания справочной литературы подобного типа. В результате, из-под пера
вылезло нечто ужасное, и все пришлось переписывать заново.
К сожалению, сроки издания нельзя бесконечно оттягивать (читатели
нервничают, издатель сердится) и в том издании, что вы держите сейчас в
руках, «доведены до ума» лишь десять первых глав из двадцати, а
остальные даны в первозданном варианте.
1

Автор просит читателя извинить его за такую ситуацию и, положа руку
на сердце, обещает, что в следующем издании (если только одно будет это
следующее издание – это ж от читателей зависит) все огрехи будут
исправлены.
Крис Касперски.
февраль 2001
Серверный Кавказ.
Версии IDA Pro
Дизассемблер IDA Pro относится к интенсивно развивающимся продуктам, –
постоянные совершенствования и бесконечные изменения, вносимые разработчиками,
породили множество версий, из которых наибольшее распространение получили 3.84,
3,84b, 3,85, 4.0, а некоторые до сих пор предпочитают использовать IDA 3.6.
К сожалению, даже близкие версии плохо совместимы между собой – прототипы и
поведение функций встроенного языка находится в постоянном изменении, затрудняя
создание переносимых скриптов.
Приводимый в книге материал в основном рассчитан на IDA Pro 4.0.1, но в ряде
случаев оговариваются особенности поведения и других версий дизассемблера.
Существуют три различных пакета поставки дизассемблера – стандартная
(IDA Pro Standard), прогрессивная (IDA Pro Advanced) и демонстрационная (IDA Pro
Demo). Отличие между IDA Pro Standard и IDA Pro Advanced заключается в количестве
поддерживаемых процессоров, полный перечень которых можно найти в прилагаемой к
IDA документации. Демонстрационный пакет представляет собой усеченный вариант
полнофункционального продукта и обладает рядом существенных ограничений: никакие
процессоры кроме семейства Intel 80x86 и типы файлов за исключением win32 PE не
поддерживаются; в поставку входят сигнатуры всего лишь двух компиляторов –
Microsoft Visual C++ 6.0 и Borland C++ Builder; функция сохранения результатов работы
заблокирована, а максимальное время продолжительности одного сеанса работы с
дизассемблером ограничено.
В каждый пакет поставки (за исключением демонстрационного) входят две
ипостаси – одна графическая под Windows-32 (в дальнейшем обозначаемая как IDAG) и
три консольных для MS-DOS, OS/2 и Windows-32. В демонстрационный пакет входит
лишь одна графическая ипостась. Обе ипостаси обладают сходными функциональными
возможностями, поэтому в книге будет описана лишь одна из них, скомпилированная для
исполнения в среде Windows-32 (в дальнейшем обозначаемая как IDAW)
Покупка IDA Pro Standard или IDA Pro Advanced дает право на бесплатное
приобретение IDA SDK (Software Development Kit) – программного пакета, позволяющего
пользователю самостоятельно разрабатывать процессорные модули, файловые
загрузчики и плагины (внешние самостоятельные модули). Это неограниченно расширяет
возможности IDA Pro, позволяя анализировать любой код, для какого бы микропроцессора
и операционной системы он ни предназначался. SDK различных версий не совместимы
друг с другом и созданные пользователем модули могут не работать в другой версии
IDA Pro, поэтому, прежде чем переходить на очередную версию IDA Pro, настоятельно
рекомендуется убедиться в сохранении работоспособности всех скриптов, модулей и
плагинов и т.д.
Рисунок 1 ”ida.console.view” Так выглядит консольная ипостась IDA Pro 4.01
Рисунок 2 “ida.gui.view” Так выглядит графическая ипостась IDA Pro 4.01
Рисунок 3 “ida.gui.view.4.14.bmp” Так выглядит графическая ипостась IDA Pro 4.14
Demo
2

Кратное введение в дизассемблирование
Одним из способов изучения программ в отсутствии исходных текстов является
дизассемблирование, – перевод двоичных кодов процессора в удобочитаемые
мнемонические инструкции. С перового взгляда кажется: ничего сложного в такой операции
нет, и один дизассемблер не будет сильно хуже любого другого. На самом же деле,
ассемблирование – однонаправленный процесс с потерями, поэтому автоматическое
восстановление исходного текста невозможно.
Одна из фундаментальных проблем дизассемблирования заключается в
синтаксической неотличимости констант от адресов памяти (сегментов и смещений).
Потребность распознавания смещений объясняется необходимостью замены конкретных
адресов на метки, действительное смещение которых определяется на этапе
ассемблирования программы.
Сказанное можно проиллюстрировать следующим примером: рассмотрим
исходную программу (a). При ассемблировании смещение строки s0, загружаемое в
регистр s0 заменяется его конкретным значением, в данном случае равным 108h, отчего,
команда “MOV DX, offset s0” приобретает вид “MOV DX, 108h”. Это влечет за собой потерю
информации – теперь уже нельзя однозначно утверждать как выглядел исходный текст, т.к.
ассемблирование “…offset s0” и “…108h” дает одинаковый результат, т.е. функция
ассемблирования не инъективна 1.
Если все машинные инструкции исходного файла, перевести в соответствующие им
символьные мнемоники (назовем такую операцию простым синтаксическим
дизассемблированием), в результате получится (b). Легко видеть – программа сохраняет
работоспособность лишь до тех пор, пока выводимая строка располагается по адресу
108h. Если модификация кода программы (c) нарушает такое равновесие, на экране
вместо ожидаемого приветствия появляется мусор – теперь выводимая строка находится
по адресу 0x10C, но в регистр DX по прежнему загружается прежнее значение ее
смещения – 0x108 (d).
mov
mov
int
ret
s0

ah,9
dx, offset s0
21h

Æ

DB 'Hello,World!',0Dh,0Ah,'$'

(а) Исходная программа

mov
ah,9
mov
dx,0108h
int
21h
ret
s0 DB 'Hello,World!',0Dh,0Ah,'$'

Æ

(b) Дизассемблированная программа
:0100 start
:0100
:0102
:0105
:0107
:0109
:010B
:010B aHelloWorld
:010C end

mov
ah,09
mov
dx,0108h
int
21h
xor
ax,ax
int
16h
ret
s0 DB 'Hello,World!',0Dh,0Ah,'$'
(с) Модифицированная программа

proc
near
mov
ah, 9
mov
dx, 108h ─┐
int
21h

xor
ax, ax

int
16h ◄────┘
retn
db 'Hello,World!',0Dh,0Ah,'$'
start

(d) Неработоспособный результат

Аналогичная проблема возникает и переводе с одного языка на другой – фраза
«это ключ» в зависимости от ситуации может быть переведена и как “this is key”, и “this is
clue”, и “this is switch”… Для правильного перевода мало простого словаря подстрочечника, необходимо еще понимать о чем идет речь, т.е. осмысливать
переводимый текст.
Человек легко может определить, что содержимое регистра DX в данном случае
1

Функция f(x) = y называется инъективной, если уравнение f(y) = x, имеет только один
корень и, соответственно, наоборот.
3

является именно смещением, а не чем ни будь иным, поскольку, его ожидает функция 0x9
прерывания 0x21. Но дизассемблеру для успешной работы мало знать одних прототипов
системных и библиотечных функций, – он должен еще уметь отслеживать содержимое
регистров, а, следовательно, «понимать» команды микропроцессора. Создание такого
дизассемблера (часто называемого контекстным) очень сложная инженерная задача,
тесно граничащая с искусственным интеллектом, которая на сегодняшний день еще никем
не решена. Существует более или менее удачные разработки, но ни одна из них не
способна генерировать 100%-работоспособные листинги.
Например, путь в исходной программе имелся фрагмент (а), загружающий в
регистр AX смещение начала таблицы, а в регистр BX индекс требуемого элемента.
Ассемблер, заменяя оба значения константами (b), создает неразрешимую задачу, – легко
видеть, что один из регистров содержит смещение, а другой индекс, но как узнать какой
именно?
MOV
AX,offset Table
MOV
BX,200h ; Index
ADD
AX,BX
Æ
MOV
AX,[BX]
(а) Исходная программа

BB 00 02
01 D8
8B 07
Æ
B8 10 00
(b) Машинный код

MOV
AX,0010
MOV
BX,0200
ADD
AX,BX
MOV
AX,Word ptr [BX]
(c) Дизассемблированный текст

Другая фундаментальная проблема заключается в невозможности определения
границ инструкций синтаксическим дизассемблером. Путь в исходной программе (a)
имелась директива выравнивания кода по адресам кратным четырем, тогда ассемблер (b)
вставит в этом месте несколько произвольных символов (как правило нулей), а
дизассемблер «не зная» об этом, примет их за часть инструкций, в результате чего
сгенерирует ни на что ни годный листинг (c).
JMP Label
Align 4
Label: XOR AX,AX
RET
(а) Исходная программа
00:
03:
05:
(c)

E9 01 00
jmp
00 33
add
C0 C3
rol
Дизассемблированный

00:
03:
04:
06:
(b)

E90100
00
33 C0
C3
Машинный код

04
[bp][di],dh
bl,-070;
текст

Контекстные дизассемблеры частично позволяют этого избежать, поскольку,
способны распознавать типовые способы передачи управления, но если программист
использует регистровые переходы, дизассемблеру придется эмулировать выполнение
программы, для определения значений регистров в каждой точке программы. Это не
только технически сложная, но и ресурсоемкая задача, решение которой еще предстоит
найти.
Дизассемблирование – творческий процесс, развивающий интуицию и абстрактное
мышление, возможно, даже особый вид искусства, позволяющего каждому проявить свою
индивидуальность. На сегодняшний день не существует ни одного полностью
автоматического дизассемблера, способного генерировать безупречно работоспособный
листинг и доводить полученный ими результат до готовности приходится человеку. Таким
образом, встает вопрос о механизмах взаимодействия человека с дизассемблером.
По типу реализации интерфейса взаимодействия с пользователем, существующие
дизассемблеры можно разделить на две категории – автономные и интерактивные.
Автономные дизассемблеры требуют от пользования задания всех указаний до начала
дизассемблирования и не позволяют вмешиваться непосредственно в сам процесс. Если
же конечный результат окажется неудовлетворительным, пользователь либо вручную
правит полученный листинг, либо указывает дизассемблеру на его ошибки и повторяет всю
4

процедуру вновь и вновь, порой десятки раз! Такой способ общения человека с
дизассемблером непроизводителен и неудобен, но его легче запрограммировать.
Интерактивные
дизассемблеры
обладают
развитым
пользовательским
интерфейсом, благодаря которому приобретают значительную гибкость, позволяя
человеку «вручную» управлять разбором программы, помогая автоматическому
анализатору там, где ему самому не справится – отличать адреса от констант, определять
границы инструкций и т.д.
Примером автономного дизассемблера является SOURCER, а интерактивного –
IDA. Преимущество SOURCER-а заключается в простоте управления, в то время как
работа с IDA требует высокой квалификации и навыков системного программирования.
Неопытные пользователи часто предпочитают SOURCER, лидирующий среди других
дизассемблеров, на небольших проектах. Но он очень плохо справляется с анализом
большого, порядка нескольких мегабайт, заковыристого файла, а с шифрованным или
π-кодом не справляется вообще! И тогда на помощь приходит IDA, которая, будучи
виртуальной программируемой машиной, может абсолютно все – стоит лишь разработать
и ввести соответствующий скрипт. А как это сделать и рассказывает настоящая книга.
Первые шаги с IDA Pro
С легкой руки Дениса Ричи повелось начинать освоение нового языка
программирования с создания простейшей программы “Hello, World!”, -- и здесь не будет
нарушена эта традиция. Оценим возможности IDA Pro следующим примером (для
совместимости
с
книгой
рекомендуется
откомпилировать
его
с
помощью
Microsoft Visual C++ 6.0 вызовом “cl.exe first.cpp” в командной строке):
#include
void main()
{
coutВерсия OS:
5.0
>Билд:
2195
>Количество агрументов:
1
>
Агрумент
01:
CRt0.demo
>Количество переменных окружения:
30
>
Переменная 29:
windir=C:\WINNT
>...
b) результат работы программы CRt0.demo.c
Очевидно, нет никакой необходимости анализировать стандартный стартовый код
2

А может и чуточку раньше
6

приложения, и первая задача исследователя – найти место передачи управления на
функцию main. К сожалению, гарантированное решение это задачи требует полного
анализа содержимого функции “Start”. У исследователей существует множество хитростей,
но все они базируются на особенностях реализации конкретных компиляторов 3 и не могут
считаться универсальными.
Рекомендуется изучить исходные тексты стартовых функций популярных
компиляторов, находящиеся в файлах CRt0.c (Microsoft Visual C) и c0w.asm (Borland C) –
это упросит анализ дизассемблерного листинга.
Ниже, в качестве иллюстрации, приводится содержимое стартового кода
программы “first.exe”, полученное в результате работы W32Dasm:
//******************** Program Entry Point ********
:00401B2C 55
push ebp
:00401B2D 8BEC
mov ebp, esp
:00401B2F 6AFF
push FFFFFFFF
:00401B31 6870714000
push 00407170
:00401B36 68A8374000
push 004037A8
:00401B3B 64A100000000
mov eax, dword ptr fs:[00000000]
:00401B41 50
push eax
:00401B42 64892500000000
mov dword ptr fs:[00000000], esp
:00401B49 83EC10
sub esp, 00000010
:00401B4C 53
push ebx
:00401B4D 56
push esi
:00401B4E 57
push edi
:00401B4F 8965E8
mov dword ptr [ebp-18], esp
Reference
|
:00401B52
:00401B58
:00401B5A
:00401B5C
:00401B62
:00401B64
:00401B6A
:00401B70
:00401B73
:00401B75
:00401B7B
:00401B7E
:00401B83
:00401B85
:00401B8A
:00401B8B
:00401B8D
:00401B8F
:00401B91
:00401B96

To: KERNEL32.GetVersion, Ord:0174h
FF1504704000
33D2
8AD4
8915B0874000
8BC8
81E1FF000000
890DAC874000
C1E108
03CA
890DA8874000
C1E810
A3A4874000
6A00
E8D91B0000
59
85C0
7508
6A1C
E89A000000
59

Call dword ptr [00407004]
xor edx, edx
mov dl, ah
mov dword ptr [004087B0],
mov ecx, eax
and ecx, 000000FF
mov dword ptr [004087AC],
shl ecx, 08
add ecx, edx
mov dword ptr [004087A8],
shr eax, 10
mov dword ptr [004087A4],
push 00000000
call 00403763
pop ecx
test eax, eax
jne 00401B97
push 0000001C
call 00401C30
pop ecx

edx
ecx
ecx
eax

Referenced by a (U)nconditional or (C)onditional Jump at Address:
3

Например, Microsoft Visual C всегда, независимо от прототипа функции main передает ей
три аргумента – указатель на массив указателей переменных окружения, указатель на
массив указателей аргументов командной строки и количество аргументов командной
строки, а все остальные функции стартового кода принимают меньшее количество
аргументов
7

|:00401B8D(C)
|
:00401B97 8365FC00
:00401B9B E8D70C0000

and dword ptr [ebp-04], 00000000
call 00402877

Reference To: KERNEL32.GetCommandLineA, Ord:00CAh
|
:00401BA0 FF1560704000
Call dword ptr [00407060]
:00401BA6 A3E49C4000
mov dword ptr [00409CE4], eax
:00401BAB E8811A0000
call 00403631
:00401BB0 A388874000
mov dword ptr [00408788], eax
:00401BB5 E82A180000
call 004033E4
:00401BBA E86C170000
call 0040332B
:00401BBF E8E1140000
call 004030A5
:00401BC4 A1C0874000
mov eax, dword ptr [004087C0]
:00401BC9 A3C4874000
mov dword ptr [004087C4], eax
:00401BCE 50
push eax
:00401BCF FF35B8874000
push dword ptr [004087B8]
:00401BD5 FF35B4874000
push dword ptr [004087B4]
:00401BDB E820F4FFFF
call 00401000
:00401BE0 83C40C
add esp, 0000000C
:00401BE3 8945E4
mov dword ptr [ebp-1C], eax
:00401BE6 50
push eax
:00401BE7 E8E6140000
call 004030D2
:00401BEC 8B45EC
mov eax, dword ptr [ebp-14]
:00401BEF 8B08
mov ecx, dword ptr [eax]
:00401BF1 8B09
mov ecx, dword ptr [ecx]
:00401BF3 894DE0
mov dword ptr [ebp-20], ecx
:00401BF6 50
push eax
:00401BF7 51
push ecx
:00401BF8 E8AA150000
call 004031A7
:00401BFD 59
pop ecx
:00401BFE 59
pop ecx
:00401BFF C3
ret
a) стартовый код программы “first.exe”, полученный дизассемблером W32Dasm
Иначе выглядит результат работы IDA, умеющей распознавать библиотечные
функции по их сигнатурам (приблизительно по такому же алгоритму работает множество
антивирусов). Поэтому, способности дизассемблера тесно связаны с его версией и
полнотой комплекта поставки – далеко не все версии IDA Pro в состоянии работать с
программами,
сгенерированными
современными
компиляторами.
(Перечень
поддерживаемых компиляторов можно найти в файле “%IDA%/SIG/list”).
00401B2C
00401B2C
00401B2C
00401B2C
00401B2C
00401B2C
00401B2C
00401B2C
00401B2C
00401B2D
00401B2F
00401B31
00401B36
00401B3B

start

proc near

var_20
var_1C
var_18
var_14
var_4

=
=
=
=
=

dword
dword
dword
dword
dword

push
mov
push
push
push
mov

ptr
ptr
ptr
ptr
ptr

-20h
-1Ch
-18h
-14h
-4

ebp
ebp, esp
0FFFFFFFFh
offset stru_407170
offset __except_handler3
eax, large fs:0
8

00401B41
push
eax
00401B42
mov
large fs:0, esp
00401B49
sub
esp, 10h
00401B4C
push
ebx
00401B4D
push
esi
00401B4E
push
edi
00401B4F
mov
[ebp+var_18], esp
00401B52
call
ds:GetVersion
00401B58
xor
edx, edx
00401B5A
mov
dl, ah
00401B5C
mov
dword_4087B0, edx
00401B62
mov
ecx, eax
00401B64
and
ecx, 0FFh
00401B6A
mov
dword_4087AC, ecx
00401B70
shl
ecx, 8
00401B73
add
ecx, edx
00401B75
mov
dword_4087A8, ecx
00401B7B
shr
eax, 10h
00401B7E
mov
dword_4087A4, eax
00401B83
push
0
00401B85
call
__heap_init
00401B8A
pop
ecx
00401B8B
test
eax, eax
00401B8D
jnz
short loc_401B97
00401B8F
push
1Ch
00401B91
call
sub_401C30
; _fast_error_exit
00401B96
pop
ecx
00401B97
00401B97 loc_401B97:
; CODE XREF: start+61↑j
00401B97
and
[ebp+var_4], 0
00401B9B
call
__ioinit
00401BA0
call
ds:GetCommandLineA
00401BA6
mov
dword_409CE4, eax
00401BAB
call
___crtGetEnvironmentStringsA
00401BB0
mov
dword_408788, eax
00401BB5
call
__setargv
00401BBA
call
__setenvp
00401BBF
call
__cinit
00401BC4
mov
eax, dword_4087C0
00401BC9
mov
dword_4087C4, eax
00401BCE
push
eax
00401BCF
push
dword_4087B8
00401BD5
push
dword_4087B4
00401BDB
call
sub_401000
00401BE0
add
esp, 0Ch
00401BE3
mov
[ebp+var_1C], eax
00401BE6
push
eax
00401BE7
call
_exit
00401BEC ; -----------------------------------------------------00401BEC
00401BEC loc_401BEC:
; DATA XREF: _rdata:00407170↓o
00401BEC
mov
eax, [ebp-14h]
00401BEF
mov
ecx, [eax]
00401BF1
mov
ecx, [ecx]
00401BF3
mov
[ebp-20h], ecx
00401BF6
push
eax
9

00401BF7
push
ecx
00401BF8
call
__XcptFilter
00401BFD
pop
ecx
00401BFE
pop
ecx
00401BFF
retn
00401BFF start
endp ; sp = -34h
b) стартовый код программы “first.exe”, полученный дизассемблером IDA Pro 4.01
С приведенным примером IDA Pro успешно справляется, о чем свидетельствует
стока “Using FLIRT signature: VC v2.0/4.x/5.0 runtime” в окне сообщений
Рисунок 7 "0x003" Загрузка библиотеки сигнатур
Дизассемблер сумел определить имена всех функций вызываемых стартовым
кодом, за исключением одной, расположенной по адресу 0х0401BDB. Учитывая передачу
трех аргументов и обращение к _exit, после возращения функцией управления, можно
предположить, что это main и есть.
Перейти по адресу 0x0401000 для изучения содержимого функции main можно
несколькими способами – прокрутить экран с помощью стрелок управления курсором,
нажать клавишу и ввести требуемый адрес в появившемся окне диалога, но проще и
быстрее всего воспользоваться встроенной в IDA Pro системой навигации. Если подвести
курсор в границы имени, константы или выражения и нажать , IDA автоматически
перейдет на требуемый адрес.
В данном случае требуется подвести к строке “sub_401000” (аргументу команды
call) и нажать на , если все сделано правильно, экран дизассемблера должен
выглядеть следующим образом:
00401000
00401000
00401000
00401000
00401000
00401000
00401001
00401003
00401008
0040100D
00401012
00401013
00401013

; -------------- S U B R O U T I N E ---------------------; Attributes: bp-based frame
sub_401000 proc near
; CODE XREF: start+AF↓p
push
ebp
mov
ebp, esp
push
offset aHelloSailor ; "Hello, Sailor!\n"
mov
ecx, offset dword_408748
call ??6ostream@@QAEAAV0@PBD@Z ; ostream::operator%d\n",long(s0)+x);

auto x,s0;x=1;s0=”3h”;
Message(">%d\n",x+long(s0));

>4

>4

На этот раз от перемены мест слагаемых сумма не изменяется!
Директивы
IDA
подде рживает
пр е проц ессо ра :






с ле дую щие

с та н да р тн ые

директивы

#define
#undef
#include
#error
#ifdef\#ifndef\#else\#endif
Внимание: консоль не поддерживает никаких директив препроцессора – их
можно использовать только в IDC-файлах.
Замечание: для использования определений, констант, приводимых в этой книге
по ходу описания функций, в исходный код скрипта необходимо включить
директиву “#inclide ”.
Вызов функций возможен и без включения файла в исходный
листинг – это необходимо только для использования определений, например,
таких, как BADADDR.
До вызова консоли IDA самостоятельно подключает к ней этот файл,
делая доступными все определения.

Предписания
IDA поддерживает следующие стандартные конструкции, изменяющие нормальный ход
выполнения программы:





if, else;
for;
while, do, break, continue;
return

Замечание: цикл “for (expr1; expr2; expr3 ) statement” в отличие от стандартного
языка Си не поддерживает более одного счетчика.
Математические и битовые операторы
Поддерживаются следующие математические операторы – сложение: “+”,
вычитание: “-“, умножение: “*”, деление “/”, приращение на единицу “++”. Операторы
“+=” и “-=” не поддерживаются.
27

Поддерживаются следующие битовые операторы – битовое И: “&”, битовое ИЛИ:”|”,
битовое НЕТ: “!”, битовое ИЛИ-ИСКЛЮЧАЮЩЕЕ-И:”^”.
Массивы
Встроенной языковой поддержки массивов, наподобие той, что присутствует в Си, у
IDA нет.
ВИРТУАЛЬНАЯ ПАМЯТЬ
Архитектура виртуальной памяти
В отличии от многих других дизассемблеров, IDA работает не с исследуемым
файлом, а с его образом, загруженным в виртуальную память. IDA эмулирует загрузчик
операционной системы, благодаря чему образ загруженного в дизассемблер файла
идентичен образу того же файла, загруженного операционной системой.
IDA использует плоскую 32-разрядную модель виртуальной памяти, предоставляя в
распоряжение пользователя немногим менее четырех гигабайт адресного пространства.
Наибольший возможный адрес равен 0xFF000000 и для удобства определен в файле
через константу MAXADDR. Попытка задания больших адресов приведет к
ошибке.
Адресное пространство виртуальной памяти может быть разбито на один или
более сегментов (см. главу «Сегменты и селекторы»), однако, подавляющее большинство
встроенных функций IDA ожидают не сегментных, а линейных адресов.
Адресное пространство виртуальной памяти не непрерывно – в нем могут
присутствовать «выключенные» адреса, попытка обращения к которым приведет к ошибке.
IDA не предоставляет функций, позволяющих манипулировать «включением» «выключением» адресов. Эта операция может быть выполнена только косвенно – адреса
«включаются» при загрузке бинарного файла и создании нового сегмента, а выключаются
при его удалении (см. описание функции SegCreate).
С каждым «включенным» адресом виртуальной памяти связана специальная
структура данных, включающая в себя 8-разрядную ячейку виртуальной памяти и 24разрядное поле атрибутов этой ячейки.
Атрибуты, в частности, указывают следует ли отображать ячейку как инструкцию
или как данные, в каком виде следует представлять операнды и т.д. Назначение каждого
бита атрибутов подробно описано в главах «Элементы», «Типы элементов», «Операнды» и
«Объекты». Поле атрибутов доступно не только для косвенного (посредством вызова
встроенных в IDA Функций), но и непосредственного чтения и изменения. Однако,
разработчики IDA настоятельно рекомендуют избегать непосредственной модификации,
поскольку, назначение тех или иных битов атрибутов в последующих версиях
дизассемблера может измениться!
Ячейка может иметь как инициализированное, так и неинициализированное
значение. Попытка чтения неинициализированной ячейки возвращает ошибку. Определить
инициализирована ячейка или нет можно по состоянию младшего бита поля атрибутов –
если ячейка инициализирована он равен одному, а если неинициализированная – нулю.
32-разрядное целое, содержащее ячейку и связанные с ней атрибуты, называется
флагом виртуальной памяти (см. рис. 13).
32

16
атрибуты

8

0
ячейка

Рисунок 13 Строение флага виртуальной памяти
28

Флаги виртуальной памяти хранятся в виртуальном массиве, подробнее об
организации которого рассказано в главе «Виртуальные массивы».
Сами же виртуальные массивы хранятся в страничной физической памяти.
Совокупность страниц физической страничной памяти в документации и контекстовой
помощи IDA называется виртуальной памятью. Такая терминологическая путаница требует
постоянного уточнения о чем собственно идет речь – образе загруженного файла или
виртуальном массиве, поэтому, во избежание двусмысленного понимания, здесь и далее
память, хранящая виртуальные массивы, будет называется страничной, а память,
хранящая образ загруженного файла – виртуальной.
Технические детали:
Виртуальное адресное пространство представляет собой совокупность индексов
всех существующих элементов виртуального массива, а «выключенные» адреса являются
отсутствующими индексами виртуального массива.
Виртуальный массив располагается в файле *.id1, формат заголовка которого
приведен в таблице 1.
смещение
0x0
0x4
0x6
0x8
0xC
0x10
+0x4
+0x4
+0x4
...

длина
0x4
0x2 (Word)
0x2 (Word)
0x4 (long)
0x4 (long)
0x4 (long)
0x4 (long)
0x4 (long)
0x4 (long)
...

значение
сигнатура “Va4” – “Virtual Array version 4”
количество непрерывных регионов памяти
количество страниц всего (одна страница равна 4 кб)
индекс (он же линейный адрес) первого кванта региона
индекс (он же линейный адрес) последнего кванта региона
смещение индекса первого кванта региона в файле *.id1
индекс (он же линейный адрес) первого кванта следующего региона
индекс (он же линейный адрес) последнего кванта региона
смещение индекса первого кванта следующего региона в файле *.id1


Таблица 1 структура файла *.id1
Если открыть файл “tutor.id1” в любом шестнадцатеричном редакторе (внимание,
это необходимо сделать до выхода из IDA, т.к. при выходе он будет автоматически
удален), его начало должно выглядеть так:
00000000: 56 61 34 00 02 00 03 00 │ 66 06 01 00 78 06 01 00
00000010: 98 39 00 00 77 07 01 00 │ 89 07 01 00 DC 5D 00 00

Va4 ☻ ♥ f♠☺ x♠☺
Ш9 w•☺ Й•☺ ▄]

??? #Художнику – используя файл 0x019_o выделить указанные ниже элементы
графически (кружочком) со стрелочкой, указывающей на его отношение к нижеследующему
описанию, причем левую часть (т.е. указание самого значения поля, отделенную
двоеточием) затем удалить.
“Va4”:
00 02:
0x10777 и 0x10788
00 03:
4 кб).
66 06 01 00:
78 06 01 00:
98 39 00 00:
памяти в этом файле

сигнатура, позволяющая распознать тип файла
два непрерывных адресных пространства 0x10666 – 0x10677 и
три страницы виртуальной памяти израсходовано (размер страницы
начальный адрес первого непрерывного регион виртуальной памяти
конечный адрес первого непрерывного региона виртуальной памяти
смещение, по которому хранится первый регион виртуальной

29

77 07 01 00:
89 07 01 00:
DC 5D 00 00:
в этом файле

начальный адрес второго непрерывного регион виртуальной памяти
конечный адрес второго непрерывного региона виртуальной памяти
смещение, по которому хранится второй регион виртуальной памяти

По смещению 0x003998 (помним об обратном порядке байтов) в файле “tutor.id1”
находится следующее содержимое:
00003998:
000039A8:
000039B8:
000039C8:
000039D8:

68
4F
64
52
0D

21
01
01
01
01

00
00
00
00
00

00│45
00│0C
00│61
00│4F
00│FF

01
01
01
01
01

00
00
00
00
00

00│4C
00│00
00│00
00│21
00│00

01
01
01
01
00

00
00
00
00
00

00│4C
00│69
00│70
00│20
00│00

01
01
01
01
00

00
00
00
00
00

00
00
00
00
00

H!
o☺
D☺
r☺
♪☺

e☺ l☺ l☺
,☺ ☺ I☺
A☺ ☺ P☺
o☺ !☺ ☺


Это и есть флаги виртуальной памяти, содержащие, ячейки и атрибуты
виртуальной памяти, читая которые через каждый четвертый байт, можно разобрать “Hello,
IDA Pro!”
Архитектура страничной памяти
Размещение виртуальной памяти в дисковом файле требует некоторого количества
оперативной памяти компьютера под кэш-буфера, поскольку побайтовый обмен с диском
крайне непроизводителен.
Кэш IDA имеет страничную организацию. Вся виртуальная память разбита на
множество блоков одинакового размера, называемых страницами. Если происходит
обращение к одной ячейке виртуальной памяти, страница, в которой эта ячейка
расположена, загружается в оперативную память целиком. Соответственно, модификация
одной ячейки виртуальной памяти влечет перезапись всей страницы.
Разбивка на страницы осуществляет маскированием n младших битов целевых
адресов, по этой причине размер страницы всегда представляет собой степень двойки.
Если размер региона, выделенный под кэш буфер (так же называемый окном), превышает
размер одной страницы, IDA может загружать в оперативную память несколько страниц
одновременно. Соответственно размер окна должен быть кратен размеру страницы.
Если окно целиком заполнено, но требуется загрузить еще одну страницу, IDA
просматривает буфер на предмет поиска не модифицированной страницы к которой
дольше всего не было обращения. А отыскав такую, замещает ее прочитанной с диска. В
противном случае (если все страницы со времени последней загрузки были
модифицированы), IDA «сбрасывает» на диск самую старую страницу и только затем
замещает ее новой.
Во избежание потерь информации при сбоях питания, зависаниях дизассемблера и
т.д., предусмотрен автоматический «сброс» модифицированных страниц через
определенные промежутки времени, длительность которых задается значением поля
“AUTOSAVE” файла и - консольной и графической версий
соответственно. По умолчанию буфера сохраняются после совершения пользователем ста
любых действий или по истечении пяти минут, при этом в окне сообщений появится
поясняющая надпись «Flushing buffers, please wait...ok»
Замечание:

IDA не учитывает «популярность» страницы (т.е. частоту ее использования или
количество обращений), а только время последнего обращения (чтения или записи).

30

??? #Художнику – перерисовать рисунок!
Рисунок 14 Окно страничной памяти
Увеличение размера страниц увеличивает вероятность того, что очередной
запрашиваемый байт окажется уже загруженным в оперативную память, но в то же время,
повышает накладные расходы на сброс модифицированных страниц и уменьшает
количество страниц в окне. Поскольку, размер окна сверху ограничен объемом доступной
оперативной памяти, возникает задача вычисления оптимального соотношения между
размер и количестве страниц.
Размеры и количество страниц задаются полями “VPAGES” и “VPAGESIZE” файла
соответственно. Если VPAGES == 0, IDA пытается самостоятельно вычислить
оптимальное значение, исходя из количества требующейся для анализа загруженного
файла виртуальной памяти.
Поскольку каждый флаг виртуальной памяти требует четыре байта страничной
памяти (т.к. помимо 8 бит содержимого ячейки хранит 24 бита атрибутов), грубо оценить
потребности страничной памяти можно умножением размера загружаемого файла на
четыре. Разумеется, в зависимости от формата файла истинное значение может
значительно отличается от расчетного и предсказанная оценка окажется неверной. Это не
нарушит работоспособности дизассемблера, поскольку недостающая память будет
выделена автоматически по мере необходимости, однако, неоптимальное соотношение
размера и количества страниц ухудшат производительность, поэтому, в некоторых случаях
его выгоднее вычислять вручную.
По умолчанию размер страницы (VPAGESSIZE) в зависимости от версии IDA равен
либо 4096 либо 8192 байт, а, поскольку, количество страниц выражается 16-разрядным
целым числом, доступное адресное пространство виртуальной памяти равно 64 и 128
мегабайт соответственно. Дальнейшее увеличение размера страниц расширяет границы
доступного адресного пространства виртуальной памяти, но одновременно с этим
ухудшает производительность за счет большей грануляции, поэтому, прибегать к нему
рекомендуется в тех и только в тех случаях, когда требуется свыше 128 мегабайт
виртуальной памяти.
Ввиду станичной адресации базы, IDA не позволяет «на лету» изменять размер
страниц и при загрузке файлов *.idb величина размера страниц берется оттуда, а значения
поля VPAGESIZE игнорируется. Поэтому, необходимо внимательно отнестись к выбору
размера страницы – в дальнейшем изменить его не удастся! Существует возможность
необратимо уничтожить результаты своей работы, выбрав слишком малый размер страниц
– в этом случае адресного пространства виртуальной памяти может не хватить и попытка
выделения очередного блока виртуальной памяти провалится, а сеанс работы с IDA
аварийно завершится!

31

Поле VPAGES, задающие размер буфера окна в страницах, по умолчанию равно
нулю, и его значение IDA самостоятельно вычисляет по следующему алгоритму (см.
таблицу 2).
??? #Верстальщику CreateNewTable
Размер файла
0 КБ -- 255 КБ
256 КБ – 1023 КБ
1024 КБ – 2559 КБ
2560 КБ – 10 МБ
> 10 МБ

Размер окна в страницах
(FILESIZE * 4) / VPAGESIZE
1048576 / VPAGESIZE
FILESIZE / VPAGESIZE
4194304 / VPAGESIZE
(FILESIZE
*
2)
/
(VPAGESIZE *5)

Размер окна в байтах
1 МБ
1 МБ
1 - 2,5 МБ
4 МБ
> 4 МБ

Таблица 2 Алгоритм автоматического выделения памяти
Важно понять – размер буфера окна не ограничивает количество доступной
страничной памяти! Окно – всего лишь кэш-буфер, и IDA будет работать даже в том
случае, если уменьшить его до размера одной страницы (правда, это чрезвычайно снизит
производительность). Операционные системы Windows и OS/2 позволяют выделить под
окно больше памяти, чем ее имеется в наличии, сбрасывая избыток на диск в файл
подкачки. В результате количество обращений к диску удваивается и производительность
дизассемблера резко падает. Рекомендуется выбирать размер окна таким образом, чтобы
он полностью умещался в физической памяти компьютера. Значения, вычисляемые IDA по
умолчанию, рассчитаны на компьютеры, обладающее 16 или менее мегабайтами
оперативной памяти, при наличие же большего его количества (на конец 2000 года
компьютер типичной конфигурации содержит 32-64 мегабайт оперативной памяти) можно
значительно улучить производительности, увеличив размер буфера окна.
Помимо виртуальной памяти, содержащей образ загруженного фала,
дизассемблеру требуется какое-то количество страничной памяти для хранения меток,
имен функций, комментариев и т.д. В терминологии IDA такая память именуется
DATEBASE_MEMORY или Memory for b-tree – память базы данных двоичного дерева.
Поле “DATEBASE_MEMORY” конфигурационного файла позволяет изменять
выделенное количество памяти под буфер базы данных. Объем памяти измеряется в
байтах, но округляется до целого числа страниц, размер которых в текущих версиях IDA
равен 8 килобайт (8.192 байт). Для нормальной работы дизассемблеру необходимо по
крайней мере 5 страниц (40 килобайт), в противном случае IDA сообщит о нехватке памяти
«bTree error: not enough memory» и аварийно завершит работу.
Если DATEBASE_MEMORY = 0, IDA самостоятельно определяет оптимальный
размер буфера по следующему алгоритму (см. таблицу 3):
Размер файла
0 – 256 КБ
256 КБ – 1 МБ
1 МБ – 2.5 МБ
2.5 МБ – 5 МБ
> 5 МБ

Размер окна в страницах
5
128
128 – 320
512
FILESIZE / 20 / PAGESIZE

Размер окна в байтах
256 КБ
1 МБ
1 МБ – 2.5 МБ
4 МБ
FILESIZE / 20

Таблица 3 Алгоритм автоматического определения размера окна
С целью увеличения производительности, IDA динамически создает список
указателей на имена меток, для работы с которым так же требуется некоторое количество
страничной памяти. Размер буфера в страницах задается значением поля NPAGES файла
32

, а размер страницы значением поля NPAGESSIZE. По умолчанию IDA
резервирует 64 страницы, объемом 1024 байт (1 КБ). Каждый указатель занимает 4 байта
страничной памяти, следовательно, 64-кб буфер вмещает свыше 16 тысяч имен. Следует
отметить, при дизассемблировании программ, написанных на Delphi, IDA генерирует
огромное количество имен и увеличение значение поля NPAGES может существенно
улучшить производительность.
При загрузке файла в окне сообщений выдается отчет о выделении страничной
памяти под нужды IDA: окно виртуальной памяти (“allocating memory for virtual array”),
буфер двоичного дерева (“allocating memory for b-tree”) и буфер указателей на имена
(“allocating memory for name pointers”).
Например, выделение памяти при загрузке файла “first.exe” в IDA 3.84 происходит
следующим образом:
bytes pages size description
--------- ----- ---- -------------------------------------------262144
32 8192 allocating memory for b-tree...
65536
16 4096 allocating memory for virtual array...
65536
64 1024 allocating memory for name pointers...
----------------------------------------------------------------Рисунок 15 "Отчет о выделении памяти при загрузке IDA"

Взаимодействие с физической памятью
Взаимодействие с физической памятью становится возможным благодаря наличию
четырех недокументированных функций _peek, _poke, _lpoke и _call, прототипы которых
приведены ниже:





long _poke(long ea, long value)
long _lpoke(long ea, long value)
long _peek(long ea, long value)
long _call(long ea)

Функции _poke и _lpoke записывают байт и длинное целое value по линейному
адресу ea физической памяти соответственно, возвращая прежнее значение ячейки.
Функция _peek читает байт по линейному адресу ea физической памяти, а функция _call
передает управление на машинный код, расположенный по линейному адресу ea
физической памяти.
Следует отметить, указанные функции по-разному функционируют в различных
версиях IDA и результат их выполнения может быть непредсказуем, поэтому, их
использование не рекомендуется.
Пример, приведенный ниже, демонстрирует копирование содержимое ПЗУ
компьютера в виртуальную память дизассемблера, с последующим анализом обработчика
прерывания INT 0x13. Ввиду различной реализации функций низкоуровневой работы с
памятью, его успешная работа гарантируется лишь при запуске из MS-DOS-версии IDA Pro.
Операционная система Windows 9x эмулирует наличие ПЗУ и позволяет функции _peek
обращаться к нему даже из 32-разрядных версий IDA Pro.
auto a;
SegCreate(0xF0000,0xFFFFF,0x0F000,0,0,0);
Message("Ждите... читаю BIOS...");
for (a=0;a tutor.bin”.
╔═[■] Load Binary or User-Defined Format file ════╗



File name: F:\IDAN\SRC\1\tutor.bin




(•) Binary file




Loading segment 0x1000
▐↓▌ (in paragraphs)║

Loading offset 0x666
▐↓▌




Processor: metapc



Change processor ▄

▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀


Analysis options ▄


▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀


[ ] Create segments




OK ▄
Cancel ▄
F1 - Help ▄


▀▀▀▀
▀▀▀▀▀▀▀▀
▀▀▀▀▀▀▀▀▀▀▀

╚═════════════════════════════════════════════════╝
Рисунок 16 Диалог загрузки бинарного файла
Дождавшись появления диалога загрузки консольной версии IDA Pro, запомните
базовый адрес сегмента, записанный в поле “Loading segment ... (in paragraphs)”, измените
адрес загрузки на любой отличный от нуля (нулевое смещение крайне ненаглядно для
иллюстрации), скажем, на 0x666, и сбросьте флажок “Create segment” – для
предотвращения автоматического создания сегмента (работа с сегментами будет
рассмотрена позже, в главе «Сегменты и селекторы»).
Замечание:

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

34

После окончания загрузки файла экран дизассемблера должен выглядеть
следующим образом:
0:00010666 ; File Name :
0:00010666 ; Format
:
0:00010666 ; Base Address:
0:00010666
0:00010666
0:00010666
0:00010667
0:00010668
0:00010669
0:0001066A
0:0001066B
0:0001066C
0:0001066D
0:0001066E
0:0001066F
0:00010670
0:00010671
0:00010672
0:00010673
0:00010674
0:00010675
0:00010676
0:00010677
0:00010677
0:00010677

F:\IDAN\SRC\1\tutor.bin
Binary File
1000h Range: 10666h - 10678h Loaded length: 0012h
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
20h
0Dh
0Ah

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

end

Слева каждой строки указывается ее линейный адрес, причем адрес первого байта
равен 0x1000*0x10+0x666, т.е. сумме базового адреса, указанного при загрузке,
умноженного на шестнадцать и адреса смещения.
Чтение ячеек виртуальной памяти осуществляется функциями – Byte(long ea),
Word(long ea) и Dword(long ea) – возвращающими байт, слово и двойное слово
соответственно. Если запрошенная ячейка не существует или не инициализирована,
функция возвращает 0xFF (при этом следует быть особенно осторожным с функциями
Word и Dword, некорректно сигнализирующих об ошибке – подробнее об этом можно
прочитать в под главах Word и Dword соответственно).
Поэтому, перед чтением ячейки памяти следует убедиться, что она есть и содержит
какое-то значение. Это можно осуществить анализом младшего бита поля атрибутов –
если он сброшен – ячейка отсутствует или не инициализирована. Получить содержимое
поля атрибутов можно вызовом функции GetFlags (см. описание функции GetFlags)
следующим образом:
if(MS_VAL & GetFlags(ea))
// значение ячейки определенно, можно читать;
else
// значение ячейки не определено либо ячейка не существует;
…или же воспользоваться макросом hasValue(F), определенным в ,
который следует вызывать так:
if(hasValue(GetFlags(ea)))
// значение ячейки определенно, можно читать;
else
// значение ячейки не определено либо ячейка не существует
Более короткий путь предоставляет макрос isLoaded(ea), определенный там же с
аналогичным назначением:

35

if(isLoaded(ea))
// значение ячейки определенно, можно читать;
else
// значение ячейки не определено либо ячейка не существует”
Замечание: макрос byteValue(F), определенный в файле , при правильном
употреблении позволяет сократить количество вызов GetFlags, а,
следовательно, увеличить производительность программы. Вызывать его
следует так:
F = GetFlags(ea);
if (hasValue(F)) val = byteValue(F);
Использование функции Byte обычно требует двух вызов GetFlags –один
раз для проверки значения ячейки, второй – для ее чтения. Если же ячейка
заведомо существует и гарантировано содержит инициализированное значение,
проверку можно опустить – в этом случае макрос byteValue не будет иметь
никаких преимуществ перед функцией Byte.
Пример использования:
auto a;
Message(“>”);
for (a=0x10666;aHello, IDA Pro!
Модификация ячеек виртуальной памяти осуществляется функциями PatchByte
(long ea, long value), PatchWord (long ea, long value) и PatchDword (long ea, long value)
записывающих байт, слово и двойное слово соответственно.
Попытка модификации несуществующей ячейки виртуальной памяти заканчивается
провалом.
С ле дую щий
прим ер
ме няет
р ег ис тр
всех
с имв о ло в
на
п р о ти в о полож н ый :
0:00010666
0:00010667
0:00010668
0:00010669
0:0001066A
0:0001066B
0:0001066C
0:0001066D
0:0001066E
0:0001066F
0:00010670
0:00010671
0:00010672
0:00010673
0:00010674
0:00010675
0:00010676
0:00010677

a) исходные
противоположный

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

данные

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
20h
0Dh
0Ah



;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

требуется

заменить

регистр

всех

символов

на

auto a;
for(a=0x10666;a0x20)
Message("'%c'",Byte(a));
Message("\n");
}
a) скрипт, демонстрирующий трассировку адресов
0:00010666○db
0:00010667○db
0:00010668○db
0:00010669○db
0:0001066A○db
0:0001066B○db
0:0001066C○db
0:0001066D○db
0:0001066E○db
0:0001066F○db
0:00010670○db
0:00010671○db
0:00010672○db
0:00010673○db
0:00010674○db

68;'h'
45;'E'
4c;'L'
4c;'L'
4f;'O'
c;
0;
69;'i'
64;'d'
61;'a'
0;
70;'p'
52;'R'
4f;'O'
21;'!'
37

0:00010675○db 20;
0:00010676○db d;
0:00010677○db ff;' '
b) результат – отображены только существующие адреса
Сводная таблица функций
??? #Верстальщику ChangeTable
функции возвращающие значение ячейки виртуальной памяти
имя функции
краткое описание
long Byte(long ea)
возвращает содержимое ячейки виртуальной
памяти, расположенной по адресу ea
long Word(long ea)
возвращает содержимое ячеек виртуальной
памяти, расположенных по адресам ea и ea+1,
располагая их в младшем и старшем байте
машинного слова соответственно.
long Dword(long ea)
возвращает содержимое ячеек виртуальной
памяти, расположенных по адресам ea, ea+1, ea+2
и ea+3, располагая их в младших и старших
байтах
младшего
и
старшего
слова
соответственно
функции модифицирующие значение ячейки виртуальной памяти
имя функции
краткое описание
void PatchByte(long ea,long записывает в ячейку виртуальной памяти,
value)
расположенную по адресу ea, значение value
void PatchWord(long ea,long записывает в ячейки виртуальной памяти,
value)
расположенные по адресам ea и ea+1, младший
и старший байт значения value соответственно
void PatchDword(long ea,long записывает в ячейки виртуальной памяти,
value)
расположенные по адресам ea, ea+1, ea+2 и
ea+3 младшие и старшие байты младшего и
старшего слова соответственно
функции трассирующие адреса виртуальной памяти
имя функции
краткое описание
long NextAddr(long ea)
возвращает следующий линейный адрес, если
он существует, в противном случае - ошибку
long PrevAddr(long ea)
возвращает предыдущий линейный адрес, если
он существует, в противном случае – ошибку
функции поиска
имя функции
краткое описание
long FindBinary(long ea,long
flag,char str)
функции, манипулирующие с флагами
имя функции
краткое описание
long GetFlags (long ea)
возращает значение флагов виртуальной
памяти
long SetFlags(long ea, long задает новые значения флагов виртуальной
flags)
памяти
long Byte (long ea)
Функция возвращает значение байта виртуальной памяти, расположенного по
38

адресу ea. Если указанный адрес не существует, функция возвращает 0хFF, сигнализируя
об ошибке, такое же значение возвратится и в том случае если ячейка имеет не
инициализированное значение.
Поэтому, до вызова функции Byte следует убедиться, что ячейка действительно
существует и имеет определенное значение. Проверить это можно, проанализировав
младший бит поля атрибутов, определенный в файле константой FF_INV, - если
он не равен нулю, то все о’кей:
if (FF_INV & GetFlags(ea))) value=Byte(ea);
else // ячейка не существует или имеет неиницилизированное значение
В файле определены два макроса hasValue(F) и isLoaded(ea),
выполняющие такие проверки. Макрос hasValue(F) ожидает флаг виртуальной памяти, а
isLoaded(ea) линейный адрес ячейки, т.е.:
if(hasValue(GetFlags(ea))) value=Byte(ea);
else //ячейка не существует или имеет неиницилизированное значение
if(isLoaded(ea)) value=Byte(ea);
else //ячейка не существует или имеет неиницилизированное значение
Альтернативой функции Byte служит вызов GetFlags с последующей маскировкой
старших 24-бит, содержащих поле атрибутов. Маской может служить определенная в
файле константа MS_VAL или непосредственное значение – 0xFF. Это может
выглядеть так: value = (MS_VAL & GetFlags(ea)).
Для увеличения производительности скрипта можно использовать макрос
byteValue(F), определенный в файле , передавая ему значение флагов, ранее
полученное для выполнения проверки существования ячейки:”F=GetFlags(ea); if
(hasValue(F)) value=byteValue(F);” – это позволяет избавиться от вызова функции
Byte, экономя тем самым некоторое количество процессорного времени.
Замечание:

если читаемые ячейки заведомо существуют и гарантированно содержат
инициализированные значения, в дополнительных проверках никакой необходимости нет и
использование макроса byteValue будет ничуть не быстрее вызова функции Byte

Пример использования:
0:00010000
db 48h ; H
0:00010001
db 65h ; e
0:00010002
db 6Ch ; l
0:00010003
db 6Ch ; l
0:00010004
db 6Fh ; o
0:00010005
db 2Ch ; ,
0:00010006
db 20h ;
0:00010007
db 49h ; I
0:00010008
db 44h ; D
0:00010009
db 41h ; A
0:0001000A
db 20h ;
0:0001000B
db 50h ; P
0:0001000C
db 72h ; r
0:0001000D
db 6Fh ; o
0:0001000E
db 21h ; !
0:0001000F
db 20h ;
0:00010010
db 0Dh ;
0:00010011
db 0Ah ;
a) исходные данные – требуется вывести на консоль содержимое отображаемых
39

ячеек памяти
auto a;
Message(“>”);
for (a=0x10000;a Hello, IDA Pro!
c) результат
Другие примеры использования функции Byte можно найти в главе «Первые шаги с
IDA Pro» и в файле “memcpy.idc”, входящим в комплект поставки IDA.
??? #Верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес ячейки виртуальной памяти
=return пояснения
== содержимое байта ячейки виртуальной памяти
==0xFF ошибка

Родственные функции: Word, Dword
Интерактивный аналог: нет
long Word (long ea)
Функция возвращает значение слова (одно слово равно двум байтам) виртуальной
памяти, расположенного по адресу ea. При попытке чтения байта, расположенного по
несуществующему адресу, равно как и имеющего неопределенное значение, функция
возвращает значение 0xFF, сигнализируя об ошибке. Наглядно продемонстрировать
работу функции позволяет следующий пример:
0:00010000
0:00010001
0:00010002
0:00010003
0:00010004
0:00010005
0:00010006
0:00010007
0:00010008
0:00010009
0:0001000A
0:0001000B
0:0001000C
0:0001000D
0:0001000E
0:0001000F
0:00010010
0:00010011

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
20h
0Dh
0Ah

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

Message(“>%X\n”, Word (0x10000));
40

>6548
Message(“>%X\n”, Word (0x0));
>FFFF
Message(“>%X\n”, Word (0x10011));
>FF0A
Message(“>%X\n”, Word (0xFFFF));
>48FF
В первом случае существуют оба адреса (т.е. 0x10000 и 0x10001) и функция
отрабатывает успешно; во втором – ни существует ни одного из них – ни 0x0, ни 0x1, в
результате чего возвращается 0xFFFF.
Но попытка прочитать слово, расположенное по адресу 0x10011, приводит к тому,
что в младшем байте возвращается значение соответствующей ячейки, а в старшем –
0xFF! Аналогично и в последнем примере – несуществующий младший байт дает 0xFF, в
то время как старший читается успешно.
Поэтому, до вызова функции Word следует убедиться, что обе ячейка
действительно существуют и имеют определенные значение. Проверить это можно,
проанализировав младший бит поля атрибутов каждой из ячеек – если он не равен нулю,
то все о’кей. О том как это сделать подробно рассказано в описании функции Byte.
??? #верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес ячейки виртуальной памяти
==return пояснения
== содержимое слова виртуальной памяти
==FF?? | ==??FF ошибка

Родственные функции: Byte, Dword.
Интерактивный аналог: нет
long Dword (long ea)
Функция возвращает значение двойного слова виртуальной памяти по указанному
адресу.
В остальном она полностью аналогична функции Word.
??? #верстальщику – change table
аргумент
ea
return

пояснение
линейный адрес ячейки виртуальной памяти
==return Пояснения
содержимое двойного слова виртуальной памяти
==(FF) ошибка

Родственные функции: Byte, Word
Интерактивный аналог: нет

41

void PatchByte (long ea, long value)
Функция модифицирует содержимое байта виртуальной памяти, расположенного
по линейному адресу ea, на значение value.
По замыслу разработчика предназначалась для падченья программы (например,
замене 7x на EB, т.е. инструкций условного перехода на безусловный переход – операция
часто сопутствующая снятию защит), чем и объясняется ее название. Однако, она нашла
применение в решении широкого круга различных задач, в частности копировании
фрагментов виртуальной памяти.
Функция не позволяет модифицировать не существующие ячейки памяти и не
сигнализирует об ошибках записи, поэтому, перед ее вызовом рекомендуется проверить
передаваемый ей линейный адрес на существование вызовом GetFlags (подробнее об
этом рассказывается в описании функции Byte).
Пример ее использования можно найти в файле “memcpy.idc”, поставляемом
вместе с IDA.
??? #верстальщику – change table
аргумент
ea
value

пояснение
линейный адрес ячейки виртуальной памяти
Записываемое значение (байт)

Родственные функции: PatchWord, PatchDword
Интекративный аналог: «~EDIT\Patch program\Change byte»
void PatchWord (long ea,long value)
Функция модифицирует содержимое слова виртуальной памяти, расположенного
по адресу ea на значение value. В остальном аналогичена PatchByte (см. описание
PathByte).
??? #верстальщику – change table
аргумент
ea
value

пояснения
линейный адрес ячейки виртуальной памяти
Записывамое значение (слово)

Родственные функции: PatchByte, PatchDword
Интерактивный аналог: «~EDIT\ Patch program\Change word»
void PatchDword (long ea,long value)
Функция модифицирует содержимое двойного слова виртуальной памяти,
расположенного по адресу ea на значение value. В остальном аналогична PatchByte (см.
описание PatchByte)
??? #верстальщику – change table
аргумент

пояснения
42

ea
value

линейный адрес ячейки виртуальной памяти
Записывамое значение (слово)

Родственные функции: PatchByte, PatchWord
Интерактивный аналог: нет
long NextAddr (long ea)
Функция возвращает следующий существующий виртуальный адрес, и BADADDR в
том случае, если такого адреса не существует. Вызов NextAddr (BADADDR) равносилен
NextAddr (0x0).
Пример использования:
0:00010000
db 48h ; H
0:00010001
db 65h ; e
0:00010002
db 6Ch ; l
0:00010003
db 6Ch ; l
0:00010004
db 6Fh ; o
0:00010005
db 2Ch ; ,
0:00010006
db 20h ;
0:00010007
db 49h ; I
0:00010008
db 44h ; D
0:00010009
db 41h ; A
0:0001000A
db 20h ;
0:0001000B
db 50h ; P
0:0001000C
db 72h ; r
0:0001000D
db 6Fh ; o
0:0001000E
db 21h ; !
0:0001000F
db 20h ;
0:00010010
db 0Dh ;
0:00010011
db 0Ah ;
a) исходные данные – требуется получить список адресов виртуальной памяти
auto a;
a=0;
while(1)
{
a=NextAddr(a);
if (a==BADADDR) break;
Message(">%x\n",a);
}
b) трассировка адресов последовательными вызовами функции NextAddr
>10000
>10001
>10002
>10003
>10004
>10005
>10006
>10007
>10008
>10009
>1000a
43

>1000b
>1000c
>1000d
>1000e
>1000f
>10010
>10011
с) результат – получение перечня существующих адресов виртуальной памяти
??? #верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес ячейки виртуальной памяти
=return пояснения
!=BADADDR следующий за ea адрес виртуальной памяти
==BADADDR ошибка

Родственные функции: PrevAddr
Интерактивный аналог: нет
long PrevAddr (long ea)
Функция возвращает предшествующий ea существующий виртуальный адрес, и
BADADDR в том случае, если такого адреса не существует.
Пример использования:
0:00010000
db 48h ; H
0:00010001
db 65h ; e
0:00010002
db 6Ch ; l
0:00010003
db 6Ch ; l
0:00010004
db 6Fh ; o
0:00010005
db 2Ch ; ,
0:00010006
db 20h ;
0:00010007
db 49h ; I
0:00010008
db 44h ; D
0:00010009
db 41h ; A
0:0001000A
db 20h ;
0:0001000B
db 50h ; P
0:0001000C
db 72h ; r
0:0001000D
db 6Fh ; o
0:0001000E
db 21h ; !
0:0001000F
db 20h ;
0:00010010
db 0Dh ;
0:00010011
db 0Ah ;
a) исходные данные – требуется получить список адресов виртуальной памяти
auto a;
a=BADADDR;
while(1)
{
a=PrevAddr(a);
if (a==BADADDR) break;
Message(">%X\n",a);
44

}
b) трассировка адресов последовательными вызовами функции PrevAddr
>10011
>10010
>1000F
>1000E
>1000D
>1000C
>1000B
>1000A
>10009
>10008
>10007
>10006
>10005
>10004
>10003
>10002
>10001
>10000
с) результат – получение перечня существующих адресов виртуальной памяти
??? #верстальщику – change table
аргумент
ea
return

пояснение
линейный адрес ячейки виртуальной памяти
=return пояснения
!=BADADDR предшествующий ea адрес виртуальной памяти
==BADADDR ошибка

Родственные функции: NextAddr
Интерактивный аналог: нет
long GetFlags(long ea)
Функция возвращает значение флагов виртуальной памяти, ассоциированных с
виртуальным адресом ea. Если указанного виртуального адреса не существует, функция
возвращает ноль.
О назначении каждого бита флагов рассказано при описании связанных с ним
функций.
??? #верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес ячейки виртуальной памяти
=return Пояснения
!=0 значение флагов
==0 ошибка

Родственные функции: SetFlags
Интерактивный аналог: нет

45

void SetFlags(long ea)
Функция задает новое значение флагов виртуальной памяти, ассоциированных с
виртуальным адресом ea. Допустимо модифицировать флаги лишь существующей ячейки
виртуальной памяти.
Внимание: настоятельно рекомендуется по возможности избегать
непосредственной модификации флагов – допущенная ошибка может привести к
зависанию дизассемблера!
??? #верстальщику – change table
аргумент
ea

пояснения
линейный адрес ячейки виртуальной памяти

Родственные функции: GetFlags
Интерактивный аналог: нет
long FindBinary(long ea,long flag,char str)
Функция ищет заданную подстроку в виртуальной памяти и в случае успешного
поиска возвращает ее линейный адрес, иначе возвращает значение BADADDR,
сигнализируя об ошибке.
В зависимости от флага направления поиск может идти как вперед (от младших
адресов к старшим), так и назад (от старших адресов к младшим), регистр символов может
как различаться, так и нет.
Аргумент ea задает линейный адрес начала поиска и не обязательно должен
существовать.
Аргумент str задает подстроку поиска, выраженную в шестнадцатеричных кодах
символов (точнее – в системе исчисления, установленной системой исчисления по
умолчанию), разделенных между собой пробелами. Суффикс “h”, равно как и префикс “x”
при этом указывать не нужно.
Аргумент flag задает направление поиска и определяет чувствительность к
регистру символов: если его младший бит установлен поиск идет от младших адресов к
старшим и, соответственно, наоборот; если первый справа бит (считая от нуля) установлен
– прописные и строчечные буквы различаются и, соответственно, наоборот.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007
seg000:0008
seg000:0009
seg000:000A
seg000:000B
seg000:000C
seg000:000D
seg000:000E
seg000:000F

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
0

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

46

Message(">%s\n",atoa(FindBinary(
41")));

SegByName("seg000"),1,"49 44

??? #верстальщику – change table
аргумент
ea

flag

return

пояснения
линейный адрес начала поиска
=flag пояснения
бит
#
0 поиск от старших адресов к младшим
0
1 поиск от младших адресов к старшим
0 не различать регистр символов
1
1 различать регистр символов
=return Пояснения
!=BADADDR линейный адрес найденной подстроки
==BADADDR ошибка

Родственные функции: нет
Интерактивный аналог: “~Search\Text”,
СЕГМЕНТЫ И СЕЛЕКТОРЫ
#Definition
Сегментом называется непрерывная область памяти, адресуемая относительно
базового адреса сегмента.
Каждый сегмент характеризуется базовым адресом сегмента, адресом начала
сегмента и адресом конца сегмента.
Базовый адрес сегмента обычно выражается в параграфах, адреса начала и конца
- в байтах.
Адрес начала сегмента задает наименьший адрес, принадлежащей сегменту; адрес
конца сегмента – адрес, на единицу больше превышающий наибольший адрес,
принадлежащий сегменту.
Никакой линейный адрес не может принадлежать более чем одному сегменту
одновременно – т.е. сегменты не могут пересекаться.
В дальнейшем, если не оговорено обратное, адрес начала сегмента обозначается
“startea”, адрес конца сегмента – “endea”, а базовый адрес – “BASE”.
Смещение первого байта в сегменте обозначается “startoffset” и связано с адресом
начала и базовым адресом следующим соотношением:

startoffset = startea – BASE * 0x10

Формула 1 Смещение первого байта в сегменте
Смещения в сегменте измеряются целыми неотрицательными числами,
следовательно, приравняв startoffset к нулю, получаем: startea ≥ (BASE* 0x10).
Сегментный адрес [BASE:offset] связан с линейным адресом следующим
соотношением:
47

ea = BASE * 0x10 + offset

Формула 2 Перевод сегментного адреса в линейный
Подавляющее большинство функций IDA работают не с сегментными, а с
линейными адресами. Пользователь же, напротив, видит на экране дизассемблера в
основном сегментные адреса, а линейные от его взора скрыты.
Для облегчения преобразования сегментных адресов в линейные предусмотрен
специальный макрос MK_FP(long BASE, long offset), возвращающий значение
BASE*0x10+offset, а так же оператор «квадратные скобки» - “[BASE, offset]” аналогичного
назначения.
Линейные адреса начала и конца сегмента представляют собой 32-битовые
значения, ограничивающие максимальный размер сегмента четырьмя гигабайтами.
Базовый адрес представляет собой 16-битовое значение, ограничивающее
адресуемую память одним мегабайтом, которого в ряде случаев оказывается
недостаточно.
Замечание: размер сегмента ограничен разрядностью линейных адресов его
начала и конца и составляет 4 гигабайта, но выбор адреса начала сегмента,
первый байт которого имеет нулевое смещение, ограничен разрядностью
базового адреса, и равен BASEmax* 0x10 = 0xFFFF * 0x10 = 0xFFFF0, т.е. немногим
менее одного мегабайта.
Выход состоит в использовании селекторов, ссылающихся на 32-разрядные
адреса, что позволяет адресовать с их помощью до 4 гигабайт.
К селектору можно обратится по его индексу в таблице селекторов. Индексы
представляют собой 16-разрядные целые значения, увеличивающиеся с каждым
очередным элементом на единицу.
Элементы таблицы – 32-разрядные базовые адреса сегмента, измеряемые в
параграфах.
Таблица селекторов представляет собой разряженный массив, допуская создание
элементов с несмежными индексами. Например, 0x5,0x07,0x16,0x88…
Если при создании сегмента в качестве базового адреса указать индекс созданного
ранее селектора, его значение будет автоматически использовано для базирования
данного сегмента. Аналогично, если создать селектор, совпадающий с базовым адресом
некоторого сегмента, для его базирования станет использоваться значение селектора, а не
базовый адрес.
С каждым сегментом связан ряд атрибутов – имя сегмента, кратность
выравнивания, разрядность и объединение. Никакой из атрибутов, включая имя,
уникальной характеристикой сегмента не является и вполне допустимо существование
двух и более сегментов с одинаковыми именами (однако, ассемблеры не смогут
откомпилировать исходный тест, содержащий несколько одноименных сегментов).
Замечание: создание двух сегментов с одинаковыми базовыми адресами
допускается, но пользоваться этой возможностью категорически не рекомендуется.
Над каждым сегментом можно выполнять следующие операции – создание и
удаление сегментов, получение и изменение основных характеристик сегментов
(линейного адреса начала, линейного адреса конца, базового адреса), получение и
изменение атрибутов сегмента (имя, кратность выравнивания, разрядность и т.д.).

48

Подробнее об этом рассказано в главе «Функции, работающие с сегментами и
селекторами».
Навигатор по функциям
Для изучения организации сегментов и селекторов рекомендуется загрузить
полученный в главе «Виртуальная память» файл “tutor.idb” в дизассемблер, при этом экран
IDA должен выглядеть так:
0:00010000
0:00010001
0:00010002
0:00010003
0:00010004
0:00010005
0:00010006
0:00010007
0:00010008
0:00010009
0:0001000A
0:0001000B
0:0001000C
0:0001000D
0:0001000E
0:0001000F
0:00010010

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
20h
0Dh

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

Для создания нового сегмента можно воспользоваться вызовом функции
SegCreate(long startea,long endea,long base,long use32,long align,long comb),
последовательно передав ей адрес начала сегмента, адрес конца сегмента, базовый адрес
сегмента, разрядность сегмента, кратность выравнивания и атрибуты (о трех последних
аргументах подробно рассказано в описании функции SegCreate, сейчас их можно принять
равными нулю), например, так: «SegCreate(0x10000, 0x10012, 0x1000, 0, 0, 0);»
seg000:0000 ; Segment type: Regular
seg000:0000 seg000
segment at 1000h private '' use16
seg000:0000
assume cs:seg000
seg000:0000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:0000
db 48h ; H
seg000:0001
db 65h ; e
seg000:0002
db 6Ch ; l
seg000:0003
db 6Ch ; l
seg000:0004
db 6Fh ; o
seg000:0005
db 2Ch ; ,
seg000:0006
db 20h ;
seg000:0007
db 49h ; I
seg000:0008
db 44h ; D
seg000:0009
db 41h ; A
seg000:000A
db 20h ;
seg000:000B
db 50h ; P
seg000:000C
db 72h ; r
seg000:000D
db 6Fh ; o
seg000:000E
db 21h ; !
seg000:000F
db 20h ;
seg000:0010
db 0Dh ;
seg000:0011
db 0Ah ;
seg000:0011 seg000
ends

49

Создав новый сегмент, IDA автоматически присвоила ему имя “seg000”, где “000”
порядковый номер (считая от нуля) созданного сегмента. Последующие сегменты будут
названы “seg001”, “seg002” и т.д.
Функция “long SegByName(char segname)” позволяет узнать линейный адрес
базового адреса сегмента 4 по его имени. Ее вызов может выглядеть, например, так:
Message(“>%X\n”, SegByName(“seg000”));
> 10000
Переименовать сегмент можно с помощью функции “success SegRename(long ea,
char name)” принимающей в качестве первого аргумента любой линейный адрес,
принадлежащий указанному сегменту, а вторым – его новое имя. Например:
SegRename(SegByName("seg000"),"MySeg");
MySeg:0000 ; Segment type: Regular
MySeg:0000 MySeg
segment at 1000h private '' use16
MySeg:0000
assume cs:MySeg
MySeg:0000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
MySeg:0000
db 48h ; H
MySeg:0001
db 65h ; e
MySeg:0002
db 6Ch ; l
MySeg:0003
db 6Ch ; l
MySeg:0004
db 6Fh ; o
MySeg:0005
db 2Ch ; ,
MySeg:0006
db 20h ;
MySeg:0007
db 49h ; I
MySeg:0008
db 44h ; D
MySeg:0009
db 41h ; A
MySeg:000A
db 20h ;
MySeg:000B
db 50h ; P
MySeg:000C
db 72h ; r
MySeg:000D
db 6Fh ; o
MySeg:000E
db 21h ; !
MySeg:000F
db 20h ;
MySeg:0010
db 0Dh ;
MySeg:0011
db 0Ah ;
MySeg:0011 MySeg
ends

Для удаления сегментов предусмотрена функция “success SegDelete (long ea,
long disable)” Если флаг “disable” равен нулю, будет удален лишь сам сегмент, а
содержимое принадлежащих ему ячеек сохранится, в противном случае вместе с
сегментом будет удалены и все принадлежащие ему адреса виртуальной памяти.
Сравните:
SegDelete(0x10000, 0);
0:00010000
0:00010001
0:00010002
0:00010003
0:00010004
0:00010005
0:00010006
0:00010007
0:00010008
0:00010009
0:0001000A
0:0001000B
0:0001000C
0:0001000D

db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh

SegDelete(0x10000, 0);
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,

╔═[■]══════════════════════════ IDA view-A ══



↑ Пустой экран дизассемблера

I
D
A
P
r
o

4

Т.е. функция возвращает базовый адрес, тут же умножая его на 0x10 для перевода в
линейный.
50

0:0001000E
0:0001000F
0:00010010
0:00010011

db
db
db
db

21h
20h
0Dh
0Ah

;!
;
;
;

Описанные выше операции можно выполнять и интерактивно – с помощью горячих
клавиш и системы меню. Для последующих экспериментов потребуется перезагрузить
исследуемый файл “tutor.bin”, восстановив его с прилагаемого к книге диска, поскольку
удаление виртуальной памяти необратимо.
Для интерактивного создания сегмента достаточно в меню “View” выбрать пункт
“Segments” и дождавшись появления списка существующих сегментов (в данном случае –
пустого), нажать клавишу и надлежащим образом заполнить соответствующие
поля появившегося диалога. Если предварительно выделить некоторую область
курсорными клавишами, удерживая , IDA автоматически подставит линейные адреса
ее начала и конца в поля адреса начала и адреса конца сегмента соответственно (см.
рисунок 17)
Рисунок 17 “0x020” Создание сегмента по выделенной области
Если поле «Segment Name» оставить пустым, IDA присвоит имя сегменту
самостоятельно.
По умолчанию базовый адрес равен наибольшему возможному значению, т.е.
Startea
BASEdef = 0x10 , при этом offsetdef = Startea AND 0xF, где offsetdef -- смещение первого
байта в сегменте. Очевидно, что offsetdef ≤ 0xF.
Например, если дозагрузить бинарный файл (~File\Load file\Additional binary file),
скажем, Crypt.com, по виртуальному адресу 0x20100 и, выделив его, попытаться создать
сегмент, IDA по умолчанию выберет базовый адрес, равный 0x2010, в результате чего
смещение первого байта в сегменте будет равно 0x0, а не 0x100! Следует очень
внимательно относится к значением, предлагаемым по умолчанию – они не всегда
соответствуют требуемым.
В действительности, базовый адрес должен быть равен 0x2000, тогда после
создания сегмента экран дизассемблера будет выглядеть так:
╔═[■]════════════════════════════════ Program Segmentation ═══════════════════════════════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ seg000
00000000 00000012 byte 1000 pub
N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 ▒
║ seg001
00000100 0000013C byte 2000 pub
N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C ■


╚2/2
═════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

Рисунок 18
Для изменения свойств сегмента достаточно подвести к нему курсор и нажать
клавишу . Появится диалоговое окно следующего вида:
╔═[■]════════ Change segment attributes ═════════════╗




║ Segment name
seg001
▐↓▌

║ Segment class
▐↓▌

║ Start address
0x20100
▐↓▌

║ End
address
0x2013C
▐↓▌



║ (•) 16-bit segment
Combination ▄ (public)║
║ ( ) 32-bit segment
▀▀▀▀▀▀▀▀▀▀▀▀▀

51


Alignment ▄ (byte)

║ [X] Move adjacent segments
▀▀▀▀▀▀▀▀▀▀▀

║ [ ] Disable addresses






OK ▄
Cancel ▄
F1 - Help ▄


▀▀▀▀
▀▀▀▀▀▀▀▀
▀▀▀▀▀▀▀▀▀▀▀

╚════════════════════════════════════════════════════╝
Рисунок 19
Интерактивно можно изменять все характеристики и атрибуты сегмента за
исключением его базового адреса. На модификацию адресов начала и конца сегмента
наложены некоторые ограничения: (BASE * 0x10) ≤ Startea > Endea, т.е. адрес начала
должен быть больше либо равен базовому адресу, умноженному на шестнадцать, а адрес
конца сегмента должен быть по крайней мере на единицу превышать адрес начала.
При изменении границ одного из смежных сегментов, IDA автоматически расширит
или сузит другой сегмент, если флаг “Move adjacent segments” установлен. Графически это
можно изобразить так:
│xxxxx│
├─────┤Å seg000


│ ↑ │
│ │ │
├──╧──┤Å seg001




├─────┤
│xxxxx│

│xxxxx│
├─────┤Å seg000


├─────┤Å seg001








├─────┤
│xxxxx│

│xxxxx│
├─────┤Å seg000



┤Å «ничейная память»


├─────┤Å seg001




├─────┤
│xxxxx│

а)

б)

с)

Рисунок 20 Изменение границ сегментов
??? #Художнику – перерисовать, сохраняя наклон штриховки. Крайняя левая
картинка должна изображать как границу сегментов тянут вверх – можно нарисовать
привязанную нитку и поднимающую ее руку.
На рисунке (10.a) показано уменьшение нижней границы сегмента seg000, к концу
которого вплотную примыкает сегмент seg001. Если флажок “Move adjacent segment”
взведен, IDA автоматически изменит адрес начала сегмента “seg001” как показано на
рисунке 10.b; напротив сброс этого флажка приведет к тому, что между сегментами
образуется дыра «ничейной памяти», изображенная на рисунке 10.c
Установка флажка “Disable addresses” автоматически уничтожит «дыру», удалив
принадлежащие ей адреса памяти, вместе с их содержимым. Внимание! Эта операция
необратима и вернуть утерянные данные обратно уже не удаться!
Для закрепления всего вышесказанного рекомендуется провести несколько
простых экспериментов. Для начала попробуйте расшить границы сегмента “seg000” до
адреса 0x20120 (или любого другого, принадлежащего сегменту “seg001”). Одним лишь
изменением атрибутов сегмента “seg000” это сделать не удаться – IDA сообщит об ошибке
“set_segm_end(10000) -> 20120: areas overlap” и, независимо от состояния флага “Move
adjacent segment”, прервет выполнение операции.
Причина в том, что автоматическое изменение границ работает с смежными и
только с смежными сегментами, а “seg000” и “seg001” таковыми, очевидно, не являются,
52

поскольку адрес конца сегмента “seg000” равен 0x10012, а адрес начала “seg001” –
0x20100.
Выполнить поставленную задачу можно, по меньшей мере, двумя путями: первое –
предварительно увеличить адрес начала сегмента “seg001” и повторить операцию; второе
- увеличить адрес конца сегмента “seg000” до максимально возможного значения (т.е.
0x20100), добившись слияния сегментов, что даст возможность расширить “seg000” до
требуемого адреса установкой флага “Move adjacent segment”.
Последовательности нажатий клавиш для первого способа: переместить курсор
в пределы сегмента “seg001” и нажать , в поившемся диалоговом окне изменить
значение поля “Start address” на 0x20120; затем переместится в границы сегмента
“seg000”, нажать и изменить значение поля “End Address” на 0x20120”. Задача
выполнена.
Последовательности нажатий клавиш для второго способа: переместить курсор
в пределы сегмента “seg000” и нажать , в появившемся диалоговом окне изменить
значение поля “End address” на 0x20100, затем повторено вызвав тот же диалог,
распахнуть сегмент до требуемо адреса, изменив значение поля “End address” на 0x20120.
Задача выполнена.
Программно изменить атрибуты сегмента можно вызовами функций SegCreate,
SegBounds, SegRename, SegClass, SegAlign, SegComb, SegAddrng
Функция success SegBounds (long ea,long startea,long endea,long disable)
принимает в качестве первого аргумента любой адрес, принадлежащий сегменту, startea,
endea – новые адреса начала и конца сегмента соответственно, ненулевое значение флага
disable удаляет виртуальную память освободившуюся при уменьшении сегмента (при
расширение
сегмента
его
значение
игнорируется).
Следует
помнить,
что
startea ≥ BEGIN_ADDRES * 0x10, т.е. верхнее расширение сегмента жестко ограничено его
базовым адресом.
Например, для восстановления сегмента “seg000” до его прежних размеров можно
воспользоваться следующим кодом: “SegBounds(0x10000,0x10000,0x10012,1);”
Если базовый адрес создаваемого сегмента больше или равен 0x10000, IDA
автоматически создает селектор для его адресации.
Например, результат выполнения “SegCreate(0x100000,0x100100,0x10000,0,0,0);”
приведет к следующему:
╔═[■]═════════════════════════════════ Program Segmentation ═══════════════════════════════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ seg000
00000000 00000012 byte 1000 pub CODE N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 ▒
║ seg001
00000100 0000013C byte 2000 pub
N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C ■
║ seg002
00000000 00000100 at
0001 pub
N FFFF FFFF FFFF FFFF FFFF 00100000 00100100 ▼
╚3/3
══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

Базовый адрес сегмента “seg002” равен 0x1, но не смотря на это, смещение
первого байта в сегменте равно 0x0, а не 0x1000000 – 0x1 * 0x10 = 0xFFFFF0. Причина в
том, что 0x1 – это селектор, а не сегмент. Чтобы убедиться в этом достаточно просмотреть
список селекторов, вызвать который можно нажатием “,”:
╔═[■]═══════════ Selectors ══════════3═[↑]═╗
║ Sel Value

║ 0001 00010000





╚1/1
════════════════════════════─┘
В отличие от базового адреса, значение селектора легко изменить, для чего
достаточно выделить его курсором и нажать . Появится диалоговое окно
следующего содержания:
╔═[■]Define a selector ════╗
53




Selector 0x1
▐↓▌


Value
0x10000
▐↓▌║



OK ▄
Cancel ▄


▀▀▀▀
▀▀▀▀▀▀▀▀

╚══════════════════════════╝
Значение селектора может совпадать с базовым адресом любого из существующих
сегментов, на него не действует ограничение startea ≥ BASE_ADDRES * 0x10, но если
присвоить селектору значение численно равное его индексу, он будет немедленно удален.
Присвоение селектору базового адреса, превышающего адрес начала сегмента,
приводит к появлению байт с отрицательными смещениями, которые автоматически
дополняются до нуля, т.е. offset = NEG (|startea - SEL_VALUE|) .
Например, после увеличения значения селектора 0x1 на один параграф экран
дизассемблера будет выглядеть так:
seg002:FFFFFFF0 seg002
seg002:FFFFFFF0
seg002:FFFFFFF0
seg002:FFFFFFF0
seg002:FFFFFFF0
seg002:FFFFFFF1
seg002:FFFFFFF2
seg002:FFFFFFF3
seg002:FFFFFFF4
seg002:FFFFFFF5
seg002:FFFFFFF6
seg002:FFFFFFF7
seg002:FFFFFFF8
seg002:FFFFFFF9
seg002:FFFFFFFA
seg002:FFFFFFFB
seg002:FFFFFFFC
seg002:FFFFFFFD
seg002:FFFFFFFE
seg002:FFFFFFFF
seg002:0000
seg002:0001
seg002:0002
seg002:0003
seg002:0004
seg002:0005
seg002:0006
seg002:0007

db
db
db
db
db
db
db
db

segment at 10001h private '' use16
assume cs:seg002
;org 0FFFFFFF0h
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
db ? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored
? ; unexplored

Исправить ситуацию можно соответствующим увеличением адреса начала
сегмента.
После
вызова
«SegBounds(0x100000,0x100010,0x100100,1);»
экран
дизассемблера должен выглядеть так:
╔═[■]═════════════════════════════════ Program Segmentation ════════════════════════════════4═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ seg000
00000000 00000012 byte 1000 pub CODE N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 ▒
║ seg001
00000100 0000013C byte 2000 pub
N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C ■
║ seg002
00000000 000000F0 at
0001 pub
N FFFF FFFF FFFF FFFF FFFF 00100010 00100100 ▼
╚3/3
═══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

Смещение первого байта в сегменте “seg002” равно нулю, чего и требовалось
достичь.
Программно создать новый селектор или изменить значение уже существующего
селектора можно с помощью функции “void SetSelector (long sel,long value)”, где sel –
индекс селектора, а value – его значение, измеряемое в параграфах.

54

Например, вызов “SetSelector(0x1,0x10000);” изменит значение селектора 0x1 и
смещение первого байта в сегменте “seg002” будет равно нулю, а вызов
“SetSelector(0x4,0x500000);” создаст новый селектор с индексом 0x4 (индексы селекторов
не обязательно должны следовать друг за другом).
┌───────────────────────────────────── Program Segmentation ────────────────────────────────4─────┐

Name
Start
End Align Base Type Cls 32es ss ds fs gs

│ seg000
00000000 00000012 byte 1000 pub CODE N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 │
│ seg001
00000100 0000013C byte 2000 pub
N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C │
│ seg002
00000010 00000100 at
0001 pri
N FFFF FFFF FFFF FFFF FFFF 00100010 00100100 │
└3/3
────────────────────────────────────────────────────────────────────────────────────┘
╔═[■]══════════ Selectors ═════════5═[↑]═╗
║ Sel Value

║ 0001 00010000

║ 0004 00500000



╚2/2
══════════════════════════─┘

Удалить селектор можно вызовом функции “void DelSelector (long sel)” либо
присвоением ему собственного индекса – SetSelector (sel, sel).
Если удалить используемый селектор, то смещение первого байта в сегменте,
который на него ссылался станет равным: offset = startea - sel * 0x10 , где sel – индекс (не
значение!) селектора.
Например, после вызова “SelDelete(0x1);” экран дизассемблера будет выглядеть
так:
╔═[■]═════════════════════════════════ Program Segmentation ════════════════════════════════4═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ seg000
00000000 00000012 byte 1000 pub CODE N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 ▒
║ seg001
00000100 0000013C byte 2000 pub
N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C ■
║ seg002
00100000 001000F0 at
0001 pri
N FFFF FFFF FFFF FFFF FFFF 00100010 00100100 ▼
╚3/3
═══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

Рисунок 21
Операция удаления обратима и повторное создание уничтоженного селектора все
вернет на свои места.
Функция long FirstSeg() возвращает линейный адрес начальный сегмента,
имеющего наименьший адрес начала, соответственно функция long NextSeg(long ea)
возвращает линейны адрес сегмента, следующего за ea.
Пример использования:
auto a;
a=FirstSeg();
while(a!=BADADDR)
{
Message(">%08x\n",a,SegName(a));
a=NextSeg(a);
}
>00010000
>00020100
>00100010
Зная адрес начала сегмента можно получить его имя вызовом функции SegName, а
зная имя сегмента можно получить его базовый адрес при помощи функции SegByName;
более быстрого способа вычисления базового адреса по-видимому не существует.
55

Передав функциям SegStart и SegEnd любой принадлежащий сегменту линейный
адрес можно получить линейный адрес его начала и конца соответственно.
Скрипт, приведенный ниже, распечатывает список всех существующих сегментов, с
указанием их основных характеристик.
auto a;
a=FirstSeg();
Message(">Name | Start
|End
|BASE\n");
Message(">--------------------––––––––-\n”);
while(a!=BADADDR)
{
Message(">%s|%08x|%08x|%08x\n",
SegName(a),a,SegEnd(a),SegByName(SegName(a))/0x10);
a=NextSeg(a);
}
Message(">-----------------------------\n\n”);
>Name | Start
|End
|BASE
>-------------------------------->seg000|00010000|00010012|00001000
>seg001|00020100|0002013c|00002000
>seg002|00100010|00100100|00010000
>--------------------------------Узнать обо всех остальных атрибутах сегмента можно при помощи функции
GetSegmentAttr.
Сводная таблица функций
??? # Верстальщику #Unfortunately Change Table
имя функции

функции преобразования адресов
краткое описание

long MK_FP (long seg, long off)
char atoa (long ea)

преобразует сегментный адрес в линейный
преобразует линейный адрес в строковой сегментный

функции, работающие с сегментами
функции создания и удаления сегментов
имя функции
краткое описание
success SegCreate(long startea,long
endea,long base,long use32,long
align,long comb)
success SegDelete (long ea,long
disable)

создает новый сегмент или изменяет атрибуты уже
существующего сегмента
удаляет сегмент и при необходимости, принадлежащие ему
адреса виртуальной памяти

функции изменения основных характеристик сегмента
имя функции
краткое описание
success SegBounds (long ea,long
startea,long endea,long disable)

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

функции получения основных характеристик сегмента
имя функции
краткое описание
long SegStart (long ea)
long SegEnd (long ea)
long SegByName (char segname)
long SegByBase(long base)

возвращает линейный адрес начала сегмента
возвращает линейный адрес конца сегмента
по имени сегмента определяет его базовый адрес
по базовому адресу сегмента определяет линейный адрес его начала

Функции изменения атрибутов сегмента
имя функции
краткое описание
56

success SegRename (long ea,char
name)
success SegAddrng (long ea,long
use32)
success SegAlign (long ea,long
alignment)
success SegComb (long segea,long
comb)
success SegClass (long ea,char class)
success SegDefReg (long ea,char
reg,long value)
success SetSegmentType(long
segea,long type)

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

функции получения атрибутов сегмента
имя функции
краткое описание
long GetSegmentAttr (long segea,long
attr)
char SegName (long ea)

возвращает атрибуты сегмента
возвращает имя сегмента

функции трассировки сегментов
имя функции
краткое описание
long FirstSeg ()
long NextSeg (long ea)

возвращает линейный адрес начала первого сегмента
возвращает линейный адрес начала следующего сегмента

Функции, работающие с селекторами
функции создания и удаления селекторов
имя функции
краткое описание
void SetSelector (long sel,long value)
void DelSelector (long sel)

создает новый селектор или изменяет значение уже
существующего селектора
удаляет селектор

утилиты
имя функции

краткое описание

long AskSelector (long sel)
long FindSelector (long val)

возвращает значение селектора в параграфах
возвращает селектор с указанным значением

long MK_FP (long seg,long off)
Функция преобразует сегментный адрес в линейный по следующей схеме
ea = seg * 0x10 + off. Перед ее использованием необходимо убедится что “seg”
представляет собой именно базовый адрес сегмента, выраженный в параграфах, а не
селектор, иначе полученный результат будет неверен.
Оператор “квадратные скобки” полностью аналогичен функции MK_FP, но обладает
более компактной формой записи (6 символов “MK_FP()” вместо двух “[]”).
Замечание: в комментариях, содержащихся в файле , часто
используется конструкция [“имя сегмента”, смещение]. Попытка использования
этой схематической конструкции в коде скриптов приведет к появлению
синтаксической ошибки, но если передать имя сегмента в строковой
переменной, оператор «квадратные скобки» автоматически подставит его
базовый адрес – если, конечно, такой сегмент существует; в противном случае
строка будет преобразована в число, согласно правилам преобразования
«строка-Æ число» в IDA-Cи (см. «Язык скриптов IDA Си» - «Объявление
переменных, типы переменных, преобразования переменных»)
Напротив, макрос MK_FP всегда преобразует переданное ему имя
сегмента в строку, даже если сегмент с таким именем существует.
Пример использования:
Message(“>[seg %X,off%X]=%X=%X\n”,0x1000,0x6,MK_FP(0x1000,0x6),[0x1000,0x6]);
>[seg 1000,off6]=10006=10006

57

??? #верстальщику – change table
аргумент
seg
off
return
long

пояснения
базовый адрес сегмента (не селектор!), выраженный в параграфах
смещение ячейки в сегменте
пояснения
32-битный линейный адрес ячейки

Родственные функции: оператор []
Интерактивный аналог: “~View\Calculate”
char atoa(long ea)
Функция преобразует линейный адрес ea в строковой сегментный, действуя по
следующему алгоритму:


если линейный адрес ea принадлежит некоторому сегменту, смещение
вычисляется относительно его базового адреса, а сам адрес записывается
в виде “имя сегмента:смещение”



если

линейный

адрес

ea

не

принадлежит ни одномусегменту,
ea
преобразование выполняется по формуле seg = 0x10; off = ea - seg.

Пример использования:
Message(">%s\n",atoa(0x200010));
>0:00200010
SegCreate(0x200000,0x201000,0x20000,0,0,0);
0. Creating a new segment (00200000-00201000) ... ... OK
Message(">%s\n",atoa(0x200010));
>seg000:0010
??? #верстальщику – change table
аргумент
ea
return

пояснения
32-разрядный линейный адрес
=return пояснения
!=”” сегментный адрес в строковом представлении
==”” ошибка

Родственные функции: нет
Интерактивный аналог: нет
success SegCreate(long startea,long endea,long base,long use32,long align,long comb)
Функция создает новый сегмент. Сегмент задается линейным адресом начала
(startea), линейным адресом конца (endea) и базовым адресом (BASE), использующимся
для адресации ячеек внутри сегмента.
58

Адрес начала задает наименьший линейный адрес, принадлежащий данному
сегменту, напротив, адрес конца, задает линейный адрес на единицу превышающий
наибольший адрес, принадлежащий указанному сегменту. Такая мера необходима для
поддержки сегментов нулевой длинны – в силу архитектурных ограничений IDA, один
линейный адрес не может является и началом, и концом сегмента одновременно, поэтому,
приходится «искусственно» увеличивать адрес конца сегмента.
Смещение первого байта внутри сегмента вычисляется по формуле
startoffset = startea - BASE * 0x10 . Поддержка базирования позволяет создавать сегменты
с произвольным начальным смещением, например, равным 0x100. Поскольку, смещения
выражаются неотрицательными величинами, начальный адрес должен быть не меньше
базы сегмента, измеряемой в байтах.
Напротив, базовый адрес по известному адресу начала сегмента и смещению
startea - startoffset
первого байта в сегменте вычисляется по формуле BASE =
0x10
Атрибут use32 задает разрядность сегмента – нулевое значение соответствует 16разрядному сегменту, любое другое – 32-разрядному. Разрядность оказывает влияние
только на способ дизассемблирования машинных инструкций, принадлежащих этому
сегменту, но независимо от разрядности смещения в сегменте всегда задаются 32битовыми значениями.
Атрибут
align
задает
кратность
выравнивания
сегмента,
помещая
соответствующую директиву в ассемблерный листинг. Никакого влияния на размещение
сегмента в виртуальной памяти атрибут align не оказывает!
Атрибут comb задает флаг комбинирования, разрешающий (запрещающий)
линкеру объединять несколько сегментов в один. Никакого влияния на объединение
сегментов в виртуальной памяти атрибут comb не оказывает – независимо от его значения
смежные сегменты не будут автоматически объединены.
Детали:
а) Базовый адрес задается неотрицательным 16-разрядным значением и может
0x10000 * 0x10
адресовать не более одного мегабайта памяти ⎛ 1024 * 1024 =1⎞. Если этого


недостаточно, следует указать вместо базового адреса селектор, который необходимо
предварительно создать вызовом функции SetSelector.
Если базовый адрес, переданный функции SegCreate, больше 0x10000, IDA
автоматически создаст новый селектор и использует его для адресации данного сегмента.
б) Если передать линейный адрес, принадлежащий некоторому сегменту, но
отличный от адреса начала (т.е. попытаться создать вложенный сегмент), функция
автоматически укорит или разобьет данный сегмент и создаст новый (см. рисунок)
seg000->║───────║
seg000->║───────║








┌─> ║─ ─ ─ ─║
seg001->║───────║




















╟─> ║─ ─ ─ ─║
seg002->║───────║






║───────║
║───────║
╚═══════╤─┐
SegCreate(x,y,.....);

59

Рисунок 22 ??? Художнику - перерисовать
Пример использования:
Пусть необходимо создать сегмент с (задача вычисления базовго адреса)
SegCreate(0x1000,0x4000,0x100,0,0,0);
0. Creating a new segment (00001000-00004000) ... ... OK
╔═[■]═══════════════════════════════ Program Segmentation ══════════════════════════════4═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds

║ seg000
00000000 00003000 at
0100 pri
N FFFF FFFF FFFF 00001000 00004000





╚1/1
═════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

SegCreate(0x2000,0x3000,0x200,0,0,0);
1. Creating a new segment (00002000-00003000) ...
Additional segment
(00003000-00004000) ...
2. Creating a new segment (00003000-00004000) ... ... OK
... OK
╔═[■]═══════════════════════════════ Program Segmentation ══════════════════════════════4═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds

║ seg000
00000000 00001000 at
0100 pri
N FFFF FFFF FFFF 00001000 00002000

║ seg001
00000000 00001000 at
0200 pri
N FFFF FFFF FFFF 00002000 00003000

║ seg002
00002000 00003000 at
0100 pri
N FFFF FFFF FFFF 00003000 00004000

╚1/3
═════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

??? #Верстальщику – change table
аргумент
startea
endea
Base
use32
aling
comb
return

пояснения
32-разрядный линейный адрес начала сегмента
величина на единицу большая последнего принадлежащего сегменту адреса

16-разрядный базовый адрес в параграфах или указатель на селектор
=use32 пояснения
==0 16-разрядный сегмент
==1 32-разрядный сегмент
кратность выравнивания начала сегмента
комбинирование сегмента
=return пояснения
==1 операция завершилась успешно
==0 ошибку

Родственные функции: SetSelector; SegClass; SegAlign; SegComb; SegAddrng;
Интерактивный аналог: “~View\Segments”, ;
success SegDelete(long ea,long disable)
Функция удаляет сегмент вместе с результатами дизассемблирования (метками,
функциями, переменными и т.д.) и адресным пространством виртуальной памяти,
принадлежащей сегменту.
Аргумент ea – представляет собой любой линейный адрес, принадлежащий
сегменту, но не адрес его конца (см. описание функции SegCreate – адрес конца сегмента
на единицу больше наибольшего адреса, принадлежащего сегменту)
60

Аргумент disable будучи неравным нулю указывает на необходимость удаления
адресного пространства виртуальной памяти, принадлежащей сегменту, в противном
случае удаляется лишь сам сегмент вместе с результатами дизассемблирования, а
содержимое ячеек виртуальной памяти остается неизменным.
Внимание: операция удаления сегментов необратима – повторное создание
уничтоженного сегмента не восстановит удаленные вместе с сегментом
метки,
функции,
переменные,
комментарии
и
другие
результаты
дизассемблирования.
Если переданный функции линейный адрес не принадлежит ни одному сегменту,
она возвратит нулевое значение, сигнализируя об ошибке. Нормальное завершение
возвращает единицу.
Пример использования:
seg000:0000 seg000
seg000:0000
seg000:0000
seg000:0000 aHelloIdaPro
seg000:0000 seg000

segment byte public '' use16
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
db 'Hello, IDA Pro! ',0Dh,0Ah
; Test
ends

а) исходный сегмент, содержащий строку “aHelloIdaPro” и комментарий “Test”
SegDelete(SegByBase(SegByName("seg000")>>4),0);
b) удаление сегмента без уничтожения виртуальной памяти
0:00010000
0:00010001
0:00010002
0:00010003
0:00010004
0:00010005
0:00010006
0:00010007
0:00010008
0:00010009
0:0001000A
0:0001000B
0:0001000C
0:0001000D
0:0001000E
0:0001000F
0:00010010

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h
50h
72h
6Fh
21h
20h
0Dh

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
I
D
A
P
r
o
!

с) результат – сегмент удален, содержимое виртуальной памяти - нет
SegCreate(0x10000,0x10012,0x1000,0,0,0);
d) Попытка создания нового сегмента для восстановления удаленного
seg000:0000
seg000:0000 ; Segment type: Regular
seg000:0000 seg000
segment at 1000h private '' use16
seg000:0000
assume cs:seg000
seg000:0000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:0000
db 48h ; H
seg000:0001
db 65h ; e
seg000:0002
db 6Ch ; l
seg000:0003
db 6Ch ; l
seg000:0004
db 6Fh ; o
seg000:0005
db 2Ch ; ,
seg000:0006
db 20h ;
seg000:0007
db 49h ; I
seg000:0008
db 44h ; D
seg000:0009
db 41h ; A
seg000:000A
db 20h ;
seg000:000B
db 50h ; P

61

seg000:000C
seg000:000D
seg000:000E
seg000:000F
seg000:0010
seg000:0011
seg000:0011 seg000

db 72h ; r
db 6Fh ; o
db 21h ; !
db 20h ;
db 0Dh ;
db 0Ah;
ends

e) результат – сегмент восстановлен, а строка и комментарий – нет.
SegDetele(0x10000,1);
f) удаление сегмента вместе с принадлежащей ему виртуальной памятью
╔═[■]═══════════════════════════════════════ IDA view-A ══════
║ пустой экран дизассемблера


j) результат – виртуальная память удалена
SegCreate(0x10000,0x10012,0x1000,0,0,0);
k) попытка создания нового сегмента для восстановления виртуальной памяти
seg000:0000 ; Segment type: Regular
seg000:0000 seg000
segment at 1000h private '' use16
seg000:0000
assume cs:seg000
seg000:0000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg000:0000
db ? ; unexplored
seg000:0001
db ? ; unexplored
seg000:0002
db ? ; unexplored
seg000:0003
db ? ; unexplored
seg000:0004
db ? ; unexplored
seg000:0005
db ? ; unexplored
seg000:0006
db ? ; unexplored
seg000:0007
db ? ; unexplored
seg000:0008
db ? ; unexplored
seg000:0009
db ? ; unexplored
seg000:000A
db ? ; unexplored
seg000:000B
db ? ; unexplored
seg000:000C
db ? ; unexplored
seg000:000D
db ? ; unexplored
seg000:000E
db ? ; unexplored
seg000:000F
db ? ; unexplored
seg000:0010
db ? ; unexplored
seg000:0011
db ? ; unexplored
seg000:0011 seg000
ends

l) результат – адресное пространство восстановлено, но содержимое виртуальной
памяти – нет.
??? #верстальщику – change table
аргумент
ea
disable
return

пояснения
линейный адрес принадлежащий сегменту
=disable пояснения
==0 не уничтожать адреса, принадлежащие сегменту
==1 уничтожать адреса, принадлежащие сегменту
=return
==1 успешное завершение операции
==0 ошибка

Родственные функции: SegCreate
Интерактивный аналог: “~Edit\Segments\Delete segment”; “~View\Segments”,
62

success SegBounds(long ea,long startea,long endea,long disable)
Функция позволяет изменять адреса начала и конца сегмента, при необходимости
уничтожая освободившуюся при уменьшении сегмента виртуальную память. Базовый
адрес она изменить не в состоянии и при возникновении необходимости его модификации
следует воспользоваться функцией SegCreate.
Перед увеличением границ сегмента необходимо убедиться что для расширения
сегмента имеется достаточное количество свободного места, в противном случае
требуется предварительно укоротить соседние сегменты на соответственную величину.
Адрес начала сегмента должен быть не меньше базового адреса сегмента,
умноженного на шестнадцать, в противном случае первые байты сегмента будет иметь
отрицательное смещение. Изменить базовый адрес можно вызовом SegCreate или
SetSelector (если базовый адрес задан селектором).
Адрес конца сегмента должен на единицу превышать наибольший адрес,
принадлежащий сегменту. Подробнее об этом рассказывается в описании функции
SegCreate.
При уменьшении сегмента ранее принадлежащая ему виртуальная память
освобождается. Позднее она может быть выделена в отдельный сегмент или же
объединена с одним из смежных сегментов. Если же ни то, ни другое не планируется, то
освободившуюся память можно удалить, передав функции ненулевое значение флага
disable.
??? #Верстальщику – change table
аргумент
ea
startea
endea
disable
return

пояснения
линейный адрес, принадлежащий сегменту
32-разрядный линейный адрес нового начала сегмента
величина на единицу большая последнего принадлежащего сегменту адреса

уничтожать освободившиеся адреса
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: SegCreate
Интерактивный аналог: ; “~View\Segments”,
long SegStart(long ea)
Функция принимает любой линейный адрес, принадлежащий сегменту, и
возвращает линейный адрес начала данного сегмента.
Если переданный адрес не принадлежит ни одному сегменту, функция возвратит
BADADDR, сигнализируя об ошибке.
Пример использования:
SegCreate(0x10000,0x20000,0x1000,0,0,0);
a) создаем сегмент с адресом начала 0x10000 и адресом конца 0x20000
0. Creating a new segment
b) сегмент успешно создан

(00010000-00020000) ... ... OK

Message(">%X\n",SegStart(0x10100));
c) вызываем функцию SegStart, передавая ей один из адресов, принадлежащих сегменту
63

>10000
d) результат – функция вернула адрес начала сегмента
??? #верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес принадлежащий сегменту

=return
!=BADADDR
==BADADDR

пояснение
линейный адрес начала сегмента

ошибка

Родственные функции: SegEnd
Интерактивный аналог: “~View\Segments”
адрес начала сегмента –––––––––––––––––––––––––––––––––––––––––––––––––––––––––--┐

╔═[■]═════════════════════════════════ Program Segmentation ═════════════════════│═════════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs


║ seg000
00000000 00010000 at
1000 pri
N FFFF FFFF FFFF FFFF FFFF 00010000 00020000 ▓




╚1/1
══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

long SegEnd(long ea)
Функция принимает любой линейный адрес, принадлежащий сегменту, и
возвращает линейный адрес конца данного сегмента.
Если указанный адрес не принадлежит ни одному сегменту, функция возвратит
значение BADADDR, сигнализируя об ошибке.
Линейный адрес конца сегмента на единицу больше наибольшего адреса,
принадлежащего сегменту. Подробнее об этом рассказывается в описании функции
SegCreate.
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
SegCreate(0x2000,0x3000,0x200,0,0,0);
a) создаем два смежный сегмента с адресами начала и конца 0x1000; 0x2000 и
0x2000; 0x3000 соответственно
0. Creating a new segment
1. Creating a new segment
b) сегменты успешно созданы

(00001000-00002000) ... ... OK
(00002000-00003000) ... ... OK

Message(">%X\n",SegEnd(0x1100));
c) вызываем функцию SegEnd, передавая ей один из адресов, принадлежащих
первому сегменту
>2000
d) результат – адрес конца первого сегмента
Message(">%X\n",SegStart(0x2000));
e) вызываем функцию SegStart, передавая ей адрес конца первого сегмента
>2000
f) результат – адрес начала второго сегмента. Таким образом, наглядно
64

продемонстрировано – адрес конца сегмента не принадлежит самому сегменту.
??? #верстальщику – change table
аргумент
ea
return

пояснения
любой 32-разрядный линейный адрес, принадлежащий сегменту
=return пояснения
!=BADADDR линейный адрес конца сегмента
==BADADDR ошибка

Родственные функции: SegStart
Интерактивный аналог: “~View\Segments”
адрес конца сегмента –––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––--┐

╔═[■]═════════════════════════════════ Program Segmentation ═════════════════════════==═====═══│═3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs


║ seg000
00000000 00010000 at
1000 pri
N FFFF FFFF FFFF FFFF FFFF 00010000 00020000 ▓




╚1/1
══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

long SegByName(char segname)
Ф у н кц ия по им ен и сегмента (с у че то м р ег ис тр а) о пре де ляе т его
базов ый адр ес .
Если базовый адрес представляет собой селектор, то функция автоматически
возвратит его значение.
Если сегмента с указанным именем не существует, функция возвратит значение
BADADDR, сигнализируя об ошибке.
Детали:
а) вызов SegByName – единственный способ определения базового адреса
сегмента. Чтобы определить базовый адрес сегмента, по некому линейному адресу,
принадлежащему
сегменту,
необходимо
воспользоваться
конструкцией
BASE = SegByName(SegName(ea)).
b) Поскольку, IDA допускает совместное существование двух и более одноименных
сегментов, определение базового адреса сегмента по ему имени позволяет определишь
базовый адрес лишь одного из них.
Но никакого другого (во всяком случае документированного) способа определения
базового адреса не существует и выходом из такой ситуации становится отказ от
использования одноименных сегментов.
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
SegRename(0x1000,"MySeg");
a) создаем новый сегмент с базовым адресом 0x1000 и тут же переименовываем
его в “MySeg”
Message(">%X\n",SegByName("MySeg"));
b) вызываем функцию SegByName передавая ей имя только что созданного
сегмента

65

>1000
c) результат – базовый адрес сегмента MySeg
SegCreate(0x2000,0x3000,0x200,0,0,0);
SegRename(0x2000,"MySeg");
d) создаем второй сегмент с именем “MySeg”, но на этом раз базовым адресом,
равным 0x2000
╔═[■]════════════════════════════════ Program Segmentation ═══════════════════════════════4═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ MySeg
00000000 00001000 at
0100 pri
N FFFF FFFF FFFF FFFF FFFF 00001000 00002000 ▒
║ MySeg
00000000 00001000 at
0200 pri
N FFFF FFFF FFFF FFFF FFFF 00002000 00003000 ■


╚2/2
═════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

e) это получилось! Теперь существуют два одноименных сегмента

Message(">%X\n",SegByName("MySeg"));
f) вызываем функцию SegByName передавая ей имя “MySeg”
>1000
g) результат – базовый адрес первого сегмента
??? #верстальщику – change table
аргумент
segname
return

пояснения
имя сегмента (регистр учитывается)
=return пояснения
!=BADADDR базовый адрес сегмента или значение селектора
==BADADDR ошибка

Родственные функции: SegRename, SegName
Интерактивный аналог: “~View\Segments”
базовый конца сегмента –––––––––--┐
┌–- имя сегмента

╔═[■]═══▼══=====═════════════════=═════▼══ Program Segmentation ═════════════════════==═=====════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs

║ seg000
00000000 00010000 at
1000 pri
N FFFF FFFF FFFF FFFF FFFF 00010000 00020000 ▓




╚1/1
══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

long SegByBase(long base)
Функция по базовому адресу (селектору) сегмента определяет линейный адрес его
начала. Если передать базовый адрес, не принадлежащий ни одному сегменту, или
существующий, но не использующийся ни одним сегментом селектор, функция возвратит
значение BADADDR, сигнализируя об ошибке.
Поскольку, IDA допускает совместное существование двух и более сегментов с
одинаковыми базовыми адресами, определение адреса начала сегмента по его базовому
адресу позволяет определишь адрес начала лишь одного из них.
Пример использования:
SegCreate(0x1100,0x2000,0x100,0,0,0);
a) создаем сегмент с базовым адресом 0x100 и линейным адресом начала 0x1100
Message(">%X\n",SegByBase(0x100));
66

b) вызываем функцию SegByBase для получения линейного адреса начала
сегмента по его базе
>1100
c) результат – линейный адрес начала сегмента
??? #верстальщику – change table
аргумент
base
return

пояснения
базовый адрес сегмента или значение селектора
=return пояснения
!=BADADDR линейный адрес начала сегмента
==BADADDR ошибка

Родственные функции: SegByName
Интерактивный аналог: “~View\Segments”
базовый конца сегмента –––––––-–––––-┐

линейный адрес начала сегмента -┐
╔═[■]═══========═════════════════=═════▼══ Program Segmentation ═════════════════════== │====════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds fs gs


║ seg000
00000000 00010000 at
100 pri
N FFFF FFFF FFFF FFFF FFFF 0001100 0002000 ▓




╚1/1
══════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

success SegRename(long ea,char name)
Функция переименовывает сегмент, возвращая при удачном завершении операции
ненулевое значение и ноль в противном случае.
Аргумент ea представляет собой любой линейный адрес, принадлежащий
сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит
ошибку.
Аргумент name задает новое имя сегмента с учетом регистра. Если имя начинается
с цифры, функция автоматически дополнит его знаком прочерка. Если в имени содержится
недопустимые символы, они будут автоматически заменены знаком прочерка.
Допустимые символы перечисляются в полях NameChars конфигурационного
файла , значение которых по умолчанию приведено в таблице ???
Попытка присвоить сегменту пустое имя (аргумент name равен “”) приводит к
ошибке.
IDA допускает существование двух и более сегментов с одинаковыми именами,
однако, использование этой возможности влечет невозможность определения базового
адреса сегмента. Подробнее – см. описание функции SegByName.
??? #Верстальщику Create New Table
платформа
PC
Java
5
6

перечень символов, допустимых в именах
"$?@" 5
“_0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
"$_@?!" 6

Служебные символы ассемблера
Символы, определенные только для специальных режимов Java-ассемблера
67

TMS320C6
PowerPC

"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ" 7
"абвгдежзийклмнопрстуфхцчшщъыьэюя";
"$_0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_0123456789."
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz”

Таблица 4
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
Message(">%s\n",SegName(0x1000));
a) создаем сегмент и тут же определяем его имя
>seg000
b) имя сегмента – “seg000”
SegRename(0x1000,"666");
Message(">%s\n",SegName(0x1000));
c) вызываем функцию SegRename для переименования сегмента в “666” и тут же
получаем имя сегмента при помощи SegName
>_666
d) результат – новое имя сегмента “_666”, - функция автоматически добавила
спереди знак прочерка, поскольку имя начиналась с цифры
SegRename(0x1000,”Русский квас”);
Message(">%s\n",SegName(0x1000));
e) вызываем функции SegRename для переименования сегмента в «Русский квас»
и тут же получаем имя сегмента при помощи SegName
>____________
f) результат – все запрещенные символы были заменены знаками прочерка
??? #верстальщику – change table
аргумент
ea
name
return

пояснения
линейный адрес, принадлежащий сегменту
новое имя сегмента
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: SegName, SegByName
Интерактивный аналог: “~View\Segments”, ;
7

Национальные (российские символы)
68

success SegAddrng(long ea,long use32)
Функция изменяет разрядность сегмента, определяя каким образом будет
дизассемблироваться машинные инструкции.
На платформе Intel 386+ префикс 0x66 перед инструкцией в 16-разрядном сегменте
указывает на использование 32-битных операндов и, соответственно, в 32-разрядном
сегменте наоборот. Изменение адресации так же затрагивает интерпретацию префикса
предопределения адреса - 0x67, видов адресации и т.д.
некорректному
Неверный
выбор
разрядности
сегмента
приводит
к
дизассемблированию – появлению бессмысленного «мусора», бессвязных инструкций.
IDA определяет разрядность сегмента на основе информации, содержащейся в
заголовках файлов. При загрузке бинарных файлов, равно как и MS-DOS exe файлов
разрядность созданных сегментов по умолчанию равна нулю. Если IDA неправильно
определила разрядность одного или нескольких сегментов, ее следует исправить вручную.
Внимание: изменение разрядности сегмента уничтожает все результаты
дизассемблирования – метки, функции, переменные и т.д.
Аргумент ea задает любой линейный адрес, принадлежащий сегменту. Если
передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Нулевое значение аргумента use32 указывает на 16-битовую разрядность сегмента
и приводит к помещению в ассемблерный листинг атрибута размера сегмента use16; в
противном случае сегмент считается 32-разрядным, а атрибут размера сегмента – use32.
Независимо от разрядности сегмента, IDA всегда выражает смещения 32-битовыми
значениями и допустимо создания 16-разрядного сегмента размером более 64 килобайт,
однако, следует помнить, что в дальнейшем такой «большой» сегмент не сможет быть
ассемблирован.
Функция не учитывает выбранный тип процессора и допускает использование 32разрядного режима даже с 8086 (!) процессором.
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
a) Создание 16-разрядного сегмента
seg000:0000 seg000
segment at 100h private '' use16
b) Сегмент успешно создан. Атрибут размера сегмента выделен жирным шрифтом.
SegAddrng(0x1000,1);
c) Вызов функции SegAddrng для изменения разрядности сегмента
seg000:00000000 seg000
segment at 100h private '' use32
e) Разрядность сегмента успешно изменена
??? #верстальщику – change table
аргумент
ea
use32
return

пояснения
линейный адрес, принадлежащий сегменту
=use32 пояснения
==0 16-разрядный сегмент
==1 32-разрядный сегмент
=return пояснения
==1 успешное завершение операции
==0 ошибка
69

Родственные функции: SegCreate
Интерактивный аналог: “~View\Segments”, ;
success SegAlign(long ea,long alignment)
Функция управляет выравниванием сегмента, помещая в ассемблерный текст
соответствующий атрибут вырывания (byte, word, dword, para, page, mempage). Подробнее
о каждом из этих атрибутов можно прочитать в документации, прилагаемой к
используемому линкеру.
Никакого влияния на дизассемблируемый образ файла функция не оказывает и
не выравнивает сегмент в виртуальной памяти. Это подтверждает следующий
эксперимент:
SegCreate(0x1003,0x2000,0x100,0,0,0);
a) создание нового сегмента с адресом начала равным 0x1003
seg000:0003 seg000
segment at 100h private '' use16
b) сегмент создан; смещение первого байта в сегменте равно трем
Message(">1%x\n",SegAlign(0x1003,saRelWord));
c) вызов функции SegAlign для выравнивания сегмента по границе слова
seg000:0003 seg000
segment word private '' use16
>1
d) результат – функция поместила в ассемблерный текст атрибут выравнивания
‘word’ (он выделена жирным шрифтом) и сигнализировала об успешном завершении. Но
линейный адрес начала сегмента не был изменен (он выделен жирным шрифтом)!
Атрибут выравнивания возымеет действие только на стадии компоновки
объективного файла, расположив сегмент по адресу, кратным двум. Это приведет к
несоответствию смещений в дизассемблируемом и целевом файле, что, скорее всего,
вызовет невозможность корректного выполнения откомпилированной программы. Для
выравнивания сегмента в виртуальной памяти следует воспользоваться функцией
SegBounds для перемещения его начала по заданному адресу.
Аргумент ea задает любой линейный адрес, принадлежащий сегменту. Если
передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент alignment представляет собой атрибут выравнивания сегмента и может
принимать одно из значений, перечисленных в таблице ???
определение
saAbs
saRelByte
saRelWord
saRelPara
saRelPage
saRelDble
saRel4K
saGroup
saRel32Bytes
saRel64Bytes
saRelQword

8

#
0
1
2
3
4
5
6
7
8
9
10

пояснения
Безусловное выравнивание
Выравнивание по границе байта (8 бит)
Выравнивание по границе слова (16 бит)
Выравнивание по границе параграфа (16 байт)
Выравнивание по границе страницы (256-байт для чипов Intel)
Выравнивание по границе двойного слова (4 байта)
Выравнивание по границе страницы (4 килобайта для PharLap OMF) 8
Segment group
Выравнивание по границе 32 байта
Выравнивание по границе 64 байта
Выравнивание по границе 8 байт

Не поддерживается стандартным линкером LINK.

70

Таблица 5
??? #верстальщику – change table
аргумент
ea
alignment
return

пояснения
линейный адрес, принадлежащий сегменту
кратность выравнивания (смотри определения в таблице выше)
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: SegCreate
Интерактивный аналог: “~View\Segments”, ;
success SegComb(long segea,long comb)
Функция управляет объединением сегментов, помещая в ассемблерный листинг
атрибут комбинирования (private, public, common, at, stack).
Атрибут комбинирования указывает компоновщику как следует комбинировать
сегменты различных модулей, имеющие одно и то же имя. Подробную информацию о
каждом из атрибутом можно найти в документации, прилагаемой к используемому линкеру.
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если
передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент comb представляет собой атрибут выравнивания, и может принимать
одно из значений, приведенных в Таблице ???
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,scPub);
a) создаем новый сегмент с атрибутом комбинирования public
seg000:0000 seg000
segment at 100h public ''
b) сегмент успешно создан, атрибут комбинирования выделен жирным шрифтом
SegComb(0x10000,scStack);
c) вызываем функцию SegComb для изменения атрибута комбинирования сегмента
seg000:0000 seg000
segment at 100h stack ''
d) атрибут комбинирования изменен
определение
scPriv
scPub

#
0
2

scPub2
scStack

4
5

scCommon
scPub3

6
7

пояснения
Атрибут private. Не может быть соединен ни с одним другим сегментом
Атрибут public. Может быть объединен с другими сегментами с учетом
требуемого выравнивания
Атрибут, определенный Microsoft, то же самое, что и scPub .
Атрибут stack. Может быть объединен с public сегментами, но с
выравниваем в один байт.
Атрибут common.
Атрибут, определенный Microsoft, то же самое, что и scPub .

Таблица 6
??? #верстальщику – change table

71

аргумент
ea
comb
return

пояснения
любой линейный адрес, принадлежащий сегменту
Атрибут (смотри определения в таблице 6)
=return Успешность завершения операции
1 Операция завершилась успешно
0 При выполнении операции произошла ошибка

Родственные функции: SegCreate
Интерактивный аналог: “~View\Segments”, ;
success SegClass(long ea,char class)
Функция изменяет атрибут класса сегмента, помещая в ассемблерный листинг
атрибут класса.
Атрибут класса представляет собой текстовую строку, указывающую линкеру
порядок следования сегментов. Если это не запрещено комбинаторным атрибутом (см.
описание SegComb), линкер объединяет вместе сегменты с одинаковым именем.
Рекомендуется назначать имена так, чтобы они отображали функциональное назначение
сегментов, например, “CODE”, “DATA”, “STACK” и т.д. Общепринятые имена перечислены в
таблице ???.
Замечание: большинство линкеров требуют, чтобы в объективном файле
присутствовал хотя бы один сегмент с именем “CODE”, в противном случае,
они могут отказаться обрабатывать такой файл или обработают его
неправильно.
Пример использования:
SegCreate(0x1000,0x2000,0,0,scPub);
a) создаем сегмент с атрибутом public
seg000:0000 seg000
segment at 100h public ''
b) сегмент создан, по умолчанию атрибут класса сегмента отсутствует
SegClass(0x1000,”MySegment”);
с) вызываем функцию SegClass для изменения атрибута класса сегмента
seg000:0000 seg000
segment at 100h public 'MySegment'
d) атрибут класса сегмента изменен (в тексте он выделен жирным шрифтом)
Класс
CODE
DATA
CONST
BSS
STACK
XTRN

Pure code
Pure data
Pure data
Uninitialized data
Uninitialized data
Extern definitions segment

Пояснения
Сегмент кода
Сегмент данных
Неиницилизированные данные
Сегмент стека

Таблица 7 Общепринятые наименования классов сегментов
??? #верстальщику – change table
72

аргумент
ea
class
return

пояснения
линейный адрес, принадлежащий сегменту
класс сегмента
=return Успешность завершения операции
1 Операция завершилась успешно
0 При выполнении операции произошла ошибка

Родственные функции: SegCreate
Интерактивный аналог: “~View\Segments”, ;
success SegDefReg(long ea,char reg,long value)
Функция определяет значение сегментных регистров, помещая в ассемблерный
текст директиву ASSUME. Это указывает дизассемблеру (ассемблеру) к какому именно
сегменту обращается тот или иной адресный операнд. Более подробную информацию по
этому вопросу можно получить, обратившись к описанию директивы ASSUME в
документации, прилагаемой к используемому ассемблеру или справочной (учебной)
литературе по языку ассемблера.
Аргумент ea задает любой линейный адрес, принадлежащий сегменту, в которой
необходимо поместить директиву ASSUME.
Аргумент reg задает сегментный регистр в символьном представлении, например,
“DS”,”ES”,”SS” и т.д. Строчечные и прописные символы не различаются. Тип процессора
при назначении сегментного регистра учитывается.
Аргумент value содержит базовый адрес сегмента, загружаемый в регистр и
выраженный в параграфах. Если сегмента с указанным базовым адресом не существует,
регистр приобретает неопределенное (“nothing”) значение.
В реализации этой функции допущена одна ошибка – независимо от успешности
выполнения, возвращаемое значение никогда не сигнализирует об ошибке.
Пример использования (см. файл “assume.idb”, содержащийся на диске,
прилагаемом к книге): пусть имеется один сегмент кода (“seg00”) и два сегмента данных
(“seg001” и “seg002”), содержащих переменные “My666” и “My777” соответственно. По
умолчанию значения сегментных регистров неопределенны и IDA не может определить к
каким именно сегментам происходит обращения в командах “mov ax,ds:[0]” и “mov
dx,es:[0]”, поэтому, вынуждена оставить операнды в виде непосредственных смещений. (в
тексте они выделены жирным шрифтом)
seg000:0000
seg000:0000
seg000:0000
seg000:0000
seg000:0003
seg000:0003
seg000:0003
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg002:0000

seg000

seg000

segment byte public 'CODE'
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing
mov
ax, ds:0
mov
dx, es:0
ends

; ═════════════════════════════════════════════════════════════
; Segment type: Pure data
seg001
segment byte public 'DATA'
assume cs:seg001
My666
dw 6666h
seg001
ends
; ═════════════════════════════════════════════════════════════
73

seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000

; Segment type: Pure data
seg002
segment byte public 'DATA'
assume cs:seg002
My777
dw 7777h
seg002
ends

Задать значения сегментных регистров можно с помощью функции DefSegReg,
вызов которой может выглядеть так:
DefSegReg(SegByName(“seg000”), “DS”, SegByName(“seg001”)>>4);
DefSegReg(SegByName(“seg000”), “ES”, SegByName(“seg002”)>>4);
В результате ее выполнения, IDA смогла отследить обращения к переменным,
автоматически подставив их имена вместо смещений (в тексте они выделены жирным
шрифтом). Поместив курсор в границы того или другого имени, нажатием клавиши
можно перейти к соответствующей ячейке памяти.
seg000:0000
seg000:0000
seg000:0000
seg000:0000
seg000:0003
seg000:0003
seg000:0003
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg001:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000
seg002:0000

seg000

seg000

segment byte public 'CODE'
assume cs:seg000
assume es:seg002, ss:nothing, ds:seg001
mov
ax, My666
mov
dx, es:My777
ends

; ═════════════════════════════════════════════════════════════════
; Segment type: Pure data
seg001
segment byte public 'DATA'
assume cs:seg001
My666
dw 6666h
; DATA XREF: seg000:0000r
seg001
ends
; ═════════════════════════════════════════════════════════════════
; Segment type: Pure data
seg002
segment byte public 'DATA'
assume cs:seg002
My777
dw 7777h
; DATA XREF: seg000:0003r
seg002
ends

??? #верстальщику – change table
аргумент
ea
reg
val
return

пояснение
линейный адрес, принадлежащий сегменту
сегментный регистр в строковом представлении (например, “DS”)
базовый адрес сегмента, загружаемого в регистр, выраженный в параграфах
всегда единица

Родственные функции: нет
Интерактивный аналог: “~Edit\Segments\Change segment register value”,

74

success SetSegmentType (long segea,long type)
Ф у н кц ия и з м е н яе т тип с ег ме н та , о каз ыва я в л и я н ие н а е го
дизассемблирование .
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если
передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
А р г ум е н т type указыв ае т на тип с егм е н та и мо же т пр и н има ть о д но из
з на че ни й , пе реч ис ленны х в таб лице ? ??. П ри с о з да н и и с егме н та ф у н к ц ие й
S e gCr ea t e е м у пр исва ив ае тс я тип « не извес тный » - S E G _ N O R M .
определение
SEG_NORM
SEG_XTRN
SEG_CODE
SEG_DATA
SEG_IMP
SEG_GRP
SEG_NULL
SEG_UNDF
SEG_BSS
SEG_ABSSYM
SEG_COMM
SEG_IMEM

#
0
1
2
3
4
6
7
8
9
10
11
12

Пояснения
Неизвестный тип
Внешний ('extern') сегмент. Инструкции исключены
Сегмент кода
Сегмент данных
Сегмент Java implementation
Group of segments
Сегмент нулевой длины
Сегмент неопределенного типа (не используется)
Неинициализированный сегмент
Сегмент с определением абсолютных символов
Сегмент с общими определениями
Внутренняя память процессора 8051

Таблица 8
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
a) создаем новый сегмент (по умолчанию неизвестного типа)
seg000:0000 ; Segment type: Regular
seg000:0000 seg000
segment at 100h private ''
seg000:0000
assume cs:seg000
seg000:0000
assume es:nothing, ss:nothing, ds:nothing
b) сегмент создан (тип выделен жирным шрифтом), автоматически внедрена
директива ASSUME для определения сегментных регистров.
SetSegmentType(0x1000,SEG_DATA);
c) вызов функции SetSegnetType для установки типа «сегмент данных»
seg000:0000 ; Segment type: Pure data
seg000:0000 seg000
segment at 100h private ''
seg000:0000
assume cs:seg000
d) тип сегмента изменен, директива ASSUME, задающая значение регистров DS,
ES и SS удавлена.
??? #верстальщику – change table
аргумент
ea
type
return

пояснения
любой линейный адрес, принадлежащий сегменту
тип сегмента (возможные значения приведены в таблице ???)
=return пояснения
!=0 успешное завершение операции
75

0

ошибка

Родственные функции: GetSegmentAttr
Интерактивный аналог: нет
long GetSegmentAttr(long segea,long attr)
Функция позволяет узнать следующие атрибуты сегмента: кратность
выравнивания, комбинацию, привилегии доступа, разрядность, флаги сегмента, селектор,
использующийся для базирования сегмента, тип сегмента и значения сегментных
регистров, определенных функцией DefSegReg.
Об атрибуте вырывания можно подробнее прочитать в описании функции SegAlign,
об атрибутах комбинации – в описании функции SegComb, о типе сегмента рассказывается
в описании функции “SetSegmentType”. Использование селекторов для базирования
сегментов подробно описано в главах «Организация сегментов», “SegCreate” и
“SetSelector”.
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если
передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент attr указывает функции – содержимое какого атрибута необходимо
возвратить. Возможные значения аргумента attr приведены в таблице ???
??? #Верстальщику – Change Table
константа
SEGATTR_ALIGN
SEGATTR_COMB

#
20
21
22

пояснения
получить выравнивание сегмента
получить атрибуты комбинации

функция
SegAlign
SegComb

привилегии доступа
SEGPERM_EXEC
1 исполнение
SEGPERM_WRITE
2 запись
SEGPERM_READ
4 чтение

23
24

32 разрядный сегмент
флаги сегмента

Внутренняя
используемая
только в IDA
SDK
SegAddrnd

SEGATTR_PERM
SEGATTR_USE32

ADDSEG_NOSREG

SEGATTR_FLAGS

ADDSEG_OR_DIE

SEGATTR_SEL
SEGATTR_DEF_ES
SEGATTR_DEF_CS
SEGATTR_DEF_SS
SEGATTR_DEF_DS
SEGATTR_DEF_FS
SEGATTR_DEF_GS
SEGATTR_TYPE

26
28
30
32
34
36
38
40

все сегментные регистры,
заданные
по
умолчанию
неопределенны
невозможно
добавить
сегмент

селектор сегмента
значение регистра ES по умолчанию
значение регистра CS по умолчанию
значение регистра SS по умолчанию
значение регистра DS по умолчанию
значение регистра FS по умолчанию
значение регистра GS по умолчанию
тип сегмента

Add_seg
(из IDA SDK)

SetSeelctor

DefSegReg

SetSegmentType

Таблица 9 Типы сегментов
??? #верстальщику – change table
аргумент
ea
Type

пояснения
линейный адрес, принадлежащий сегменту
тип сегмента (возможные значения приведены в таблице 9)

76

=return
!=0
0

return

Успешность завершения операции
Операция завершилась успешно
При выполнении операции произошла ошибка

Родственные функции: SegAddrng, SegAling, SegComb, SegClass, SegDefReg,
SetSegmentType
Интерактивный аналог: нет
char SegName(long ea)
Функция возвращает имя сегмента, заданного любым принадлежащим ему
линейным адресом ea. Если передать адрес не принадлежащий никакому сегменту,
функция возвратит пустую строку.
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
SegRename(0x1000,”MySeg”);
a) создаем сегмент и тут же переименовываем его в “MySeg”
MySeg:0000 MySeg
segment at 100h private ''
b) сегмент успешно создан (имя выделено жирным шрифтом)
Message(">%s\n",SegName(0x1000));
c) вызываем функцию SegName для получения имени сегмента
>MySeg
d) результат – имя сегмента
??? #Верстальщику Table Change
аргумент
ea
return

пояснения
линейный адрес, принадлежащий сегменту
=return Пояснение
!=”” имя сегмента
“” ошибка

Родственные функции: SegRename, SegByName
Интерактивный аналог: по умолчанию имя сегмента отображается в адресе каждой ячейки
long FirstSeg()
Функция возвращает линейный адрес начала сегмента с наименьшим линейным
адресом начала. Если не существует ни одного сегмента, функция возвратит значение
BADADDR, сигнализируя об ошибке.
Замечание: FirstSeg обычно используется в паре с NextSeg для получения списка
адресов начала всех существующих сегментов.
Пример использования:
SegCreate(0x1000,0x2000,0x9,0,0,0);
77

SegCreate(0x100,0x200,0x10,0,0,0);
a) создаем два сегмента с адресами начала 0x100 и 0x1000.
Message(">%X\n",FirstSeg());
b) вызываем функцию FirstSeg для получения наименьшего линейного адреса
сегмента
>100
c) результат – линейный адрес сегмента с наименьшим линейным адресом начала
??? #Верстальщику Change Table
аргумент
нет

пояснения
Нет

return

=return
!=BADADDR
==BADADDR

пояснение
линейный
адрес
начала
сегмента
наименьшим линейным адресом
ошибка

с

Родственные функции: NextSeg
Интерактивный аналог: “~View\Segments”
линейный адрес начала сегмента с наименьшим линейным адресом начала -┐
╔═[■]═══=======═════════=═══===══ Program Segmentation ═════════════════│═══========════3═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds


║ seg000
00000000 00000100 at
0010 pri
N FFFF FFFF FFFF 00000100 00000200

║ seg001
00000F70 00001F70 at
0009 pri
N FFFF FFFF FFFF 00001000 00002000



╚═══════════════════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

long NextSeg(long ea)
Функция возвращает линейный адрес начала сегмента, линейный адрес начала
которого больше чем переданный линейный адрес ea. Если сегмента удовлетворяющего
такому условию не существует, функция возвращает значение BADADDDR, сигнализируя
об ошибке.
Передаваемый функции линейный адрес не обязательно должен быть адресом
начала какого-то сегмента, более того, он вообще может не принадлежать ни одному
сегменту – это ничуть не помешает функции возвратить линейный адрес следующего
сегмента, при условии, что такой сегмент есть.
Замечание: NextSeg обычно используется в паре с FirstSeg для получения списка
адресов начала всех существующих сегментов.
Конструкция NextSeg(0) аналогичная FirstSeg() при условии, что
начальные адреса всех сегментов отличны от нуля (как правило, так
практически всегда и бывает).
Замечание: функции, возвращающей линейный адрес начала сегмента, линейный
адрес которого меньше переданного линейного адреса ea, не существует. Тем
не менее, эта задача может быть решена на основе имеющихся функций
FirstSeg и NextSeg
Ниже приводится пример реализации функции PrevSeg, работающей по
следующему алгоритму: вызовами NextSeg сегменты перебираются один за
другим до тех пор, пока линейный адрес начала очередного сегмента не
превысит переданное значение ea. Затем – возвращается линейный адрес
78

начала предыдущего сегмента.
static PrevSegEx(ea)
{
аuto a;
a=0;
while (SegEnd(NextSeg(a))100
>1000
>10000
d) результат – линейные адреса начала всех существующих сегментов
??? #Верстальщику ChangeTable
аргумент
ea
return

пояснение
линейный адрес не обязательно принадлежащий какому-нибудь
сегменту
=return Пояснение
!=BADADDR линейный адрес начала следующего сегмента
==BADADDR ошибка

Родственные функции: FirstSeg
Интерактивный аналог: “~View\Segments”
линейный адрес начала следующего сегмента–-------------------┐
╔═[■]═══════════════════════════════ Program Segmentation ═══════════════════│═════════5═[↑]═╗

Name
Start
End Align Base Type Cls 32es ss ds


║ seg000
00000000 00000100 at
0010 pri
N FFFF FFFF FFFF 00000100 │ 00000200

║ seg001
00000000 00001000 at
0100 pri
N FFFF FFFF FFFF 00001000 ◄ 00002000

║ seg002
00000000 00010000 at
1000 pri
N FFFF FFFF FFFF 00010000
00020000


79



╚═════════════════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘

void SetSelector(long sel,long value)
Функция создает новый селектор или изменяет значение уже существующего
селектора. Создаваемый селектор не должен совпадать с базовым адресом ни одного
сегмента, иначе для базирования этого сегмента будет автоматически использоваться
созданный селектор, а не его базовый адрес, что приведет к искажению всех смещений
внутри сегмента.
Максимальное допустимое количество селекторов равно 4096 (0x1000 в
шестнадцатеричной системе исчисления), а индексы селекторов могут принимать любые
значение от 0x0 до 0xFFFF включительно.
Аргумент sel задает 16-разрядный индекс создаваемого или модифицируемого
селектора. Старшее слово, передаваемого 32-битного значения автоматически
обрезается, в результате чего существует угроза непреднамеренного искажения другого
селектора, что подтверждает следующий эксперимент:
SetSelector(0x1,0x666);
Message(">%X\n",AskSelector(0x1));
a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его
значение
>666
b) селектор имеет значение 0x666
SetSelector(0x10001,0x777);
c) пытаемся создать селектор с индексом 0x10001 и значением 0x777
Message(">%X\n",AskSelector(0x1));
d) проверяем значение селектора 0x1
>777
e) результат – значение селектора 0x1 искажено! Старшие 16 бит индекса 0x10001
были
обрезаны,
в
результате
чего
был
модифицирован
селектор
0x10001 AND 0xFFFF == 0x1
Аргумент value содержит 32-разрядное значение базы сегмента в параграфах. Оно
будет автоматически использовано для базирования сегмента базовый адрес которого
равен индексу данного селектора.
Замечание: функция DeleteAll (см. описание функции DeleteAll), удаляющая все
сегменты, не уничтожает селекторов и их приходится удалять «вручную»
вызовом DelSelector.
??? #Верстальщику Change Table
аргумент
sel
val
return

пояснения
16-разрядный индекс селектора
32-разрядное значение селектора в параграфах
=return пояснение
void функция не возвращает никакого значения

Родственные функции: AskSelector, DelSelector
Интерактивный аналог: “~View\Selector”, - создает новый,
80

изменяет значение уже существующего селектора.
void DelSelector(long sel)
Функция удаляет селектор, если он существует, в противном случае ничего не
происходит. Если данный селектор использовался для базирования сегмента, новый
базовый адрес сегмента будет равен base = sel * 0x10, а смещение первого байта в
сегмента соответственно: startoffset = startea - sel * 0x10. Создав заново уничиженный
селектор вызовом SetSelector, можно все вернуть на свои места.
Аргумент sel задает 16-разрядный индекс удаляемого селектора. Старшее слово,
передаваемого 32-битного значения автоматически обрезается, в результате чего
существует угроза непреднамеренного уничтожения другого селектора, что подтверждает
следующий эксперимент:
SetSelector(0x1,0x666);
Message(">%X\n",AskSelector(0x1));
a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его
значение
>666
b) селектор имеет значение 0x666
DelSelector(0x10001,0x777);
c) пытаемся удалить селектор с индексом 0x10001
Message(">%X\n",AskSelector(0x1));
d) проверяем значение селектора 0x1
>FFFFFFFF
e) результат – селектора 0x1 был уничтожен! Старшие 16 бит индекса 0x10001
были обрезаны, в результате чего был удален селектор 0x10001 AND 0xFFFF == 0x1
??? #Верстальщику Change Table
аргумент
sel
return

пояснения
16-разрядный индекс уничтожаемого селектора
=return пояснение
void функция не возвращает никакого значения

Родственные функции: SetSelector
Интерактивный аналог: “~View\Selectors”,
long AskSelector(long sel)
Функция возвращает значение селектора в параграфах. Если запрошенный
селектор отсутствует, функция возвратит переданное ей значение, сигнализируя об
ошибке (внимание! не BADADDR). Поскольку, значение селектора не может быть равно его
индексу, никакой неоднозначности не возникает, однако, автору книги совершенно
непонятно чем вызвано такое решение – не лучше ли при неуспешном завершении
функции возвращать BADADDR – значение, которое не может иметь ни один селектор?
Аргумент sel задает 16-разрядный индекс запрашиваемого селектора. Старшее
слово передаваемого 32-битного значения автоматически обрезается, в результате
существует возможность обращения совсем к другому селектору, чем предполагалось.
81

Ввиду всего вышесказанного проверка успешности завершения функции должна
выглядеть так:
if ((selvalue=AskSelector(sel)) == (sel & 0xFFFF))
// ошибка
else
// успешное завершение функции
??? #Верстальщику Change Table
аргумент
sel
return

пояснения
16-разрядный индекс запрашиваемого селектора
=return пояснения
sel & 0xFFFF ошибка
!=(sel & 0xFFF) 32-разрядное значение селектора в параграфах

Родственные функции: SetSelector, FindSelector
Интерактивный аналог: “~View\Selectors”
┌─── Индекс селектора

┌─── значение селектора
╔═▼[■]══▼══ Selectors ══4═[↑]═╗
║ Sel Value

║ 0001 00000666

║ 0002 00000999



╚1/2
═══════════════─┘
long FindSelector(long val)
Функция возвращнаходит индекс селектора с указанным значением val,
выраженном в параграфах.
Если существуют два и более селектора с идентиченым значениями, функция
возвращает индекс первого из них в порядке создания. Если же ни одного селектора с
таким значением не существет, функция возращает младшие 16 бит переданного ей
агрумента обратно.
Поскольку, значение селектора не может быть равно его индексу, никакой
неоднозначности не возникает, однако, автору книги совершенно непонятно чем вызвано
такое решение – не лучше ли при неуспешном завершении функции возвращать BADADDR
– значение, которое не может иметь ни один селектор?
Ввиду всего вышесказанного проверка успешности завершения функции должна
выглядеть так:
if ((sel=FindSelector(selvalue)) == (selvalue & 0xFFFF))
// ошибка
else
// успешное завершение функции
Замечание: поскольку с данным базовым адресом может существовать только
один сегмент, в создании двух и более селекторов с одинаковыми значениями
никакой необходимости нет. Тем не менее, функция SetSelector не
препятствует этому и существования двух и более селекторов с одинаковыми
индексами в принципе возможно, – один селектор может использоваться для
базирования некоторого сегмента, а остальные простаивать. В такой
82

ситуации функция FindSelector может возвратить неверный результат,
поэтому, перед ее вызовом следует убедиться, что существует не более
одного селектора с каждым значением. Единственный документированный
способ решения этой задачи заключается в последовательном переборе всех
возможных селекторов в интервале от 0x0 до 0xFFFF.
??? #Верстальщику Change Table
аргумент
val
return

пояснения
32-разрядное значение селектора в параграфах для поиска
=return пояснение
==(val & 0xFFFF) ошибка
!=(val & 0xFFF) 16-разрядный индекс селектора с указанным
значением

Родственные функции: SetSelector, AskSelector
Интерактивный аналог: “~View\Selectors”
┌─── Индекс селектора

┌─── значение селектора
╔═▼[■]══▼══ Selectors ══4═[↑]═╗
║ Sel Value

║ 0001 00000666

║ 0002 00000999



╚1/2
═══════════════─┘
ЭЛЕМЕМЕНЫ
#Defenition
С каждым адресом виртуальной памяти связано 32-разрядное поле флагов (см.
главу «Виртуальная память»). Флаги хранят содержимое ячейки, описывают ее
представление и указывают на наличие связанных с ней объектов.
Содержимое ячейки: флаги хранят содержимое ячейки виртуальной памяти (в
восьми младших битах) или указывают на то, что ячейка содержит неинициализированное
значение (см. главу «Виртуальная память»).
Представление: флаги определяют будет ли отображаться данный байт в виде
данных или машинной инструкции. Они так же позволяют уточнить в виде каких именно
данных (массива, строки, переменной, непосредственного значения или смещения) он
должен отображаться (см. главу «Типы элементов»)
Связанные объект: флаги указывают на наличие на наличие связанных с ячейкой
объектов – меток, имен, комментариев и т.д. Сами объекты хранятся в отдельном
виртуальном массиве, проиндексированного линейными адресами. В принципе без флагов,
ссылающихся на объекты можно было бы и обойтись, но тогда бы пришлось при
отображении каждой ячейки просматривать все виртуальные массивы на предмет поиска
объектов, ассоциированных с данным линейными адресом, что отрицательно сказалось бы
на производительности дизассемблера. Напротив, перенос этой информации в флаги
позволяет ускорить работу – обращение к виртуальному массиву происходит только в тех
случаях, когда с ячейкой заведомо связан какой-то объект. (см. главу «Объекты»)
Размер машинных инструкций и типов данных различен, и это не дает возможности
быстро определить к какой инструкции (переменной) принадлежит ячейка с таким-то
линейным адресом. Можно провести следующую аналогию – текст со словами не

83

отделенными друг от друга пробелами из произвольной точки читать невозможно,
поскольку неизвестно чем является та или иная буква – началом слова или его серединой.
Образно говоря, при дизассемблировании IDA разбивает исследуемый файл на
«слова», в терминологии разработчиков - элементы.
Элементом называется последовательность смежных ячеек виртуальной памяти,
содержащих одну самостоятельную конструкцию языка ассемблера, с которой можно
оперировать как с одним целым, – инструкцию, строку, переменную и т.д.
Первая ячейка элемента называется головой, а все, последующие за ней –
хвостом. Элементы единичной длины состоят только из головы и не имеют хвоста.
Признаком головы является ненулевое значение бита FF_DATA (см. таблицу 10)
поля флагов. Соответственно признаком хвоста является нулевое значение бита FF_DATA
и ненулевое значение бита FF_TAIL.
Свойства элемента определяются свойствами его головы. Свойства головы
определяются флагами, связанными с данной ячейкой виртуальной памяти.
Существуют элементы двух видов – элементы кода (CODE) и элементы
данных.(DATE).
Вид элемента задается сочетанием дух битов FF_DATA и FF_TAIL следующим
образом (см. таблицу 11) – если бит головы (FF_DATA) установлен, то значение бита
хвоста (FF_TAIL) интерпретируется типом элемента – его единичное значение задает тип
CODE, в противном случае – DATA; напротив, если бит головы сброшен, единичное
значение бита FF_TAIL трактуется признаком хвоста элемента; если же оба бита FF_DATA
и FF_TAIL сброшены – элемент считается неопределенным (unexplored), т.е.
несуществующим.


A

обозначение
назначение




FF_DATA

маска



0x400

бит

Голова

9
поле флагов
FF_TAIL
голова
хвост
тип элемента
признак
хвоста
==0
==1
==1
DATA CODE
0x200

8
FF_IVL
==1
==0
инициализи не
рованный
инициализи
байт
рованный
байт
0x100

7

1
0
поле содержимого
MS_VAL
содержимое
ячейки

0xFF

Таблица 10 устройство элемента
FF_DATA
0

1

неопределенный элемент
FF_UNK

элемент данных, голова
FF_DATA

хвост элемента
FF_FAIL

элемент кода, голова
FF_CODE

0

FF_TAIL

1

Таблица 11 Определение типа элемента
Хвостовые флаги в двенадцати старших битах содержат смещения относительно
начала и конца элемента. Флаги, расположенные по четному линейному адресу –
относительно конца, а флаги расположенные по нечетному линейному адресу –
относительно его начала.

84

Это позволяет легко определить к какому элементу принадлежит такая-то ячейка, а
так же узнать линейный адрес начла и конца элемента, на основании которых легко
вычислить его длину.
Замечание: в файле , входящим в IDA SDK определена
функция, возвращающая значение хвостовых бит “inline ushort gettof(flags_t F) { return
ushort((F & TL_TOFF) >> TL_TSFT); }”
Внимание: созвать новые объекты, использовать или изменять хвостовые
биты может только ядро IDA, но не пользовательские модули! В противном
случае это может привести к некорректной работе и зависанию дизассемблера.
Элементы могут существовать только внутри сегментов – попытка создания
элемента по адресу, не принадлежащему ни одному сегменту, обречена на провал.
Навигатор по функциям
Созданием элементов всецело занимается ядро IDA; пользовательские скрипты
хотя и имеют достаточные для «ручного» создания элементов привилегии, пользоваться
этими привилегиями категорически не рекомендуется - даже незначительная ошибка
способна вызвать непредсказуемое поведения дизассемблера вплоть до его полного
«зависания».
Напротив, работая с уже созданными ядром элементами, пользователь может
решить ряд задач, облегчающих дизассемблирование исследуемого файла. Пусть,
например, требуется отыскать в тексте все условные переходы, стоящие после инструкции
“test”, следующей за вызовом функции, иначе говоря, произвести поиск по шаблону “call
*\test *,*\j? *” и вывести протокол отчета.
Это можно осуществить с помощью следующего листинга, сердцем которого
является цикл, вызывающий функцию NextHead, последовательно переходящей от одной
машинной инструкции к другой:
#include
static main()
{
auto a;
a=0;
while(a!=BADADDR)
{
if (isCode(GetFlags(a)))
if( (GetMnem(a)=="call")
&& (GetMnem(NextHead(a,BADADDR))=="test")
&& (Byte(NextHead(NextHead(a,BADADDR),BADADDR)) > 0x6F)
&& (Byte(NextHead(NextHead(a,BADADDR),BADADDR)) < 0x80))
Message(">%s %4s %s\n>%s %4s %s,%s\n>%s %s %s\n>-------\n",
atoa(a),GetMnem(a),GetOpnd(a,0),
atoa(NextHead(a,BADADDR)),
GetMnem(NextHead(a,BADADDR)),
GetOpnd(NextHead(a,BADADDR),0),
GetOpnd(NextHead(a,BADADDR),1),
atoa(NextHead(NextHead(a,BADADDR),BADADDR)),
GetMnem(NextHead(NextHead(a,BADADDR),BADADDR)),
GetOpnd(NextHead(NextHead(a,BADADDR),BADADDR),0));

85

}
}

a=NextHead(a,BADADDR);

Результат его работы может быть следующим (в данном примере использовался
файл first.exe –см. главу «Первые шаги с IDA Pro»)
>004010C0 call ostream::opfx(void)
>004010C5 test eax,eax
>004010C7 jz loc_4010E0
>-------------------------->0040111F call ios::~ios(void)
>00401124 test [esp+4+arg_0],1
>00401129 jz loc_401132
>-------------------------->004011BE call ios::~ios(void)
>004011C3 test [esp+4+arg_0],1
>004011C8 jz loc_4011D1
>--------------------------...
Две функции NextHead и PrevHead обеспечивают прямую (от младших адресов к
страшим) и обратную (от старших к младшим) трассировку элементов, возвращая
линейный адрес головы следующего элемента или значение BADADDR если достигнут
последний элемент в цепочке.
Это позволяет рассматривать анализируемый код не как поток бессвязных байт, а
упорядоченную последовательность инструкций и данных. Разница между ними
заключается в том, что поиск байт из интервала 0x70-0x7F в первом случае обнаружит не
только все условные переходы, но и множество других инструкций и данных, частью
которых является ячейка с таким значением, например, “DW 6675h”; “MOV AX, 74h”;
напротив, во втором случае можно быть уверенным, что анализируется именно начало
инструкции, а не нечто иное.
Разбивка потока байт на элементы позволяет решить и другую задачу –
определить какой именно инструкции (или данным) принадлежит такой-то линейный адрес,
т.е. по линейному адресу ячейки определить адрес головы элемента, которому эта ячейка
принадлежит.
Манипулируя с флагами узнать это достаточно просто – достаточно
проанализировать старшие 12 бит хвостовых атрибутов – флаги, расположенные по
четному линейному адресу – содержат количество оставшихся байт до конца элемента, а
флаги расположенные по нечетному линейному адресу – до его начала.
Реализация функции, возвращающей адрес головы элемента по любому
принадлежащему элементу адресу может выглядеть так:
#include
static MyGetHead(ea)
{
auto off,F;
F=GetFlags(ea);
if (!F) return -1;
if (!(F & FF_TAIL))
return ea;

//Адрес не принадлежит ни одной ячейке
//Это голова, возвращает ее адрес

if (ea & 1)
// ...нечетный линейный адрес
return (ea - (F >> 20));

86

}

return MyGetHead(ea-1);

// ...четный линейный адрес

Недостатком такого подхода является его потенциальная несовместимость с
последующими версиями IDA Pro, но обойтись для решения проблемы одними лишь
высокоуровневыми функциями, встроенными в IDA, не получается.
Задача могла бы быть решена при помощи вызовов функций ItemSize и ItemEnd,
предназначенных для вычисления длины и адреса конца элемента соответственно,
очевидно, адрес начала элемента равен ItemEnd(ea) – ItemSize(ea), но ItemSize
возвращает не размер элемента, а смещение до его конца!
Ниже приведен другой вариант реализации функции MyGetItemHeadEA, вместо
низкоуровневых манипуляций с флагами, использующий вызов PrevHead, однако,
полностью отказаться от обращений к флагам не удается – приходится вызывать макрос
isTail, определенный в файле для проверки не является ли переданный адрес
адресом головы элемента.
static MyGetItemHeadEA(ea)
{
if (!GetFlags(ea)) return –1; // ошибка
if (!isTail(GetFlags(ea))
return ea;
return PrevHead(ea,0);

// голова

}
Пара функций NextNotTail и PrevNotTail возвращают адрес следующего
(предыдущего) элемента или бестипового байта. В полностью дизассемблированной
программе не должно быть ни одного бестипового байта, но они могут присутствовать на
промежуточной стадии анализа – все ячейки, на которые IDA не смогла распознать ни
одной ссылки, она оставляет бестиповыми байтами, которые надлежит перевести в
элементы кода или данных пользователю.
Сводная таблица функций
функции, возвращающие основные характеристики элементов
имя функции
краткое описание
long ItemSize (long ea)
возвращает расстояние до конца элемента
(не его размер!)
long ItemEnd (long ea)
возвращает
значение
на
единицу
превышающее линейный адрес конца
элемента
функции трассировки элементов
имя функции
краткое описание
возвращает линейный адрес следующей
long NextHead (long ea)
long NextHead (long ea, long maxea) головы элемента
возвращает линейный адрес предыдущей
long PrevHead (long ea)
головы элемента
long PrevHead (long ea, long minea)
long NextNotTail (long ea)
возвращает линейный адрес следующей
головы элемента или неопределенного
байта
long PrevNotTail (long ea)
возвращает линейный адрес предыдущей
головы элемента или неопределенного
байта
87

long ItemSize(long ea)
В контекстной помощи IDA сообщается, что эта функция определяет размер
элемента, как и следует из ее названия. На самом же деле она возвращает расстояние в
байтах до конца элемента, что доказывает следующий эксперимент:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

auto a,b;
a=SegByName("seg000");
for(b=0;bea:1000 -H-> 14
>ea:1001 -e-> 13
>ea:1002 -l-> 12
>ea:1003 -l-> 11
>ea:1004 -o-> 10
>ea:1005 -,-> 9
>ea:1006 -I-> 8
>ea:1007 -D-> 7
>ea:1008 -A-> 6
>ea:1009 - -> 5
>ea:100A -P-> 4
>ea:100B -r-> 3
>ea:100C -o-> 2
>ea:100D -!-> 1
>ea:100E -1-> 4
>ea:100F -2-> 3
>ea:1010 -3-> 2
>ea:1011 -4-> 1
c) результат: функция ItemSize возвращает расстояние до конца объекта в байтах
Минимальное значение, возвращаемое функцией, равно единице. Это же значение
возвращается при возникновении ошибки – если функции передать адрес, не
принадлежащий ни одному элементу.
Альтернативная реализация функции ItemSize содержится в файле “kpnc.idc”,
находящимся на диске, прилагаемом к этой книге. Ее исходный код приведен ниже (см.
MyGetItemSize)
static MyGetItemHeadEA(ea)
{
if (GetFlags(ea) & FF_DATA) // голова
return ea;
if (GetFlags(ea) & FF_TAIL) // хвост
return PrevHead(ea,0);
// если не голова и не хвост - ошибка
return -1;
}
88

static MyGetItemSize(ea)
{
if (GetFlags(ea) & MS_CLS) // элемент?
return ItemEnd(ea) - MyGetItemHeadEA(ea);
}

return -1;

Пример использования:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

auto a,b;
a=SegByName("seg000");
for(b=0;bea:1000 -H-> 15
>ea:1001 -e-> 15
>ea:1002 -l-> 15
>ea:1003 -l-> 15
>ea:1004 -o-> 15
>ea:1005 -,-> 15
>ea:1006 -I-> 15
>ea:1007 -D-> 15
>ea:1008 -A-> 15
>ea:1009 - -> 15
>ea:100A -P-> 15
>ea:100B -r-> 15
>ea:100C -o-> 15
>ea:100D -!-> 15
>ea:100E -1-> 5
>ea:100F -2-> 5
>ea:1010 -3-> 5
>ea:1011 -4-> 5
c) результат – корректное выполнение функции
??? #Верстальщику – Change Table
аргумент
ea
return

пояснение
линейный адрес, принадлежащий элементу
=return Пояснения
!=0 расстояние до конца элемента в байтах (не его
размер!)
==1 ошибка

Родственные функции: нет

89

Интерактивный аналог: нет
long ItemEnd(long ea)
Функция возвращает значение на единицу превышающее линейный адрес конца
элемента, заданного линейным адресом ea.
Если переданный адрес не принадлежит ни одному сегменту, функция возвратит
единицу, сигнализируя об ошибке.
Замечание: если по указанному адресу расположен бестиповой байт (т.е.
переданный адрес не принадлежит ни одному элементу), функция возвратит не
ошибку, а линейный адрес следующей ячейки.
Пример использования:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

Message(“>%s\n”,atoa(ItemEnd(SegByName(“seg000”))));
b) вызываем функцию ItemEnd, передавая ей адрес, принадлежащий элементу
“Hello, IDA Pro!”.
>seg000:000E
c) результат – функция вернула значение, на единицу превышающее адрес конца
данного элемента
??? #Верстальщику Change Table
аргумент
ea
return

пояснение
линейный адрес, принадлежащий элементу или бестиповому байту
=return пояснения
!=1 значение на единицу превышающее линейный
адрес конца элемента
==1 ошибка

Родственные функции: нет
Интерактивный аналог: нет
long NextHead(long ea)
(версия IDA 3.85 и младше)
Возвращает линейный адрес головы следующего элемента, в противном случае –
BADADDR, сигнализируя об ошибке. Переданный функции линейный адрес ea не
обязательно должен принадлежать какому-то элементу – он может даже вообще не
существовать.
Пример использования:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

90

Message(“>%s\n”,atoa(NextHead(SegByName(“seg000”))));
b) вызываем функцию NextHead, передавая ей адрес, принадлежащий элементу
“Hello, IDA Pro!”.
>seg000:000E
c) результат – функция вернула адрес головы следующего элемента.
Другой пример использования функции NextHead приведен в файле ,
распространяемого вместе с IDA.
Замечание: линейный адрес начала следующего элемента будет возвращен
даже в том случае, если элемент находится в другом сегменте.
??? #Верстальщику – Change Table
аргумент
ea
return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
=return пояснения
!=BADADDR линейный адрес головы следующего элемента
==BADADDR ошибка

Родственные функции: PrevHead
Интерактивный аналог: нет
long NextHead(long ea, long maxea)
(версия IDA 4.0 и старше)
В версии 4.0 прототип функции NextHead(long ea) (см. ее описание) был изменен,
добавлением еще одного аргумента – maxea, ограничивающего диапазон адресов,
доступных функции.
Функция возвращает адрес головы следующего элемента, при условии, что он
меньше, чем maxea, т.е. отвечает следующему условию ea < return value < maxea .
Изменение прототипа повлекло за собой неработоспособность всех ранее
созданных скриптов, использующих эту функцию и необходимости внесения в них
исправлений – замены NextHead(ea) на NextHead(ea, BADADDR).
Замечание: ограничение максимального адреса облегчает написание скриптов,
работающих с выделенными регионами (см. описание функций SelStart и SelEnd)
??? #Верстальщику – Change Table
аргумент
ea
maxea
return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
значение на единицу превышающее наибольший адрес, доступный
функции
=return пояснения
!=BADADDR линейный адрес головы следующего элемента
==BADADDR ошибка

Родственные функции: PrevHead
91

Интерактивный аналог: нет
long PrevHead(long ea)
(версия IDA 3.85 и младше)
Функция возвращает линейный адрес предыдущей головы элемента (не головы
предыдущего элемента!). Если указать на хвост элемента, функция возвратит адрес его
головы. Переданный функции линейный адрес ea не обязательно должен принадлежать
какому-то элементу – он может даже вообще не существовать.
Пример использования:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

Message(“>%s\n”,atoa(PrevHead(SegByName(“seg000”)+0x2)));
b) вызываем функцию PrevHead, передавая ей адрес принадлежащий элементу
“Hello, IDA Pro!”.
>seg000:000E
c) результат – функция вернула адрес предыдущей головы элемента (этого
элемента)
??? #Верстальщику – Change table
аргумент
ea

return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
=return пояснения
!=BADADDR линейный адрес предыдущей головы элемента (не
головы предыдущего элемента!)
==BADADDR ошибка

Родственные функции: NextHead
Интерактивный аналог: нет
long PrevHead(long ea, long minea)
(версия IDA 4.0 и старше)
В версии 4.0 прототип функции PrevHead(long ea) (см. ее описание) был изменен,
добавлением еще одного аргумента – minea, ограничивающего диапазон адресов,
доступных функции.
Функция возвращает адрес предыдущей головы элемента, при условии, что он не
меньше, чем minea, т.е. отвечает следующему условию minea ≥ return value > ea .
Изменение прототипа повлекло за собой неработоспособность всех ранее
созданных скриптов, использующих эту функцию и необходимости внесения в них
исправлений – замены PrevHead(ea) на PrevHead(ea, 0).
Замечание: ограничение максимального адреса облегчает написание скриптов,
работающих с выделенными регионами (см. описание функций SelStart и SelEnd)

92

??? #Верстальщику – Change Table
аргумент
ea
minea
return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
минимальный адрес, доступный функции
=return пояснение
!=BADADDR линейный адрес предыдущей головы элемента (не
головы предыдущего элемента!)
==BADADDR ошибка

Родственные функции: NextHead
Интерактивный аналог: нет
long NextNotTail(long ea)
Функция возвращает линейный адрес головы следующего элемента или
бестипового байта. Переданный функции линейный адрес ea не обязательно должен
принадлежать какому-то элементу – он может даже вообще не существовать.
Пример использования:
seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

Message(“>%s\n”,atoa(NextNotTail(0)));
b) вызываем функцию NextNotTail, передавая ей нулевой адрес.
>seg000:0000
c) результат – функция вернула адрес головы первого элемента
??? #Верстальщику – Change table
аргумент
ea
return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
=return пояснения
!=BADADDR линейный адрес головы следующего элемента или
бестипового байта
==BADADDR ошибка

Родственные функции: PrevNotTail
Интерактивный аналог: нет
long PrevNotTail(long ea)
Функция возвращает линейный адрес головы предыдущего элемента (не
предыдущей головы элемента!). Переданный функции линейный адрес ea не обязательно
должен принадлежать какому-то элементу – он может даже вообще не существовать.
Пример использования:

93

seg000:0000 aHelloIdaPro
seg000:000E a1234
a) исходные данные

db 'Hello,IDA Pro!'
db '1234'

Message(“>%s\n”,atoa(NextNotTail(BADADDR)));
b) вызываем функцию PrevNotTail, передавая ей значение BADADDR
>seg000:000E
c) результат – функция вернула адрес головы самого последнего из существующих
элемента
Замечание: в отличие от функции NextNotTail, функция PrevNotTail игнорирует
бестиповые байты.
??? #Верстальщику – Change table
аргумент
ea
return

пояснение
линейный адрес, не обязательно принадлежащий какому-то
элементу
==return пояснения
!=BADADDR линейный адрес головы предыдущего элемента
==BADADDR ошибка

Родственные функции: PrevNotTail
Интерактивный аналог: нет
ТИПЫ ЭЛЕМЕНТОВ
#Definition
Поле флагов головы элемента определяет является ли данный элемент элементом
кода или элементом данных (см. главу «Элементы»), а так же уточняет его тип. Например,
один и та же цепочка из четырех байт может быть двойным словом, типом float, ASCIIстрокой, массивом байт или двойных слов и т.д.
Замечание: типотизация данных – одно из немаловажных достоинств IDA Pro,
ощутимо
повышающее
качество
дизассемблирования.
В
полностью
дизассемблированной программе не должно быть ни одного байта данных
неопределенного типа.
IDA Pro поддерживает следующие типы данных – байт, слово, двойное слово,
четвертное слово, восьмерное слово, float, double, packed real, ASCII-строка, массив,
состоящих из любых вышеперечисленных типов, а так же тип align – байты,
использующиеся для выравнивания кода (данных) по кратным адресам (см. таблицу 12)
??? #Верстальщику – change table
константа
FF_BYTE
FF_WORD
FF_DWRD
FF_QWRD
FF_TBYT

#
0x00000000
0x10000000
0x20000000
0x30000000
0x40000000

тип
байт
слово
двойное слово
четвертное слово
восьмерное слово
94

FF_ASCI
FF_STRU
FF_XTRN
FF_FLOAT
FF_DOUBLE
FF_PACKREAL
FF_ALIGN

0x50000000
0x60000000
0x70000000
0x80000000
0x90000000
0xA0000000
0xB0000000

ASCII-строка
структура
внешние данные неизвестного размера
float
double
упакованное десятичное целое
директива выравнивания

Таблица 12 поддерживаемые типы данных
Цепочка бестиповых байт может быть преобразована в любой поддерживаемый
IDA Pro тип данных, при условии что имеет достаточный для такой операции размер. Уже
существующий элемент данных, также может быть преобразован в данные другого типа,
если имеет достаточный для такой операции размер, либо за его хвостом следует цепочка
бестиповых байт необходимой длины.
Если в результате преобразования, размер элемента уменьшается, его остаток
преобразуется в один или несколько бестиповых байт.
Примеры:
seg000:0000 Var
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005 Var2
seg000:0006
seg000:0007
seg000:0008
seg000:0009
seg000:000A

db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
49h
44h
41h
20h

;
;
;
;
;

H
e
l
l
o

;
; I
; D
; A
;

Бестиповая переменная “Var” может быть преобразована в байт, слово, двойное
слово, float, ASCII-строку, но попытка преобразовать ее в четверное, восьмерное слово,
double, packed real приведет к ошибке – поскольку тому препятствует элемент данных,
расположенный по адресу “seg000:0005”. Если же его уничтожить, преобразование пройдет
успешно. Аналогично:
seg000:0000 Var
seg000:0002
seg000:0003

dw 6548h
db 6Ch
db 6Ch

а) преобразование Var в двойное
слово возможно

seg000:0000 Var
seg000:0002

dw 6548h
dw 6C6Ch

b) преобразование Var в двойное слово
невозможно – требуется предварительно
уничтожить следующий за ним элемент

Два и более последовательных элементов одного типа могут быть объединены в
массив – как бы макроэлемент, собирающий их всех под одну крышу. С массивом IDA Pro
работает как с единым целым, в частности, в начале каждой строки указывает не адрес
текущего элемента, а адрес начала массива:
seg000:0000
seg000:0000

db 6Ch, 6Fh, 2Ch, 20h, 49h, 44h, 41h, 20h, 50h, 72h, 6Fh
db 21h, 0

Функции трассировки элементов (см. главу «Элементы») будут возвращать только
адреса начала массива и следующего за массивом элемента, но не адреса элементов
самого массива! Аналогично, функции изменения представления операндов (см. главу

95

«Операнды») будут изменять представление всех элементов массива одновременно, но
никак не выборочно.
Поэтому, разумно объединять в массив данные лишь тогда, когда они имеют
одинаковый тип, одинаковое представление и нет никакой необходимости в
индивидуальной работе ни с одним элементом. В противном случае создание массива
доставит больше неудобств, чем пользы.
Важно понять – массив в IDA Pro это один элемент, а не совокупность множества
элементов другого типа.
Максимальная длина элемента ограничена четырьмя килобайтами – это связано с
тем, что под хвостовые биты, содержащие смещения относительно начала (конца)
элемента отведено 12 разрядов, отсюда и ограничение на длину.
Если требуется создать строку или массив большего размера, единственный выход
– создать два (или более) массивов (строк), расположив их последовательно друг за
другом.
Элемент данных может иметь тип align – указывающий, что эти байты
используются для выравнивания кода (данных) по кратным адресам. Формально тип align –
такой же точно тип как, например, слово или двойное слово, и с ним можно выполнять
точно те же операции, что и над любым другим элементом данных. Единственное его
отличие от состоит в том, что принадлежащие ему байты, в ассемблерный листинг не
попадают:
seg000:0000 db
seg000:0001 db
seg000:0002 db

48h ; H
65h ; e
6Ch ; l

seg000:0000
seg000:0001
seg000:0002

db 48h ; H
align 2
db 6Ch ; l

Элемент кода не имеет никаких типов, разрядность инструкций определяется
разрядностью сегмента, а логика работы дизассемблера – типом выбранного процессора.
Элемент данных не может быть непосредственно преобразован в элемент кода и,
соответственно, наоборот. Элемент кода может быть создан только из цепочки бестиповых
байт достаточной длины.
Если после создания элемента кода, IDA может определить адрес следующей
выполняемой инструкции, она автоматически пытается создать в соответствующем месте
очередной элемент кода – так продолжается до тех пор, пока ей не встретится инструкция
передающее управление по адресу, который IDA вычислить не в состоянии. Это может
быть, например, регистровый переход, команда выхода из подпрограммы (прерывания) и
т.д. Возможности создания одного элемента кода в IDA Pro нет.
Строго говоря, элементы кода и данных не являются неделимым целым – IDA Pro
предоставляет возможность выборочной работы с их операндами (если они есть) – см.
главу «Операнды».
Навигатор по функуциям
Группа функций MakeByte, MakeWord, MakeDword, MakeQword, MakeFloat,
MakeDouble, MakePackedReal, MakeTbyte предназначена для преобразования цепочки
бестиповых байт (или уже существующего элемента данных) в элемент данных типа байт,
слово, двойное слово, четвертное слово, float, double, PackedReal и восьмерное слово
соответственно.
Интерактивный аналог первый трех функций пункт “Data” меню “~Edi” (или «горячая
клавиша “D”»>, циклично преобразующий тип элемента, находящегося под курсором в
байт, слово и двойное слово. При необходимости в эту последовательность можно
включить и другие типы данных, вызвав диалог “Setup data types” из меню “Options”
(«горячая клавиша – “Alt-D”»).
Функция MakeStr преобразует цепочку бестиповых байт в ASCII-строку заданной
длины или попытается определить ее автоматически. Автоматически распознаются длины
следующих типов строк– ASCIIZ-строк, заканчивающихся символом нуля; PASCAL-строк,
96

начинающихся с байта, содержащего длину строки и DELPHI-строк, начинающиеся со
слова (двойного слова), содержащего длину строки. Если строка не принадлежит ни к
одному из этих трех типов, концом строки считается:
а) первый нечитабельный ASCII-символ.
b) неинициализированный байт
c) голова элемента кода или данных
d) конец сегмента
Функция MakeArray создает массив состоящий из данных одного типа – байтов,
слов, двойных слов, четверных слов, двойных слов в формате float, четверных слов в
формате double, packed real, tbyte. Бестиповые байты могут стать частью массива любого
типа. Строки не могут быть элементами никакого массива.
Тип массива определяется типом его первого элемента. Все остальные элементы
массива на момент его создания должны быть представлены бестиповыми байтами, последовательность типизированных данных не может быть преобразована в массив.
Элементы массива записываются в строку, отделяясь друг от друга знаком
запятой. Если два или более подряд идущих элемента имеют одно и то же значение (в том
числе и неинициализированное) для сокращения ассемблерного листинга используется
конструкция “DUP”.
Функция Align помещает в ассемблерный файл директиву выравнивания align и
исключает из дизассемблируемого листинга байты, используемые для выравнивания.
Функция MakeCode создает по указанному адресу элемент кода, выполняя
дизассемблирование первой машинной инструкции. Если это возможно, автоматически
дизассемблируется и другие инструкции. Это происходит в следующих случаях:
а) текущая инструкция не изменяет нормального выполнения программы и за ее
концом расположены бестиповые байты;
b) текущая инструкция изменяет нормальное выполнение программы, осуществляя
переход по непосредственному адресу, тогда IDA продолжит дизассемблирование с этого
адреса
Если встречается инструкция, изменяющая адрес перехода непредсказуемым
образом (например, RET) IDA прекращает дизассемблирование.
Во время дизассемблирования IDA при необходимости создает перекрестные
ссылки и автогенерируемые метки.
Функция MakeUnkn разрушает элемент, заданный любым принадлежащим ему
адресом, превращая его содержимое в бестиповые байты. Объекты, связанные с
элементом (например, метки, комментарии) при этом не уничтожаются.
Сводная таблица функций
функции создания новых элементов, преобразования и уничтожения элементов
имя функции
каткое описание
success MakeByte(long ea)
создает (преобразует) ячейку в байт
success MakeWord(long ea)
создает (преобразует) ячейку в слово (2 байта)
success MakeDword(long
создает (преобразует) ячейку в двойное слово (4
ea)
байта)
success MakeQword(long
создает (преобразует) ячейку в четвертное слово
ea)
(8 байт)
success MakeFloat(long ea)
создает (преобразует) ячейку в тип float
(представление с плавающей запятой 4 байта)
success MakeDouble(long
создает (преобразует) ячейку в
тип Double
ea)
(представление с плавающей запятой 8 байт)
success MakePackReal(long создает (преобразует) ячейку в тип PackReal (от 10
ea)
до 12 байт)
97

success MakeTbyte(long ea) создает (преобразует) ячейку в тип Tbyte (10 байт)
success MakeStr (long
создает ASCII строку
ea,long endea)
success MakeArray (long
создает массив
ea,long nitems)
success MakeAlign(long
создает директиву выравнивания
ea,long count,long align)
long MakeCode(long ea)
дизассемблирует одну (или больше) инструкций
void MakeUnkn (long ea,long уничтожает элемент
expand);
функции возвращающие свойства элементов
имя функции
краткое описание
char GetMnem (long ea)
возвращает мнемонику инструкции в символьном
виде
функции, поиска элементов
имя функции
краткое описание
long FindCode(long ea, long возвращает
линейный
адрес
ближайшего
flag)
элемента кода
long FindData(long ea,long возвращает
линейный
адрес
ближайшего
flag)
элемента данных
long
FindUnexplored(long возвращает
линейный
адрес
ближайшего
ea,long flag)
бестипового байта
long FindExplored(long ea, возвращает
линейный
адрес
ближайшего
long flag);
элемента
success MakeByte(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
байт.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в байт, а хвост элемента (если он есть) – в бестиповые байты.
Если по данному адресу находится хвост элемента данных, голова или хвост
элемента кода, функция возвратит ошибку.
Наличие неинициализированных байт в создаваемом или преобразуемом элементе
не является препятствием для выполнения этой функции.
Пример использования:
1. эксперимент
seg000:0000
a) исходные данные

db ? ; unexplored

Message(“>%x\n”,MakeByte(SegByName(“seg000”)));
b) вызываем функцию MakeByte для создания нового элемента данных типа байт,
передавая ей адрес бестипового байта
seg000:0000
db ?
>1
с) результат – элемент данных типа байт успешно создан
Замечение: в ассемблерном листинге бестиповые байты помечаются
комментарием “unexplored” (или ASCII кодом содержимого), сигнализирующим о
неисследованости данной ячейки. В полностью дизассемблированной программе
не должно остаться ни одного неисследованого байта – тип каждой ячейки
должен быть задан явно.
98

2. эксперимент
seg000:0000 aHelloSailor
a) исходные данные

db 'Hello, Sailor'

Message(“>%x\n”,MakeByte(SegByName(“seg000”)));
b) вызываем функцию MakeByte для преобразования элемента типа «строка» в
элемент типа «байт», передавая ей линейный адрес головы данного элемента
seg000:0000 aHelloSailor
db
48h
seg000:0001
db
65h ; e
seg000:0002
db
6Ch ; l
seg000:0003
db
6Ch ; l
seg000:0004
db
6Fh ; o
seg000:0005
db
2Ch ; ,
seg000:0006
db
20h ;
seg000:0007
db
53h ; S
seg000:0008
db
61h ; a
seg000:0009
db
69h ; i
seg000:000A
db
6Ch ; l
seg000:000B
db
6Fh ; o
seg000:000C
db
72h ; r
seg000:000D
db
66h ; f
>1
c) результат- успешное преобразование; хвост элемента типа строка преобразован
в бестиповые байты.
3. эксперимент
seg000:0000 aHelloSailor
a) исходные данные

db 'Hello, Sailor'

Message(“>%x\n”,MakeByte(1+SegByName(“seg000”)));
b) вызываем функцию MakeByte для преобразования ячейки, принадлежащей
хвосту элемента данных типа «строка», в элемент типа «байт».
seg000:0000 aHelloSailor
db 'Hello, Sailor'
>0
c) результат – функция возвратила ошибку, не выполнив преобразования
4. эксперимент
seg000:0000
a) исходные данные

PUSH AX

Message(“>%x\n”,MakeByte(1+SegByName(“seg000”)));
b) вызываем функцию MakeByte для преобразования элемента кода в элемент
данных типа байт
seg000:0000
PUSH AX
>0
c) результат – функция возвратила ошибку, не выполнив преобразования
??? #Верстальщику – change table
аргрумент
ea

пояснения
линейный адрес бестипового байта или головы элемента данных
99

return

=return
==1
==0

пояснения
успешное завершение
ошибка

Родственные функции: MakeWord,
MakeDouble, MakePAckReal, MakeTbyte.

MakeDword,

MakeQword,

MakeFloat,

Интерактивный аналог: “~Edit\Data”;
success MakeWord(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
слово, длиной два байта. Порядок следования младших и старших байт зависит от
выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт
располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в слово, а хвост элемента (если он есть) – в бестиповые байты.
Если размер элемента недостаточен для преобразования, но следом за его хвостом
находятся бестиповые байты, они будут автоматически присоединены к новому элементу.
В противном случае (если размер элемента недостаточен для преобразования, а следом
за его хвостом находится другой элемент не находится ничего) функция возвратит ошибку
не выполнив преобразования. Для выполнения такого преобразования необходимо
предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание
функции MakeUnkn)
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все слово
приобретает неинициализированное значение.
Пример использования:
1. эксперимент
seg000:0000
seg000:0001
a) исходные данные

db ? ; unexplored
db ? ; unexplored

Message(“>%x\n”,MakeWord(SegByName(“seg000”)));
b) вызываем функцию MakeWord для создания нового элемента данных типа
слово, передавая ей адрес цепочки из двух бестиповых байта
seg000:0000
dw ?
>1
с) результат – элемент данных типа слво успешно создан
2.эксперимент
seg000:0000
seg000:0001
a) исходные данные

db ? ; unexplored
db ?

Message(“>%x\n”,MakeWord(SegByName(“seg000”)));
b) вызываем функцию MakeWord для создания нового элемента данных типа
слово, передавая ей адрес бестипового байта
seg000:0000
seg000:0001

db ? ; unexplored
db ?
100

>0
c) результат – функция завершилась с ошибкой, т.к. для создания нового элемента
не достаточно места – по адресу seg000:0001 находится элемент данных типа байт,
который не может быть автоматически присоединен к слову. Для выполнения
преобразования его необходимо уничтожить вызовом MakeUnkn
MakeUnkn(SegByName("seg000")+1,0);
d) вызываем функцию MakeUnkn для удаления элемента данных, расположенного
по адресу “seg000:0001”
seg000:0000
db ? ; unexplored
seg000:0001
db ? ; unexplored
e) резултат – элемент данных успешно разрушен.
Message(“>%x\n”,MakeWord(SegByName(“seg000”)));
f) повторно вызываем функцию MakeWord, на этот раз передавая ей адрес цепочки
из двух бестиповых байт
seg000:0000
dw ?
>1
g) результат – элемент данных типа слво успешно создан
??? #Верстальщику – change table
аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte,
MakeDouble, MakePAckReal, MakeTbyte.

MakeDword,

MakeQword,

MakeFloat,

Интерактивный аналог: “~Edit\Data”;
success MakeDword(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
двойное слово, длиной четыре байта. Порядок следования младших и старших байт
зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт
располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в двойное слово, а хвост элемента (если он есть) – в бестиповые
байты. Если размер элемента недостаточен для преобразования, но следом за его
хвостом находятся бестиповые байты, они будут автоматически присоединены к новому
элементу. В противном случае (если следом за его хвостом находится другой элемент не
находится ничего) функция возвратит ошибку не выполнив преобразования. Для
выполнения такого преобразования необходимо предварительно уничтожить мешающие
элементы вызовом MakeUnkn (см. описание функции MakeUnkn)
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все
двойное слово приобретает неинициализированное значение.
Пример использования:

101

1. эксперимент
seg000:0000
seg000:0001
seg000:0002
seg000:0003
a) исходные данные

db
db
db
db

?
?
?
?

;
;
;
;

unexplored
unexplored
unexplored
unexplored

Message(“>%x\n”,MakeDword(SegByName(“seg000”)));
b) вызываем функцию MakeDword для создания нового элемента данных типа
двойное слово, передавая ей адрес цепочки из четырех бестиповых байта
seg000:0000
dd ?
>1
с) результат – элемент данных типа двойное слово успешно создан
2. эксперимент
seg000:0000
seg000:0001
seg000:0002
a) исходные данные

db ? ; unexplored
db ? ; unexplored
dw ?

Message(“>%x\n”,MakeDword(SegByName(“seg000”)));
b) вызываем функцию MakeDword для создания нового элемента данных типа
двойное слово, передавая ей адрес бестипового байта
seg000:0000
db ? ; unexplored
seg000:0001
db ? ; unexplored
seg000:0002
dw ?
>0
c) результат – функция завершилась с ошибкой, т.к. для создания нового элемента
не достаточно места – по адресу seg000:0002 находится элемент данных типа слово,
который не может быть автоматически присоединен к двойному слову. Для выполнения
преобразования его необходимо уничтожить вызовом MakeUnkn
MakeUnkn(SegByName("seg000")+2,0);
d) вызываем функцию MakeUnkn для удаления элемента данных, расположенного
по адресу “seg000:0002”
seg000:0000
db ? ; unexplored
seg000:0001
db ? ; unexplored
seg000:0002
db ? ; unexplored
seg000:0003
db ? ; unexplored
e) резултат – элемент данных успешно разрушен.
Message(“>%x\n”,MakeDword(SegByName(“seg000”)));
f) повторно вызываем функцию MakeDword, на этот раз передавая ей адрес
цепочки из четырех бестиповых байт
seg000:0000
dd ?
>1
g) результат – элемент данных типа слво успешно создан
??? #Верстальщику – change table
аргрумент

пояснения
102

ea
return

линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte, MakeWord, MakeQword, MakeFloat, MakeDouble,
MakePackReal, MakeTbyte.
Интерактивный аналог: “~Edit\Data”;
success MakeQword(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
четвертное слово, длиной восемь байт. Порядок следования младших и старших байт
зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт
располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в четвертное слово, а хвост элемента (если он есть) – в
бестиповые байты. Если размер элемента недостаточен для преобразования, но следом
за его хвостом находятся бестиповые байты, они будут автоматически присоединены к
новому элементу. В противном случае, если следом за его хвостом находится другой
элемент или не находится ничего, функция возвратит ошибку, не выполнив
преобразования. Для выполнения преобразования необходимо предварительно
уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все
двойное слово приобретает неинициализированное значение.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007

db
db
db
db
db
db
db
db

?
?
?
?
?
?
?
?

;
;
;
;
;
;
;
;

unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored

a) исходные данные
Message(“>%x\n”,MakeQword(SegByName(“seg000”)));
b) вызываем функцию MakeQword для создания нового элемента данных типа
четвертное слово, передавая ей адрес цепочки из восьми бестиповых байта
seg000:0000
dq ?
>1
с) результат – элемент данных типа четвертное слово успешно создан
??? #Верстальщику – change table
аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
103

==0

ошибка

Родственные функции: MakeByte, MakeWord, MakeDword, MakeFloat, MakeDouble,
MakePackReal, MakeTbyte.
Интерактивный аналог: (“~Options\Setup data types”; ),
Замечение: для включения типа «четвертного слова» в список типов
данных, пролистываемых нажатием клавиши , необходимо, вызвав диалог
“Setup data types” установить галочку напротив пункта “Quadro word”.
success MakeFloat(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
float, длиной четыре байта. Представление типа float завис от выбранного процессора. На
микропроцессорах серии Intel 80x86 он имеет следующее строение (см. рисунок ???)
З
н
а
к

Порядок (8бит)

Мантисса (23 бита)

Рисунок 23 Представление типа float на микропроцессорах серии Intel 80x86
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в двойное слово типа float, а хвост элемента (если он есть) – в
бестиповые байты. Если размер элемента недостаточен для преобразования, но следом
за его хвостом находятся бестиповые байты, они будут автоматически присоединены к
новому элементу. В противном случае, если следом за его хвостом находится другой
элемент или не находится ничего, функция возвратит ошибку, не выполнив
преобразования. Для выполнения преобразования необходимо предварительно
уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из четырех байт имеет неинициализированное значение, все
двойное слово приобретает неинициализированное значение.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
a) исходные данные

db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh

;
;
;
;
;

H
e
l
l
o

Message(“>%x\n”,MakeFloat(SegByName(“seg000”)));
b) вызываем функцию MakeFloat для создания нового элемента данных типа float,
передавая ей адрес цепочки из четырех бестиповых байта
seg000:0000
dd 1.1431391e27
>1
с) результат – элемент данных типа float успешно создан
??? #Верстальщику – change table
104

аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte,
MakeDouble, MakePackReal, MakeTbyte.

MakeWord,

MakeDword,

MakeQword,

Интерактивный аналог: (“~Options\Setup data types”; ),
Замечение: для включения типа «четвертного слова» в список типов
данных, пролистываемых нажатием клавиши , необходимо, вызвав диалог
“Setup data types” установить галочку напротив пункта “Float ”.
success MakeDouble(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
double, длиной восемь байт. Представление типа double зависит от выбранного
процессора. На микропроцессорах серии Intel 80x86 он имеет следующее строение (см.
рисунок ???)
З
н
а
к

Порядок (11 бит)

мантисса (52 бит)

Рисунок 24 Представление типа double на микропроцессорах серии Intel 80x86
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в четвертное слово типа double, а хвост элемента (если он есть)
– в бестиповые байты. Если размер элемента недостаточен для преобразования, но
следом за его хвостом находятся бестиповые байты, они будут автоматически
присоединены к новому элементу. В противном случае, если следом за его хвостом
находится другой элемент или не находится ничего, функция возвратит ошибку, не
выполнив преобразования. Для выполнения преобразования необходимо предварительно
уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из восьми байт имеет неинициализированное значение, все
четверное слово приобретает неинициализированное значение.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007
a) исходные данные

db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
53h

;
;
;
;
;
;
;
;

H
e
l
l
o
,
S

105

Message(“>%x\n”,MakeDouble(SegByName(“seg000”)));
b) вызываем функцию MakeDouble для создания нового элемента данных типа
double, передавая ей адрес цепочки из восьми бестиповых байта
seg000:0000
dq 2.635692361932979e92
>1
с) результат – элемент данных типа double успешно создан
??? #Верстальщику – change table
аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte,
MakeQword, MakePackReal, MakeTbyte.

MakeWord,

MakeDword,

MakeQword,

Интерактивный аналог: (“~Options\Setup data types”; ),
Замечение: для включения типа «четвертного слова» в список типов
данных, пролистываемых нажатием клавиши , необходимо, вызвав диалог
“Setup data types” установить галочку напротив пункта “Double ”.
success MakePackReal(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
packed real, занимающий в результате от обстоятельств от десяти до двенадцати байт.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в packed real, а хвост элемента (если он есть) – в бестиповые
байты. Если размер элемента недостаточен для преобразования, но следом за его
хвостом находятся бестиповые байты, они будут автоматически присоединены к новому
элементу. В противном случае, если следом за его хвостом находится другой элемент или
не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для
выполнения преобразования необходимо предварительно уничтожить мешающие
элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если один или более байт имеют неинициализированное значение, они никак не
влияют на содержимое остальных и вся цепочка байт packed real не принимает
неинициализированного значения.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007
seg000:0008
seg000:0009
a) исходные данные

db
db
db
db
db
db
db
db
db
db

?
?
?
?
?
?
?
?
?
?

;
;
;
;
;
;
;
;
;
;

unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored

106

Message(“>%x\n”,MakePackReal(SegByName(“seg000”)));
b) вызываем функцию MakePackReal для создания нового элемента данных типа
packed real, передавая ей адрес цепочки из десяти бестиповых байта
seg000:0000
db ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
>1
с) результат – элемент данных типа packed real успешно создан
??? #Верстальщику – change table
аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte, MakeWord, MakeDword, MakeFloat, MakeQword,
MakeDouble, MakeTbyte.
Интерактивный аналог: (“~Options\Setup data types”; ),
Замечение: для включения типа «четвертного слова» в список типов
данных, пролистываемых нажатием клавиши , необходимо, вызвав диалог
“Setup data types” установить галочку напротив пункта “Packeed real”.
success MakeTbyte(long ea)
Функция создает по переданному ей линейному адресу ea элемент данных типа
tbyte, длиной десять байт. Порядок следования младших и старших байт зависит от
выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт
располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных,
функция преобразует его в tbyte, а хвост элемента (если он есть) – в бестиповые байты.
Если размер элемента недостаточен для преобразования, но следом за его хвостом
находятся бестиповые байты, они будут автоматически присоединены к новому элементу.
В противном случае, если следом за его хвостом находится другой элемент или не
находится ничего, функция возвратит ошибку, не выполнив преобразования. Для
выполнения преобразования необходимо предварительно уничтожить мешающие
элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному
адресу находится хвост элемента данных, голова или хвост элемента кода.
Если один или более байт имеют неинициализированное значение, они никак не
влияют на содержимое остальных и вся цепочка байт tbyte не принимает
неинициализированного значения.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007

db
db
db
db
db
db
db
db

?
?
?
?
?
?
?
?

;
;
;
;
;
;
;
;

unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
unexplored
107

seg000:0008
seg000:0009

db ? ; unexplored
db ? ; unexplored

a) исходные данные
Message(“>%x\n”,MakeQword(SegByName(“seg000”)));
b) вызываем функцию MakeTbyte для создания нового элемента данных типа tbyte,
передавая ей адрес цепочки из десяти бестиповых байта
seg000:0000
db ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
>1
с) результат – элемент данных типа tbyte успешно создан
??? #Верстальщику – change table
аргрумент
ea
return

пояснения
линейный адрес бестипового байта или головы элемента данных
=return пояснения
==1 успешное завершение
==0 ошибка

Родственные функции: MakeByte, MakeWord, MakeDword, MakeQword, MakeFloat,
MakeDouble, MakePackReal.
Интерактивный аналог: (“~Options\Setup data types”; ),
Замечение: для включения типа «четвертного слова» в список типов
данных, пролистываемых нажатием клавиши , необходимо, вызвав диалог
“Setup data types” установить галочку напротив пункта “Tbyte.
success MakeStr(long ea,long endea)
Функция преобразует последовательность бестиповых байт в ASCII-строку,
автоматически
назначая
ей
метку,
стиль
которой
задается
вызовом
“SetLongPrm(INF_STRTYPE)” (см. описание функции SetLongPrm).
Аргумент ea задает линейный адрес начала цепочки бестиповых байт или головы
элемента данных, преобразуемого в строку. Если по данному адресу находится хвост
элемента данных, голова или хвост элемента кода, функция возвратит ошибку, не
выполнив преобразования.
Аргумент endea задает линейный адрес конца строки. Если передать функции
значение BADADDR, то IDA предпримет попытку вычислить адрес конца автоматически.
Поддерживаются следующие типы строк – ASCIIZ-строки, заканчивающиеся символом
нуля; PASCAL-строки, начинающиеся с байта, содержащего длину строки и DELPHIстроки, начинающиеся со слова (двойного слова), содержащего длину строки. Если строка
не принадлежит ни к одному из этих трех типов, концом строки считается:
а) первый нечитабельный ASCII-символ. Перечень читабельных символов
содержится в поле “AsciiStringChars” конфигурационного файла . Любой символ,
не входящий в этот список, трактуется ограничителем длины строки. По умолчанию
содержимое поля “AsciiStringChars” для кодировки cp866 следующее:
"\r\n\a\v\b\t\x1B"
" !\"#$%&'()*+,-./0123456789:;?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~"
108

"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"
"абвгдежзийклмноп░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
"└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
"рстуфхцчшщъыьэюя";
b) неинициализированный байт
c) голова элемента кода или данных
d) конец сегмента
Если на пути от адреса начала строки до адреса ее конца встретится хотя бы один
неинициализированный байт, элемент кода или данных, функция возвратит ошибку без
преобразования строки.
Замечание: вплоть до версии 3.85 эта функция была реализована с ошибкой и
передача значения BADADDR не приводила к автоматическому определиению
конца строки.
Пример использования:
seg000:0000
db
seg000:0001
db
seg000:0002
db
seg000:0003
db
seg000:0004
db
seg000:0005
db
seg000:0006
db
seg000:0007
db
seg000:0008
db
seg000:0009
db
seg000:000A
db
seg000:000B
db
seg000:000C
db
seg000:000D
db
а) исходные данные – ASCIIZ-строка.

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
53h
61h
69h
6Ch
6Fh
72h
0

;
;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
S
a
i
l
o
r

MakeStr(SegByName(“seg000”),BADADDR);
b) вызываем функцию MakeStr для преобразования цепочки бестиповых байтов в
ASCII-строку с автоматическим определением ее конца
seg000:0000 aHelloSailor
db 'Hello, Sailor',0
с) результат – успешное создание строки с автоматическим назначением метки,
состоящей из допустимых в имени символов
??? #Верстальщику change table
аргумент
ea
endea

return

пояснения
линейный адрес начала цепочки бестиповых байт или головы
элемента данных
!=BADADDR
линейный адрес коцна строки
==BADADDR
указание
вычислять
адрес
конца
строки
автоматически
=return пояснения
==1 успешное завершение операции
==0 ошибка
109

Родственные функции: нет
Интерактивный аналог: “~Edit\ASCII”;
Замечение: при нажатии клавиши , IDA пытаестя создать по текущему
адресу, ASCII-строку автоматически определяя ее длину. Для явного задания
требуемой длины, необходимо предварительно выделить соответствующую
область курсорными клавишами, удерживаня нажатым или мышью,
удерживая нажатой правую кнопку.
success MakeArray(long ea,long nitems)
Функция создает массив состоящий из данных одного типа – байтов, слов, двойных
слов, четверных слов, двойных слов в формате float, четверных слов в формате double,
packed real, tbyte. Бестиповые байты могут стать частью массива любого типа. Строки не
могут быть элементами никакого массива.
Тип массива определяется типом его первого элемента. Все остальные элементы
массива на момент его создания должны быть представлены бестиповыми байтами, последовательность типизированных данных не может быть преобразована в массив.
Элементы массива записываются в строку, отделяясь друг от друга знаком
запятой. Если два или более подряд идущих элемента имеют одно и то же значение (в том
числе и неинициализированное) для сокращения ассемблерного листинга используется
конструкция “DUP”.
Аргумент ea задает линейный адрес первого элемента массива или линейный
адрес начала цепочки бестиповых байт.
Аргумент nitems задает размер массива, выраженный в количестве элементов.
Массив создается даже в том случае, когда nitems равен единице.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007
seg000:0008
seg000:0009
seg000:000A
seg000:000B
seg000:000C
a) исходные данные

db
db
db
db
db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
53h
61h
69h
6Ch
6Fh
72h

;
;
;
;
;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
S
a
i
l
o
r

MakeArray(SegByName(“seg000”),14);
b) вызываем функцию MakeArray для преобразования последовательности
бестиповых байт в массив байт
seg000:0000 db 48h, 65h, 2 dup(6Ch), 6Fh, 2Ch, 20h, 53h, 61h, 69h
seg000:0000 db 6Ch, 6Fh, 72h, 0
с) результат – успешно созданный массив.
Внимание: если все элементы массива не умещаются на одной строке, они
110

автоматически переносятся на следующую, но слева от них указывается не
адрес данного элемента массива, а адрес головы массива!
Замечание: для изменения размеров массива (усечения или расширения)
достаточно передать функции адрес его начала и новую длину.
??? #Верстальщику – change table
аргумент
ea
nitems
return

пояснения
линейный адрес первого элемента массива или линейный адрес
головы уже существующего массива
размер массива, выраженный в количестве элементов
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: нет
Интерактивный аналог: “~Edit\Array”;
success MakeAlign(long ea,long count,long align)
Функция помещает в ассемблерный файл директиву выравнивания align и
исключает из дизассемблируемого листинга байты, используемые для выравнивания.
Замечание: микропроцессоры серии Intel 80x86 используют выравнивание
используется для ускорения доступа к данным и инструкциям (подробнее об
этом можно технической документации фирмы Intel), но существуют
процессоры, которые требуют обязательного выравнивания и при обращении к
не выровненным данным (инструкциям) генерируют исключение.
Аргумент ea задает линейный адрес первого байта, использующегося для
выравнивания. Если по этому адресу расположен хвост элемента данных, голова или
хвост элемента кода, функция возвратит ошибку.
Аргумент count задает количество байт, использующихся для выравнивания.
Значение count должно быть больше нуля и меньше кратности выравнивания, т.е.
2align > count > 0, в противном случае функция возвратит ошибку.
Аргумент align задает кратность выравнивания и представляет собой показатель
степени с основанием два. Т.е. если align равен четырем, то кратность выравнивания –
шестнадцати, т.к. 24=16. Если align равен нулю, функция определяет необходимую степень
выравнивания автоматичен, используя наибольшее возможное значение.
Для изменения величины выравнивания достаточно передать функции MakeAlign
линейный адрес уже существующей директивы Align и новые значения count и align.
Пример использования:
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
a) исходные данные

db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh

;
;
;
;
;

H
e
l
l
o

MakeAlign(SegByName(“seg000”)+1,3,2);
111

b) вызываем функцию MakeAlign для помещения по адресу seg000:0001 директивы
align 4. Для выравнивания используются три байта – seg0001, seg0002 и seg0003.
seg000:0000
db 48h ; H
seg000:0001
align 4
seg000:0004
db 6Fh ; o
c) результат – успешное создание директивы выравнивания
??? #верстальщику – change table
аргумент
ea
count
align

return

пояснения
линейный адрес первого байта, использующегося для выравнивания
или уже существующей директивы align
число байт, использующихся для выравнивания
=align пояснения
==[1..5] показатель степени выравнивания с основанием два
==0 автоматическое определение кратности выравнивания
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: нет
Интерактивный аналог: “~Edit\Structs\Other\ Create alignment directive”;
long MakeCode (long ea)
Функция
создает
по
указанному
адресу
элемент
кода,
выполняя
дизассемблирование первой машинной инструкции. Если это возможно, автоматически
дизассемблируется и другие инструкции. Это происходит в следующих случаях:
а) текущая инструкция не изменяет нормального выполнения программы и за ее
концом расположены бестиповые байты;
b) текущая инструкция изменяет нормальное выполнение программы, осуществляя
переход по непосредственному адресу, тогда IDA продолжит дизассемблирование с этого
адреса
Если встречается инструкция, изменяющая адрес перехода непредсказуемым
образом (например, RET) IDA прекращает дизассемблирование.
Во время дизассемблирования IDA при необходимости создает перекрестные
ссылки и автогенерируемые метки. Подробнее об этом можно прочитать в главах
«Перекресые ссылки» и «Глобальные настойки» соотвественно.
Замечание: IDA эмулирует выполнение кода на виртуальном процессоре, с
целью отслеживания изменения регистра указателя команд, и дизассемблирует
все инструкции, на которые он указывает или может указывать при
определенных обстоятельствах.
Благодаря этому дизассемблируется все вызываемые функции, условные
переходы и все косвенные ссылки, которые IDA в состоянии распознать
(например, если это не запрещено настойками, она может автоматически
преобразовывать 32-разрядные непосредственные операнды по модулю больше
0x10000 в смещения на код – см. главу «Глобальные настойки»).
При
успешном
завершении
функция
дизассемблированной инструкции, выраженную в
возвращается ноль, сигнализируя об ошибке.

возвращает
длину
байтах, в противном

первой
случае

112

Переча линейного адреса головы уже существующего элемента кода, привет к
повторному анализу инструкции; будут заново созданы перекрестные ссылки, авто
генерируемые метки и т.д., а функция возвратит длину инструкции, расположенной по
линейному адресу ea.
Пример использования:
seg000:0100 start
seg000:0101
seg000:0102
seg000:0103
seg000:0104
seg000:0105
seg000:0106
seg000:0107
seg000:0108
seg000:0109
seg000:010A
seg000:010B
seg000:010C
seg000:010D
seg000:010E
seg000:010F
seg000:0110
seg000:0111
seg000:0112
seg000:0113
seg000:0114
seg000:0115
a) исходные данные

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

83h
0C6h
6
0FFh
0E6h
0B9h
0BEh
14h
1
0ADh
91h
56h
80h
34h
66h
46h
0E2h
0FAh
0FFh
0E6h
18h
0

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

Г

ц


н
С
V
А
4
f
F
т
·
ц

Message(">%X\n",MakeCode(SegByName("seg000")+0x100));
b) вызываем функцию MakeCode для дизассемблирования кода
seg000:0100
add
si, 6
seg000:0103
jmp
si
seg000:0103 ; ───────────────────────────
seg000:0105
db 0B9h ; ╣
seg000:0106
db 0BEh ; ╛
seg000:0107
db 14h ;
seg000:0108
db
1;
seg000:0109
db 0ADh ; н
seg000:010A
db 91h ; С
seg000:010B
db 56h ; V
seg000:010C
db 80h ; А
seg000:010D
db 34h ; 4
seg000:010E
db 66h ; f
seg000:010F
db 46h ; F
seg000:0110
db 0E2h ; т
seg000:0111
db 0FAh ; ·
seg000:0112
db 0FFh ;
seg000:0113
db 0E6h ; ц
seg000:0114
db 18h ;
seg000:0115
db
0;
>3
с) результат – функция дизассемблировала две инструкции и остановилась,
113

встретив регистровый переход, целевой адрес которого она предсказать не в силах; по
завершению дизассемблирования функция возвратила длину первой инструкции, равную
трем байтам
Message(">%X\n",MakeCode(SegByName("seg000")+0x106));
d) повторно вызываем функцию MakeCode, передавая ей адрес следующей
инструкции (значение регистра SI при загрузке com файла равно 0x100, а после
выполнения инструкции ADD SI, 6 – 0x106, следовательно целевой адрес перехода JMP SI
равен 0x106)
seg000:0100
add
si, 6
seg000:0103
jmp
si
seg000:0103 ; ───────────────────────────────────────────────────────────────
seg000:0105
db 0B9h ; ╣
seg000:0106 ; ───────────────────────────────────────────────────────────────
seg000:0106
mov
si, 114h
seg000:0109
lodsw
seg000:010A
xchg
ax, cx
seg000:010B
push
si
seg000:010C
seg000:010C loc_0_10C:
; CODE XREF: seg000:0110j
seg000:010C
xor
byte ptr [si], 66h
seg000:010F
inc
si
seg000:0110
loop
loc_0_10C
seg000:0112
jmp
si
seg000:0112 ; ───────────────────────────────────────────────────────────────
seg000:0114
db 18h ;
seg000:0115
db
0;
>3
e) результат – функция продолжила дизассемблирование, автоматически создавая
перекрестные ссылки и автогенерируемые метки, до тех пор пока не встретила инструкцию
регистрового перехода, целевой адрес которого предсказать не в силах.
??? #Верстальщику – chabge table
аргумент
ea
return

пояснения
линейный адрес бестипового байта или головы уже существующего
элемента кода
=return пояснения
!=0 длина первой дизассемблируемой инструкции
==0 ошибка

Родственные функции: нет
Интерактивный аналог: “~Edit\Code”’
char GetMnem(long ea)
Функция возвращает символьную мнемонику инструкции элемента кода,
расположенного по линейному адресу ea. Для получения операндов (если они есть)
следует воспользоваться функцией GetOpnd (см. главу «Операнды»)
Пример использования:
seg000:0000
mov
ah, 9
a) исходные данные – требуется получить символьную мнемонику инструкции
114

Message(“>%s\n”,GetMnem(SegByName(“seg000”)));
b) вызов функции GetMnem
>mov
c) результат – мнемоника инструкции в символьном представлении
??? #Верстальщику – chabge table
аргумент
ea
return

пояснения
линейный адрес элемента кода
=return пояснения
!=”” мнемоника в символьном представлении
==”” ошибка

Родственные функции: GetOpnd
Интерактивный аналог: нет

void MakeUnkn(long ea,long expand)
Функция разрушает элемент, заданный любым принадлежащим ему адресом,
превращая его содержимое в бестиповые байты. Объекты, связанные с элементом
(например, метки, комментарии) при этом не уничтожаются.
Замечание: автогенирируемые метки, назначаемые ASCII-строкам при их
разрушении удаляется
Аргумент ea задает любой линейный адрес, принадлежащий разрушаемому
элементу.
Аргумент expand будучи неравным нулю указывает на необходимость разрушения
всей цепочки элементов, связанных друг с другом перекрестными ссылками типа «ссылка
на следующую инструкцию» (см. главу «Перекрестные ссылки»)
Пример использования:
1. Эксперимент
seg000:0000 aHelloSailor
a) исходные данные

db 'Hello, Sailor',0

MakeUnkn(SegByName(“seg000”)+0x1,0);
b) вызов функции MakeUnkn для разрушения элемента данных типа «ASCII-строка»
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0004
seg000:0005
seg000:0006
seg000:0007
seg000:0008

db
db
db
db
db
db
db
db
db

48h
65h
6Ch
6Ch
6Fh
2Ch
20h
53h
61h

;
;
;
;
;
;
;
;
;

H
e
l
l
o
,
S
a
115

seg000:0009
db 69h
seg000:000A
db 6Ch
seg000:000B
db 6Fh
seg000:000C
db 72h
с) результат – элемент данных разрушен

;
;
;
;

i
l
o
r

2. Эксперимент
seg000:0100
seg000:0103
a) исходные данные

add
jmp

si, 6
si

MakeUnkn(SegByName(“seg000”),0);
b) вызов функции MakeUnkn для разрушения только одного элемента кода
seg000:0100 start
db 83h ; Г
seg000:0101
db 0C6h ; ╞
seg000:0102
db
6 ;
seg000:0103 ; ─────────────────────────
с) разрушен один элемент кода
3. Эксперимент
seg000:0100
seg000:0103
a) исходные данные

add
jmp

si, 6
si

MakeUnkn(SegByName(“seg000”),1);
b) вызов функции MakeUnkn для разрушения всей цепочки элементов кода
seg000:0100 start
db 83h ; Г
seg000:0101
db 0C6h ; ╞
seg000:0102
db
6 ;
seg000:0103
db 0FFh ;
seg000:0104
db 0E6h ; ц
seg000:0105
db 0B9h ; ╣
с) результат – вся цепочка элементов кода разрушена
??? #верстальщику – change table
агрумент
ea

пояснения
любой линейный адрес, принадлежащий разрушаемому элементу
разрушение только одного элемента кода или данных
==0

expand
!=0
return

=return
==1
==0

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

Родственные функции: нет
Интерактивный аналог: “~Edit\Undefine”;
Замечение:

нажатие



равносильно

вызову

MakeUnk(ScreenEA(),1)

и
116

разрушает всю цепочку элементов кода. При необходимости разрушения одного
элемента, его следует предварительно выделить курсорными клавишами,
удерживая нажатым или мышью, удерживая нажатой левую кнопку.
long FindCode(long ea,long flag)
Функция ищет ближайший к переданному ей линейному адресу ea элемент кода,
возвращая в случае успешного завершения поиска адрес его головы. В зависимости от
флага направления поиск может идти как вперед (от младших адресов к старшим), так и
назад (от старших адресов к младшим). Переданный функции линейный адрес в этот
диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь
сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
seg000:0100
mov
ax, 9
seg000:0103
mov
dx, 133h
a) исходные данные – требуется получить линейный первого элемента кода
Message(“>%s\n”,atoa(FindCode(0,1)));
b) вызов функции FindCode – адрес начала поиска равен нулю, единичное значение
флага направление указывает вести поиск с увеличением адресов
>seg000:0100
результат – линейный первого элемента кода
??? #Верстальщику – change table
аргумент
ea
flag
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
=return пояснения
!=BADADDR линейный адрес элемента кода
==BADADDR ошибка

Родственные функции: FindData, FindExplored, FindUnexplored
Интерактивный аналог:”~Nabigate\Search for\Next Code”;
long FindData(long ea,long flag)
Функция ищет ближайший к переданному ей линейному адресу ea элемент кода,
возвращая в случае успешного завершения поиска адрес его головы. В зависимости от
флага направления поиск может идти как вперед (от младших адресов к старшим), так и
назад (от старших адресов к младшим). Переданный функции линейный адрес в этот
диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь
сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
117

seg000:0000
mov
ah, 9
seg000:0002
mov
dx, 108h
seg000:0005
int
21h
seg000:0005
seg000:0007
retn
seg000:0007 ; ──────────────────────────────
seg000:0008 aHelloIda
db 'Hello, IDA'
a) исходные данные – требуется получить линейный последнего элемента данных
Message(“>%s\n”,atoa(FindData(BADADDR,0)));
b) вызов функции FindData
>seg000:0108
результат – линейный адрес последнего элемента данных
??? #Верстальщику – change table
аргумент
ea
flag
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
=return пояснения
!=BADADDR линейный адрес элемента данных
==BADADDR ошибка

Родственные функции: FindCode, FindExplored, FindUnexplored
Интерактивный аналог:”~Nabigate\Search for\Next Data”;
long FindExplored(long ea,long flag)
Функция ищет ближайший к переданному ей линейному адресу ea элемент кода
или данных, возвращая в случае успешного завершения поиска адрес его головы. В
зависимости от флага направления поиск может идти как вперед (от младших адресов к
старшим), так и назад (от старших адресов к младшим). Переданный функции линейный
адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какомунибудь сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
seg000:0100
DB 99h ; Щ
seg000:0101
DW 666h
a) исходные данные – требуется получить линейный первого элемента кода или
данных
Message(“>%s\n”,atoa(FindExplored(0,1)));
b) вызов функции FindExplored – адрес начала поиска равен нулю, единичное
значение флага направление указывает вести поиск с увеличением адресов
>seg000:0101
результат – линейный первого элемента
118

??? #Верстальщику – change table
аргумент
ea
flag
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
=return пояснения
!=BADADDR линейный адрес элемента любого вида
==BADADDR ошибка

Родственные функции: FindCode, FindData, FindUnexplored
Интерактивный аналог:”~Nabigate\Search for\Next explored”;
long FindUnexplored(long ea,long flag)
Функция ищет ближайший к переданному ей линейному адресу ea бестиповой байт,
возвращая в случае успешного завершения поиска его адрес. В зависимости от флага
направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от
старших адресов к младшим). Переданный функции линейный адрес в этот диапазон
поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
seg000:0100
DW 666h
seg000:0102
DB 99h ; Щ
a) исходные данные – требуется получить линейный первого бестипового байта
Message(“>%s\n”,atoa(FindUnexplored(0,1)));
b) вызов функции FindUnexplored – адрес начала поиска равен нулю, единичное
значение флага направление указывает вести поиск с увеличением адресов
>seg000:0102
результат – линейный первого бестипового байта
??? #Верстальщику – change table
аргумент
ea
flag
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
=return пояснения
!=BADADDR линейный адрес бестипового байта
==BADADDR ошибка

Родственные функции: FindCode, FindData, FindExplored
Интерактивный аналог:”~Nabigate\Search for\Next Unexplored”;
119

ОПЕРАНДЫ
#definition
Элементы могут содержать один и более операндов, включающих в себя
непосредственные значения. Одно и то же непосредственное значение может являться и
константой, и смещением в сегменте, и базовым адресом сегмента. Константа в свою
очередь может отображаться в двоичной, восьмеричной, десятичной и шестнадцатеричной
системе исчисления или символьной ASCII-строки.
Тип и представление каждого операнда определяется значением соответствующих
битов флагов (см. таблицу 14). Две группы битов определяют представление первого и
второго операнда; если же элемент содержит более двух операндов, третий и все
последующие операнды имеют тот же самый тип и представление, что и второй операнд.
Если ни один бит атрибутов операнда не установен, операнд не имеет никакого
типа, (т.е. имеет тип “void”) и отображается в системе исчисления принятой по умолчанию.
В зависимости от настоек IDA Pro бестиповые операнды могут отображаться другим
цветом (по умолчанию красным).
Определить имеет ли некий элемент кода непосредственный операнд или нет
можно анализом бита FF_IMMD флагов. Если он установлен – элемент кода имеет
непосредственный операнд, и, соответственно, наоборот. Значение бита FF_IMMD не
зависит от типа непосредственного операнда – чем бы он ни был: смещением, константой,
базовым адресом сегмента или имел тип void, – флаг FF_IMMD указывает на сам факт
наличия (отсутствия) непосредственного операнда, но никак не связан с его типом.
В полностью дизассемблированной программе не должно быть ни одного операнда
с типом void, – типы всех операндов должны заданы явно в соответствии с их назначением,
которое можно узнать путем анализа программы.
В некоторых случаях IDA Pro позволяет автоматически отличить смещения от
констант:
а) используя информацию о перемещаемых элементах (fixup info) IDA Pro может
автоматически преобразовать бестиповые операнды в базовые адреса сегментов и
смещения
b) в 32-разрядном сегменте, инструкция, имеющая непосредственный операнд,
содержащий значение 0x10000 и выше, автоматически преобразуется в смещение, если
это разрешено настойками
c) то же, что и в пункте b но для данных
d) непосредственный операнд инструкции push, следующей за инструкцией,
заносящий в стек базовый адрес сегмента, автоматически преобразуется в смещение,
если это разрешено настройками
e) непосредственный операнд, копируемый инструкций MOV в один из регистров
общего назначения, автоматически преобразуется в смещение, если он предшествует
инструкции MOV, заносящий в один из сегментных регистров базовый адрес сегмента
f) непосредственный операнд, копируемый инструкций MOV в ячейку памяти
независимо от способа ее адресации, автоматически преобразуется в смещение, если он
предшествует инструкции MOV, заносящий в ячейку памяти базовый адрес сегмента.
Авто-анализатор IDA Pro непрерывно совершенствуется, и новые версии
становятся способными автоматически распознать смещения все в большем и большем
числе случаев, - однако, вместе с этим увеличивается и процент ложных срабатываний,
поэтому, окончательная доводка дизассемблируемого листинга ложится на плечи
пользователя.

120

флаг
FF_0VOID
FF_0NUMH
FF_0NUMD
FF_0CHAR
FF_0SEG
FF_0OFF
FF_0NUMB
FF_0NUMO
FF_0ENUM
FF_0FOP
FF_0STRO
FF_0STK
флаг
FF_1VOID
FF_1NUMH
FF_1NUMD
FF_1CHAR
FF_1SEG
FF_1OFF
FF_1NUMB
FF_1NUMO
FF_1ENUM
FF_1FOP
FF_1STRO
FF_1STK

представление первого слева операнда
#
представление
0x00000000
тип void
0x00100000
шестнадцатеричное представление
0x00200000
десятичное представление
0x00300000
символьное представление
0x00400000
представление в виде базового адреса сегмента
0x00500000
представление в виде смещения в сегменте
0x00600000
бинарное представление
0x00700000
восьмеричное представление
0x00800000
представление в виде перечисления
0x00900000
пользовательское представление
0x00A00000 представление в виде смещения в структуре
0x00B00000 представление в виде стековой переменной
представление второго слева операнда
#
представление
0x00000000
тип void
0x00100000
шестнадцатеричное представление
0x00200000
десятичное представление
0x00300000
символьное представление
0x00400000
представление в виде базового адреса сегмента
0x00500000
представление в виде смещения в сегменте
0x00600000
бинарное представление
0x00700000
восьмеричное представление
0x00800000
представление в виде перечисления
0x00900000
пользовательское представление
0x00A00000 представление в виде смещения в структуре
0x00B00000 представление в виде стековой переменной

Таблица 13 возможные представления непосредственных операндов элементов
типа данные и код

Сводная таблица функций
функции, изменяющие отображение операндов
название функции
краткое описание
success OpBinary(long ea,intотображает операнд (операнды) в двоичном виде
n)
success OpOctal(long ea,int отображает операнд (операнды) в восьмеричном
n)
виде
success
OpDecimal(long отображает операнд (операнды) в десятичном
ea,int n)
виде
success OpHex(long ea,int отображает
операнд
(операнды)
в
n)
шестнадцатеричном виде
success OpChr (long ea,int отображает операнд (операнды) в символьном
n)
виде
success
OpNumber(long отображает операнд (операнды) в систем
ea,int n)
исчисления принятой по умолчанию
success OpOff (long ea,int отображает операнд (операнды) в виде смещения,
n,long base)
отсчитываемого относительно начала сегмента

121

success OpOffEx(long ea,int
n,long
reftype,long
target,long base,long tdelta)
success OpSeg(long ea,int
n)

отображает операнд (операнды) в виде смещения,
отсчитываемого относительно любого адреса,
принадлежащего сегменту
отображает операнд (операнды) в виде имени
сегмента, базовый адрес которого равен значению
операнда
success OpAlt(long ea,long отображает
операнд
(операнды)
в
виде
n,char str)
символьной строки, заданной пользователем
success OpSign(long ea,int отображает операнд (операнды) в знаковой или
n)
целочисленной форме (функция работает как
триггер)
success OpStkvar(long ea,int отображает
непосредственное
значение,
n)
использующее для базовой адресации в виде
имени локальной переменной
функции, возвращающие операнды
название функции
краткое описание
char GetOpnd(long ea,long возвращает операнд в символьном представлении
n)
long GetOpType(long ea,
возвращает тип операнда
long n)
long GetOperandValue (long возвращает значение операнда
ea,long n)
char AltOp (long ea,long n)
возвращает
операнд,
определенный
пользователем
функции, обеспечивающие поиск операндов
название функции
краткое описание
long FindVoid(long ea, long возвращает линейный адрес очередного операнда
flag)
неопределенного типа
long FindImmediate (long ea, возвращает линейный адрес очередного элемента
long flag, long value);
с операндами, имеющими указанное значение
char Demangle (char name,
long disable_mask)

возвращает незамангленное имя метки

success OpBinary(long ea,int n)
Функция отображает операнд (операнды) в двоичном виде, добавляя в его конце
суффикс ‘b’.
Пример использования:
seg000:0000
a) исходные данные

mov

ax,41h

OpBinary(SegByName(“seg000”),1);
b) вызов функцию OpBinary для преобразования второго слева операнда в
двоичный вид.
seg000:0000
mov
ax, 1000001b
с) результат – второй слева операнд преобразован в двоичный вид
??? #верстальщику – change table

122

аргумент
ea
n

return

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpOcatl, OpDeciminal,.OpHex, OpChr, OpNumer
Интерактивный анлог: “~Edit\Operand types\Binary”;
success OpOctal(long ea,int n)
Функция отображает операнд (операнды) в восьмеричном виде, добавляя в его
конце суффикс ‘o’.
Пример использования:
seg000:0000
a) исходные данные

mov

ax,41h

OpOctal(SegByName(“seg000”),1);
b) вызов функцию OpOctal для преобразования второго слева операнда в
восьмеричный вид.
seg000:0000
mov
ax, 101o
с) результат – второй слева операнд преобразован в восьмеричный вид
??? #верстальщику – change table
аргумент
ea
n

return

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpBinary, OpDeciminal,.OpHex, OpChr, OpNumer
Интерактивный анлог: «~Edit\Operand types\Octal»
success OpDecimal(long ea,int n)
Ф у н кц ия отоб раж ае т о пер анд (о пера нды ) в дес ятичном в и де . П р име р
ис по ль зова н ия :
seg000:0000

mov

ax,41h
123

a) исходные данные
OpDecimal(SegByName(“seg000”),1);
b) вызов функцию OpDecimal для преобразования второго слева операнда в
десятичный вид.
seg000:0000
mov
ax, 65
с) результат – второй слева операнд преобразован в десятичный вид
??? #верстальщику – change table
аргумент
ea
n

return

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpBinary, OpOctal,.OpHex, OpChr, OpNumer
Интерактивный анлог: «Edit\Operand types\Decimal»;
success OpHex(long ea,int n)
Функция отображает операнд (операнды) в шестнадцатеричном виде, добавляя в
его конце суффикс ‘h’.
Пример использования:
seg000:0000
a) исходные данные

mov

ax,65

OpHex(SegByName(“seg000”),1);
b) вызов функцию OpHex для преобразования второго слева операнда в
шестнадцатеричный вид.
seg000:0000
mov
ax, 41h
с) результат – второй слева операнд преобразован в шестнадцатеричный вид
??? #верстальщику – change table
аргумент
ea
n

return

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

124

Родственные функции: OpBinary, OpOctal, OpDeciminal,. OpChr, OpNumer
Интерактивный анлог: «~Edit\Operand types\Hexadeciminal»;
success OpChr(long ea,int n)
Функция отображает операнд (операнды) в символьном виде, заключая его в
кавычки. Если операнд содержит один или больше нечитабельных байт, функция
возвратит ошибку. Перечень читабельных символов содержится в поле “AsciiStringChars”
конфигурационного файла . По умолчанию содержимое поля “AsciiStringChars” для
кодировки cp866 следующее:
"\r\n\a\v\b\t\x1B"
" !\"#$%&'()*+,-./0123456789:;?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~"
"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"
"абвгдежзийклмноп░▒▓│┤╡╢╖╕╣║╗╝╜╛┐"
"└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀"
"рстуфхцчшщъыьэюя";
Замечание: порядок следования старших и младший байт зависит от
выбранного типа процессора. У микропроцессоров серии Intel 80x86 младший
байт располагается по меньшему адресу, а старший, соответственно,
наоборот.
Пример использования:
1. Эксперимент
seg000:0000
a) исходные данные

mov

ax,65

OpChr(SegByName(“seg000”),1);
b) вызов функцию OpChar для преобразования второго слева операнда в
символьный вид.
seg000:0000
mov
ax, ‘A’
с) результат – второй слева операнд преобразован в шестнадцатеричный вид
2. Эксперимент
seg000:0000
a) исходные данные

dq

4944412050726F21h

OpChr(SegByName(“seg000”),0);
b) вызов функции OpChr для преобразования первого слева операнда в
символьный вид
seg000:0000
dq
с) результат – успешное преобразование

'IDA Pro!'

??? #верстальщику – change table
аргумент
ea
n

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
125

return

==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpBinary, OpOctal, OpDeciminal,. OpHex, OpNumer
Интерактивный анлог: «Edit\Operand types\Chaster»;
success OpNumber(long ea,int n)
Функция отображает операнд (операнды) в форме исчисления принятой по
умолчанию. По умолчанию системой исчисления по умолчанию назначена
шестнадцатеричная система исчисления.
Пример использования:
seg000:0000
a) исходные данные

mov

ax,65

OpNumber(SegByName(“seg000”),1);
b) вызов функцию OpNumber для преобразования второго слева операнда в
систему исчисления по умолчанию.
seg000:0000
mov
ax, 41h
с) результат – второй слева операнд преобразован в шестнадцатеричный вид
??? #верстальщику – change table
аргумент
ea
n

return

пояснения
линейный адрес элемента, которому принадлежит операнд
=n операнд
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpBinary, OpOctal, OpDeciminal,. OpHex, OpChr.
Интерактивный анлог: «Edit\Operand types\ Number»;
success OpOff(long ea,int n,long base)
Функция отображает операнд (операнды) в виде смещения относительно заданного
сегмента, автоматически создавая автогенерируемую метку по целевому адресу (если
целевой адрес не имеет метки) и перекрестную ссылку соответствующего типа (см. главу
«Перекрестные ссылки»). Разрядность операнда, представляемого в виде смещения,
должна быть равна разрядности соответствующего сегмента, иначе функция возвратит
ошибку.
А р г ум е н т e a з а дае т линейный а дрес элем ента , ко то ром у пр ин а длеж и т
126

о пера нд .
Аргумент base задает базовый адрес сегмента, выраженный в байтах (не
параграфах!) относительного которого отсчитывается смещение.
А р г ум е н т n з а да е т о пера нд , о тобр ажаем ый в в ид е с ме щен и я (см .
таблицу) .
Д л я выполнения об ра тно й о пер ац ии , т.е . пр ео бразо ва нию с ме ще н и я к
н е пос р е дс т в е н но м у з на че н ию , д ос та то ч но пе ре да ть функции нулевой
базов ый адр ес сегмента .
Пример использования:
seg000:0100 start
proc near
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, 108h
seg000:0105
int
21h
seg000:0107
retn
seg000:0107 start
endp
seg000:0107
seg000:0107 ; ──────────────────────────────────────────
seg000:0108
db 'Hello,World!',0Dh,0Ah,'$
seg000:0108 seg000
ends
a) исходные данные
OpOff(SegByName("seg000")+0x102,1,SegByName("seg000"));
b) вызов функции OpOff для отображения константы, загружаемой в регистр DX в
виде смещения относительно текущего сегмента
seg000:0100 start
proc near
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset asc_0_108 ; "Hello,World!\r\n$"
seg000:0105
int
21h
seg000:0107
retn
seg000:0107 start
endp
seg000:0107
seg000:0107 ; ──────────────────────────────────────────────────────────
seg000:0108 asc_0_108
db 'Hello,World!',0Dh,0Ah,'$' ; DATA XREF: start+2o
seg000:0108 seg000
ends
с) результат – константа, загружаемая в регистр DX отображена в виде смещения,
предваренного директивой “offset”, автоматически создана метка и перекрестна ссылка (в
тексте они выделены жирным шрифтом).
Ближайший аналог (~Edit\Operad types\Offset by any segment)
??? #Верстальщику – change table
аргумент
ea
n

base
return

пояснения
линейный адрес элемента, котрому принадлежит операнд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные
операнды
==-1 все операнды
базовый адрес сегмента, выраженный в байтах (не параграфах!)
относительного которого отсчитывается смещение
=return пояснения
==1 успешное завершение операции
127

==0

ошибка

Родственные функции: OpOffEx
Интерактивный аналог: “~Edit\Operad types\Offset by any segment”;
success OpOffEx(long ea,int n,long reftype,long target,long base,long tdelta)
Функция отображает операнд (операнды) в виде смещения, отсчитываемого от
любого заданного адреса, не обязательно совпадающего с базовым адресом сегмента.
Такая необходимость возникает например в случае обращения к элементу структуры,
смещение которого требуется отсчитывать относительно начала этой структуры.
Данная функция является усовершенствованным вариантом функции OpOff и
поддерживает не только смещения, разрядность которых равна разрядности
соответствующего сегмента, но смещения записанные в восьми или шестнадцати младших
(старших) битах шестнадцати и тридцати двух разрядных операндов соответственно (см.
таблицу ???). При этом остальные биты операнда маскируются операцией «логического и»
AND.
А р г ум е н т e a з а дае т линейный а дрес элем ента , ко то ром у пр ина длеж ит
о пера нд .
А р г ум е н т n з а да е т о пера нд , о тобр ажаем ый в в ид е с ме щен и я (см .
таблицу ?? ?)
Аргумент reftype задает тип смещения и может принимать одно из значений,
перечисленных в таблице ???
Аргумент target задает линейный адрес целевого смещения выраженный в байтах,
относительного которого будет отсчитыватся смещение операнда. Если в качестве
целевого смещения передать значение BADADDR, целевое смещение будет вычислено
автоматически по следующей формуле: target = operand_value - tdelta +base
Аргрумент base задает базовый адрес сегмента, выраженный в байтах,
относительно которого задается целевое смещение.
Аргумент tdelta задает относительное смещение, отсчитываемое относительно
целевого смещения.Относительное смещение может быть как положительным, так и
отрицательным. Если оно равно нулю, то данная функция становится эквивалентна
функции OpOff (см. описание функции OpOff).
Значение операнда должно соответствовать следующему соотношению
operand_value = target + tdelta - base, в противном случе функция вернет ошибку.
определение
REF_OFF8
REF_OFF16
REF_OFF32
REF_LOW8

#
0
1
2
3

REF_LOW16

4

REF_HIGH8

5

REF_HIGH16

6

тип смещения
8-битное смещение
16-битное смещение
32-битное смещение
смещение представлено 8 младшими
битами 16 битного непосредственного
значенияt
смещение представлено 16 младшими
битами 32 битного непосредственного
значенияt
смещение представлено 8 старшими
битами 16 битного непосредственного
значенияt
смещение представлено 16 старшими
битами 32 битного непосредственного
значенияt

128

Таблица 14
Пример использования:
seg000:0100 start:
seg000:0100
mov
ax, 105h
seg000:0103
retn
seg000:0103 ; ────────────────────────────────────
seg000:0104 MyStruc
db 0
seg000:0105
dw 6666h
seg000:0107
dw 9999h
seg000:0107 seg000
ends
seg000:0107
a) исходные данные – требуется представить непосредственное значение,
загружаемое в регистр AX в виде смещения, отсчитываемого относительного начала
струкуры MyStruc.
OpOffEx(SegByName("seg000")+0x100,1,REF_OFF16,
SegByName("seg000")+0x104,SegByName("seg000"),1);
b) вызов функции OpOffEx для представления непосредственного значения в виде
смещения, отсчитываемого относительно начала структуры MyStruc.
Пояснение:
линейный
адрес
структуры
MyStruc
равен
SegByName(“seg000”)+0x104, следовательно, целевой адрес tagreg равен
SegByName(“seg000”)+0x104;
базовый адрес сегмента, которому принадлежит структура, будучи
выраженным в байтах равен SegNyName(“seg000”), следовательно, аргумент
base равен SegByName(“seg000”);
смещение искомого элемента относительно начала структуры равно
operand_value – offset MyStruc, т.е. в непосредственных значениях – 0x105 –
0x104 = 1, следовательно, аргумент tdelta равен 1;
операнд представляет собой 16-разрядное непосредственное значение,
поэтому, тип смещения - REF_OFF16.
seg000:0100 start:
seg000:0100
mov
ax, offset MyStruc+1
seg000:0103
retn
seg000:0103 ; ──────────────────────────────────────────
seg000:0104 MyStruc
db 0
; DATA XREF: seg000:0100o
seg000:0105
dw 6666h
seg000:0107
dw 9999h
seg000:0107 seg000
ends
с) результат – непосредственное значение теперь представлено в виде смещения,
отсчитываемого от начала структуры MyStruc
Замечение: в данном примере было допустимо использовать
автоматическое определение целевого адреса, однако, для большей ясности оно
было вычислено вручную.
??? #Верстальщику – chabge table
аргумент
ea
n

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
129

==-1

все операнды
==reftype
#
==REF_OFF8
0
==REF_OFF16
1
==REF_OFF32
2
==REF_LOW8
3

reftype

target

base
tdelta
return

тип смещения
8-битное смещение
16-битное смещение
32-битное смещение
смещение представлено 8 младшими битами 16
битного непосредственного значенияt
==REF_LOW16
4
смещение представлено 16 младшими битами
32 битного непосредственного значенияt
==REF_HIGH8
5
смещение представлено 8 старшими битами 16
битного непосредственного значенияt
==REF_HIGH16
6
смещение представлено 16 старшими битами
32 битного непосредственного значенияt
==target пояснения
!=BADADDR целевое смещение
==BADADDR вычислять целевое смещение автоматически по следующей
формуле target = operand_value - tdelta +base
базовый адрес сегмента, выраженный в байтах (не параграфах!)
относительное смещение, считаемое относительно целевого
смещения; может быть как положительным, так и отрицательным
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: OpOff
Интерактивный аналог: “~Edit\Operad types\User-defined offset”;
success OpSeg(long ea,int n)
Функция отображает операнд (операнды) в виде имени сегмента, базовый адрес
которого равен значению операнда. Если сегмента с таким базовым адресом не
существует, функция возвращает ошибку.
Замечание: в процессе загрузки файла IDA автоматически преобразует
все перемещаемые элементы в базовые адреса соответствующих сегментов.
Пример использования:
seg000:0000
mov
ax, 1000h
a) исходные данные – требуется представить непосредственный операнд,
загружаемый в регистр ax в виде имени сегмента
OpSeg(SegByName(“seg000”),1);
b) вызов функции OpSeg для преобразования непосредсвтенного операнда в имя
сегмента с соответствующим базовым адресом
seg000:0000
mov
ax, seg seg000
c) результат – непосредственный операнд теперь представлен в виде имени
сегмента с соответствующим базовым адресом
??? #Верстальщику – change table
аргумент

пояснения
130

ea
n

return

линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные операнды
==-1 все операнды
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: нет
Интерактивный аналог:”~ Edit\Operand types\ Segment”
success OpAlt(long ea,long n,char str)
Функция отображающая операнды в виде символьной строки, заданной
пользователем. Никаких ограничений на переданную строку не налагается – она может
содержать любые символы, кроме символа с кодом нуля, служащим признаком конца
строки.
Пример использования:
seg000:0000
a) исходные данные

mov

ax, 9

OpAlt(SegByName(“seg000”),0,”Регистр AX”);
b) вызов функции OpAlt для переименования первого слева операнда в строку
«Регистр AX».
Регистр AX, 9
seg000:0000
mov
c) результат – операнд успешное переименован
??? #Верстальщику – change table
аргумент
ea
n

return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные
операнды
==-1 все операнды
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: AltOp
Интерактивный аналог: “~Edit\Operand types\ Enter operand manually”;
success OpSign(long ea,int n)
Функция отображает операнд в знаковой или целочисленной форме, работая как
триггер – если до ее вызова операнд отображался в целочисленной форме, после станет
отображаться в знаковой и, соответственно, наоборот.
131

Пример использования:
seg000:0000
mov
ax, 0FFFFh
a) исходные данные – требуется отобразить непосредственное значение,
загружаемое в регистр AX в знаковой форме
OpSign(SegByName(“seg000”),1);
b) вызов функции OpSign для отображения
загружаемого в регистр AX в знаковой форме

непосредственного

значения,

seg000:0000
mov
ax,-1
с) результат - непосредственное значение, загружаемое в регистр AX теперь
отображается в знаковой форме.
??? #Верстальщику – change table
аргумент
ea
n

return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные
операнды
==-1 все операнды
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: нет
Интерактивный аналог: “~Edit\Operand types\ Change Sign”;
success OpStkvar(long ea,int n)
Функция отображает непосредственное значение, используемые для базовой
адресации относительно регистров BP (EBP) и SP (ESP) в виде стековой переменной.
Сама стековая переменная должна быть предварительно создана вызовом MakeLoacal
(см. описание функции MakeLocal).
Значение регистров BP (EBP) и SP (ESP) IDA в каждой точке программы IDA по
возможности определяет автоматически, облегчая тем самым анализ кода, генерируемого
оптимизируемыми компиляторами, использующими для адресации локальных переменных
регистр SP (ESP) значение которого подвержено частым изменениям. Для ручного задания
значения регистра SP (ESP) предусмотрена функция SetSpDiff, к вызову которой
приходится прибегать в случае невозможности определить значение стекового регистра
автоматическим анализатором.
Замечание:
IDA
эмулирует
выполнения
некоторых
наиболее
употребляемых инструкций, таких как PUSH, POP, ADD, SUB и т.д., для
отслеживания изменения значения регистра SP (ESP). Более сложные операции
с регистрами пока не поддерживаются.
Пример использования:
seg000:0000 start
seg000:0000
seg000:0002

proc near
mov
bp, sp
sub
sp, 10h
132

seg000:0005
mov
word ptr [bp-2], 666h
seg000:000A
add
sp, 10h
seg000:000D
retn
seg000:000D start
endp
а) исходные данные – требуется представить непосредственное знаечние,
вычитаемое из регистра bp в виде имени локальной переменной.
MakeLocal(SegByName(“seg000”),SegByName(“seg000”)+0xD,"[BP-2]","MyVar");
b) вызов функции MakeLocal (см. описание MakeLocal) для создания локальной
переменной MyVar, расположенной двумя байтам «выше» конца кадра стека
OpStkvar(SegByName(“seg000”),0);
c) вызов функции OpStkvar для отображения непосредственного значения в виде
имени ранее созданной локальной переменной
seg000:0100 start
proc near
seg000:0100
seg000:0100 MyVar
= word ptr -2
seg000:0100
seg000:0100
mov
bp, sp
seg000:0102
sub
sp, 10h
seg000:0105
mov
[bp+MyVar], 666h
seg000:010A
add
sp, 10h
seg000:010D
retn
seg000:010D start
endp
d) результат – непосредственное значение отображено в виде имени локальной
переменной MyVar (в тексте она выделена жирным шрифтом)
Замечание: подробнее о поддержке локальных переменных можно
прочитать в главе «Функции»
??? #Верстальщику – change table
аргумент
ea
n

return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева операнд
==-1 все операнды
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: нет
Интерактивный аналог: ”Edit\Operand types\ Stack variable”;
char GetOpnd(long ea,long n)
Функция возвращает операнд в строковом виде, т.е. том виде, в каком
дизассемблер отображает его на экране.
Пример использования:
seg000:0000
mov
ax, 9
a) исходные данные – требуется получить операнды в том виде, в котором они
133

отображены на экране.
Message(“>%s,%s\n”,GetOpnd(SegByName(“seg000”),0),
GetOpnd(SegByName(“seg000”),1));
b) вызов функции GetOpnd для получения операндов в том виде, в котором они
отображены на экране
>ax, 0
c) результат
??? #верстальщику – change table
аргумент
ea
n
return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева операнд
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: GetOpType, GetOperandValue
Интерактивный аналог: нет
char AltOp (long ea,long n)
Функция возвращает операнд, определенный пользователем (см. описание
функции OpAlt).
seg000:0000
a) исходные данные

mov

Регистр AX, 9

Message(“>%s\n”,AltOp(SegByName(“seg000”),1));
b) вызов функции AltOp для получения операнда, опеределенного пользователем
>Регистр AX
c) результат – получен операнд, определенный пользователем
??? #верстальщику – change table
аргумент
ea
n
return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева операнд
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: OpAlt
Интерактивный аналог: нет

134

long GetOpType (long ea,long n)
Функция возвращает тип операнда (см. таблицу ???), принадлежащему элементу
кода (не данных!). Тип операнда, за исключением типов определенных для всех
процессоров, зависит от выбранного микропроцессора.
Тип операнда определяется не его представлением на экране, а инструкциями, в
состав которых он входит. Так, например, при попытке определения второго слева
операнда конструкции “mov dx,offset MyLabel” функция вернет тип непосредственное
значение, несмотря на то, что он представлен в виде смещения.
Общие для всех процессоров
# тип операнда
1 регистр общего назначения
2 ячейка памяти
3 базовый регистр + [индексный]
4 базовый регистр + [индексный] + смещение
5 непосредственное значение
6 непосредственный far-адрес
7 непосредственный near-адрес
Intel 80x86
# тип операнда
8 386+ трассировочный регистр
9 386+ отладочный регистр
10 386+ контрольный регистр
11 Регистр FPP (сопроцессора)
12 MMX регистр
8051
# тип операнда
8
9 бит
10
80196
# тип операнда
8
[внутренняя память]
9
10 смещение[внутренняя память]
ARM
# тип операнда
8 регистр сдвига
9 MLA-операнд
10 регистр (для LDM/STM)
11
CDP
регистр сопроцессора
12
LDC/STC
Power PC
# тип операнда
8 регистр указателя стека
9 регистры плавающей запятой
10 SH & MB & ME
поле
11 CR
бит
TMS320C5
# тип операнда
8 спарка регистров (A1:A0..B15:B14)
135

Z8
#
8
9
Z80
#
8

тип операнда
@внутренняя память
@Rx
тип операнда
условие

Таблица 15
Пример использования:
seg000:0000
mov
ax, 9
a) исходные данные – требуется определить тип обоих операндов
Message(“>%x, %x\n”,GetOpType(SegByName(“seg000”),0),
GetOpType(SegByName(“seg000”),1));
b) вызов функции GetOpType для определения типов операндов
>1,5
с) результат – по таблице ??? определяем тип операндов – регистр общего
назначения и непосредственное значение соответственно.
??? #верстальщику – change table
аргумент
ea
n

return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева, третий (если он есть) и все остальные
операнды
=return пояснения
>1 тип операнда (см. таблицу ???)
==0 элемент не имеет операндов
==BADADDR ошибка

Родственные функции: GetOpnd, GetOperandValue
Интерактивный аналог: нет
longGetOperandValue(long ea,long n)
Функция возвращает значение непосредственного операнда, принадлежащему
элементу кода (не данных!), т.е. типу #5 (см. описание функции GetOpType).
Пример использования:
seg000:0000
mov
ax, 9
a) исходные данные – требуется получить значение непосредственного операнда
Message(“>%x\n”,GetOperandValue(SegByName(“seg000”),1));
b) вызов функции GetOperandValue для получения значения непосредственного
операнда
136

>9
с) результат – значение непосредственного операнда
??? #верстальщику – change table
аргумент
ea
n
return

пояснения
линейный а дрес элем ента , ко то ром у п р ин а д леж и т о пера нд
=n пояснения
==0 первый слева операнд
==1 второй слева операнд
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: GetOpnd, GetOpType
Интерактивный аналог: нет
long FindVoid (long ea,long flag)
Функция ищет ближайший к переданному ей линейному адресу ea операнд типа
“void”, возвращая в случае успешного завершения поиска адрес головы элемента кода,
которому он принадлежит. В зависимости от флага направления поиск может идти как
вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим).
Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно
должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
seg000:0100
mov
ax, 9
seg000:0103
mov
dx, 133h
a) исходные данные – требуется получить линейный адрес элемента, содержащего
операнд типа “void”
Message(“>%s\n”,atoa(FindVoid(0,1)));
b) вызов функции FindVoid – адрес начала поиска равен нулю, единичное значение
флага направление указывает вести поиск с увеличением адресов
>seg000:0103
результат – линейный адрес элемента, содержащего операнд типа void, найден
??? #Верстальщику – change table
аргумент
ea
flag
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
=return пояснения
!=BADADDR линейный адрес элемента, которому принадлежит
найденный операнд
137

==BADADDR

ошибка

Родственные функции: FindImmediate
Интерактивный аналог:”~Nabigate\Search for\Next void”;
long FindImmediate(long ea,long flag,long value)
Функция ищет ближайший к переданному ей линейному адресу ea операнд типа
константа со значением равным value. В случае успешного поиска возвращается адрес
головы элемента кода, которому этот операнд принадлежит.
В зависимости от флага направления поиск может идти как вперед (от младших
адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции
линейный адрес в этот диапазон поиска не входит и не обязательно должен
принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска – если его младший бит установлен
поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
seg000:0100
mov
ax, 9
seg000:0103
mov
dx, 133h
a) исходные данные – требуется получить линейный адрес элемента, содержащего
операнд типа константа, значение которой равно 9
Message(“>%s\n”,atoa(FindImmediate(0,1,9)));
b) вызов функции FindImmediate – адрес начала поиска равен нулю, единичное
значение флага направление указывает вести поиск с увеличением адресов.
>seg000:0100
результат – линейный адрес элемента, содержащего операнд типа константа,
значение которой равно 9
??? #Верстальщику – change table
аргумент
ea
flag
value
return

пояснения
линейный адрес начала поиска, не обязательно принадлежащий
какому-нибудь сегменту
=flag пояснения
==1 прямое направление поиска
==0 обратное направление поиска
искомое значение константы
=return пояснения
!=BADADDR линейный адрес элемента, которому принадлежит
найденный операнд
==BADADDR ошибка

Родственные функции: FindVoid
Интерактивный аналог:”~Nabigate\Search for\Immediate”; , ”~Nabigate\Search
for\Next Immediate”;

138

ОБЪЕКТЫ
#Definition
С каждым элементом (бестиповым байтом) могут быть связаны три объекта –
метка, перекрестная ссылка и комментарий. IDA поддерживает два типа меток – метки,
определенные пользователем и метки, автоматически сгенерированные IDA, а так же
четыре типа комментариев – постоянный комментарий, отображаемый справа от
элемента и отделяемый от него знаком «точка с запятой» (обычный ассемблерный
комментарий), повторяемый комментарий, отображаемый справа от комментируемого
элемента и возле всех ссылок на данный элемент, и два вида многострочных
комментариев предваряющих и замыкающих комментируемый элемент. О перекрестных
ссылках подробно рассказано в главе «Перекрестные ссылки».
Каждый элемент может иметь не более одной метки и до четырех комментариев
различного типа одновременно. Метки и комментарии хранятся в отдельном виртуальном
массиве, проиндексированным линейными адресами, а на наличие связанных с элементом
(бестиповым байтом) объектом указывают флаги (см. таблицу 16)
В принципе без флагов, ссылающихся на объекты можно было бы и обойтись, но
тогда бы пришлось при отображении каждой ячейки просматривать все виртуальные
массивы на предмет поиска объектов, ассоциированных с данным линейными адресом,
что отрицательно сказалось бы на производительности дизассемблера. Напротив, перенос
этой информации в флаги позволяет ускорить работу – обращение к виртуальному
массиву происходит только в тех случаях, когда с ячейкой заведомо связан какой-то объект
Разрушение элемента не вызывает автоматического уничтожение связанных с ним
объектов – каждый объект должен быть удален по отдельности соответствующими
функциями.
константа
FF_COMM
FF_REF
FF_LINE
FF_NAME
FF_LABL
FF_FLOW
FF_VAR

#
0x00000800
0x00001000
0x00002000
0x00004000
0x00008000
0x00010000
0x00080000

пояснения
комментарий
перекрестная ссылка
много строчечный комментарий
метка, определенное пользователем
метка, автоматически сгенерированное IDA
перекрестная ссылка с предыдущей инструкции
переменная

Таблица 16 Флаги, указывающие на наличие связанных объектов

Сводная таблица функций
функции, создающие и уничтожающие объекты
имя функции
краткое описание
success MakeName (long создает метку
ea, char name)
success JmpTable (long создает таблицу переходов
jmpea, long tableea, long
nitems, long is32bit)
success MakeComm (long создает постоянный комментарий
ea, char comment)
success MakeRptCmt (long создает повторяемый комментарий
ea, char comment)

139

void ExtLinA (long ea,long n, создает строку комментария перед элементом
char line)
void ExtLinB (long ea,long n, создает строку комментария за элементом
char line);
void DelExtLnA (long ea, удаляет строку комментария перед элементом
long n)
void DelExtLnB (long ea, удаляет строку комментария за элементом
long n)
void MakeVar(long ea)
помечает элемент, флажком «переменная»
функции, возвращающие элементы
имя функции
краткое описание
char Name (long ea)
возвращает имя метки, при необходимости
выполняя замену недопустимых символов
char GetTrueName (long ea) возвращает имя метки
char Comment (long ea)
возвращает постоянный комментарий
char RptCmt (long ea)
возвращает повторяемый комментарий
char LineA (long ea,long возвращает строку комментария, стоящего до
num);
элемента
char LineB (long ea,long возвращает строку комментария, стоящего за
num);
элементом
функции, поиска объектов
имя функции
краткое описание
long
LocByName
(char возвращает линейный адрес метки с заданным
name)
именем
success MakeName(long ea,char name)
Функция создает метку, расположенную по линейному адресу ea, с именем name.
Переданный линейный адрес должен быть либо адресом головы элемента любого вида,
либо адресом бестипового байта; в противном случае функция возвратит ошибку. Имя
метки должно состоять только из допустимых символов, перечень которых для каждой
платформы содержится в поле “NameChars” конфигурационного файла .
платформа
PC

Java

TMS320C6
PowerPC

перечень символов, допустимых в именах меток
"$?@" 9
“_0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
"$_@?!" 10
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ" 11
"абвгдежзийклмнопрстуфхцчшщъыьэюя";
"$_0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_0123456789."
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz”

9

Служебные символы ассемблера
Символы, определенные только для специальных режимов Java-ассемблера
11
Национальные (российские символы)
10

140

Таблица 17 перечень символов, допустимых в именах меток
Если по указанному адресу расположена уже существующая метка, в результате
работы функции она будет переименована.
Удалить метку можно, переименовав ее, в пустую строку. Удаление возможно
только в том случае, если во всем дизассемблируемом тексте на данную метку нет ни
одной ссылки, в противном случае IDA Pro тут же создаст новое автогенерирумое (dummy)
имя.
Замечание: “MakeName” помимо переименования меток, так же изменяет имена
функций, если ей передать адрес начала функции (см. главу «Функции»)
Пример использования:
seg000:0000
mov
ah, 9
a) исходные данные – требуется создать метку с именем “NoName” по адресу
seg000:000
MakeName(SegByName(“seg000”),”NoName”);
b) вызов функции MakeName для создания метки
seg000:0000 NoName
mov
c) результат – метка успешно создана

ah, 9

??? #Верстальщику – change table
аргумент
ea
name
return

пояснения
линейный адрес головы элемента любового вида или бестипового
байта
имя метки
=return пояснения
==1 успешное завершение операции
==0 ошибка

Родственные функции: GetTrueName
Интерактивный аналог: “~Edit\Name”
success MakeComm(long ea,char comment)
Функция создает комментарий comment, размещая его справа от элемента,
расположенного по линейному адресу ea. Переданный линейный адрес должен быть либо
адресом головы элемента любого вида, либо адресом бестипового байта; в противном
случае функция возвратит ошибку.
Комментарий автоматически отделяется от элемента символом «точка с запятой» и
в самой строке комментария его указывать не нужно. Величина отступа задается
настойками IDA (см. главу «Глобальные настойки»).
Строка комментария может содержать как символы латиницы, так и символы
кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси
IDA, в которой они были созданы.
Удалить комментарий можно задав в качестве нового пустую строку. Удаляются в
том числе, и некоторые комментарии, автоматически создаваемые IDA.
Функция поддерживает спецификатор переноса строки
'\n', автоматически
создавая новую строку и перенося на нее хвост комментария.
141

Пример использования:
seg000:0000
mov
ah, 9
a) исходные данные – требуется вставить комментарий
MakeComm(0x1275C,"Функция 0x9 – печать строки");
b) вызов функции MakeComm для вставки комментария
seg000:0000

mov

ah, 9

c) результат – вставленный комментарий

; Функция 0x9 – печать строки

??? #Верстальщику – change table
аргумент
ea
comment
return

пояснения
линейный адрес головы элемента любого вида или бестипового
байта
строка комментария
=return пояснения
==1 успешное завершение операции
=0 ошибка

Родственные функции: MakeRptCmt, ExrLinA, ExtLinB
Интерактивный аналог: “~Edit\Comments\Enter comment”;
success MakeRptCmt(long ea,char comment)
Функция создает повторяемый комментарий comment, размещая его справа от
элемента, расположенного по линейному адресу ea. Переданный линейный адрес должен
быть либо адресом головы элемента любого вида, либо адресом бестипового байта; в
противном случае функция возвратит ошибку.
Комментарий автоматически отделяется от элемента символом «точка с запятой» и
в самой строке комментария его указывать не нужно. Величина отступа задается
настойками IDA (см. главу «Глобальные настойки»).
Строка комментария может содержать как символы латиницы, так и символы
кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси
IDA, в которой они были созданы.
Удалить комментарий можно задав в качестве нового пустую строку. Функция
поддерживает спецификатор переноса строки '\n', автоматически создавая новую строку и
перенося на нее хвост комментария.
Отличие повторяемого комментария от постоянное заключается в том, что
повторяемый комментарий автоматически отображается около всех элементов,
ссылающихся на элемент, помеченный повторяемым комментарием.
Замечание: повторяемый комментарий может оказаться очень полезным на
начальной стадии анализа программы, когда осмысленные имена переменным и
функциям дать еще затруднительно, но какие-то мысли по поводу их назначения
уже имеются, которые и можно высказать в комментарии, автоматически
повторяемом возле всех ссылок на эту переменную (функцию), облегчая тем
самым исследование кода.
Пример использования:
seg000:0100

mov

ah, 9

142

seg000:0102
mov
dx, offset aHello
seg000:0105
int
21h
;
seg000:0107
retn
seg000:0107 ; ──────────────────────────────────────────────────────────────────────────
seg000:0108 aHello
db 'Hello,',0
; DATA XREF: seg000:0102↑o
seg000:0108
;

a) исходные данные – требуется вставить комментарий к метке aHello,
автоматически повторяемый возле всех инструкций, ссылающихся на эту метку.
MakeRptCmt(SegByName(“seg000”)+0x108,”Это повторяемый комментарий”);

b) вызов функции MakeRptCmt для создания повторяемого комментария
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHello ; Это повторяемый комментарий
seg000:0105
int
21h
; DOS - PRINT STRING
seg000:0105
; DS:DX -> string terminated by "$"
seg000:0107
retn
seg000:0107 ; ──────────────────────────────────────────────────────────────────────────
seg000:0108 aHello
db 'Hello,',0
; DATA XREF: seg000:0102↑o
seg000:0108
; Это повторяемый комментарий

с) результат – повторяемый комментарий создан – теперь он будет отображаться
возле всех элементов, ссылающихся на метку aHello (обратите внимание на текст,
выделенный в листинге жирным шрифтом)
??? #Верстальщику – change table
аргумент
ea
comment
return

пояснения
линейный адрес головы элемента любого вида или бестипового
байта
строка повторяемого комментария
=return пояснения
==1 операция выполнена успешно
==0 ошибка

Родственные функции: MakeComm, ExrLinA, ExtLinB
Интерактивный аналог: “Edit\Comments\Enter repeatable comment”; %s\n”, Name(SegByName(“seg000”)));
b) вызов функции Name для получения имени метки
> aHelloIdaPro
c) результат – имя метки получено
??? #Верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес
=return пояснения
!=”” имя метки, в том виде, в котором оно отображено
на экране

12

Служебные символы ассемблера
Символы, определенные только для специальных режимов Java-ассемблера
14
Национальные (российские символы)
13

148

==””

ошибка

Родственные функции: MakeName, GetTrueName
Интерактивный аналог: имя метки (функции) отображается справа от адреса
char GetTrueName(long ea)
Функция возвращает полное имя метки (функции), расположенной линейному
адресу ea, не проверяя его на наличие недопустимых символов и не производя их
автоматической замены (см. описание функции Name)
Пример использования:
seg000:0000 _HelloIdaPro
db 'Hello, IDA Pro! ',0Dh,0Ah
a) исходные данные – требуется получить подлинное имя меткм
Message(“>%s\n”, GetTrueName(SegByName(“seg000”)));
b) вызов функции GetTrueName для получения имени метки
>%HelloIdaPro
c) результат – подлинное имя метки получено (сравните его с отображаемым на
экране)
??? #Верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес
=return пояснения
!=”” подлинное имя метки (функции)
==”” ошибка

Родственные функции: MakeName, Name
Интерактивный аналог: нет
char Comment(long ea)
Функция возвращает строку постоянного комментария, расположенного по
линейному адресу ea. Если с данным адресом не связан никакой комментарий, функция
возвращает пустую строку, сигнализируя об ошибке.
Пример использования:
seg000:0000

mov

ah, 9

; Функция 0x9 – печать строки

a) исходные данные – требуется получить постоянный комментарий

Message(“>%s\n”,Comment(SegByName(“seg000”)));
b) вызов функции Comment для получения постоянного комментария
> Функция 0x9 – печать строки
c) результат
??? #Верстальщику – change table
аргумент

пояснения
149

ea
return

линейный адрес
=return пояснения
!=”” строка постоянного комментария
==”” ошибка

Родственные функции: MakeComment
Интерактивный аналог: постоянный комментарий отображается справа от
элемента
char RptCmt(long ea)
Функция возвращает строку повторяемого комментария, расположенного по
линейному адресу ea. Если с данным адресом не связан никакой комментарий, функция
возвращает пустую строку, сигнализируя об ошибке.
Пример использования:
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHello ; Это повторяемый комментарий
seg000:0105
int
21h
; DOS - PRINT STRING
seg000:0105
; DS:DX -> string terminated by "$"
seg000:0107
retn
seg000:0107 ; ──────────────────────────────────────────────────────────────────────────
seg000:0108 aHello
db 'Hello,',0
; DATA XREF: seg000:0102↑o
seg000:0108
; Это повторяемый комментарий

a) исходные данные – требуется получить строку повторяемого комментария
Message(“>%s\n”,RptCmt(SegByName(“seg000”)+0x108)));
b) вызов функции RptCmt для получения повторяемого комментария
> Это повторяемый комментарий
c)результат – строка повторяемого комментария
Внимание: функция RptCmt ожидает именно адрес повторяемого комментария, а
не адрес элементов, ссылающихся на элемент, связанный с повторяемым
комментарием.
Т.е.
в
приведенном
выше
примере
вызов
RptCmt(SegByName(“seg000”)+0x102)) вернул бы пустую строку.
??? #Верстальщику – change table
аргумент
ea
return

пояснения
линейный адрес
=return пояснения
!=”” строка повторяемого комментария
==”” ошибка

Родственные функции: MakeRptCmt
Интерактивный аналог: повторяемый комментарий отображается справа от
элемента, и всех ссылок на данный элемент
char LineA(long ea,long num)
Функция возвращает строку num многострочечного комментария, помещенного
перед элементом, расположенным по линейному адресу ea.
150

Пример использования:
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHello
seg000:0105 ; Строка 1
seg000:0105 ; Строка 2
seg000:0105
int
21h
;
a) исходные данные – требуется получить первую строку многострочечного
комментария
Message(“>%s\n”,LineA(SegByName(“seg000”)+0x105,0)));
b) вызов функции LineA для получения первой строки многострочечного
комментария.
> ; Строка 1
с) результат
??? #Верстальщику – change table
аргумент
ea
n
return

пояснения
линейный адрес
номер строки комментария от 0 до 500 включительно.
=return пояснения
!=”” строка повторяемого комментария
==”” ошибка

Родственные функции: LineB
Интерактивный аналог: многострочечный комментарий отображается перед
комментируемым элементном
char LineB(long ea,long num)
Функция возвращает строку num многострочечного комментария, помещенного за
элементом, расположенным по линейному адресу ea.
Пример использования:
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHello
seg000:0102 ; Строка 1
seg000:0102 ; Строка 2
seg000:0105
int
21h
;
a) исходные данные – требуется получить первую строку многострочечного
комментария
Message(“>%s\n”,LineB(SegByName(“seg000”)+0x102,0)));
b) вызов функции LineB для получения первой строки многострочечного
комментария.
> ; Строка 1
с) результат
??? #Верстальщику – change table
151

аргумент
ea
n
return

пояснения
линейный адрес
номер строки комментария от 0 до 500 включительно.
=return пояснения
!=”” строка повторяемого комментария
==”” ошибка

Родственные функции: LineA
Интерактивный аналог: многострочечный комментарий отображается перед
комментируемым элементном
long LocByName(char name)
Функция возвращает линейный адрес метки (имени функции) с именем name. Если
ни одной метки (функции) с указанными именем не существует, функция возвращает
значение BADADDR, сигнализируя об ошибке.
Функция чувствительна к регистру символов и различает имена, набранные
строчечными и прописными буквами.
Внимание: функции требуется передавать подлинные имена меток, а не имена,
отображаемые на экране, прошедшие через фильтр замены недопустимых
символов (см. описание функции GetTrueName)
Пример использования:
seg000:0000 aHelloIdaPro
db 'Hello, IDA Pro! ',0Dh,0Ah
a) исходные данные – требуется получить адрес метки “aHelloIdaPro”
Message(“>%s\n”,atoa(LocByName(“aHelloIdaPro”)));
b) вызов функции LocByName для получения адреса метки
>seg000:0000
c) результат – адрес метки “aHelloIdaPro”
??? #Верстальщику – change table
аргумент
name
return

пояснения
имя метки (функции) с учетом регистра
=return пояснения
!=BADADDR линейный адрес метки (функции)
==BADADDR ошибка

Родственные функции: нет
Интерактивный аналог: “~View\Names”
??? all – дальше начинается не переработанный вариант
ФУНКЦИИ

152

#Definition
Как только подпрограммы стали неотъемлемой конструкцией любого языка,
возникли проблемы с их классификацией.
Начала всему положил BASIC, в котором операторы сплошь и рядом спутаны с
переменными, функции с операторами, а подпрограммы представляют наименее развитую
конструкцию языка.
Затем было предложено называть подпрограмму, не возвращающую результатов
своей работы процедурой, а возвращающую функцией. Именно такая классификация и
была использована в языке Pascal.
Разумеется, было много путаницы. Мало того, что трудно представить себе
процедуру, совсем ничего не возвращающую. По крайней мере она выводит что-то на
экран, или в порты ввода-вывода, пускай даже меняет значения глобальных переменных –
то есть все-таки что-то возвращает.
Потом, любая процедура имеет доступ к аргументам, передаваемым по ссылке, и
может их модифицировать, даже если она ничего не возвращает с точки зрения языка.
Иными словами выражение:
Resultant := MyProc (arg1, ard2);
С точки зрения языка Pascal бессмысленно,
процедура не может ничего
возвращать.
Если опуститься на уровень реализации компилятора Turbo-Pascal, то грубо говоря,
функции возвращают результат своей работы в регистре AX, а процедуры оставляют его
неопределенным.
То есть другими словами, функцией называется то, что возвращает результат
своей работы в регистре AX.
В языке Си все совсем иначе. Не зависимо от того, возвращает что ни будь
подпрограмма или нет, она называется функцией.
Процедур в Си нет. Функция всегда что-то возвращает, по крайней мере
бестиповое значение void, которые можно присвоить переменной.
Не будем вспоминать сейчас те языки, где классификация подпрограмм еще более
запутана или вовсе не развита.
Сейчас же важно представить себе, как будет работать со всем этим IDA. Знает ли
она что-нибудь об этих языковых конструкциях?
Для ответа на этот вопрос необходимо уточнить, что нужно понимать под термином
«знает».
Прежде всего она, как и любой другой дизассемблер явно поддерживает только те
конструкции, которые «понимает» целевой ассемблер.
С другой стороны, автоматический анализатор способен распознавать различные
языковые конструкции, которые могут напрямую и не поддерживаться ассемблером.
Популярные ассемблеры MASM и TASM кроме низкоуровневой поддержки
подпрограмм, обеспечивают ряд типовых операций с ними, такими как, например,
передача аргументов через стек и обращения к ним, абстрагируясь от модели памяти (все
вычисления ложатся на плечи ассемблера).
Однако, эту конструкцию создатели языка почету-то окрестили процедурой, чем
окончательно всех запутали. С одной стороны ассемблер не может автоматически
присваивать переменным и регистрам результат, возвращенный процедурой, но с другой
это еще никак не означает, что процедура не может ничего возвращать.
Воистину, «называемое дао, не есть дао». Впрочем, само по себе такой расклад
вещей достаточно приемлем – поскольку, ассемблер, как и любой другой язык, имеет свои
соглашения и может совсем не интересоваться, как та же конструкция называется у других.
Но ведь IDA это не просто дизассемблер, но и немного декомпилятор, то есть
мотивом ее использования, зачастую служит необходимость получить хотя бы отдаленное
представление об исходном тексте программы, пусть и в другой форме.

153

Однако,
поддерживать
отдельные
языковые
конструкции
было
бы
нецелесообразно, да и в ряде случаев и вовсе невозможно. Поэтому было принято
решение остановиться на одной универсальной конструкции, которая бы с успехом
подходила для любого языка.
Названа же она была функцией. Но в ассемблере не существует такого понятия!
Поэтому для выражения функции приходится пользоваться средствами ассемблера, в
котором она (функция) называется процедурой, но ничем другими принципиально, кроме
названия не отличается.
Это иногда приводит к небольшой путанице. Так, например, если
дизассемблировать программу, написанную на 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

154

seg000:002E PROGRAM
seg000:002E
__SystemInit(void)
seg000:0033
seg000:0038
seg000:0039
seg000:003B
seg000:003D
overflow check (AX)
seg000:0042
seg000:0045
seg000:0046
seg000:0048
seg000:0048 PROGRAM

proc near
call
@__SystemInit$qv ;
call
push
mov
xor
call

sub_5_D
bp
bp, sp
ax, ax
@__StackCheck$q4Word ; Stack

call
pop
xor
call
endp

sub_0_6
bp
ax, ax
@Halt$q4Word

; Halt(Word)

На самом же деле никакого противоречия тут нет. Компиляция однонаправленный
процесс с потерями, поэтому можно забыть о существующих конструкциях в языкеисточнике.
Код, сгенерированный компилятором одинаково хорошо похож, как на процедуру,
так и на функцию:
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
bp

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

proc near

seg000:002D sub_0_6

endp

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

Назначение
155

success MakeFunction(long start,long
end);

Создает функцию

success DelFunction(long ea);

Удаляет функцию

success SetFunctionEnd(long ea,long
end);

Изменяет линейный адрес конца функции

long NextFunction(long ea);

Возвращает
линейный
следующей функции

адрес

начала

long PrevFunction(long ea)

Возвращает
линейный
предыдущей функции

адрес

начала

long GetFunctionFlags(long ea);

Возвращает атрибуты функции

success
SetFunctionFlags(long
ea,long flags);

Устанавливает атрибуты функции

char GetFunctionName(long ea);

Возвращает имя функции

void SetFunctionCmt(long ea, char
cmt, long repeatable);

Устанавливает
комментарий
(постоянный и повторянымый)

char GetFunctionCmt(long ea, long
repeatable);

Возвращает комментарий функции

long ChooseFunction(char title);

Открывает диалоговое окно со списком всех
функций

char GetFuncOffset(long ea);

Преобразует линейный адрес к строковому
смещению от начала функции

long GetFrame(long ea);

Возвращает ID фрейма функции

long GetFrameLvarSize(long ea);

Возвращает размер фрейма функции

long GetFrameLvarSize(long ea);

Возвращает размер локальных переменных
функции в байтах

long GetFrameArgsSize(long ea)

Возвращает размер аргументов функции в
байтах

long GetFrameSize(long ea);

Возвращает полный
фрейма в байтах

long
MakeFrame(long
ea,long
lvsize,long frregs,long argsize)

Создает фрейм функции или модифицирует
уже существующий

long GetSpd(long ea);

Возвращает значение регистра
произвольной точке функции

long GetSpDiff(long ea);

Возвращает
относительное
изменение
регистра SP указанной инструкцией

размер

функции

стекового

SP

в

156

success SetSpDiff(long ea,long delta);

Корректирует изменение
указанной командой

регистра

SP,

long FindFuncEnd(long ea)

Определяет линейный адрес конца функции

success MakeFunction(long start,long end);
Вызов MakeFunction позволяет создавать функцию. IDA не различает функций и
процедур – в ее терминологии все это функции.
Каждая функция обладает принадлежащим ей непрерывным диапазонов адресов.
В его границах может отслеживаться значения указателя стека, создаются ссылки на
следующие инструкции и так далее. То есть ряд вызовов API работает исключительно с
функциями.
Для создания функции достаточно только указать линейный адрес ее начала и
конца. Функции могут создаваться только внутри сегментов и располагаться только с
начала, а не середины машинных инструкций.
В то же время допускается в качестве конца задавать адрес, приходящейся на
середину инструкции. IDA все равно его округлит до адреса конца предыдущей инструкции.
Например:
seg000:002A
seg000:002D
seg000:0030
seg000:0033
seg000:0036

mov
call
mov
call
retn

si, 211h
sub_0_DD
si, 2BAh
sub_0_DD

MakeFunction(0x1002A,0x10037);
seg000:002A
seg000:002A
seg000:002A
seg000:002A
seg000:002A
seg000:002D
seg000:0030
seg000:0033
seg000:0036
seg000:0036
seg000:0036
seg000:0037
seg000:0037

; _______________ S U B R O U T I N E
sub_0_2A

sub_0_2A

proc near
mov
si, 211h
call
sub_0_DD
mov
si, 2BAh
call
sub_0_DD
retn
endp

; _______________ S U B R O U T I N E

При этом функции автоматически дается имя, вид которого зависит от настоек. По
умолчанию оно предваряется префиксом ‘sub’ (от subroutine - то есть процедура; забавно
– ведь в терминологии IDA она называется функцией) и последующим смещением внутри
сегмента.
Если вместо адреса конца функции указать константу BADADDR, то IDA
попытается самостоятельно определить его.
Этот механизм довольно бесхитростен (концом функции считается инструкция ret
или jmp) и довольно часто приводит к ошибкам и занижает адрес.
157

Разберем, например, такой случай. Путь некая функция имеет более одной точки
выхода. В этом случае IDA часто принимает за конец функции первый встретившийся их
них, а второй так и остается в «хвосте».
Это не упрек в несовершенстве алгоритма, а просто предостережения от всецелого
доверия ему. В действительности же машинный анализ никогда не станет настолько
совершенным, что бы полностью заменить человека.
Обратите внимание, что вызов MakeFunction провалится, если выделенная под
функцию область будет помечена как undefined. Что и видно из следующего примера:
seg000:002A
seg000:002B
seg000:002C
seg000:002D
seg000:002E
seg000:002F
seg000:0030
seg000:0031
seg000:0032
seg000:0033
seg000:0034
seg000:0035
seg000:0036

db
db
db
db
db
db
db
db
db
db
db
db
db

0BEh
11h
2
0E8h
0ADh
0
0BEh
0BAh
2
0E8h
0A7h
0
0C3h

Message(“0x%X \n”,MakeFunction(0x1002A,0x10037));
0
Но в то же время, если в качестве адреса конца указать константу BADADDR, то
функция будет успешно создана!
seg000:002A
seg000:002B
seg000:002C
seg000:002D
seg000:002E
seg000:002F
seg000:0030
seg000:0031
seg000:0032
seg000:0033
seg000:0034
seg000:0035
seg000:0036

db
db
db
db
db
db
db
db
db
db
db
db
db

0BEh
11h
2
0E8h
0ADh
0
0BEh
0BAh
2
0E8h
0A7h
0
0C3h

Message(“0x%X \n”,MakeFunction(0x1002A,-1));
1

seg000:002A ; _______________ S U B R O U T I N E
_______________________________________
seg000:002A
seg000:002A
seg000:002A sub_0_2A
proc near
seg000:002A
mov
si, 211h
158

seg000:002D
call
sub_0_DD
seg000:0030
mov
si, 2BAh
seg000:0033
call
sub_0_DD
seg000:0036
retn
seg000:0036 sub_0_2A
endp
seg000:0036
seg000:0037
seg000:0037 ; _______________ S U B R O U T I N E
_______________________________________
Операнд
Start

End

Return

Пояснения
Линейный адрес начала функции. Функция не может начинаться с
середины инструкции
==end
Пояснения
Линейный адрес конца функции. Может приходиться
!=-1
на середину инструкции. IDA его округлит до адреса
конца предыдущей инструкции.
==-1
IDA автоматически вычисляет адрес конца функции и
при необходимости преобразует undefined в
инструкции
Завершение
Пояснения
0
Вызов завершился не успешно. Функция не была
создана
1
Вызов завершился Успешно

success DelFunction(long ea);
Вызов DelFunction позволяет удалять функцию, указав любой, принадлежащий ей
адрес. Вместе с функцией уничтожаются все локальные переменные, и аргументы, если
они есть. Все остальное (инструкции, перекрестные ссылки, метки) остается нетронутым.
Например:
.text:00400FFF ; _____________ S U B R O U T I N E
____________________________________
.text:00400FFF
.text:00400FFF ; Attributes: library function
.text:00400FFF
.text:00400FFF __amsg_exit
proc near
; CODE
XREF: __setenvp+4Ep
.text:00400FFF
;
__setenvp+7Dp ...
.text:00400FFF
.text:00400FFF arg_0
= dword ptr 4
.text:00400FFF
.text:00400FFF
cmp
dword_0_408758, 2
.text:00401006
jz
short loc_10_40100D
.text:00401008
call
__FF_MSGBANNER
.text:0040100D
.text:0040100D loc_10_40100D:
; CODE
XREF: __amsg_exit+7j
.text:0040100D
push
[esp+arg_0]
159

.text:00401011
.text:00401016
.text:0040101B
.text:00401021
.text:00401022
.text:00401023
.text:00401023 __amsg_exit

call
push
call
pop
pop
retn
endp

__NMSG_WRITE
0FFh
off_0_408050
ecx
ecx

DelFuncton(0x400FFF);
.text:00400FFF __amsg_exit:
XREF: __setenvp+4Ep
.text:00400FFF
__setenvp+7Dp ...
.text:00400FFF
.text:00401006
.text:00401008
.text:0040100D
.text:0040100D loc_10_40100D:
XREF: .text:00401006j
.text:0040100D
.text:00401011
.text:00401016
.text:0040101B
.text:00401021
.text:00401022
.text:00401023
Операнд
ea
Return

; CODE
;
cmp
jz
call

dword_0_408758, 2
short loc_10_40100D
__FF_MSGBANNER
; CODE

push
call
push
call
pop
pop
retn

dword ptr [esp+4]
__NMSG_WRITE
0FFh
off_0_408050
ecx
ecx

Пояснения
Любой линейный адрес, принадлежащий функции
Завершение
Пояснения
0
Вызов завершился не успешно. Функция не была
создана
1
Вызов завершился Успешно

success SetFunctionEnd(long ea,long end);
Позволяет изменить линейный адрес конца функции. Для этого достаточно лишь
передать любой адрес, принадлежащий функции и новое значение конца.
Например:
seg000:22C0 start
seg000:22C0
seg000:22C3
seg000:22C6
seg000:22C9
seg000:22CC
seg000:22CF
seg000:22D2
seg000:22D4
seg000:22D5

proc near
call
sub_0_22DD
call
sub_0_2325
call
sub_0_235B
call
sub_0_2374
call
sub_0_23B6
call
sub_0_23F8
jnz
loc_0_22DA
nop
nop
160

seg000:22D6
seg000:22D7
seg000:22DA
seg000:22DA loc_0_22DA:
seg000:22DA
seg000:22DA start

nop
call
call
endp

sub_0_2412
sub_0_2305

SetFunctionEnd(0x122C3,0x122СF);
seg000:22C0 start
seg000:22C0
seg000:22C3
seg000:22C6
seg000:22C9
seg000:22CC
seg000:22CF
seg000:22CF start
seg000:22D2
seg000:22D4
seg000:22D5
seg000:22D6
seg000:22D7
seg000:22DA
seg000:22DA loc_0_22DA:
seg000:22DA

proc near
call
sub_0_22DD
call
sub_0_2325
call
sub_0_235B
call
sub_0_2374
call
sub_0_23B6
call
sub_0_23F8 ; Æ источник
endp
jnz
loc_0_22DA ; Å приемник
nop
nop
nop
call
sub_0_2412
call

sub_0_2305

Однако при этом не удаляется перекрестная ссылка на следующую команду, что
может иметь неприятные последствия, например, при попытке пометить функцию как
undefined, что и видно на следующем примере:
MakeUnkn(0x122C0,1);
seg000:22C0 start
seg000:22C1
seg000:22C2
seg000:22C3
seg000:22C4
seg000:22C5
seg000:22C6
seg000:22C7
seg000:22C8
seg000:22C9
seg000:22CA
seg000:22CB
seg000:22CC
seg000:22CD
seg000:22CE
seg000:22CF
seg000:22D0
seg000:22D1
seg000:22D2
seg000:22D3
seg000:22D4
seg000:22D5
seg000:22D6

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

0E8h
1Ah
0
0E8h
5Fh
0
0E8h
92h
0
0E8h
0A8h
0
0E8h
0E7h
0
0E8h
26h
1
75h
6
90h
90h
90h

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

ш
ш
_
ш
Т
ш
и
ш
ч
ш
&
u
Р
Р
Р
161

Кроме того, если в качестве нового конца указать адрес, уже принадлежащий
какой-нибудь функции (разумеется, кроме текущей), то вызов провалиться.
seg000:2305
seg000:2305
seg000:2306
seg000:2309
seg000:230B
seg000:230B
seg000:230B
seg000:230D
seg000:230D
seg000:230D
seg000:230D
seg000:230D
seg000:230D
seg000:2310
seg000:2313
seg000:2316
seg000:2319
seg000:231B
seg000:231C
seg000:231D
seg000:231E
seg000:2321
seg000:2321
seg000:2321
seg000:2324
seg000:2324

sub_0_2305

sub_0_2305

proc near
sti
call
sub_0_1CA
mov
ah, 4Ch
int
21h
endp

; _______________ S U B R O U T I N E
sub_0_230D

loc_0_2321:
sub_0_230D

proc near
mov
si, 2C51h
call
sub_0_DD
mov
si, 2C4Dh
call
sub_0_2E2
jnb
loc_0_2321
nop
nop
nop
mov
si, 2A2Dh
call
retn
endp

sub_0_DD

Message(“0x%X \n”,
SetFunctiinEnd(0x12305,0x12310)
);
1
Если функция возвращает отличное от нуля число, то это признак не успешности
завершения операции. Следовательно, адрес конца не был изменен. (Повторный дамп для
экономии не приводится)
Напротив, если за концом функции расположены данные (можно даже массив), то
новый адрес конца будет успешно установлен.
seg000:292F sub_0_292F
proc near
seg000:292F
inc
bx
seg000:2930
loop
loc_0_292F
seg000:2932
nop
seg000:2933
retn
seg000:2933 sub_0_292F
endp
seg000:2933
seg000:2933 ; ---------------------------------seg000:2934*word_0_2934
dw 0
seg000:2934*
seg000:2936*byte_0_2936
db 0
162

SetFuctionEnd(0x12930,0x12934);
seg000:292F sub_0_292F
proc near
seg000:292F
inc
bx
seg000:2930
loop
loc_0_292F
seg000:2932
nop
seg000:2933
retn
seg000:2933
seg000:2933 ; ---------------------------------seg000:2934*word_0_2934
dw 0
seg000:2934*
seg000:2934 sub_0_292F
endp
seg000:2936*byte_0_2936
db 0
Можно даже указать на середину массива или ячейки. Функция завершиться
успешно, адрес будет изменен, но он перестанет отображаться на экране, поскольку IDA
забыла его округлить или проверить на корректность!
Это подтверждает следующий пример, проделанный над куском кода, приведенном
выше.
Message(“0x%X \n”,
SetFuctionEnd(0x12930,0x12935)
);
0
seg000:292F sub_0_292F
proc near
seg000:292F
inc
bx
seg000:2930
loop
loc_0_292F
seg000:2932
nop
seg000:2933
retn
seg000:2933
seg000:2933 ; ---------------------------------seg000:2934*word_0_2934
dw 0
seg000:2934*
seg000:2936*byte_0_2936
db 0
Однако, в действительности, то, что конец функции не отображается на экране,
еще ничего не значит. Попробуем убедиться, что IDA действительно не выполнила
округления, и адрес 0x12936 не принадлежит функции.
Message(“0x%X \n”,
SetFuctionEnd(0x12936,0x12933)
);
1
Ага, вызов SetFunctionEnd возвратил ошибку, следовательно, адрес 0x12936
действительно не принадлежит функции. Попробуем теперь уменьшить его на единицу:
Message(“0x%X \n”,
SetFuctionEnd(0x12935,0x12933)
163

);
0
seg000:292F sub_0_292F
proc near
seg000:292F
inc
bx
seg000:2930
loop
loc_0_292F
seg000:2932
nop
seg000:2933
retn
seg000:2933
seg000:2933 ; ---------------------------------seg000:2934*word_0_2934
dw 0
seg000:2934*sub_0_292F
endp
seg000:2936*byte_0_2936
db 0
Выходит, что как это ни парадоксально, но линейный адрес конца функции лежал
посередине ячейки word_02934, где он, разумеется, не мог быть отображен. Описание этой
ошибки IDA (которая должна быть устранена в последующих версиях), вероятно, не стоил
бы такого пристального внимания, если бы эти таинственные исчезновения конца функций
не случались так часто.
Это не нарушает работоспособности дизассемблера, но сбивает с толку
пользователя и вызывает в нем мнимое подозрение, что в базе IDA серьезные сбои или
ошибки, и что лучше ее удалить и начать проект заново, чем работать с ошибками,
которые еще не известно чем могут обернуться в будущем.
На самом деле никаких поводов для беспокойства нет. Необходимо поправить
адрес конца функции и можно продолжить работать дальше.
Операнд
ea
end
Return

Пояснения
Любой линейный адрес, принадлежащий функции
Новый линейный адрес конца функции.
Завершение
Пояснения
0
Вызов завершился не успешно. Функция не была
создана
1
Вызов завершился Успешно

long NextFunction(long ea);
Вызов возвращает линейный адрес начала функции следующий за ‘ea’. Что бы
получить адрес первой функции необходимо вызвать NextFunction(0).
Если больше ни одной функции возвратить невозможно, то функция возвращает
ошибку BADADDR.
Пример использования:
seg000:0000 sub_0_0
seg000:0000
seg000:0001
……………..
seg000:0027
seg000:0028
seg000:0029
seg000:0029 sub_0_0

proc near
push
ax
push
bx
pop
pop
retn
endp

bx
ax

164

seg000:0029
seg000:002A
seg000:002A ; ___________ S U B R O U T I N E
____________________
seg000:002A
seg000:002A
seg000:002A sub_0_2A
proc near
seg000:002A
mov
si, 211h
seg000:002D
call
sub_0_DD
seg000:0030
mov
si, 2BAh
seg000:0033
call
sub_0_DD
seg000:0036
retn
seg000:0036 sub_0_2A
endp
seg000:0036
seg000:0037
seg000:0037 ; _______________ S U B R O U T I N E
________________
seg000:0037
seg000:0037
seg000:0037 sub_0_37
proc near
seg000:0037
seg000:0037
seg000:0037
push
ax
seg000:0038
push
bx
auto a;
a=0;
while ((a=NextFunction(a))!=-1)
Message("%x \n",a);
10000
1002a
10037
Операнд
ea
Return

Пояснения
Линейный адрес
Завершение
Пояснения
!=BADADDR Линейный адрес начала следующей функции
BADADDR
Ошибка

long PrevFunction(long ea)
Вызов возвращает линейный адрес предыдущий функции. Что бы получить адрес
последней функции необходимо вызвать PrevFunction(BADADDR).
seg000:0000 sub_0_0
seg000:0000
seg000:0001
……………..
seg000:0027
seg000:0028
seg000:0029
seg000:0029 sub_0_0

proc near
push
ax
push
bx
pop
pop
retn
endp

bx
ax

165

seg000:0029
seg000:002A
seg000:002A ; ___________ S U B R O U T I N E
____________________
seg000:002A
seg000:002A
seg000:002A sub_0_2A
proc near
seg000:002A
mov
si, 211h
seg000:002D
call
sub_0_DD
seg000:0030
mov
si, 2BAh
seg000:0033
call
sub_0_DD
seg000:0036
retn
seg000:0036 sub_0_2A
endp
seg000:0036
seg000:0037
seg000:0037 ; _______________ S U B R O U T I N E
________________
seg000:0037
seg000:0037
seg000:0037 sub_0_37
proc near
seg000:0037
seg000:0037
seg000:0037
push
ax
seg000:0038
push
bx
auto a;
a=0x10038;
while ((a=PrevFunction(a))!=-1)
Message("%x \n",a);
10037
1002a
10000
Операнд
Ea
Return

Пояснения
Линейный адрес
Завершение
Пояснения
!=BADADDR Линейный адрес начала предыдущей функции
BADADDR
Ошибка

long GetFunctionFlags(long ea);
Вызов GetFunctionFlags позволяет узнать атрибуты функции.
отдельных битов в возвращаемом значении показано в таблице ниже.
Определение
FUNC_NORET
FUNC_FAR
FUNC_LIB

Значение
0x00000001
L
0x00000002
L
0x00000004

Назначение

Пояснения
Функция не возвращает управления
FAR (далекая) функция
Библиотечная функция

166

FUNC_STATIC
FUNC_FRAME
FUNC_USERFAR
FUNC_HIDDEN

L
0x00000008
L
0x00000010L
0x00000020
L
0x00000040
L

Статическая функция
Функция использует для указателя кадра стека регистр BP
Функция определена пользователем как далекая
(FAR)
Скрытая функция

Подробнее о каждом атрибуте будет рассказано ниже.
FUNC_NORET
Этот атрибут устанавливается тем функциям, что не возвращают
управления командой ret. Однако в большинстве случаев IDA автоматически не
присваивает его.
Так, например, в следующей функции он не установлен.
seg000:2305 sub_0_2305
seg000:2305
seg000:2306
seg000:2309
seg000:230B
seg000:230B sub_0_2305

proc near
sti
call
sub_0_1CA
mov
ah, 4Ch
int
21h
endp

Message(“%b \n”,
GetFunctionFlags(0x12305)
);
0
Если в вашей ситуации это обстоятельство окажется критичным, то можно
написать собственный скрипт, выполняющий такие проверки и устанавливающий
атрибуты через вызов SetFunctionFlags.
FUNC_FAR
«Далекая» функция. IDA считает далекими все функции, оканчивающиеся
командой далекого возврата – retf.
Этот механизм достаточно несовершенен и может давать сбои. Так,
например, в следующем фрагменте эмуляцию вызова CALL FAR \ RET IDA
интерпретировала, как далекий возврат из функции.
Это не упрек в сторону IDA, поскольку ради таких частных случаев
бессмысленно вносить дополнительные усовершенствования в дизассемблер, но
учитывать этот факт всегда стоит, вручную корректируя адрес конца и атрибуты
функции.
seg000:048B sub_0_48B
seg000:048B
seg000:048B
seg000:048C
seg000:048D
seg000:0490
seg000:0494
seg000:0498

proc far
pushf
push
push
push
push
retf

cs
offset locret_0_499
word ptr ds:74Dh
word ptr ds:74Bh

167

seg000:0498 sub_0_48B
endp ; sp = -0Ah
seg000:0498
seg000:0499 ; -----------------------------------------seg000:0499
seg000:0499 locret_0_499:
seg000:0499
retn
Message(“%b \n”,
GetFunctionFlags(0x1048B)
);
10
FUNC_LIB
Таким флагом отмечены стандартные «библиотечные» функции. То есть те,
чьи сигнатуры известны FLIRT.
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:00401106
.text:00401108
.text:0040110D
.text:0040110D
.text:0040110D
.text:00401111
.text:00401116
.text:0040111B
.text:00401121
.text:00401122
.text:00401123
.text:00401123

; Attributes: library function
__amsg_exit

proc near

arg_0

= dword ptr

4

cmp
jz
call

dword_0_408758, 2
short loc_0_40110D
__FF_MSGBANNER

push
call
push
call
pop
pop
retn
endp

[esp+arg_0]
__NMSG_WRITE
0FFh
off_0_408050
ecx
ecx

loc_0_40110D:

__amsg_exit

Message(“%b \n”,
GetFunctionFlags(0x4010FF)
);
100
FUNC_FRAME
Функция использует в качестве указателя на кадр стека регистр BP (EBP).
IDA определяет это отслеживая последовательность
PUSH BP
MOV BP, SP

168

Большинство современных оптимизирующих компиляторов отказались от
такого подхода, и адресуют локальные переменные и аргументы непосредственно
по регистру ESP. IDA распознает такую ситуацию и отслеживает значение ESP по
ходу исполнения процедуры, освобождая пользователя от львиной доли работы.

seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B8
seg000:20B9
seg000:20BB
seg000:20BB
seg000:20BD
seg000:20BE
seg000:20C0
seg000:20C4
seg000:20C6
seg000:20C9

; Attributes: bp-based frame
sub_0_20B8

proc near

var_80
var_6B
var_62

= byte ptr -80h
= byte ptr -6Bh
= byte ptr -62h
push
mov
int

bp
ah, 2Fh
21h

push
mov
sub
mov
lea
int

bx
bp,
sp,
ah,
dx,
21h

sp
80h
1Ah
[bp+var_80]

Message(“%b \n”,
GetFunctionFlags(0x4010FF)
);
10000
Обратите внимание, что IDA распознала эту комбинацию, даже когда
команды push bp и mov bp,sp оказались достаточно разнесены.
FUNC_USERFAR
Этот атрибут IDA устанавливает, когда пользователь вручную меняет тип
функции с NEAR на FAR, вызывая диалог ‘Modify Function’ с помощью команды
меню ~ Edit \ Function \ Edit Function.

169

Обратите внимание, что это не относится к вызовам SetFunctionFlags!
Последняя устанавливает именно тот набор атрибутов, который ей передается.

FUNC_HIDDEN
Этот атрибут устанавливается у «свернутых» функций. Свернуть любую
функцию можно однократным нажатием Gray ‘-‘, переместив курсов в ее границы.
Кроме этого, в зависимости от настоек, IDA может автоматически сворачивать все
библиотечные функции для экономии места.
dseg:027B ; [00000009 BYTES: COLLAPSED FUNCTION sub_0_27B. PRESS KEYPAD "+" TO EXPAND]

Message(“%b \n”,
GetFunctionFlags(0x4010FF)
);
100000
Заметим, что в результате упущения этот тип не определен в IDC.IDC и что
бы им пользоваться необходимо это сделать самостоятельно, внеся новую строчку:
#define FUNC_HIDDEN

0x00000040L

// a hidden function

Все атрибуты функция IDA отображает в виде комментариев, расположенных в
начале функции.
dseg:0271 ; Attributes: library function
dseg:0271
dseg:0271 __checknull
proc near
170

dseg:0271
dseg:0271 __checknull

retn
endp

dseg:0272 ; Attributes: library function bp-based frame
dseg:0272
dseg:0272 __terminate
proc

Операнд
Ea
Return

Пояснения
Линейный адрес, принадлежащий функции
Завершение
Пояснения
!=BADADDR Набор атрибутов функции (смотри таблицу выше)
BADADDR Ошибка

success SetFunctionFlags(long ea,long flags);
Позволяет устанавливать атрибуты функции. Подробнее об этом было сказано в
описании функции GetFunctionFlags.
Операнд
Ea
flag
Return

Пояснения
Линейный адрес, принадлежащий функции
Атрибуты функции (смотри таблицу в описании GetFunctionFlags)
Завершение
Пояснения
!=BADADDR Набор атрибутов функции (смотри таблицу выше)
BADADDR
Ошибка

Как уже отмечалось в описании функции SetFunctionFlags, часто IDA автоматически
не распознает функции, не возвращающие управления (нет команды ref в завершении
функции).
Если это критично, то нужный атрибут можно установить вручную. Покажем это на
следующем примере:
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0274
dseg:0276
dseg:0279
dseg:0279

; Attributes: library function bp-based frame
__terminate

proc near

arg_0

= byte ptr

__terminate

mov
mov
mov
int
endp

; COD
2

bp, sp
ah, 4Ch ; 'L'
al, [bp+arg_0]
21h

; DOS
; AL

SetFunctionFilegs
(
0x10272,
GetFunctionFlags(0x10272) + 1
)

171

dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0274
dseg:0276
dseg:0279
dseg:0279

; Attributes: library function noreturn bp-based frame
__terminate

proc near

arg_0

= byte ptr

__terminate

mov
mov
mov
int
endp

; CODE XREF: sub_0_3C7+44p
2

bp, sp
ah, 4Ch ; 'L'
al, [bp+arg_0]
21h

; DOS - 2+ - QUIT WITH EXIT
; AL = exit code

В большинстве случаев атрибуты никакого влияния на функции не оказывают. Так,
например, если в приведенном примере сбросить флаг FUNC_FRAME, то это не повлечет
за собой автоматического удаления всех локальных переменных, адресуемых через BP.
SetFunctionFilegs
(

0x10272,
GetFunctionFlags(0x10272) – 0x10;
)
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0272
dseg:0274
dseg:0276
dseg:0279
dseg:0279

; Attributes: library function
__terminate

proc near

arg_0

= byte ptr

__terminate

mov
mov
mov
int
endp

; CODE XREF: sub_0_3C7+44p
2

bp, sp
ah, 4Ch ; 'L'
al, [bp+arg_0]
21h

; DOS - 2+ - QUIT WITH EXIT
; AL = exit code

Но вот установка флага FUNC_HIDDEN приведет к незамедлительному
сворачиванию функции и сброс соответственно, наоборот.
SetFunctionFilegs
(

0x10272,
GetFunctionFlags(0x10272) + 0x40;
)
dseg:0272 ; [00000009 BYTES: COLLAPSED FUNCTION __terminate. PRESS KEYPAD "+" TO EXPAND]

char GetFunctionName(long ea);
Возвращает имя функции. Если указанный адрес не принадлежит ни одной из
функций, то возвращается пустая строка.
Поскольку функции без имени не бывает, то неоднозначной ситуации не возникает.
Операнд
Ea
Return

Пояснения
Любой линейный адрес, принадлежащий функции
Завершение
Пояснения
172

!=””
“”

Имя функции
Ошибка

Пример использования:
dseg:025E __cleanup
dseg:025E
dseg:0263
dseg:0264

proc near
mov
es, cs:DGROUP@
push
si
push
di

Message(“%s \n”,
GetFunctionName(0x10263)
);
__cleanup

void SetFunctionCmt(long ea, char cmt, long repeatable);
Задает комментарий, который размещается впереди функции. IDA поддерживает
символ переноса, поэтому комментарий может располагаться на нескольких строках.
Существует возможность повторять тот же комментарий в точке вызова функции
(так называемый repeatable comment). Для этого необходимо установить флаг ‘repeatable’
равным единице.
Например:
SetFunctionCmt(0x10271,”Hello IDA 4.0”,1);
dseg:0271 ; Hello IDA 4.0
dseg:0271 ; Attributes: static
dseg:0271
dseg:0271 __checknull
proc near
sub_0_3C7+2Cp
dseg:0271
retn
dseg:0271 __checknull
endp

; CODE XREF:

Если перейти по перекрестной ссылке к месту вызова функции, то там будет можно
обнаружить следующее:
dseg:03F0
dseg:03F3
4.0
dseg:03F6
dseg:03FA

call
call

__restorezero
__checknull

cmp
jnz

[bp+arg_2], 0
loc_0_40F

; Hello

IDA

Если в строке комментария будет присутствовать символ переноса, то экран будет
выглядеть так:
SetFunctionCmt(0x10271,”Hello \nIDA 4.0”,1);

173

dseg:0271 ; Hello
dseg:0271 ; IDA 4.0
dseg:0271 ; Attributes: static
dseg:03F3
dseg:03F3
dseg:03F6

call

__checknull

cmp

[bp+arg_2], 0

; Hello
; IDA 4.0

Не рекомендуется перегружать листинг повторяемыми комментариями. Ведь
всегда можно обратиться за разъяснениями к самой функции.
Наиболее полезны они на начальной стадии дизассемблирования, когда
назначение большинства функций плохо понятны и дать им осмысленное имя никак не
удается. Тогда в повторяемом комментарии отражают все, что на данный момент известно
о каждой из функций и по мере исследования текста, уточняют. На финальной же стадии
дизассемблирования, повторяемые комментарии обычно убирают.
Обратите внимание, каждая функция может обладать комментариями сразу двух
типов, но в заголовке будет отображаться только один из них – ‘regular’.
Например:
SetFunctionCmt(0x10271,”Hello IDA 4.0”,1);
SetFunctionCmt(0x10271,”Hello World”,0);
dseg:0271 ; Hello World
dseg:0271 ; Attributes: static
dseg:03F3
dseg:03F6

call
cmp

Операнд
Ea
Cmp
Repeatable

__checknull
[bp+arg_2], 0

; Hello IDA 4.0

Пояснения
Любой линейный адрес, принадлежащий функции
Строка комментария, включая символ переноса
Флаг
Пояснения
0 Неповторяемый комментарий
1 Повторяемый комментарий

char GetFunctionCmt(long ea, long repeatable);
Позволяет получить как повторяемый, так и постоянный комментарии. Для этого
необходимо задать любой линейный адрес, принадлежащий функции.
Подробнее о повторяемых комментариях можно прочитать в описании функции
SetFunctionCmt
Например:
dseg:0271
dseg:0271
dseg:0271
dseg:0271
dseg:0271
dseg:0271

; Hello IDA 4.0
; Attributes: static
__checknull
__checknull

proc near
retn
endp

Message(“%s \n”,
GetFunctionCmt(0x010271,1)
174

);
Hello, IDA 4.0
Message(“%s \n”,
GetFunctionCmt(0x010271,0)
);
Обратите внимание, что необходимо правильно указывать тип комментария
(повторяемый или нет) иначе функция вернет совсем не то, что от нее ожидается.
Операнд
Ea
Repeatable
Return

Пояснения
Любой линейный адрес, принадлежащий функции
Флаг
Пояснения
0 Неповторяемый комментарий
1 Повторяемый комментарий
Завершение
Пояснения
!=”” Комментарий
“” Ошибка

long ChooseFunction(char title);
Создает диалоговое окно содержащие список всех существующих функций с
краткой сводной информацией о каждой из них.
Возвращает линейный адрес начала выбранной функции или BADADDR, если ни
одна функция не была выбрана.
Пример использования:
Message(“0x%X \n”,
ChooseFunction(“List”)
);

175

0x401020

Поле
Function Name
Segment
Start
Length

Имя функции
Сегмент, владеющий функцией
Линейный адрес начала
Длина функции в байтах
Атрибут

Определение

Пояснение

R
F
L
S
B
M
I
C
D
V

!FUNC_NORET

FUNC_STATIC

Функция, возвращающая управление
FAR (Далекая) функция
Библиотечная функция
Static – функция

FUNC_FRAME

BP используется как указатель на кадр стека

FUNC_MEMBER

FUNC_DTR

member function
Виртуальная функция
Конструктор
Деструктор

FUNC_VARARG

Функция с переменным числом аргументов

RFLSBMICDV

*
*
*
*
*

FUNC_FAR
FUNC_LIB

FUNC_VIRTUAL
FUNC_CTR

* Не поддерживается в текущих версиях. Зарезервировано для будущего
использования.
Подробнее узнать об атрибутах функции можно в описании SetFunctionFlags.
Операнд
title

Пояснения
Заголовок дианового окна
176

Завершение

Return

!=BADADDR
BADADDR

Пояснения
Линейный адрес начала выбранной функции
Ошибка

По умолчанию подсвечивается функция, в границах которой находится
курсор. В противном случае подсвечивается ближайшая функция, лежащая
«выше» курсора (то есть в более младших адресах)
Для поиска подстроки в именах функции предусмотрена специальная
клавишная комбинация Регистр символов при этом будет игнорироваться.
Для продолжения поиска необходимо нажать .
В контекстной помощи сообщатся, что с помощью клавиш и
можно соответственно добавлять или удалять функции. Но на самом деле в
данном случае эти возможности недоступны.
или двойной щелчок мышью выбирают функцию и возвращают
управление, закрывая диалоговое окно.

char GetFuncOffset(long ea);
Преобразует линейный адрес к строковому представлению в следующем формате:
ИмяФункции+СмещениеОтносительноНачалаФункции. Смещение представлено в
шестнадцатеричном виде без префиксов и постфиксов.
Например:
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:00401106
.text:00401108
.text:0040110D
.text:0040110D
.text:0040110D
.text:00401111
.text:00401116
.text:0040111B
.text:00401121
.text:00401121
.text:00401121
.text:00401122
.text:00401123

__amsg_exit

proc near

arg_0

= dword ptr

loc_0_40110D:

__amsg_exit

4

cmp
jz
call

dword_0_408758, 2
short loc_0_40110D
__FF_MSGBANNER

push
call
push
call
pop
endp

[esp+arg_0]
__NMSG_WRITE
0FFh
off_0_408050
ecx

pop
retn

ecx

Message(“%s \n”,
GetFuncOffset(0x401108)
);
__amsg_exit+9
Операнд
ea
Return

Пояснения
Линейный адрес, принадлежащий хотя бы одной функции
Завершение
Пояснения
!=”” Смещение относительно начла функции (строка)
177

“”

Ошибка

Если смещение относительно функции равно нулю, то вызов GetFuncOffset
возвратит только одно имя.
long FindFuncEnd(long ea);
Описание этой функции, приведенное в контекстной помощи, немного запутанное и
с первого взгляда назначение этой функции не ясно.
Но на самом деле она очень находит широкое применение в автономных скриптах.
Основное ее назначение – определение линейного адреса при создании функции.
Это сопряжено со следующими трудностями – прежде всего необходимо, что бы
адрес конца не превышал линейного адреса следующей за ней функции, поскольку
функции перекрываться не могут.
Например:
seg000:22C0
seg000:22C0
seg000:22C3
seg000:22C6
seg000:22C9
seg000:22CC
seg000:22CF
seg000:22D2
seg000:22D4
seg000:22D5
seg000:22D6
seg000:22D7
seg000:22DA
seg000:22DA
seg000:22DA
seg000:22DD
seg000:22DD
seg000:22DD
seg000:22DD
seg000:22DD
seg000:22DD

start:
call
call
call
call
call
call
jnz
nop
nop
nop
call

sub_0_22DD
sub_0_2325
sub_0_235B
sub_0_2374
sub_0_23B6
sub_0_23F8
loc_0_22DA

call

halt

sub_0_2412

loc_0_22DA:

;

; _______________ S U B R O U T I N E ___
sub_0_22DD

proc near
call
sub_0_28CC

;

Функция start не завершается командой возврата ret. Вместо этого она перывает
выполнение программы, процедурой Halt.
Если пытаться создать функцию, определяя линейный адрес ее конца поиском ret,
то полученный адрес будет принадлежать функции sub_0_22DD!
Следовательно, адрес конца не может превышать линейный адрес следующей
функции.
Вторая проблема заключается в отождествлении инструкции возврата. Это может
быть все что угодно. И RETN, и RETF…
Таким образом, определение конца функции «вручную» оказывается слишком
утомительно. И тогда стоит прибегнуть к вызову FindFincEnd.
Что она делает? Она возвращает линейный адрес на единицу больший линейного
адреса конца функции, которая может быть успешно создана.
Таким образом, задача создания определения адреса конца функции для ее
создания упрощается, тем более, что FindFuncEnd ищет не первую встретившуюся ей
инструкцию возврата, а последнюю в цепочке перекрестных ссылок на следующую
команду (подробнее об этом рассказано в главе «Перекрестные ссылки»).
178

Отсюда следует тот замечательный факт, что она поддерживает функций со
множественными возвратами (а таким, как правило большинство).
Например:
seg000:0100
seg000:0100
seg000:0103
seg000:0106
seg000:0106
seg000:0106
seg000:0106
seg000:0108
seg000:010A
seg000:010B
seg000:010B
seg000:010B
seg000:010B
seg000:010E
seg000:010E
seg000:010F

start:

mov
mov
int

ax, 3D01h
dx, 10Fh
21h

jb
loc_0_10B
retn
; ------------------------------------loc_0_10B:

mov
ax, 0FFFFh
retn
; ------------------------------------aMyfile
db 'MyFile',0

Message("0x%X \n",
FindFuncEnd(0x10103)
);
0x1010F
Обратите внимание, что IDA эмулировала выполнение команды условного
перехода и правильно определила точку выхода из функции.
Однако, если быть уж совсем «буквоедом», то можно заметить, что строка aMyFIle
вероятнее всего принадлежала функции, но IDA автоматически не смогла это распознать.
Поэтому иногда результат работы функции все же приходится корректировать.
Очень важный факт – линейный адрес должен указывать на начало команды, иначе
вызов провалиться.
Например:
Message("0x%X \n",
FindFuncEnd(0x10102)
);
0xFFFFFFFF
То же самое произойдет если по указанному адресу будет расположены данные,
так что функцию будет создать невозможно.
Если же функция уже существует, то вызов FindFuncEnd так же возврат адрес ее
конца:
seg000:0100 start
seg000:0100
seg000:0103
seg000:0106
seg000:0106
seg000:0106
seg000:0106
seg000:0108
seg000:010A

proc near
mov
ax, 3D01h
mov
dx, 10Fh
int
21h

jb
retn

loc_0_10B

179

seg000:010B
seg000:010B
seg000:010B
seg000:010B
seg000:010E
seg000:010E
seg000:010F
seg000:010F

; -----------------------------------loc_0_10B:

mov
ax, 0FFFFh
retn
; -----------------------------------aMyfile
db 'MyFile',0
start
endp

Message("0x%X \n",
FindFuncEnd(0x10103)
);
0x10116
То, что последний отображаемый адрес равен 0x10F это небольшой баг IDA. На
самом деле это адрес начала стоки, но не ее конца. Как нетрудно вычислить, адрес конца
строки равен 0x115, следовательно функция FindFuncEnd работает правильно.
В описании этой функции в idc.idc утверждается, что требуется передать линейный
адрес начала функции, но это не так. С таким же успехом функция принимает любой,
принадлежащий ей адрес, если он приходится на начало инструкции.
Операнд
ea
Return

Пояснения
Линейный адрес, конца функции
Завершени Пояснения
е
!=BADADD ID структуры обеспечивающий доступ к локальным
R переменным и аргументам
BADADDR Ошибка

long GetFrame(long ea);
Возвращает ID фрейма функции (если он есть) или BADADDR в противном случае.
Это значение может интерпретироваться только IDA, и с токи зрения пользователя лишено
всякого смысла (как и всякий дескриптор)
Все локальные переменные и аргументы объединены в одну структуру, с которой
можно работать, как и с любой с помощью функций, описанных в разделе «Структуры»
Если функция не содержит ни одной локальной переменной и не имеет ни одного
аргумента, то вызов GetFrame возвратит ошибку BADADDR.
Пример использования:
.text:004010FF __amsg_exit
.text:004010FF
.text:004010FF arg_0
.text:004010FF
.text:004010FF
.text:00401106
.text:00401108
.text:0040110D
.text:0040110D loc_0_40110D:
.text:0040110D
.text:00401111

proc near
= dwordptr

4

cmp
jz
call

dword_0_408758, 2
short loc_0_40110D
__FF_MSGBANNER

push
call

[esp+arg_0]
__NMSG_WRITE
180

.text:00401116
.text:0040111B
.text:00401121
.text:00401122
.text:00401123
.text:00401123 __amsg_exit

push
call
pop
pop
retn
endp

0FFh
off_0_408050
ecx
ecx

Message(“%x \n”,
GetFrame(0x40110D)
);
ff000162
Операнд
ea
Return

Пояснения
Линейный адрес, принадлежащий функции
Завершени Пояснения
е
!=BADADD ID структуры обеспечивающий доступ к локальным
R переменным и аргументам
BADADDR Ошибка

long GetFrameLvarSize(long ea);
Возвращает размер локальных переменных функции (в байтах). Если функция не
имеет локальных переменных, то возвращается ноль. Если указанный адрес не
принадлежит ни одной функции, возвращается ошибка BADADDR.
Например:
.text:00401806
.text:00401806
.text:00401806
.text:00401806
.text:00401806
.text:00401806
.text:00401809
.text:0040180A

__ioinit
var_44
var_12
var_10

proc near
= byte ptr -44h
= word ptr -12h
= dword ptr -10h
sub
push
push

esp, 44h
ebx
ebp

Message(“0x%X \n”,
GetFrameLvarSize(0x401809)
);
0x44
Операнд
Ea

Return

Пояснения
Линейный адрес, принадлежащий функции
Завершени Пояснения
е
!=0 Размер локальных переменных функции в байтах
!=BADADD
R
0 Функция не имеет локальных переменных
BADADDR Ошибка
181

long GetFrameRegsSize(long ea);
Возвращает размер сохраненных в стековом фрейме регистров. Для 32-разрядных
программ он равен четырем (четыре байта на регистр) и для 16-разрядных соответственно
двум (два байта не регистр)
Если функция не имеет кадра стека, то возвращается ноль и BADADDR если
указанный адрес не принадлежит ни одной функции.
Пример использования:
.text:0040124A __XcptFilter
.text:0040124A
.text:0040124A arg_0
.text:0040124A arg_4
.text:0040124A
.text:0040124A
.text:0040124B
.text:0040124D
.text:0040124E

proc near
= dword ptr
= dword ptr
push
mov
push
push

8
0Ch

ebp
ebp, esp
ebx
[ebp+arg_0]

Message(“0x%X \n”,
GetFrameRegsSize(0x40124A)
);
4
seg000:2092 sub_0_2092
seg000:2092
seg000:2092 var_40
seg000:2092
seg000:2092
seg000:2093

proc far
= byte ptr -40h
push
mov

bp
bp, sp

Message(“0x%X \n”,
GetFrameRegsSize(0x12093)
);
2
Операнд
Ea

Return

Пояснения
Линейный адрес, принадлежащий функции
Завершени Пояснения
е
!=0 Размер сохраненных регистров в стековом фрейме
!=BADADD
R
0 Функция не имеет кадра стека
BADADDR Ошибка

182

long GetFrameArgsSize(long ea);
Возвращает размер (в байтах) аргументов, передаваемых функции. IDA определят
эту величину на основании анализа команд, очищающих стек от локальных переменных.
Обычно компиляторы используют два принципиально различных соглашения для этого.
Паскаль – соглашение предписывает функции самой очищать стек от локальных
переменных перед возвратом из функции. Независимо от способа реализации после
возврата из функции стек должен быть сбалансирован. На платформе Intel практически
всегда для этого используют команду RET N, где N число байт, которые нужно вытолкнуть
с верхушки стека после возврата. В этом случае IDA просто возвращает аргумент, стоящий
после RET.
Например:
Pascal_func:
Push bp
Mov
bp,sp
Mov
ax,[BP+4]
RET
2
Endp
PUSH
CALL

10
Pascal_func

Си – соглашение предписывает очищать локальные переменные вызываемому
коду. При выходе из функции стек остается несбалансированным. Поэтому необходимо
скорректировать его верхушку.
Долгое время не оптимизирующие компиляторы использовали для этого команду
ADD SP, N. Где N размер аргументов в байтах. Очевидно, что IDA так же без проблем
могла распознать такую ситуацию.
Например:
C_func:
Push
Mov
Mov
RET
Endp
PUSH
CALL
ADD

bp
bp,sp
ax,[BP+4]

10
C_func
SP,2

Но с появлением оптимизирующих компиляторов все изменилось. Они могли
выталкивать аргументы командой POP в неиспользуемые регистры или вовсе оставлять
стек несбалансированным на то время пока к нему нет обращений. Поэтому в определении
размера аргументов стали возможны ошибки.
C_opimize_func:
Push bp
Mov
bp,sp
Mov
ax,[BP+4]
RET
Endp
PUSH
CALL

10
C_optimize_func
183

OR
JZ
MOV
Xxx:
POP
RET

AX,AX
xxx
AX,[BX]
AX

Даже для человека с первого взгляда не очевидно назначение команды POP AX.
Кроме того, современные компиляторы поддерживают «совмещенные аргументы», что
делает задачу определения их размера практически невозможной.
Допустим, что по ходу программы необходимо передать один и то же аргумент
нескольким функциям.
H=open(“MyFile”,”rb”);
read(buff,10,H);
seek(20,1,H);
По идее Си-компилятор должен был бы сгенерировать следующий код.
PUSH offset arb
PUSH offset aMyFile
CALL open
ADD SP,4
MOV [offset H],AX
PUSH [offset H]
PUSH [10]
PUSH buff
CALL read
ADD SP,6
PUSH [offset H]
PUSH 1
PUSH 20
CALL seek
ADD SP,6
Как нетрудно видеть один и тот же аргумент – дескриптор файла многократно
заносится в стек, а потом выталкивается из него. Поэтому оптимизирующие компиляторы
поступят, скорее всего, приблизительно так:
PUSH
PUSH
CALL
PUSH

offset arb
offset aMyFile
open
AX

PUSH [10]
PUSH buff
CALL read
ADD SP,4
PUSH 1
PUSH 20
CALL seek
ADD SP,10
184

Разобраться сколько аргументов принимает каждая функция одним лишь анализом
балансировки стека абсолютно невозможно!
После выхода из первой функции стек остается несбалансированным. Вторая
функция очищает только два аргумента, оставляя в стеке дескриптор файла для
последующих функций.
И «отдуваться» за все это приходится третей функции, которая выталкивает из
стека аж 5 слов! Но на самом деле размер его аргументов гораздо скромнее.
Можно, конечно, попытаться отслеживать аргументы, к которым функция
обращается и, выбрав из них тот, что лежит «внизу» всех, вычислить на основе этого
размер аргументов.
Однако такой подход предполагает, что функции известно число переданных ей
аргументов, что в случае с Си - компиляторами неверно. Ведь перенос заботы об очистке
стека с самой функции на вызывающий ее код объясняется как раз тем, что в Си функции
не знают, сколько точно им параметров было передано.
Поэтому можно только удивляться, что даже на оптимизированном коде IDA
сравнительно редко ошибается.
Операнд
Ea

Return

Пояснения
Линейный адрес, принадлежащий функции
Завершени Пояснения
е
!=0 Размер аргументов в байтах
!=BADADD
R
0 Функция не имеет кадра стека
BADADDR Ошибка

long GetFrameSize(long ea);
Возвращает полный размер стекового фрейма в байтах. Он вычисляется по
следующей формуле:
FrameSize == FrameLvarSize + FrameArgsSize + FrameRegsSize +
ReturnAddresSize

То есть сумме размеров локальных переменных, аргументов, сохраненных в стеке
регистров и адреса возврата всех вместе взятых.
Подробнее о каждой из них можно прочитать в описании функций
GetFrameLvaerSize, GetFrameArgsSize, GetFrameRegsSize.
Специальной функции, возвращающий значение размера адреса возврата не
существует, однако, он может быть вычислен по следующей формуле:
ReturnAddresSize == FrameSize - FrameLvarSize + FrameArgsSize +
FrameRegsSize

185

Поскольку в стековый фрейм входит и адрес возврата, то независимо от того,
имеет ли функция локальные переменные или нет, он не может быть равен нулю.
Примеры использования:
seg000:0000 start
seg000:0000
seg000:0003
seg000:0006
seg000:0009
seg000:0009 start

proc near
call
sub_0_A
call
sub_0_10
call
sub_0_16
retn
endp

Message(“0x%X \n”,
GetFrameSize(0x10000)
);
2
seg000:0010 sub_0_10
seg000:0010
seg000:0011
seg000:0012
seg000:0014
seg000:0015
seg000:0015 sub_0_10

proc near
push
bp
push
ax
mov
bp, sp
pop
bp
retn
endp ; sp = -2

Message(“0x%X \n”,
GetFrameSize(0x10010)
);
6
Message(“0x%X \n”,
GetFrameRegsSize(0x10010)
);
4
Как видно, в последнем случае стековый фрейм состоял из адреса возврата и
сохраненных в стеке регистров. Однако, если команды расположить по другому, то
результат изменится:
seg000:000A sub_0_A
seg000:000A
seg000:000B
seg000:000D
seg000:000E
seg000:000F
seg000:000F sub_0_A

proc near
push
bp
mov
bp, sp
push
ax
pop
bp
retn
endp ; sp = -2

Message(“0x%X \n”,
GetFrameSize(0x1000A)
);
4
186

Message(“0x%X \n”,
GetFrameRegsSize(0x1000A)
);
2
Все команды, лежащие «ниже» (то есть в более старших адресах) относительно
команды mov bp, sp (которая и определят стековый фрейм) в него не попадают и можно
безболезненно заносить (выталкивать) команды из стека, не боясь разрушить стековый
фрейм.
Операнд
Ea
Return

Пояснения
Линейный адрес, принадлежащий функции
Завершени Пояснения
е
!=BADADD Размер стекового фрейма в байтах
R
BADADDR Ошибка

long MakeFrame(long ea,long lvsize,long frregs,long argsize);
Создает фрейм стека функции или модифицирует уже существующий. Для этого
достаточно указать любой адрес, принадлежащий функции и размеры области для
локальных переменных, сохраненных регистров и аргументов.
Они расположены во фрейме в следующей последовательности.
Стековый фрейм
Локальные переменные
Сохраненные регистры
Аргументы, переданные функции
Адрес возврата из функции
При успешном завершении функция возвращает ID структуры, обеспечивающий
доступ ко всем вышеперечисленным элементам. В противном случае функция вернет
ошибку BADADDR.
Операнд
Ea
lvsize
frrgs
argsize
Return

Пояснения
Любой линейный адрес, принадлежащий функции
Размер локальных переменных в стековом фрейме
Размер сохраненных регистров в стековом фрейме
Размер аргументов, передаваемых функции
Завершение
Пояснения
!=BADADDR
BADADDR

ID структуры, обеспечивающей
стекового фрейма
Ошибка

доступ

ко

всем

элементам

Модифицирование уже существующего фрейма стека может повлечь за собой
удаление локальных переменных.
Например:
.text:00401487 __setargv

proc near
187

.text:00401487
.text:00401487 var_8
.text:00401487 var_4
.text:00401487
.text:00401487
.text:00401488
.text:0040148A
.text:0040148B

= dword ptr -8
= dword ptr -4
push
mov
push
push

ebp
ebp, esp
ecx
ecx

MakeFrame(0x401487,0,0,0);
.text:00401487 __setargv
.text:00401487
.text:00401488
.text:0040148A
.text:0040148B
.text:0040148C

proc near
push
ebp
mov
ebp, esp
push
ecx
push
ecx
push
ebx

Аргументы же функции никогда не удаляются из стекового фрейма, даже при
уменьшении размера выделенного для них региона до нуля. Однако, это нарушает
целостность всего фрейма и локальных переменных, лежащих «выше»
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401521
.text:00401523
.text:00401526
.text:00401529

sub_0_401520

proc near

arg_0
arg_4
arg_8
arg_C
arg_10

=
=
=
=
=

dword
dword
dword
dword
dword

push
mov
mov
mov
push

ptr
ptr
ptr
ptr
ptr

8
0Ch
10h
14h
18h

ebp
ebp, esp
ecx, [ebp+arg_10]
eax, [ebp+arg_C]
ebx

MakeFrame(0x401520,0,0,0);
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401520
.text:00401521
.text:00401523
.text:00401526
.text:00401529

sub_0_401520

proc near

arg_0
arg_4
arg_8
arg_C
arg_10

=
=
=
=
=

dword
dword
dword
dword
dword

push
mov
mov
mov
push

ptr
ptr
ptr
ptr
ptr

8
0Ch
10h
14h
18h

ebp
ebp, esp
ecx, [ebp+arg_10]
eax, [ebp+arg_C]
ebx

188

long GetSpd(long ea);
Возвращает значение регистра SP (ESP) в произвольной точке функции
относительно его оригинального значения.
IDA использует достаточно простой алгоритм, отслеживающий только основные
команды модифицирующие стековый регистр.
Для специфичных случаев предусмотрена ручная коррекция (смотри описание
функции SetSpDiff) но в большинстве случаев IDA и сама справляется с этой задачей.
Пример использования:
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:00401106
.text:00401108
.text:0040110D
.text:0040110D
.text:0040110D
.text:00401111
.text:00401116
.text:0040111B
.text:00401121
.text:00401122
.text:00401123
.text:00401123
.text:00401123

__amsg_exit

proc near

arg_0

= dword ptr

4

cmp
jz
call

dword_0_408758, 2
short loc_0_40110D
__FF_MSGBANNER

push
call
push
call
pop
pop
retn
endp

[esp+arg_0]
__NMSG_WRITE
0FFh
off_0_408050
ecx
ecx

loc_0_40110D:

__amsg_exit

Message(“%d \n”,
GetSpd(0x4010FF)
);
0
Message(“%d \n”,
GetSpd(0x401111)
);
-4
Message(“%d \n”,
GetSpd(0x401116)
);
-8
Message(“%d \n”,
GetSpd(0x401122)
);
-4
Message(“%d \n”,
189

GetSpd(0x401123)
);
0
В точке входа в функцию значение SP (ESP) всегда равно нулю. Затем, в нашем
примере, оно изменяется командой push, заносящей в стек двойное слово.
Обратите внимание, что значение ESP изменяется только после завершения
команды – то есть с адреса начала следующей.
Относительное
значение ESP
0
-4

Адрес

Инструкция

.text:0040110D
.text:00401111

push
call

[esp+arg_0]
__NMSG_WRITE

В точке выхода из функции значение SP (ESP) так же должно равняться нулю. В
противном случае стек был бы несбалансированным, и команда возврата вытолкнула из
стека не адрес возврата, а что-то совсем иное.
В таком случае вероятнее всего, что IDA не смогла отследить все инструкции,
модифицирующие значения стекового регистра (или сделала это неправильно).
Рекомендуется обнаружить это место и скорректировать его вызовом SetSpDiff.
Операнд
Ea
Return

Пояснения
Линейный адрес в теле функции
Относительное значение стекового регистра SP (ESP)

long GetSpDiff(long ea);
Возвращает относительное изменение стекового регистра SP (ESP) командой,
расположенной по линейному адресу ‘ea’.
Например:
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:004010FF
.text:00401106
.text:00401108
.text:0040110D
.text:0040110D
.text:0040110D
.text:00401111
.text:00401116
.text:0040111B
.text:00401121
.text:00401122
.text:00401123
.text:00401123

__amsg_exit

proc near

arg_0

= dword ptr

loc_0_40110D:

__amsg_exit

4

cmp
jz
call

dword_0_408758, 2
short loc_0_40110D
__FF_MSGBANNER

push
call
push
call
pop
pop
retn
endp

[esp+arg_0]
__NMSG_WRITE
0FFh
off_0_408050
ecx
ecx

Message(“%d \n”,
190

GetSpd(0x4010FF)
);
0
Message(“%d \n”,
GetSpd(0x401111)
);
-4
Message(“%d \n”,
GetSpd(0x401116)
);
-8
Message(“%d \n”,
GetSpd(0x401122)
);
-4
Message(“%d \n”,
GetSpd(0x401123)
);
0
Относительное
значение ESP
0
-4

Адрес

Инструкция

.text:0040110D
.text:00401111

push
call

[esp+arg_0]
__NMSG_WRITE

Как и в случае с GetSpd необходимо задавать адрес начала следующей команды
или точнее, конца текущей.
Операнд
Ea
Return

Пояснения
Линейный адрес конца команды в теле функции
Относительное изменение стекового регистра SP (ESP)

success SetSpDiff(long ea,long delta);
Задает изменение стекового регистра SP (ESP) командой, лежащей по указанному
линейному адресу. Дело в том, что IDA использует достаточно простой алгоритм,
отслеживания SP (ESP), который не учитывает ряда особенностей некоторых экзотических
команд.
Однако, в настоящее время этот механизм настольно усовершенствован, что
практически невозможно придумать в каком случае команда SetSpDiff могла бы оказаться
полезной.
Возьмем следующий, достаточно надуманный пример:
191

seg000:0000
seg000:0000
seg000:0000
seg000:0001
seg000:0002
seg000:0003
seg000:0005
seg000:000A
seg000:000B
seg000:000D
seg000:0010
seg000:0011
seg000:0013
seg000:0014
seg000:0016
seg000:0016

000
002
004
006
006
006
004
004
004
002
002
004
004

start

start

public start
proc near
push
ax
push
ax
push
bp
mov
bp, sp
mov
word ptr [bp+2], 2
pop
bp
mov
bp, sp
mov
cx, [bp+0]
pop
ax
add
sp, cx
push
ax
add
sp, cx
retn
endp ; sp = -4

Message(“%d \n”,
GetSpDiff(0x10013)
);
0
Message(“%d \n”,
GetSpDiff(0x10016)
);
0

Что бы узнать значение SP после завершения команды add sp, cx IDA, очевидно,
должна знать чему равен регистр CX. Что бы его отследить пришлось бы включать в
дизассемблер полноценный эмулятор 0x86 процессора. Пока это еще не реализовано и
IDA предполагает, что значение CX равно нулю и, таким образом, уже неправильно
определяет значение SP во всех нижележащих точках функции.
Исправить положение можно ручной коррекцией значения SP. Функция SetSpDiff
задает изменение регистра SP после выполнения команды. Для этого необходимо
передать линейный адрес конца, а не начала команды.
В нашем случае необходимо скорректировать величину изменения SP командами
ADD SP, CX расположенными по адресам seg000:0011 и seg000:0014. Линейные адреса
команд соответственно равны seg000:0013 и seg000:0016. Их и необходимо передать
функции вместе с действительной величиной изменения SP.
SetSpDiff(0x10013,2);
SetSpDiff(0x10016,2);
seg000:0000 000
seg000:0000
start
seg000:0000
seg000:0001 002
seg000:0002 004

public start
proc near
push
ax
push
ax
push
bp
192

seg000:0003
seg000:0005
seg000:000A
seg000:000B
seg000:000D
seg000:0010
seg000:0011
seg000:0013
seg000:0014
seg000:0016
seg000:0016

Операнд
Ea
delta
Return

006
006
006
004
004
004
002
000
002
000

start

mov
mov
pop
mov
mov
pop
add
push
add
retn
endp

bp, sp
word ptr [bp+2], 2
bp
bp, sp
cx, [bp+0]
ax
sp, cx
ax
sp, cx

Пояснения
Линейный адрес конца команды
Величина изменения SP указанной командой
Завершение
Пояснения
1 Успешно
0 Ошибка

success MakeLocal(long start,long end,char location,char name)
версия 3.74 и старше
С версии 3.74 IDA поддерживает локальные переменные, которые в
большинстве же случаев распознает и создает автоматически. Но иногда она не способна
правильно их распознать, и тогда эта миссия ложиться на плечи пользователя. Подробнее
о локальных переменных можно прочитать в специальной главе «Локальные переменные»
посвященной непосредственно им.
'MakeLocal' полный аналог («~Edit\Functions\Stack variables»). В прототипе
функции 'MakeLocal' указывается область видимости локальной переменной ('start' и
'end'), однако существующие версии IDA (вплоть до IDA 4.0) не поддерживает такой
возможности и область видимости локальной переменной распространяется целиком на
всю функцию.
Функция принимает следующие операнды:
операнд
end

start

location

Пояснения
Этот операнд игнорируется. Обычно его оставляют равным нулю,
но из соображений совместимости с последующими версиями
рекомендуются задавать конец функции или константу 'BADADDR'
- тогда область локальной переменной будет определена IDA
автоматически.
Этот операнд в существующих версиях должен совпадать с началом
функции, иначе MakeLocal возвратит ошибку (в последующих версиях
start должен определять адрес начала видимости локальной
переменной)
Смешение переменной в кадре стека, задаваемое в виде строкового
выражения
"[BP+XX]",
где
"xx"
представлено
в
шестнадцатеричном исчислении.
Спецификатор 'x' можно ставить, а можно не ставить - все равно
значение будет трактоваться как шестнадцатеричное.
Интересной
недокументированной
особенностью
является

193

возможность задавать другие регистры, помимо BP, например 'AX',
однако это не возымеет никакого значения, все равно будет
трактоваться как 'BP'

name

Return

Это есть суть имя создаваемой переменной со всеми ограничениями,
наложенными
на имена и метки. Признаком хорошего тона
является выбор такой нотации, что бы локальные переменные легко
визуально отличались от остальных. IDA всем автоматически всем
создаваемым локальным переменным присваивает имя 'var_xx'.
==return пояснения
==1 Локальная переменная успешно создана
==0 Ошибка

Hot Key


Menu
Edit\Functions\Stack variables

Кроме локальных переменных этой же функцией можно создавать и
размещенные в стеке аргументы, т.к. фактически это те же локальные переменные,
только размещенные по другую сторону кадра стека (с положительным смещением,
а не отрицательным).
IDA в большинстве случаев самостоятельно
автоматически
определяет
аргументы функций (называя их 'arg_xx') и вмешательство пользователя обычно не
требуется.
Пример:
MakeLocal(ScreenEA(),0,"[bp+0x4]","MyVar");
.text:00401124 sub_0_401124
.text:00401124
.text:00401124 MyVar
.text:00401124
.text:00401124

proc near
= dword ptr
push

4

[esp+MyVar]

success SetReg (long ea,char reg,long value);
Функция устанавливает значение сегментных регистров. IDA автоматически
отслеживает их значение (и изменение) и хранит его для каждой точки кода.
Этот механизм достаточно совершенен и обычно вмешательства не требуется,
однако в некоторых случаях IDA неправильно вычисляет значение сегментых регистов,
например, если модификацией управляет отдельный поток и тогда требуется
вмешательство пользователя.
SetReg генерирует директиву ассемблера ASSUME, помещаемую в исходный код.
При этом регистр должен указывать на начало сегмента.
Все существующие ассемблеры поддерживают именно такой режим, но
программистам иногда требуется установить сегментный регистр по произвольному адресу
внутри сегмента (например, для организации плавающего кадра окна для преодоления 64
КБ барьера реального режима на сегмент) SetReg нормально принимает такие значения.
Например:
dseg:0000 start
dseg:0000
dseg:0003
dseg:0005
dseg:0005
dseg:0008

proc near
mov
ax, seg dseg
mov
ds, ax
assume ds:dseg
mov
dx, offset aHelloSailor ;
call
WriteLn
194

dseg:000B
dseg:000D
dseg:000E
dseg:0010
dseg:0010
dseg:0013
dseg:0016
dseg:0018
dseg:0018 start

mov
ax, ds
inc
ax
mov
ds, ax
assume ds:nothing
mov
dx, 2Fh ; '/'
call
WriteLn
mov
ah, 4Ch
int
21h
endp

dseg:0020 aHelloSailor
dseg:002F
dseg:003F aHelloIda
dseg:003F dseg
dseg:003F
dseg:003F
dseg:003F

db 'Hello,Sailor',0Dh,0Ah,'$'
db '$$$$$$$$$$$$$$$$'
db 'Hello,IDA!',0Dh,0Ah,'$'
ends
end start

Смещение 0x2F в строке dseg:0x10 на самом деле указывает на строку dseg:0x3F,
т.к. перед этим значение DS было увеличено на единицу (один параграф равен
шестнадцати байтам) Как «объяснить» это дизассемблеру?
Переведем курсор на строку ‘dseg:0x10’ и используем следующую команду:
SetReg (SreenEA (),”DS”, 0x1001);
Теперь если преобразовать операнд в смещение получиться следующее:
dseg:0010 loc_0_10:
dseg:0010

mov

; DATA XREF: start+10o
dx, offset aHelloIda - offset loc_0_10 ; "Hello,IDA!\r\n$"

Заметим по комментарию, что теперь IDA правильно определила ссылку. Однако,
сгенерировала неверный код.
«offset aHelloIda - offset loc_0_10» будет работать только до тех пор, пока метка
loc_o_10 будет расположена по смещению 0x10, и нам необходимо заменить ее
константой 0x10. Для этого воспользуется, например, функций OpAlt.
SetReg изменяет значение сегментного регистра в каждой точке до следующего
‘ASSUME’ или конца сегмента.
Операнд
‘ea’
‘reg’
‘value’
Return

пояснения
линейный адрес
символьное название регистра. (“CS”,”DS”,”ES” и т.д.)
значение регистра в параграфах
==return пояснения
==1 Операция была выполнена успешно
==0 Ошибка

Функция SetReg эквивалентна команде меню «~EDIT\Segments\Change segment
register value».

long GetReg (long ea,char reg);
Возвращает значение сегментных регистров в произвольной точке программы.
Подробнее об этом можно прочитать в описании функции SetReg.
195

операнд
‘ea’
‘reg’
Return

Пояснения
линейный адрес, в котором необходимо определить значение
регистра
символьное имя регистра. Например “DS”, “GS” и так далее
==return Пояснения
!=0xFFFF Значение сегментного регистра в параграфах
==0xFFFF Ошибка

Функция возвращает 16-битное целое, содержащие значение сегментного регистра
в параграфах. В 32-битных программах функция обычно возвращает не непосредственное
значение, а селектор.
Для получения искомого адреса необходимо воспользоваться функцией
AskSelector. Поскольку селекторы «визуально» неотличимы от адресов сегментов, то для
уверенности необходимо вызывать AskSelector всякий раз для проверки на
принадлежность возвращаемого значения к селекторам. Если селектор с указанным
номером не существует, то это непосредственное значение.
Если регистр не существует (например “MS”) или не определен, то функция и в том
и другом случае вернет ошибку 0xFFFF, а не BADADDR, как утверждает прилагаемая к IDA
документация.
Пример использования:
seg000:0000 seg000
seg000:0000

segment byte public 'CODE' use16
assume cs:seg000

Message (“%x \n”,
GetReg (0x10000,”CS”)
);
1000
.text:00401000 _text
.text:00401000

segment para public 'CODE' use32
assume cs:_text

Message (“%x \n”,
GetReg (ScreenEA (),”CS”)
);
1
Message (“%x \n”,
AskSelector (1)
);
0

ПЕРЕКРЕСТНЫЕ ССЫЛКИ

196

ЧТО ТАКОЕ ПЕРЕКРЕСТНЫЕ ССЫЛКИ?
Долгое время SOURCER лидировал среди других дизассемблеров в умении
находить и восстанавливать перекрестные ссылки. На этом, правда, его основные
достоинства и оканчивались, но все равно он активно использовался для исследования
программного обеспечения.
Что же такое перекрестные ссылки и почему они так важны для популярности
дизассемблера? Покажем это на следующем примере. Рассмотрим простейший случай.
Допустим, исходный файл выглядел так:
.MODEL TINY
.CODE
ORG 100h
Start:
MOV
LEA
INT
RET
s0
DB "Hello,
END Start

AH,9
DX,s0
21h
Sailor!",0Dh,0Ah,'$'

После ассемблирования он будет выглядеть следующим образом:
seg000:0100 start
proc near
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHelloSailor ;
"Hello, Sailor!\r\n$"
seg000:0105
int
21h
seg000:0105
seg000:0107
retn
seg000:0107 start
endp
seg000:0107
seg000:0107 ; -------------------------------------------------------------------------seg000:0108 aHelloSailor
db 'Hello, Sailor!',0Dh,0Ah,'$'
seg000:0108 seg000
ends
Допустим, мы хотим узнать, какой код выводит эту строку на экран. Когда-то для
этого приходилось кропотливо изучать весь листинг, и то не было шансов, что с первого
раза выводящий строку код удастся рассмотреть.
Поэтому, эту задачу возложили на плечи дизассемблера. Так, что бы машина сама
анализировала выполнение программы и восстанавливала все связи. Это невероятно
упростило анализ программ, как впрочем, и взлом защит.
Стало достаточно только найти в строку, которая защита выводит в случае
неудачного завершения проверки (ключевой дискеты ли, или пароля – совершенно не
важно), как дизассемблер поможет мгновенно найти, вводящий ее код, а значит, и
локализовать защиту каким бы длинной программа не оказалась.
Как правило, где-то неподалеку расположен условный переход, передающий
управление этой ветви. Стоит только изменить его на противоположный, как защиту можно
считать взломанной.
Но ведь же не для хакерства же были придуманы перекрестные ссылки!
Разумеется, нет! Помощь хакерам это только побочный эффект (хотя и очень приятный
для них). Значительно важнее поддержка перекрестных ссылок чтобы правильно
дизассемблировать код!
Покажем это на следующем примере:
197

.MODEL TINY
.CODE
ORG 100h
Start:
LEA
AX,s0
PUSH AX
CALL Print
RET
s0
DB 'Hello, Sailor!',0Dh,0Ah,'$'
Print:
POP AX
POP DX
PUSH AX
MOV AH,9
INT 21h
RET
END Start
Первые четыре строки любой дизассемблер разберет без труда. Но вот дальше
начнутся сложности. Как узнать, что следом идет текстовая строка, а не исполняемый код?
Если же дизассемблировать как код из соображений одной лишь надежды на это, то
полуученый результат станет похожим на бред и вся программа окажется
дизассемблированной неправильно.
Поэтому приходится эмулировать ее исполнение и отслеживать все косвенные и
непосредственные переходы. Если ассемблер сумеет распознать вызов подпрограммы, то
ссылку на строку он восстановит с куда большей легкостью.
Однако, тут скрывается один подводный камень. Эмуляция (даже частичная)
требует больших накладных расходов, и если каждый раз ее выполнять «налету», то
никаких вычислительных ресурсов не хватит для нормальной работы!
Поэтому приходится прибегать к компиляции. Да, именно к компиляции. Ведь
компиляция это только перевод с одного языка в другой, а не обязательно в машинные
коды. В данном случае, как раз запутанный язык ассемблера (а точнее одних лишь ссылок)
преобразуется к максимально производительно и компактной форме записи. Или другими
словами можно сказать, что перекрестные ссылки – это сохраненный результат работы
эмулятора.
Кроме того, если выполнение программы однонаправлено, то есть часто
невозможно сказать, выполнение какой инструкции предшествовало текущей, то
перекрестные ссылки предоставляют такую возможность!
Можно начать изучение программы с любой точки, свободно двигаясь по ходу ее
исполнения как взад, так и вперед. Это, в самом деле, очень удобно. Ведь в большинстве
случаев не требуется изучить всю программу целиком, а только один из ее компонентов.
Например, тот, что отвечает за взаимодействие с интересующим вас форматом файла.
Предположим, что в заголовке находится некая сигнатура, которая проверятся
исследуемой программой и находится в дизассемблируемом листинге в «прямом виде».
Тогда можно по перекрестным ссылкам, перейти на процедуру загрузки файла и
начать ее изучение. И это не единственный пример. Перекрестные ссыпки активно
используются при дизассемблировании программ любой сложности и относятся к
незаменимым компонентам дизассемблера.
Однако, как нетрудно догадаться, что гарантировано можно отслеживать только
непосредственные ссылки, такие как CALL 0x666, а уже MOV DX,0x777 может быть как
смещением, так и константой, а про инструкции типа CALL BX говорить и вовсе не
приходится – для вычисления значения регистра BX потребуется весьма не хилый
эмулятор процессора.

198

Поэтому не удивительно, что большинство ассемблеров отслеживало
перекрестные ссылки из рук вон плохо. Даже первые версии IDA не были совершенны в
этом плане.
То есть поддержка перекрестных ссылок имелась, но их созданием приходилось
заниматься человеку (ведь IDA изначально планировалась как интерактивная среда!), а не
машине.
Но с течением времени ситуация изменялась и интеллектуальные механизмы IDA
улучшались. С версии 3.7 она уже значительно превосходила в этом отношении все
остальные существующие в мире ассемблеры, включая SOURCER, и продолжала
совершенствоваться!

ALMA MATER
Из предыдущей главы можно сделать вывод, что реально поддержка того, что
подразумевается под термином «перекрестные ссылки» состоит как минимум из
механизма отслеживания перекрестных ссылок и механизма работы с перекрестными
ссылками, сохраненными в некотором внутреннем формате дизассемблера.
Устройство и способности интеллектуального анализатора IDA тема для
отдельного разговора, здесь же будет говориться исключительно о работе с уже
созданными перекрестными ссылками.
С первого взгляда возможно будет даже не понятно, о чем идет речь. Если
перекрестная ссылка уже создана, то какие могут быть проблемы? На самом деле все не
так просто. Перекрестная ссылка может быть создана (и при том не в единственном числе),
а может быть, и нет. Как узнать это наверняка? И как получить адрес, на который
перекрестная ссылка ссылается?
Вот для этого и предусмотрено почти два десятка функций, поддерживающих
работоспособность IDA. Все они ниже будут подробно рассмотрены, но сначала
рассмотрим механизмы взаимодействия с перекрестными ссылками, не углубляясь в
детали.
Итак, любая перекрестная ссылка состоит из источника и приемника. В
контекстной помощи IDA источник обозначается как ‘from’, а приемник – как ‘to’.
Источником называется операнд, ссылающийся на примем ник, но не наоборот!
Разберем в свете этого приведенный выше пример:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.

.MODEL TINY
.CODE
ORG 100h
Start:
MOV
AH,9
LEA
DX,s0
INT
21h
RET
s0
DB "Hello, Sailor!",0Dh,0Ah,'$' ; ⇐
END Start

;
; ⇒
;
;

Так в строке 8 должна быть создан источник перекрестной ссылки, а в строке 11 –
приемник.
Часто вызывает путаницу, что IDA создает комментарий к перекрестной ссылке только
возле приемника. Это, разумеется, правильно, потому что источник в комментариях не
нуждается – в большинстве случаев очевидно на что ссылается операнд (хотя в случае
инструкций подобных CALL BX этого сказать нельзя), а вот по виду приемника источник
установить невозможно.
Вот IDA и отображает его в виде комментария:

199

seg000:0100
public start
seg000:0100 start proc near
seg000:0100
mov
ah, 9
seg000:0102
mov
dx, offset aHelloSailor
seg000:0105
int
21h
seg000:0105
seg000:0107
retn
seg000:0107 start
endp
seg000:0107
seg000:0107 ; -----------------------------------------------------seg000:0108 aHelloSailor
db 'Hello, Sailor!',0Dh,0Ah,'$' ; DATA
XREF: start+2o
seg000:0108 seg000
ends
Обратите внимание, что IDA ничем не выделила строку 0x102 – создается иллюзия, что
здесь ничего нет. Но на самом деле именно с этим адресом и связана перекрестная
ссылка, а точнее, ее источник.
Практически для всех манипуляций с перекрестными ссылками нужно знать пару
значений – линейные адреса источника и приемника. Впрочем, есть и такие функции, что
возвращают источник (источники) для указанного адреса приемника. Но об них поговорим
позднее, а пока остановимся на том факте, что одновременно по одному и тому же адресу
может располагаться несколько как приемников, так и источников.
Начнем в первого, как более простого для понимания.
seg000:0100
org 100h
seg000:0100
assume es:nothing, ss:nothing, ds:seg000, fs:nothing, gs:nothing
seg000:0100
seg000:0100
public start
seg000:0100 start:
; "Hello, World!\r\n$"
seg000:0100
push
offset aHelloWorld
seg000:0103
call
Print
seg000:0106
push
offset aHelloSailor ; "Hello, Sailor!\r\n$"
seg000:0109
call
Print
seg000:010C
retn
seg000:010D
seg000:010D; _____________ S U B R O U T I N E _______________________________________
seg000:010D
seg000:010D
seg000:010D Print
proc near
; CODE XREF: seg000:0103p
seg000:010D
; seg000:0109p
seg000:010D
pop
ax
seg000:010E
pop
dx
seg000:010F
push
ax
seg000:0110
mov
ah, 9
seg000:0112
int
21h
; DOS - PRINT STRING
seg000:0112
; DS:DX -> string terminated by "$"
seg000:0114
retn
seg000:0114 Print
endp ; sp = 2
seg000:0114
seg000:0114 ; -----------------------------------------------------------------------seg000:0115 aHelloWorld
db 'Hello, World!',0Dh,0Ah,'$' ; DATA XREF: seg000:0100o
seg000:0125 aHelloSailor
db 'Hello, Sailor!',0Dh,0Ah,'$' ; DATA XREF: seg000:0106o
seg000:0125 seg000
ends
-0001010D: sub_0_10D

В этом примере процедура Print вызывалась из двух точек кода, о чем
свидетельствуют две перекрестные ссылки, проставленные IDA как комментарии.
Следовательно, один приемник может иметь и более одного источника.
Что бы изучить вызывающий эту процедуру код достаточно только подвести курсор
в границы адреса, указанного в перекрестной ссылке и нажать Enter и при желании
возвратиться назад по .
200

Так же можно переместить курсор в любое место строки 0x10D и выбрать пункт
меню ~ View \ Cross references. Появится окно следующего вида:

Поскольку довольно часто встречается, что на один приемник ссылаются десятки
(а то и больше!) различных источников, то IDA считает не рациональным отображать их в
виде комментариев и показывает по умолчанию лишь две первые из них (перекрестные
ссылки отсортированы по линейным адресам источников – от младших адресов, к
старшим), то просматривать остальные приходится именно в таком окне.
Как получить программно линейный адрес источника будет рассказано несколько
позже, поскольку это делает по разному в зависимости от типов перекрестных ссылок.
Сейчас же рассмотрим ту ситуацию, когда один источник имеет более одного
приемника. С первого взгляда это абсурдно, но выйти за рамки непосредственных
операндов, то можно вообразить себе следующую ситацию:
seg000:0002
seg000:0004
seg000:0004
seg000:0006
seg000:0009
seg000:0009
seg000:000C
seg000:000C
seg000:000C
seg000:000C
seg000:000E
seg000:000F
seg000:000F
seg000:000F
seg000:000F
seg000:000F
seg000:0011
seg000:0012
seg000:0012
seg000:0012

mov
ds, ax
assume ds:seg000
mov
ah, 6
mov
di, offset off_0_25
jmp
short Print
; --------------------------------------------------Def_1:

; CODE XREF: start+1Bu
; DATA XREF: seg000:0025o
dl, 31h ; '1'

mov
retn
; ---------------------------------------------------Def_2:

; CODE XREF: start+1Bu
; DATA XREF: seg000:0027o
dl, 32h ; '2'

mov
retn
; ----------------------------------------------------Print:

; CODE XREF: start+9j
201

seg000:0012
seg000:0012
seg000:0014
seg000:0017
seg000:0019
seg000:001B
seg000:001D
seg000:001F
seg000:0021
seg000:0021
seg000:0021
seg000:0021
seg000:0023
seg000:0023
seg000:0023
seg000:0023
seg000:0025
seg000:0027
seg000:0029
seg000:002B
seg000:002D
seg000:002F
seg000:002F
seg000:002F
seg000:002F
seg000:002F
seg000:0031
seg000:0032
seg000:0032
seg000:0032
seg000:0034

; start+1Fj
mov
bx, [di]
add
di, 2
or
bx, bx
jz
loc_0_21
call
bx
int
21h
jmp
short Print
; -----------------------------------------------------loc_0_21:
start

mov
int
endp

; CODE XREF: start+19j
ah, 4Ch
21h
; AL = exit code

; ---------------------------------------------------off_0_25
dw offset Def_1
; DATA XREF: start+6o
dw offset Def_2
dw offset def_3
dw offset def_4
dw 0
; ----------------------------------------------------def_3:

def_4:

; CODE XREF: start+1Bu
; DATA XREF: seg000:0029o
mov
retn

dl, '3'

mov
retn

dl, '4'

Подобные примеры не редкость и встречаться с ними приходится довольно таки
часто. Обратим внимание на следующую строку:
seg000:001B

call

bx

Она осуществляет последовательную передачу управления функциям, читаемых в
цикле из списка, а, следовательно, ссылается более чем на один адрес.
К сожалению пока IDA не умеет автоматически вычислять значение регистра BX и,
следовательно, не может ни создать перекрестных ссылок, ни даже дизассемблировать
вызываемые этой строкой функции. Скорее всего они будут помечены как ‘unexplored’.
Поэтому эта часть работы ложится на плечи пользователя. Часто при этом
дизассемблируют код, но забывают создать перекрестные ссылки. Что при этом
получается? А то, что вернувшись к дизассемблируемому файлу спустя некоторое время,
вы уже не будете помнить какой код вызывает эти функции и анализ придется начинать
сначала.
Но что бы создавать перекрестные ссылки нужно быть осведомленным в их
архитектуре, чему и посвящена следующая глава.

АРХИТЕКТРУА ПЕРЕКРЕСТНЫХ ССЫЛОК

202

В предыдущей главе говорилось, что не зависимо от типа, перекрестная ссылка
состоит из двух разных частей – источника и приемника. Каждый из них связан с
определенным линейным адресом. Причем с любым адресом может быть связано
одновременно как несколько приемников, так и несколько источников.
Другими словами с каждым линейным адресом может быть ассоциирован список
источников (приемников). А, следовательно, нужно быть готовыми для работы со списками.
Но для начала разберемся с тем, какие типы перекрестных ссылок существуют, ибо для
работы с ними используются различные функции.
В первом приближении их всего два. Это ссылки на код и ссылки на данные.
Ссылки на код встречаются всякий раз, когда какая-то инструкция нарушает нормальное
выполнение кода программы и изменяет (возможно, лишь при некоторых обстоятельствах)
значение регистра – указателя команд.
Говоря проще – такие ссылки образуют все команды условного и безусловного
перехода и вызова подпрограмм, такие как JMP, CALL, JZ и тому подобные.
С перекрестными ссылками на данные мы сталкиваемся всякий раз, когда какая-то
инструкция обращается к данным, по их смещению. Например, LEA, MOV xx, offset и так
далее, в том числе и DW offset MyData.
Но есть еще и третий тип, который кардинально отличается от первых двух уже
тем, что является внутренним типом перекрестных ссылок IDA
и грубо говоря,
пользователем знать о его существовании, а уж тем более вникать в технические детали
реализации совсем необязательно.
Однако, это помогает лучше понять работу многих команд, поэтому ниже мы его
рассмотрим.
Разумеется, речь идет о «ссылке на следующую команду» (Ordinary flow в
терминологии IDA). Именно с помощью его IDA и отслеживает выполнение программы. Это
перекрестная ссылка указывает на следующую команду при нормальном исполнении
программы. Покажем это на следующем примере:
seg000:0012
mov
bx, [di]
; ⇒
seg000:0014
add
di, 2
;⇐⇒
seg000:0017
or
bx, bx
;⇐⇒
seg000:0019
jz
loc_0_21
;⇐⇒
seg000:001B
call
bx
;⇐⇒
seg000:001D
int
21h
;⇐⇒
seg000:001F
jmp
short Print ;⇐
seg000:0021 ; -----------------------------------seg000:0021
seg000:0021 loc_0_21:
seg000:0021
mov
ah, 4Ch
; ⇒
seg000:0023
int
21h
;⇐
seg000:0023 start
endp
Два цвета используются только лишь для облечения восприятия, – что бы было
можно отличий пары приемник-источник друг от друга. Как видно, цепочка проходит сквозь
все команды, пока не доходит до команды безусловного перехода. Тут она и обрывается.
Адрес перехода можно узнать по перекрестной ссылке типа «код», которая
автоматически образуется здесь. Что это дает? Возможность трассировки программы,
например, для определения адреса конца функции, который заканчивается, как правило
RET, то есть так же инструкцией безусловной передачи управления без возврата в
текущую последовательность команд, в отличие от CALL, которая после выполнения
подпрограммы передает управление следующей за ней команде.
Словом, если все упростить, то нет никакого смысла выделять ссылку на
следующую команду в отдельный тип. Она хорошо описывается одним лишь типом ссылки
на код, поскольку текущая инструкция как бы вызывает следующую (ну во всяком случае
203

фактически происходит именно так – или другими словами – текущая инструкция передает,
или может передавать, управление следующей). Вот в этих случаях и создается ссылка
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 loc_0_17
seg000:0017

jnb
mov
xor
mov
int

loc_0_17
ah, 3Ch ; '20 Интерактивный выбор номера слота
Комментарий к закладке

long GetMarkedPos(long slot);
Функция возвращает линейный адрес закладки по указанному slot. Подробнее о
закладках можно прочитать в описании функции SetMarkedPos.
Например:

auto a;
for (a=1;a20
==return
!=BADADDR
==BADADDR

Пояснения
Номер слота
Интерактивный выбор номера слота
Пояснения
Линейный адрес закладки
Ошибка

char GetMarkComment(long slot);
Функция возвращает комментарий к закладке, расположенной по указанному slot.
Подробнее о закладках можно прочитать в описании функции SetMarkedPos.
Например:

auto a;
for (a=1;a20
==return

Return

!=””
==””

Пояснения
Номер слота
Интерактивный выбор номера слота
Пояснения
Комментарий к закладке
Ошибка

ГЕНЕРАЦИЯ ВЫХОДНЫХ фАЙЛОВ

int GenerateFile(long type, long file_handle, long ea1, long ea2, long flags);
Функция генерирует выходной файл. Аналогичена по действию команде меню
«~File\Produce output file».
Типичный пример использования приведен в файле Analyst.idc, поставляемым
вместе с IDA.
Допустимы следующие типы отчетов:
Определение
OFILE_MAP
OFILE_EXE
OFILE_IDC
OFILE_LST
OFILE_ASM
OFILE_DIF

Тип файла отчета
файл с отладочной информацией
exe файл
база IDA в виде IDC файла
полный файл отчета
готовый к ассемблированию файл
файл различий (более известный как crk)

MAP-файл записывается в стандарте Borland и выглядит приблизительно
следующимобразом:
Start

Stop

Length Name

00000H 032E9H 032EAH seg000
Address
0000:0002
0000:0206
0000:03EA
0000:22C0
0000:2970
0000:297F
0000:2980
0000:298F

Class
CODE

Publics by Value
MyLabelName
aScreen_log
aDeifxcblst
start
aOtkrivaemFail
aMyfile
aYfile
aCalc

Program entry point at 0000:22C0
Он обычно используется для облегчения отладки программ. В коде становится
легче ориентироваться по символьным меткам, переменным и функциям. Например,
вместо абсолютного адреса для точки останова можно указывать его имя. Говорящие
названия улучшают восприятие кода и не дают запутаться и повторно возвращаться к уже
проанализированным фрагментам.
Указанный формат поддерживают Borland Turbo Debuger, Periscope, а так же другие
отладчики. Популярный Soft-Ice имеет в стандартной поставке конвертор, преобразующий
такие файлы к своему собственному формату.

334

Некоторые отладчики не поддерживают сегментацию, а другие имеют ограничение
на количество имен, поэтому генерацией файла можно управлять. Для этого определены
следующие значения флага ‘flag’:
определение
GENFLG_MAPSEGS
GENFLG_MAPNAME

пояснения
включать в файл карту сегментов
включать «dummy» имена.

«Dummy» имена представляют собой автоматически генерируемые IDA имена,
используемые для определения меток, процедур и данных. Они выглядят в виде sub_, loc_,
off_, seg_ и так далее. Обычно с целью не захламления листинга они не включаются в
файл.
EXE файл генерируется после того, как программа была изменена функциями
PatchByte или PatchWord. Эти функции не изменяют оригинального файла, а только
содержимое базы IDA, и что бы изменения возымели действия необходимо сгенерировать
новый файл.
К сожалению IDA поддерживает очень ограниченный список форматов. Вот это и
все, что можно получить на выходе:
1.
2.
3.
4.
5.
6.
7.

MS DOS .exe
MS DOS .com
MS DOS .drv
MS DOS .sys
general binary
Intel Hex Object Format
MOS Technology Hex Object Format

При этом exe файл генерируется заново. Он содержит ту же таблицу
перемещаемых элементов (то есть ее невозможно изменить),
а все неиспользуемые
структуры заполняются нулями. Следует иметь ввиду, что некоторые программы
чувствительны к таким изменениям и откажут в работе.
К сожалению, не поддерживаются PE и другие win32 файлы. В этом случае (а так
же когда exe файл чувствителен к неиспользуемым полям, – например, в свободное
пространство заголовка иногда может быть помещен оверлей) можно сохранить различия
в DIF файле и затем любой из поддерживающих его многочисленных утилит
модифицировать оригинальный файл.
IDA позволяет сохранять базу в виде текстового IDC файла. Это обеспечивает ее
переносимость между различными версиями. Дело в том, что основной рабочий формат
IDB в любой момент может измениться и база перестанет загружаться в новые версии. Для
преодоления этой проблемы и был введен текстовой формат.
Заметим, что это далеко не вся база и часть информации оказывается необратимо
утерянной, например, отсутствует виртуальная память и для анализа вновь потребуется
исходный файл, кроме того, загружаться IDC файл будет гораздо медленнее IDB,
поскольку потребуется вновь все заново дизассемблировать. Поэтому применять данный
формат в качестве рабочего совершенно бессмысленно.
Но что же представляет из себя IDC файл? Как нетрудно догадаться по его
расширению это обыкновенный скрипт!
static Segments(void)
{
SegCreate(0x10000,0x132ea,0x1000,0,1,2);
SegRename(0x10000,"seg000");
SegClass (0x10000,"CODE");
SetSegmentType(0x10000,2);
}
335

И его можно безболезненно редактировать в отличие от бинарного IDB формата.
Например, если IDA что-то неправильно дизассемблирует, то положение будет нетрудно
исправить, отредактировав нужным образом скрипт.
LST представляет собой копию дизассемблированного файла, в том виде, в каком
он отображается на экране IDA. Выглядит он приблизительно так:
seg000:0100 loc_0_100:
seg000:0100
seg000:0103
seg000:0105
seg000:0106

cmp
jz
inc
jmp

byte ptr [bx+si], 0
loc_0_108
bx
short loc_0_100

Разумеется, он не пригоден для последующего ассемблирования и может
использоваться только в качестве «твердой копии экрана». В демонстрационной версии
генерация LST файла не поддерживается.
ASM файл – это дизассемблированный
ассемблированию. Выглядит он следующим образом:

файл

полностью

готовый

к

p586n
; -------------------------------------------------------------------; Segment type: Pure code
seg000
segment byte public 'CODE' use16
assume cs:seg000
assume es:nothing, ss:nothing, ds:nothing, fs:nothing,
; _______________ S U B R O U T I N E ________________________________
sub_0_0
proc near
; CODE XREF: sub_0_22DD+1E_p
push
ax
push
bx
push
cx
push
dx
mov
ax, 3D02h

В демонстрационных версиях вывод дизассемблированного текста в ASM файл не
поддерживается.
DIF хранит в себе результаты сравнения оригинального и модифицированного
функциями PatchByte и PatchWord файлов. Для некоторых форматов IDA позволяет
генерировать исполняемый (или бинарный) файл, с учетом изменений.
Однако в большинстве случаев этих возможностей оказывается недостаточно
(например, не поддерживаются win32 форматы) и тогда приходится прибегать к
сохранению всех изменений в отдельном файле.
Формат его показан ниже:
This difference file is created by The Interactive Disassembler
xsafe-iv.exe
00002390: 0C 11
В нем нетрудно распознать типичный crk файл, который поддерживается многими
утилитами (например, cra386) или модифицировать исходный файл вручную. Несложно
написать скрипт на IDA-си, который будет выполнять такую работу автоматически.
Для генерации любого типа файлов требуется задать виртуальный адрес начала и
конца области. Если требуется вывести файл целиком, то в качестве адреса начала
336

можно задать 0, а в качестве конца константу BADADDR или –1.
Функция GenerateFile не работает с именами файлов, она требует дескриптора уже
открытого на запись файла. В упрощенном виде ее вызов может выглядеть так:
auto a;
a=fopen("myfile.ext","wt");
GenerateFile (OFILE_ASM, a, 0, -1,0);
fclose (a);
Поскольку только в исключительно редких случаях требуется модификация только
что сгенерированного файла, то полезно будет создать макрос или функцию, включающую
в себя приведенный выше текст. Это упросит ее вызов и позволит программисту не
отвлекаться на посторонние мелочи.
операнд
type
file_habdle
ea1
ea2
flags

Пояснения
Тип генерируемого файла
Дескриптор открытого файла
Линейный адрес начала области для отображения в файле
Линейный адрес конца области для отображения в файле
Флаги, управляющие, генерацией файла

ФАЙЛОВЫЙ ВВОД – ВЫВОД
IDA обладает развитым файловым вводом \ выводом, что открывает поистине
неограниченные возможности. Можно самостоятельно загружать файлы любых форматов,
можно создать отчеты и листинги любых видов. Можно распаковывать или
модифицировать исполняемые файлы. Но даже при желании работать с принтером, или,
например, модемом!
Все это богатство возможностей реализуется относительно небольшим набором
стандартных функций Си. Работа с файлами в IDA - Си ничем не отличается от
«классического» Си. За тем, может быть, исключением, что ввиду отсутствия массивов в их
общепринятом понимании, используется посимвольный, а не блочный обмен.

long

fopen

(char file,char mode);

Функция открывает файл и возвращает его обработчик в случае успешного
завершения этой операции.
Прототип функции полностью совпадает с аналогичной функцией в стандартной
библиотеке Си. Действительно, реализация этой функции в IDA только передает
управление библиотечной функции qfopen(char *,char *) без какой – либо дополнительной
обработки аргументов.
Необходимые атрибуты доступа задаются
флагом mode в виде простой
символьной строки. Их возможные значения будут показаны ниже.
Атрибут
w
r

Назначение
Открывает файл для записи. Если файл не существует, то он
автоматически создается. Содержимое любого непустого файла
будет уничтожено начиная с текущей позиции (по умолчанию с
начала файла).
Открывает файл для чтения. Если указанный файл не существует,
337

a
r+
w+
a+

Тип файла
t

b

то функция возвратит ошибку (NULL)
Открывает файл для записи и перемещает его указатель в конец, (то
есть фактически открывает файл для до записи). Если указанный
файл не существует, то он будет автоматически создан.
Открытие файла на запись и чтение. Если файл не существует, то
функция возвратит ошибку
Открытие файла на запись и чтение. Если файл не существует, то он
будет автоматически создан. Содержимое уже существующего
файла будет уничтожено
Открывает файл на чтение и дозапись в конец. Если файл не
существует, то он автоматически будет создан
Пояснения
Открыть файл, как текстовой. В этом режиме символ CTRL-Z (ASCII
27) трактуется, как конец файла. Так же по-особому транслируется
символ переноса ‘\n’. Компилятор превращает его в код 0xA. При
записи же его в текстовой файл функция на самом деле поместит
комбинацию 0xD 0xA – интерпретируемую сервисами MS-DOS и
некоторыми элементами управления Windows, как перенос на
следующую строку.
Часто с текстовыми файлами удобнее работать, открывая их как
бинарные (смотри ниже)
Открыть файл как бинарный. В этом режиме все символы
транслируются AS IS, без каких либо изменений.

Функцию необходимо вызвать обязательно с указанием атрибута доступа и типа
файла, иначе она завершиться с ошибкой.
Если файл по каким-то причинам открыть не удалось, то функция возвратит ноль, в
противном случае дескриптор открытого файла.
Примеры использования:
Del file.dem
Message(“0x%X \n”,fopen(“file.dem”,”wb”);
1
dir file.dem
file.dem

0

11.11.99

13:33 file.dem

Message(“0x%X \n”,fopen(“Long File Name”,”wb”);
1
dir longfi~1
LONGFI~1

0

11.11.99

15:06 Long File Name

Message(“0x%X \n”,fopen(“myfile”,”r+b”);
0
Обратите внимание, что IDA возвращает один и тот же обработчик при открытии
различных файлов, хотя прежние файлы не были явно закрыты. Это говорит о том, что они
закрываются автоматически, после того как скрипт завершит свою работу.
Часто забывают, что в Windows сохранилась поддержка имен устройств, идущая
еще со времен CP\M. Поэтому, что бы вывести данные на печать достаточно открыть на
338

запись устройство “PRN” и направить в него необходимые данные.
Например:
writestr(fopen(“PRN”,”wt”),”Hello,Printer!”);
Необходимо лишь учитывать, что эта печать идет в обход менеджера печати и,
кроме того, так нельзя получить доступ к сетевому принтеру. Но в большинстве случаев и
этих возможностей окажется достаточно.
Точно так же можно читать данные с консоли или выводить их на нее. Конечно, при
первой же перерисовке окна сообщений они будут стерты, но это наоборот, скорее
достоинство, чем недостаток. Действительно, зачем загромождать окно сообщений?

Операнд
File
mode
Return

Пояснения
Имя файла (при необходимости с полным или частичным путем).
Обе версии IDA (консольная и GUI) поддерживает длинные файлы.
Атрибуты доступа и типа файла.
Завершение Пояснения
Успешное Дескриптор открытого файла (!=0)
Ошибка ==0

Закрытие всех открытых файлов гарантируется только при корректном выходе из
IDA. И хотя, несмотря, на то, что операционная система гарантированно закроет все
файлы, порожденные любым процессом (в том числе и IDA) при его завершении, может
потеряться часть данных, которая в этом момент находилась во внутренних буферах IDA и
еще не была записана на диск.
void

fclose

(long handle);

Функция закрывает файлы, открытые с помощью fopen. В момент закрытия файла в
него записываются все данные, находящиеся в этот момент во внутренних буферах, а
файловый объект (то есть то, на что ссылается дескриптор) уничтожается, предотвращая
утечку ресурсов.
Файлы автоматически закрываются в момент завершения работы породившего их
скрипта (при условии, что обладающая дескриптором процедура не описана как static), а
так же при корректном завершении работы IDA.
В противном же случае операционная система все равно освободит все ресурсы,
принадлежащие процессу, но при этом не будут записаны данные, оставшиеся во
внутренних буферах.
Функция не возвращает результата успешности операции.
Операнд
handle

Пояснения
Дескриптор открытого файла

Пример использования:
Auto a;
A=fopen(“PRN”,”wt”);
If (a!=-1)
writestr(a,”Hello,Printer!”);
fclose(a);
339

Обратите внимание, что в приведенном примере fclose выполняется даже тогда,
когда файл не был успешно открыт. Это не ошибка, поскольку fclose(0) не приводит ни к
каким побочным последствиям.
long

filelength

(long handle);

Функция возвращает логическую длину открытого файла. То есть длину с учетом не
записанных внутренних буферов, которая может не совпадать с физическим размером
файла на диске в данный момент.
Длина символьных устройств (таких, как PRN, например) всегда равна нулю.
Например:
Message(“0x%X \n”,filelngth(fopen(“PRN”, “wt”));
0x0
Операнд
handle

long

fseek

Пояснения
Дескриптор открытого файла

(long handle,long offset,long origin);

Функция позиционирует указатель в открытом файле. Флаг origin, задающий
необходимое позиционирование может принимать следующие значения, перечисленные
ниже в таблице:
origin
0
1
2

Значение
Позиционировать относительно начала файла
Позиционировать относительно текущей позиции
Позиционировать относительно конца файла

Правда вплоть до версии 4.0 эта функция реализована с ошибкой, приводящей к
тому, что флаг ‘1’ трактуется точно так, как и ‘0’ – то есть относительно начла файла.
Это видно на следующем примере:
auto a;
a=Fopen(“myfile”,”wt”);
fseek(a,0x10,0);
Message(“0x%X \n”,ftell(a));
fseek(a,0x0,1);
Message(“0x%X \n”,ftell(a));
fclose(a);
0x10
0x0
Так же не поддерживается отрицательная адресация относительно начала файла.
Относительно конца файла можно свободно позиционироваться в двух направлениях.
auto a;
a=Fopen(“myfile”,”wt”);
fseek(a,0x0,2);
340

Message(“0x%X \n”,ftell(a));
fseek(a,0x5,2);
Message(“0x%X \n”,ftell(a));
fseek(a,-0x5,2);
Message(“0x%X \n”,ftell(a));
fclose(a);
0x100
0x105
0x100
Напомним, что ранние версии DOS содержали ошибку, приводящую к тому, что
наращивании размера файла с помощью функции позиционирования сверх определенного
размера (зависящего от многих обстоятельств) нарушалась целостность FAT16.
Та же ошибка повторена в первых реализациях FAT32 (Windows 95 OSP0, в народе
прозванная «Лебединая редакция»)
При перемещении указателя за конец файла в него подает, информация,
расположенная в выделяемых ему дисковой подсистемой кластерах. При этом файл не
должен быть открыт на запись, иначе игнорируя флаг, функция будет вычислять смещение
относительно файла и его содержание окажется утерянным!
Так же оно окажется утерянным, если указать неверное отрицательное смещение
или origin > 2.
Операнд
Handle
Offset
Origin
Return

long

ftell

Пояснение
Обработчик открытого файла
Смещение (относительно конца файла - знаковое)
Указывает, относительно чего отсчитывается смещение (смотри
таблицу выше)
Завершение Возвращаемое значение
Успешно
0
Ошибка
!=0

(long handle);

Функция возвращает текущую позицию указателя в открытом файле относительно
его начала.
Операнд
handle
Return

success loadfile

Пояснения
Дескриптор открытого файла
Завершение
Возвращаемое значение
Успешно
Текущая позиция
Ошибка
-1

(long handle,long pos,long ea,long size);

Функция позволяет загружать бинарный файл (или его часть) в произвольный
регион виртуальной памяти IDA. Это позволяет писать свои динамические загрузчики, но и
эмулировать работу оверлеев, а так же многое другое.
Перед началом операции искомый файл необходимо открыть в бинарном режиме
функцией fopen с правами только на чтение. Если открыть на запись, то его содержимое
окажется необратимо уничтожено!
341

Затем указать позицию в файле для чтения (аргумент pos). Позиция всегда
считается относительно начала файла, не зависимо от текущего положения указателя.
Далее указать виртуальный линейный адрес, по которому будет скопирован
фрагмент файла. Операция завершится независимо от того, производится загрузка в
границах существующего сегмента или вне оных. При необходимости IDA выделяет
дополнительную виртуальную память для загрузки.
Последний аргумент, передаваемый функции – это число загружаемых из файла
байт. Если оно превосходит длину «хвоста» файла (то есть от указанной позиции до
конца), то IDA выдаст предупреждение:

Can't read input file (file structure error?), only part
of file will be loaded...

И загрузит столько байт, сколько сможет.
seg000:2C93 aWatchAvialable db 'Watch avialable DOS memory...........................'
auto a;
a=fopen("readme.txt","rb");
loadfile(a,0,0x12C93,0x40);
seg000:2C93 aWatchAvialable db 'This patch allows you to permanently access the bonus’

Обратите внимание, что загрузка не вызывает реассемблирования исследуемой
программы. При этом только перезаписывается соответствующий регион виртуальной
памяти, но не меняются связанные с ней флаги!
Это хорошо видно на следующем примере:
seg000:02E4 sub_0_2E4
seg000:02E4
seg000:02E5
seg000:02E7
seg000:02E9
seg000:02E9
seg000:02E9 MyLabel:
seg000:02E9
seg000:02EC
seg000:02EF
seg000:02F2
seg000:02F3
seg000:02F3
seg000:02F3 sub_0_2E4

proc near
push
ds
xor
ax, ax
mov
ds, ax
assume ds:nothing

; CODE XREF: seg000:232Ep
; DS == NULL

mov
ax, ds:413h
shl
ax, 6
cmp
ax, 0A000h
pop
ds
assume ds:seg000
retn
endp

auto a;
a=fopen("readme.txt","rb");
loadfile(a,0,0x102E4,0x40);
seg000:02E4 sub_0_2E4
seg000:02E4
seg000:02E5
seg000:02E7
seg000:02E9
seg000:02E9
seg000:02E9 MyLabel:
seg000:02E9
seg000:02EC
seg000:02EF
seg000:02F2
seg000:02F3

proc near
push
sp
push
7369h
jnb
loc_0_309
assume ds:nothing

; CODE XREF: seg000:232Ep
; DS == NULL

jo
loc_0_34C
arpl
[bx+si+20h], bp
popa
outsw
assume ds:seg000

342

seg000:02F3
seg000:02F3 sub_0_2E4

ja
endp

near ptr loc_0_367+1

Обратите внимание, что не только сохранились прежние метки, комментарии и
перекрестные ссылки, но и оказался неверно дизассемблированным код! Но это не ошибка
IDA, а ее архитектурная особенность. Вместе с обычными перекрестными ссылками
сохранились и так называемые ссылки на следующую инструкцию. Поэтому вновь
загруженный код был дизассемблирован с учетом прежнего «каркаса» то есть линейный
адресов начала инструкций.
Что бы исправить ситуацию, необходимо пометить измененный фрагмент, как
undefined и потом его заново ассемблировать. В результате получится следующее:
seg000:02E4
seg000:02E5
seg000:02E8
seg000:02EB
seg000:02ED
seg000:02F0
seg000:02F1
seg000:02F2

push
push
and
jz
push
ins
ins
outsw

sp
7369h
[bx+si+61h], dh
loc_0_350
6120h
byte ptr es:[di], dx
byte ptr es:[di], dx

Чаще всего эту функцию используют для частичного дизассемблирования файла.
Например, если внутри
много мегабайтовой DLL необходимо исследовать лишь
небольшой фрагмент, то нет нужды несколько часов ждать пока IDA дизассемблирует ее
целиком – достаточно лишь загрузить требуемый фрагмент.
Кроме того, многие приложения во время работы подгружают различные свои
компоненты с диска. Если их так же необходимо исследовать, то для этого можно
воспользоваться loadfile.
Иногда даже не требуется создавать для этого дополнительный сегмент, загрузив
данные за его границы.
seg000:32A0
seg000:32A0
seg000:32A0
seg000:32A0 seg000
seg000:32A0
seg000:32A0
seg000:32A0

db 0E2h, 20h, 0A4h, 0A0h, 2 dup(0ADh), 0EBh, 0A9h, 20h
db 0ACh, 0A5h, 0E5h, 0A0h, 0ADh, 0A8h, 0A7h, 0ACh, 21h
db 0
ends
end start

auto a;
a=fopen("readme.txt","rb");
loadfile(a,0,0x102E4,0x10);
seg000:32A0
seg000:32A0
seg000:32A0
seg000:32A0 seg000
seg000:32A0
0:000132EA
0:000132EB
0:000132EC
0:000132ED
0:000132EE
0:000132EF
0:000132F0
0:000132F1
0:000132F2
0:000132F3

db 0E2h, 20h, 0A4h, 0A0h, 2 dup(0ADh), 0EBh, 0A9h, 20h
db 0ACh, 0A5h, 0E5h, 0A0h, 0ADh, 0A8h, 0A7h, 0ACh, 21h
db 0
ends
end start
db 54h ; T
db 68h ; h
db 69h ; i
db 73h ; s
db 20h ;
db 70h ; p
db 61h ; a
db 74h ; t
db 63h ; c
db 68h ; h

Доступ к загруженным данным может быть осуществлен, например, вызовами Byte.
Для интерактивной же работы (например, что бы преобразовать загруженные данные в
строку) все же придется создать сегмент (как это сделать рассказано в описании функции
SegCreate)
343

MySeg:000A ; Segment type: Regular
MySeg:000A MySeg
MySeg:000A
MySeg:000A
MySeg:000A
MySeg:000A aThisPatchAllow
MySeg:000A MySeg
MySeg:000A

segment byte public '' use16
assume cs:MySeg
;org 0Ah
assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
db 'This patch allows you to permanently access the bonus track '
ends

Строго говоря, приведенный пример дизассемблирован не правильно. Если
программа подгружала ресурсы из текстового файла динамически во время работы, то
перемещение их в сегмент может даже нарушить ее работоспособность после
ассемблирования и уж точно не изменит алгоритм, так что бы файловый обмен заменился
обращением к памяти.
Но на этапе исследования дизассемблируемого кода это невероятно удобно.
Можно даже вычислить какие инструкции, какой код загружают, и создать перекрестные
ссылки для обеспечения быстрого перехода между различными фрагментами
дизассемблируемого текста.
Операнд
handle
pos
ea
Size
Return

success savefile

Пояснения
Обработчик открытого только на чтение файла
Позиция в файле, относительно его начла
Линейный адрес начала региона виртуальной памяти
Число байт для чтения
Завершение Пояснения
0 Функция завершилась неуспешно
1 Функция завершилась успешно

(long handle,long pos,long ea,long size);

Функция, обратная loadfile (смотри описание выше). Она позволяет сохранить
фрагмент виртуальной памяти на диске в виде файла.
Например:
seg000:03D3
seg000:03D3
seg000:03D4
seg000:03D5
seg000:03D9
seg000:03DC
seg000:03DC
seg000:03DC
seg000:03DE
seg000:03E0
seg000:03E1
seg000:03E4
seg000:03E6
seg000:03E7
seg000:03E7
seg000:03E7
seg000:03E8
seg000:03E9
seg000:03E9
seg000:03E9
seg000:03EA

sub_0_3D3

proc near
; CODE XREF: seg000:03C7p
push
ax
push
bx
mov
al, byte ptr es:loc_0_F+1
mov
bx, 3EAh

loc_0_3DC:

; CODE XREF: seg000:03E4j
cmp
jz
inc
cmp
jnz
inc

[bx], al
loc_0_3E7
bx
byte ptr [bx], 0
loc_0_3DC
bx
bx
ax

sub_0_3D3

pop
pop
retn
endp

aDeifxcblst

db 'DEIFXCBLST',0

loc_0_3E7:

; CODE XREF: seg000:03DEj

auto a;
a=fopen(“fileme”,"wb");

344

savefile(a,0,0x103D9,0x200);
╔═[ ]═══════════════════════════ F:\IDAF\fileme ═════════════════════ 23:28:03 ╗

║00000000: 50 53 26 A0 10 00 BB EA 03 38 07 74 07 43 80 3F ¦ PS&а .+ъ 8 t CА?
║00000010: 00 75 F6 43 5B 58 C3 44 45 49 46 58 43 42 4C 53 ¦ .uЎC[X+DEIFXCBLS





Возможность сохранения отдельных фрагментов файла очень полезна и может
стать основой для множества утилит (например, такой, что извлекает все текстовые
строки, встретившиеся в программе в отдельный файл)
Кроме того, она пригодится, если необходимо сохранить модифицированный
вызовами PatchByte файл на диск. Дело в том, что IDA не поддерживает экспорт ни во что
другое, кроме com и MS-DOS EXE. И то, и другое, очевидно, давным-давно устарело. И
поддержку формата популярных сегодня PE файлов придется реализовывать
самостоятельно.
Перед началом операции необходимо открыть целевой файл на запись с помощью
функции fopen и передать savefile его дескриптор.
Позиция в файле для записи может быть выбрана любая, как внутри, так и вне
него. Однако, в последнем случае возможно разрушение FAT, поэтому необходимо
соблюдать дополнительные меры предосторожности.
Размер записываемого фрагмента может превосходить длину дизассемблируемого
файла, в этом случае «хвост» будет заполнен символами 0xFF, (именно такое значение
возвращает функция Byte при попытке чтения несуществующих адресов), но функция, не
смотря на это завершится без ошибки.
Операнд
handle
pos
ea
Size
Return

long

fgetc

Пояснения
Обработчик открытого на запись файла
Позиция в файле, относительно его начла
Линейный адрес начала региона виртуальной памяти
Число байт для записи
Завершение Пояснения
0 Функция завершилась неуспешно
1 Функция завершилась успешно

(long handle);

Функция читает один байт из файла. При этом файл должен быть предварительно
открыт вызовом fopen с правами, разрешающими чтение. Относится к функциям
стандартной библиотеки Си.
При неуспешном возращении возвращает ошибку BADADDR – иначе очередной
считанный символ. Если не достигнут конец файла, то указатель увеличивается на
единицу.
Пример использования:
auto a,ch;
a=fopen(“readme.txt”,”rt”);
while((ch=fgetc(a))!=-1)
Message(ch);
fclose(a);
This patch allows you to permanently access the bonus track and bonus car
without winning the tournaments.

Операнд
handle

Пояснения
Дескриптор открытого с правами на чтения файла
345

Завершение
Норма
Ошибка

Return

long

fputc

Пояснения
Считанный символ
BADADDR

(long byte,long handle);

Функция записывает один байт в файл. Файл должен быть предварительно открыт
с правами на запись функцией fopen.
При неуспешной записи возвратит ошибку BADADDR, иначе ноль.
Операнд
byte
handle
Return

long

fprintf

Пояснения
Записываемый символ
Дескриптор открытого с правами на запись файла
Завершение
Пояснения
Норма
0
Ошибка
BADADDR

(long handle,char format,...);

Ближайший аналог известной функции sprintf, однако, вместо буфера результат
копируется в файл. Очевидно, что файл должен быть предварительно открыт с правами
на запись вызовом fopen.
Например:
auto a,s0;
s0=0x123;
a=fopen(“CON”,”wt”);
fprintf(a, "%x \n",s0);
123
Управляющие символы стандартные, и частично совместимые с 'printf' и
полностью совместимы со спецификаторами функции Message встроенного языка IDA.
Сф
%d

%x

%X

%o
%u

пояснение
десятичное длинное знаковое целое
Пример:
Message(“%d”,0xF);
15
шестнадцатеричное длинное целое строчечными символами
Пример:
Message(“%x”,10);
a
шестнадцатеричное длинное целое заглавными символами
Пример:
Message(“%X”,10);
A
восьмеричное длинное знаковое целое
Пример:
Message(“%o”,11);
13
десятичное длинное беззнаковое целое
346

%f

%c

%s

%e

%g

Пример:
Message(“%u”,-1);
4294967295
десятичное с плавающей точной
Пример:
Message(“%f”, 1000000);
1.e6
символьное значение
Пример:
Message(“%c”,33);
!
строковое значение
Пример:
Message(“%s”,”Hello, Word! \n”);
Hello, Word!
вывод чисел в экспоненциальной форме
Пример:
Пример:
Message(“%e”, 1000000);
1.e6
вывод чисел в экспоненциальной форме
ЗАМЕЧАНИЕ: В оригинале спецификатор '%g' заставляет
функцию саму решать, в какой форме выводить число - с десятичной
точкой или
в экспоненциальной
форме,
из
соображений
здравомыслия и удобочитаемости. IDA всегда при задании этого
спецификатора представляет числа в экспоненциальной форме.
вывод указателя (не поддерживается)
ЗАМЕЧАНИЕ: вместо спецификатора '%p' IDA использует '%a',
преобразующее линейный адрес в
строковой
сегментный,
и
автоматически подставляет имя сегмента.
Так, например, 'Message("%a \n",0x10002)' выдаст 'seg000:2'.
Обратите внимание, что таким способом нельзя узнать адрес
переменной.
Пример:

auto a;
a="Hello!\n";
Message("%a \n",a);
0

%p
%+d

%+x

%nd

Возвращается ноль, а не указатель на переменную.
вывод десятичного целого всегда со знаком, не опуская плюс.
в оригинале - вывод шестнадцатеричного целого всегда со знаком, но ida
воспринимает эту конструкцию точно так же как и ‘x'.
'n' длина выводимого десятичного числа, при необходимости дополняемая
слева пробелами.
Например:
Message("Число-%3d \n”,1);
Число- 1
Если выводимое число не укладывается в 'n' позиций, то оно выводится
целиком.
Например:
Message("Число-%3d \n”,10000);
Число-10000
'n' длина выводимого шестнадцатеричного числа, при необходимости
дополняемая слева пробелами.
Например:
347

Message("Число-%3x \n”,1);
Число- 1
Если выводимое число не укладывается в 'n' позиций, то оно выводится
целиком.
Например:
Message("Число-%3x \n”,0x1234);
Число-1234

%nd

%0nx

%#x

%#o
%n

long

‘n’ длина выводимого десятичного числа, при необходимости дополняемая
слева незначащими нулями.
Пример:
Message("Число-%03d",1);
Число-001
Если выводимое число не укладывается в ‘n’ позиций, то оно выводится
целиком.
Пример
Message("Число-%03d",1000)
Число-1000
‘n’ длина выводимого шестнадцатеричного числа, при необходимости
дополняемая слева незначащими нулями.
Пример:
Message("Число-%03x",0x1);
Число-001
Если выводимое число не укладывается в ‘n’ позиций, то оно выводится
целиком.
Пример:
Message("Число-%03x",0x1234);
Число-1234
Вывод префикса ‘0x’ перед шестрадцатиричными числами
Пример:
Message(“%#x”,123);
0x123
Вывод префикса ‘0’ перед восьмеричными числами
Пример:
Message(“%#o”,1);
01
Количество выведенных символов (не поддерживается)

readshort

(long handle,long mostfirst);

Функция считывает два байта из файла. До начала операции файл должен быть
открыт функцией fopen с правами на чтение.
Примечательной особенностью данной функции является возможность трансляции
знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит,
расположен «слева», то есть, идет самым старшим в слове. Наоборот, если флаг mostfirst
равен единице, то функция будет ожидать, что знаковый бит, расположен «справа» то есть
идет самым младшим в слове.
В случае если во время выполнения функции возникнут ошибки, то будет
возращена константа BADADDR – иначе 16-битное прочитанное значение.

348

Операнд
handle
mostfirst
Return

long

readlong

Пояснения
Дескриптор открытого с правами на чтение файла
==0 Знаковый байт самый старший в слове
==1 Знаковый байт самый младший в слове
Завершение
Пояснения
Норма
Прочитанное 16-битное знаковое слово
Ошибка
BADADDR

(long handle,long mostfirst);

Функция считывает четыре байта из файла. До начала операции файл должен
быть открыт функцией fopen с правами на чтение.
Примечательной особенностью данной функции является возможность трансляции
знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит,
расположен «слева», то есть, идет самым старшим в двойном слове. Наоборот, если флаг
mostfirst равен единице, то функция будет ожидать, что знаковый бит, расположен
«справа» то есть идет самым младшим в двойном слове.
В случае если во время выполнения функции возникнут ошибки, то будет
возращена константа BADADDR – иначе 32-битное прочитанное значение. Формально
функция не возвращает ошибку, потому что она неотличима от возможного 32-битного
значения.
Однако в результате ошибки BADADDR все же возвращается. Например:
Message(“0x%X \n”,readlong(123));
0xFFFFFFFF
Операнд
handle
mostfirst
Return

long

writeshort

Пояснения
Дескриптор открытого с правами на чтение файла
==0 Знаковый байт самый старший в слове
==1 Знаковый байт самый младший в слове
Завершение
Пояснения
Норма
Прочитанное 16-битное знаковое слово
Ошибка
BADADDR

(long handle,long word,long mostfirst);

Функция записывает два байта в файл. До начала операции файл должен быть
открыт функцией fopen с правами на запись.
Примечательной особенностью данной функции является возможность трансляции
знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит,
расположен «слева», то есть, идет самым старшим в слове. Наоборот, если флаг mostfirst
равен единице, то функция будет ожидать, что знаковый бит, расположен «справа» то есть
идет самым младшим в слове.
В случае если во время выполнения функции возникнут ошибки, то будет
возращено ненулевое значение.

349

Операнд
Handle
Mostfirst
Return

long

writelong

Пояснения
Дескриптор открытого с правами на запись файла
==0 Знаковый байт самый старший в слове
==1 Знаковый байт самый младший в слове
Завершение
Пояснения
Норма
0
Ошибка
!=0

(long handle,long dword,long mostfirst);

Функция записывает четыре байта в файл. До начала операции файл должен
быть открыт функцией fopen с правами на запись.
Примечательной особенностью данной функции является возможность трансляции
знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит,
расположен «слева», то есть, идет самым старшим в двойном слове. Наоборот, если флаг
mostfirst равен единице, то функция будет ожидать, что знаковый бит, расположен
«справа» то есть идет самым младшим в двойном слове.
В случае если во время выполнения функции возникнут ошибки, то будет
возращено ненулевое значение.
Операнд
Handle
Mostfirst
Return

char

readstr

Пояснения
Дескриптор открытого с правами на запись файла
==0 Знаковый байт самый старший в слове
==1 Знаковый байт самый младший в слове
Завершение
Пояснения
Норма
0
Ошибка
!=0
(long handle);

Функция читает стоку из файла (с текущей позиции до символа EOL). До начала
операции файл должен быть открыт функцией fopen с правами на чтение.
Не зависимо от заданного типа при открытии файла (текстовой или двоичный)
readstr всегда правильно распознает конец стоки представленный как 0xD 0xA, так и 0xA.
Однако если файл открыт как текстовой, то функция будет преобразовывать все символы
0xA в 0xD 0xA. Что можно наблюдать на следующем примере:
auto a;
a=fopen("readme.txt","rb");
Message(readstr(a));

This patch allows you to permanently access the bonus track and bonus car♪
auto a;
a=fopen("readme.txt","rt");
Message(readstr(a));

This patch allows you to permanently access the bonus track and bonus car
Операнд
Handle

Пояснения
Дескриптор открытого с правами на чтение файла
350

Return

long

writestr

Завершение
Норма
Ошибка

Пояснения
Считанная строка
“”

(long handle,char str);

Функция записывает стоку в файл. До начала операции файл должен быть открыт
функцией fopen с правами на запись.
Если файл открыт как текстовой, то функция будет преобразовывать все символы
0xA в 0xD 0xA.
Операнд
Handle
str
Return

Пояснения
Дескриптор открытого с правами на чтение файла
Записываемая строка
Завершение
Пояснения
Норма
0
Ошибка
!=0

ВИРТУАЛЬНЫЕ МАССИВЫ
ОРГАНИЗАЦИЯ МАССИВОВ
IDA поддерживает два типа массивов, и это иногда порождает небольшую
путаницу.
Первое, массив как структура данных дизассемблируемого файла, (см ~ Edit \
Array) для повышения их удобно читаемости, но принципиально ничем ни отличающийся
от тех же данных записанных построчено.
seg000:0006
seg000:0006

db 0A0h,0ACh,0AEh,0A3h,0AEh, 20h,0ADh,0A0h
db 0A0h, 20h,0ADh,0A0h,0A4h,0AEh, 20h,0AEh

И массивы как выделенные области памяти под нужды скрптовых программ. Вот
их-то мы сейчас и рассмотрим. Они концептуально очень сильно отличаются от
привычных для нас массивов языков Си и Паскаль.
Скорее это объект, который в Microsoft непременно бы назвали CArray,
предоставляющий соответствующие API, но скрывающий реализацию всех своих
методов.
Уникальность массивов IDA в том, что они поддерживают смешанный тип
данных. В одном и том же массиве можно хранить как числа, так и стоки. Правда
обработку типов (или в принятой терминологии тегов) IDA возлагает на наши плечи и нам
придется явно указывать стоковое ли это значение или нет.

351

Очень приятно, что IDA поддерживает разряженные массивы, то есть
индексированные произвольным образом. С первого взгляда они могут напоминать списки,
но на самом деле это не так. Обыкновенные разряженные массивы.
Это дает очень большую экономию в тех случаях, когда диапазон индексов
значительно превосходит реально используемые данные.
Однако, как уже упоминалось выше, в ряде случаев выгоднее не пользоваться
массивами, а создать для этих целей сегмент в виртуальной памяти. Это может,
например, упростить ввод \ вывод данных, т.к. эту задачу можно возложить на файловый
загрузчик IDA, точно так же и вывод готовых данных можно осуществить штатными
функциями, - например, всего одной командой сохранить данные в файле – в любом из
многочисленных поддерживаемых IDA форматов.
Но есть задачи, в которых массивы несравненно удобнее. Например, это уже
отмечавшаяся
работа со списками или строковыми данными и, кроме того, массивы
хорошо подходят в качестве долговременного хранилища данных.
Массивы
сохраняются в базе IDA (а точнее в Btree) до момента
их
принудительного удаления. Это же, разумеется, относиться и к сегментам, но массивы
в отличие от последних не загромождают дизассемблируемый текст.
Попробуем составить нехитрый скрипт, нечто вроде "мимоходных заметок".
Некоторых пришедших в голову программиста мыслей, которые и с одной стороны
забывать не хочется, но и с другой не имеющим никакого отношения к собственно
дизассемблируемому тексту.
Что-то в стиле боевого крика "Пусик хочет кушать", который приходит в голову
программиста на восемнадцатом часу изнуряющей работы и не уйдет, пока не будет
записан.
Для этого нам потребуется познакомиться с базовыми операциями над массивами.
Начнем с создания.
Что бы как-то различать массивы, каждый из них дается уникальное имя (до
120 символов, при этом может начинаться с цифры) и связанный с ним идентификатор,
который возвращает функция создания сегмента в случае успешного завершения:
long CreateArray(char name);
Если массив с таким именем уже существует, то функция возвратит BADADRR.
Иначе же мы получим идентификатор массива, который необязательно сохранять, ибо
в любой момент при первой необходимости его можно узнать по имени массива. Но что-то
одно из двоих сохранить все же придется.
Как
узнать
идентификатор ранее созданного массива при перезапуске
скрипта? Конечно, можно его сохранить как в самой базе, так и во внешнем файле, но
удобнее получить его по имени массива, воспользовавшись следующей функцией:
long GetArrayId(char name);
Если указанного массива не существует, то она возвратит BADADDR, в
противном случае идентификатор. С помощью идентификатора массив в любой момент
можно переименовать функцией:
success RenameArray(long id,char newname);
С другой стороны, если Вам не нравятся конструкции типа:
auto ID;
ID=GetArrayId("MyArray");
RenameArray(ID,"MyRenamedArray");
то можно непосредственно получить идентификатор "на лету" типа:

352

RenameArray(GetArrayId("MyArray"),"MyRenamedArray");
это экономит одну переменную и ставит под вопрос удобочитаемость листинга
(с одной стороны видеть каждый раз перед глазами имя массива удобнее, а с другой
одноименная переменная ничуть ну хуже)
Кроме того, подобный подход может изрядно понизить скорость работы особенно в
цикле. Но он имеет какую-то особую притягательность, и многие программисты часто
используют его вопреки здравому смыслу (Вообще-то программисты и здравый смысл
понятия мало совместимые)
Создавая массивы, необходимо помнить, что они располагаются не в
оперативной памяти, исчезая после перезапуска IDA, а в базе. И перезапуск не разрушает
их.
Увлекшись
созданием
массивов,
особенно
на
этапе
знакомства
и
экспериментирования с ними, можно не только "скушать" порядочно ресурсов, но и
заблокировать многие имена, так что потом
при попытке создания массива с
идентичным именем возникнет непонятно с чем связанная на первый взгляд ошибка.
Поэтому сразу же, как необходимость в массиве отпадет, его следует удалить
функцией:
void DeleteArray(long id);
Жалко, что не предусмотрено возможности создания массивов, автоматически
удаляющихся при выходе из IDA. Однако, немного поразмыслив, можно найти не
очень красивое, но, тем не менее успешно работающие решение.
При запуске выполним следующие действия (для этого достаточно включить эту
строку в файл ida.idc):
CreateArray("SysListTempArray");
Теперь определим функцию:
static СreateTempArray(Name)
{
auto a,temp;
temp=GetLastIndex('S',GetArrayId("SysListTempArray"));
a=CreateArray(Name);
if (a>0) SetArrayString(GetArrayId("SysListTempArray",++temp,Name);
return a;
}
При выходе из IDA уже нетрудно будет удалить все временные массивы из базы
автоматически.
Однако, в этом на первый взгляд логичном поступке, есть одна ошибка. Давайте
подумаем, а что случиться, если сеанс работы будет аварийно завершен? Правильно,
наш скрипт не получит управления и временные массивы не будут удалены!
Поэтому необходимо очищать их при входе (запуске) IDA. При этом массив
"SysListTempArray" будет необходимо создавать только один раз для каждой новой базы.
Этот пример еще раз наглядно демонстрирует всю мощь интегрированного
языка IDA. Любые ваши пожелания и фантазии могут быть воплощены в простой или
сложный (но чаще всего все же простой) скрипт, который выполнить большую часть
работы за вас.
При этом нет никакой необходимости связываться с автором и ждать исполнения
пожеланий в последующих версиях (или попросту говоря через неопределенное время).
Массивы IDA имеют и другую уникальность. Один и тот же элемент (а точнее
индекс) может одновременно содержать строковое и числовое значения, причем оба не
перекрывают друг друга. Т.е. вместо одного массива мы как бы получаем целых два!
353

Для задания значений элементов используется пара функций:
success SetArrayLong (long id,long idx,long value);
success SetArrayString(long id,long idx,char str);
Причем обе функции могут принимать как символьный, так и числовой тип
значения.
SetArrayString(id,idx,0x21)
занесет
в ячейку знак '!' и соответственно
SetArrayLong (id,idx,'!*') - 0x2A21.
Это бывает очень удобно для преобразования типов данных, которое IDA
выполняет автоматически.
Примечательно, что не нужно предварительно каким-либо образом задавать
размер массива. Просто указываете требуемый индекс вот и все.
Всего доступно 0x100000000
индексов (32 бита), что позволяет расширять
массивы, не только "вперед", но и "назад".
IDA прекрасно справляется с отрицательными указателями. Не стоит, однако
забывать, что отрицательные указатели на самом деле трактуются как беззнаковые и
расширение массива "назад" происходит по кольцу.
Чтение элементов массива выполняется несколько неожиданным способом.
Вместо двух функций GetArrayLong и GetArrayString используется одна:
char or long GetArrayElement(long tag,long id,long idx);
Уточнение типа, требуемого элемента выполняется через тег. Если он равен 'A',
то функция возвратит числовое значение, и строковое в противном случае.
Впрочем, в IDC.IDC для строковых значений рекомендуется явно указывать тег
'S', поскольку логика его обработки в последующих версиях может быть изменена.
Можно так же использовать и определения AR_LONG и AR_STR, однако, на мой
взгляд, их несколько утомительнее писать. С другой стороны, использовать
непосредственные
значения
более
рискованно
в
плане
возможной
несовместимости с последующими версиями.
idx - это индекс элемента массива. Традиционно в большинстве языков
программирования (например, Си) нет никаких средств навигации по индексам и даже
невозможно
узнать,
сколько
элементов содержит массив и какие из них
инициализированные, а какие нет.
Всех этих проблем нет в IDA.. Индекс первого элемента поможет узнать функция:
long GetFirstIndex(long tag,long id);
Если массив не содержит ни одного элемента, то она возвращает значение -1, в
противном случае индекс первого элемента. Обратите внимание, что он не обязательно
будет равен нулю, а может принимать любое значение. Первым считается
инициализированный элемент с наименьшим индексом.
Соответственно, индекс последнего элемента поможет найти функция:
long GetLastIndex(long tag,long id);
Следующий
функций:

или

предыдущий

индекс

в

цепочке

можно

найти с помощью

long GetNextIndex(long tag,long id,long idx);
и
long GetPrevIndex(long tag,long id,long idx);
354

Заметим, что список элементов не замкнут в кольцо и при достижении обоих его
концов функции возвратят ошибку, а не "перескочат" на следующий конец.
Ну и, наконец, удалить любой элемент массива можно с помощью функции:
success DelArrayElement(long tag,long id,long idx);
Теперь можно попробоватьреализовать наш проект "Записная книжка". Начнем
с создания массива. С первого взгляда стоило бы реализовать такую конструкцию:
if (GetArrayId("Notepad")==-1) CreateArray("Notepad");
однако, можно ограничиться вызовом CreateArray("Notepad"), т.к. если массив уже
существует, то функция вернет ошибку вот и все. И если обращаться к массиву по
имени, то совершенно необязательно сохранять его ID.
Реализуем функцию "NotepadAdd" для внесения новых записей:
static NotepadAdd(s0)
{
SetArrayString(GetArrayId("Notepad"),
GetLastIndex(GetArrayId("Notepad"))+1,
s0);
}
И естественно просмотр онных:
static NotepadPrint()
{
auto a;
a=0;
Message("Блокнот: \n");
while((a=GetNextIndex('S',GetArrayId("Notepad"),a))>0)
Message("%s \n",GetArrayElement('S',GetArrayId("Notepad"),a));
}
Чуть позже мы добавим к "Блокноту" соответствующий интерфейс, а пока будем
пользоваться его функциями с консоли. Нажмем и введем
NotepadAdd("Это только тест");
и нажмем . Затем вызовем консоль еще раз и введем еще одну строку
NotepadAdd("Пусик хочет кушать");
Попробуем посмотреть содержимое блокнота командой
NotepadPrint();
Блокнот:
Это только тест
Пусик хочет кушать
А теперь реализуем наш "универсальный
сравним с предбудущими результатами.

расшифровщик"

на массивах и

355

auto a,temp;
CreateArray("MyArray");
for (a=SegStart(0x10000);aGetArrayElement('A',GetArrayId("MyArra
y"),a)) temp=a;
a=GetNextIndex('A',GetArrayId("MyArray"),a);
}
// процедура дешифровки
//
DeleteArray(GetArrayId("MyArray"));
Как видно, массивы имеют определенные преимущества перед использованием
виртуальной памяти сегментов для своих нужд.
Поскольку созданный массив заполнен едва ли не на треть, то переход по
элементам списка функцией GetNextIndex() заметно быстрее перебора всего массива в
цикле, как это было в предбудущем примере.
Кроме того, нет никакого риска прочитать неинициализированные элементы
массива, что позволяет избежать многих трудноуловимых ошибок.
Большой неожиданностью явилась поддержка IDA Perl-подобных ассоциативных
массивов.
Кардинальное отличие их обычных заключается в возможности
индексирования элементов строковыми значениями.
Например:
a["Москва"] = "Москва-Столица";
a["Кремль"] = "Старинное здание в Москве";
Конечно, IDA использует другой синтаксис, и данный пример приведен только
для облегчения понимания сущности этой возможности.
При этом внутренне
представление
индексов
таких
массивов очень
компактно и быстродествющие.
Ассоциативные массивы можно считать полу документированной особенностью
IDA. В контекстной помощи им уделено всего несколько строк, а в прототипах функций
отсутствуют комментарии (впрочем, разработчик IDA считает, что этого вполне достаточно)
Впрочем, с какой-то стороны это и оправдано. Ассоциативные массивы
реализованы "поверх" существующих, и отличаются только тем, что используют
строковые, а не числовые индексы.
К ним применимы функции CreateArray, GetArrayID, RenameArray и остальные.
Различие лишь в том, что все функции ассоциативных массивов не имеют тегов. Это
означает, что один и тот же индекс не может одновременно ссылаться на строковое и
числовое значение. Поэтому следующий пример вернет '0x1234', т.к. последнее
присвоение затирает предыдущее.
SetHashString(GetArrayId("MyArray"),"1st","Это строка");
SetHashLong (GetArrayId("MyArray"),"1st",0x1234);
Message("%x \n",GetHashLong(GetArrayId("MyArray"),"1st"));

356

Можно так же безболезненно присваивать ячейке строковое значение, а
считывать числовое (и, естественно, наоборот). Это бывает иногда полезно для
преобразования данных. Чтение значений осуществляется функциями:
long

GetHashLong(long id,char idx);

char

GetHashString(long id,char idx);

Не смотря на то, что эти функции работают непосредственно с массивом
созданным CreateArray они не могу видеть или модифицировать элементы, заданные
SetArrayLong\SetArrayString, поэтому можно безбоязненно использовать один массив
для разных нужд.
Удалить любой элемент ассоциативного массива можно с помощью функции:
success DelHashElement(long id,char idx);
При удалении элементов IDA неявно инициализирует случайным значением, в
отличие от DelArrayElement, которая в этом случае как и следует ожидать обнуляет
ячейку.
Однако, это не вызовет проблемы, если для доступа к элементам
использовать функции:
char

GetFirstHashKey(long id);

char

GetNextHashKey(long id,char idx);

char

GetLastHashKey(long id);

char

GetPrevHashKey(long id,char idx);

Все они возвращают строковое представление
использованы в функциях GetHashLong\GetHashString.

индексов,

и могут быть

МЕТОДЫ
Функция

Назначение

long CreateArray(char name)

Создает новый массив

long GetArrayId(char name)

Возвращает идентификатор массива по его
имени

success RenameArray(long id,char
newname)

Переименовывает массив

void DeleteArray(long id)

Удаляет массив

success SetArrayLong(long id,long
idx,long value)

Присваивает значение индексу массива
типа «длинное целое»

success SetArrayString(long id,long
idx,char str)

Присваивает значение индексу массива
типа «строка»

char or long GetArrayElement(long

Возвращает значения обоих типов
357

tag,long id,long idx

элементов

success DelArrayElement(long
tag,long id,long idx)

Удаляет один элемент массива

long GetFirstIndex(long tag,long id);

Возвращает индекс первого элемента

long GetLastIndex(long tag,long id);

Возвращает индекс последнего элемента

long GetNextIndex(long tag,long
id,long idx)

Возвращает следующий индекс элемента
массива

long GetPrevIndex(long tag,long
id,long idx)

Возвращает предыдущий индекс элемента
массива

success SetHashLong(long id,char
idx,long value

Присваивает значение элементу массива
типа «длинное целое»

success SetHashString(long id,char
idx,char value);

Присваивает значение элементу массива
типа «строка»

long

GetHashLong(long id,char idx)

Возвращает значение элемента типа
«длинное целое»

char
idx)

GetHashString(long id,char

Возвращает значение элемента типа
«строка»

success DelHashElement(long id,char
idx)

Удаляет элемент ассоциативного массива

char

GetFirstHashKey(long id)

Возвращает индекс первого элемента
массива

char

GetLastHashKey(long id)

Возвращает индекс последнего элемента
массива

char
idx)

GetNextHashKey(long id,char

Возвращает индекс следующего элемента
массива

char
idx);

GetPrevHashKey(long id,char

Возвращает индекс предыдущего элемента
массива

long CreateArray(char name);
Функция создает новый разряженный массив, в котором можно будет хранить
данные обоих типов – как строковые, так и длинные целые.
Массив сохраняется в базе IDA как элемент Btree до тех пор, пока не будет
принудительно удален из нее. Никаких ограничений на размер массива не налагается.
Каждый массив должен иметь свое уникальное имя (два массива с одинаковыми
именами существовать не могут). Массивы имеют собственное пространство имен (то есть
358

можно создать метку или сегмент совпадающую с именем уже существующего массива и
наоборот)
На имена наложены следующие ограничения – длина до 120 символов, может
начинаться с цифры, но не должен содержать пробелов.
При успешном завершении функция возвращает идентификатор массива, в
противном случае (массив с таким именем уже существует?) BADADDR.
Пример:
Message("0x%X \n",
CreateArray("MyArray")
);
0xFF000041
Операнд
name
Return

Пояснения
Имя массива
==return Пояснения
!=BADADDR Идентификатор массива
==0 Ошибка

long GetArrayId(char name);
Функция возвращает идентификатор массива по его имени. Это позволяет не
сохранять идентификаторы массивов, полученные при их создании, а обращаться к
массивам по имени.
Например:
Message("0x%X \n",
CreateArray("MyArray")
);
Message(“0x%X \n”
GetArrayId(“MyArray”)
);
DeleteArray(
GetArrayId(“MyArray”)
);
Message(“0x%X \n”
GetArrayId(“MyArray”)
);
0xFF000041
0xFF000041
0xFFFFFFFF
Операнд
name
Return

Пояснения
Имя массива
==return Пояснения
!=BADADDR Идентификатор массива
==0 Ошибка

359

success RenameArray(long id,char newname);
Функция позволяет изменить имя массива, заданного идентификатором. Обычно
используется редко.
Например:
Message("0x%X \n",
CreateArray("MyArray")
);
0xFF000041
RemaneArray(
GetArrayId(“MyArray”),
“MyNewname”
);
Message(“0x%X \n”
GetArrayId(“MyNewName”)
);
0xFF000041
Операнд
id
Newname
Return

Пояснения
Идентификатор массива
Новое имя массива
==return Пояснения
==1 Успешное завершение
==0 Ошибка

void DeleteArray(long id);
Функция удаляет массив, заданный идентификатором. Необходимо помнить, что
массивы, как элемент Btree хранятся в базе IDA то того момента, пока не будут удалены.
Это можно сделать, например, следующим образом:
DeleteArray(
GetArrayId(“MyArray”)
);
Операнд
id

Пояснения
Идентификатор массива

success SetArrayLong(long id,long idx,long value);
Функция присваивает значение типа «длинное целое» элементу массива,
заданного идентификатором.
Индекс массива выражается 32-битным целым числом. Разряженные массивы
позволяют эффективно хранить данные, не резервируя памяти под несуществующие
элементы.

360

Поэтому индексы не обязательно должны следовать один за другим. Так,
например, массив может состоять всего из двух индексов, – скажем 0х0 и 0х10000, – при
этом будет потрачено всего две ячейки памяти.
Необходимо помнить, что один и тот же индекс, одного и того же массива может
хранить одновременно данные двух тиров – как строковые, так и длинные целые и
никакого «затирания при этом не происходит».
Пример использования:
SetArrayLong(
GetArrayId(“MyArray”),
0x100,
0x666);
Операнд
id
idx
value
Return

Пояснения
Идентификатор массива
Индекс массива
Присваиваемое значение типа «длинное целое»
==return Пояснения
==1 Успешное завершение
==0 Ошибка

success SetArrayString(long id,long idx,char str);
Функция присваивает значение типа «строка» элементу массива, заданного
идентификатором.
Индекс массива выражается 32-битным целым числом. Разряженные массивы
позволяют эффективно хранить данные, не резервируя памяти под несуществующие
элементы.
Поэтому индексы не обязательно должны следовать один за другим. Так,
например, массив может состоять всего из двух индексов, – скажем 0х0 и 0х10000, – при
этом будет потрачено всего две ячейки памяти.
Необходимо помнить, что один и тот же индекс, одного и того же массива может
хранить одновременно данные двух тиров – как строковые, так и длинные целые и
никакого «затирания при этом не происходит».
Пример использования:
SetArrayString(
GetArrayId(“MyArray”),
0x100,
“MyString”);
Операнд
id
idx
str
Return

Пояснения
Идентификатор массива
Индекс массива
Присваиваемое значение типа «строка»
==return Пояснения
==1 Успешное завершение
==0 Ошибка

361

char or long GetArrayElement(long tag,long id,long idx);
Функция служит для чтения обоих типов элементов разряженных массивов. Выбор
интересующего типа осуществляется тегом tag.
Он может принимать следующие значения:
Определение
AR_LONG

'A'

Значение
Элемент типа «длинное целое»

AR_STR

'S'

Элемент типа «строка»

Запрашиваемый индекс должен быть инициализирован ранее, иначе функция
вернет ошибку.
Пример использования:
SetArrayLong(
GetArrayId(“MyArray”),
0x100,
0x666);
SetArrayString(
GetArrayId(“MyArray”),
0x100,
“MyString”);
Message(“%s \n0x%X\n”,
GetArrayElement(AR_STR,
GetArrayId(“MyArray”),
0x100),
GetArrayElement(AR_LONG,
GetArrayId(“MyArray”),
0x100),
);
MYString
0x666
Операнд
tag
id
idx
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
Индекс массива
==return Пояснения
==1 Успешное завершение
==0 Ошибка

success DelArrayElement(long tag,long id,long idx);
Функция удаляет указанный тип элемента разряженного массива. Тип задается
тегом tag, который может принимать следующие значения, перечисленные ниже в таблце:

362

Определение
AR_LONG

'A'

Значение
Элемент типа «длинное целое»

AR_STR

'S'

Элемент типа «строка»

Пример использования:
DelArrayElement(AR_LONG,
GetArrayId(“MyArray”),
0x100);
Операнд
tag
id
idx
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
Индекс массива
==return Пояснения
==1 Успешное завершение
==0 Ошибка

long GetFirstIndex(long tag,long id);
Функция возвращает индекс первого элемента разряженного массива. В отличие от
«обычных» массивов, известных нам по языками С и Pascal, разряженные массивы могут
начинаться с любого индекса, а не обязательно с нулевого.
«Первым» индексом разряженного массива будет индекс инициализированного
элемента с наименьшим числовым значением.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetArrayLong(a,0x100,0x666);
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
Message("0x%X \n",
GetFirstIndex(AR_LONG,a)
);
DeleteArray(a);
0x77
Операнд
tag
id
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
==return Пояснения
!=BADADDR Индекс первого элемента разряженного массива
==BADADDR Ошибка

363

long GetLastIndex(long tag,long id);
Функция возвращает индекс последнего элемента разряженного массива. Обратите
внимание, что число элементов разряженного массива обычно много меньше, чем индекс
последнего из них.
Так, например, массив может состоять всего из трех элементов с индексами,
скажем, (0x5, 0x777, 0x666777) – тогда функция GetLastIndex возвратит 0x666777, тогда как
размер массива равен всего лишь трем.
К сожалению не предоставлено никаких функций, позволяющих узнать размер
массива. Все что можно сделать это пройтись по цепочке элементов функциями
GetNextIndex (GetPrevIndex).
Поэтому, вызов GetLastIndex используется очень редко, так как в нем особой
нужды обычно и не бывает.
Пример использования:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetArrayLong(a,0x100,0x666);
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
Message("0x%X \n",
GetLastIndex(AR_LONG,a)
);
DeleteArray(a);
0x210
Операнд
tag
id
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
==return Пояснения
!=BADADDR Индекс последнего элемента разряженного массива
==BADADDR Ошибка

long GetNextIndex(long tag,long id,long idx);
Функция возвращает следующий индекс разраженного массива. Как уже было
сказано выше, в разряженных массивах индексы не обязательно следуют друг за другом, а
могут быть разделены «дырами» произвольного размера.
Поэтому, для «путешествия» по цепочке инициализированных элементов
массива и была введена функция GetNextIndex.
Передаваемый текущий индекс (idx) не обязательно должен существовать в
природе, - функция возвращает первый же инициализированный за ним элемент.
Это дает возможность отказаться от использования функции GetFirstIndex,
поскольку GetNextIndex(,,0) ему полностью эквивалентен, что и показано на примере,
приведенном ниже:
auto a,b;
b=0;
DeleteArray(GetArrayId("MyArray"));
364

a=CreateArray("MyArray");
SetArrayLong(a,0x100,0x666);
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
while(1)
{
b=GetNextIndex(AR_LONG,a,b);
if (b==-1) break;
Message("0x%X \n",b);
}
DeleteArray(a);
0x77
0x100
0x210
Операнд
tag
id
idx
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
Индекс массива
==return Пояснения
!=BADADDR Следующий индекс
==BADADDR Ошибка

long GetPrevIndex(long tag,long id,long idx)
Функция возвращает предыдущий индекс разраженного массива. Как уже было
сказано выше, в разряженных массивах индексы не обязательно следуют друг за другом, а
могут быть разделены «дырами» произвольного размера.
Поэтому, для «путешествия» по цепочке инициализированных элементов
массива и была введена функция GetPrevIndex.
Передаваемый текущий индекс (idx) не обязательно должен существовать в
природе, - функция возвращает первый же предшествующий ему инициализированный
элемент.
Это дает возможность отказаться от использования функции GetPrevIndex,
поскольку GetPrevIndex(,,-1) ему полностью эквивалентен, что и показано на примере,
приведенном ниже:
auto a,b;
b=0;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetArrayLong(a,0x100,0x666);
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
while(1)
{
b=GetPrevIndex(AR_LONG,a,b);
if (b==-1) break;
Message("0x%X \n",b);
}
DeleteArray(a);
365

0x210
0x100
0x77
Операнд
tag
id
idx
Return

Пояснения
==tag
Значение
AR_STR
Элемент типа «строка»
AR_LONG
Элемент типа «длинное целое»
Идентификатор массива
Индекс массива
==return Пояснения
!=BADADDR Предыдущий индекс
==BADADDR Ошибка

АССОЦИАТИВНЫЕ МАССИВЫ
ОБ АССОЦИАТИВНЫХ МАССИВАХ
Ассоциативные массивы это маленькое чудо IDA. Большинству программистам
возможно ранее никогда не приходилось сталкиваться ни с чем подобным. К сожалению, о
них очень мало (ну совсем ничего) не сказано в контекстной помощи, поэтому будет не
лишим остановиться на них и рассмотреть подробнее.
Один из распространенных языков, поддерживающим такие конструкции можно
назвать Perl, который ввиду свой кросс - платформенности стал очень популярен среди
разработчиков Internet – приложений. Но многие тысячи поклонников Си и Pascal могли и
вовсе не слышать о нем, а уж тем более изыскивать время для досконального изучения.
Когда-то авторитет программиста зависел от числа освоенных им языков. Сегодня
же ситуация переменилась – разработчики предпочитают сосредоточиваться не только на
одном языке, но даже компиляторы, зачастую совершенно не интересуясь, что происходит
у «соседей».
К сожалению, ставшие популярными языки многие конструкции реализуют далеко
не лучшим образом, если вообще реализуют это. Так случилось и с ассоциативными
массивами.
Чем они отличаются от своих собратьев? И какие возможности предоставляют при
этом?
Уникальность ассоциативных массивов в том, что они индексируются строковыми
значениями. Причем строковыми в буквальном смысле этого слова, - никакого
хеширования или иного преобразования в численный вид не проводится.
Это позволят значительно упрощать многие алгоритмы, раньше выглядевшие
громоздкими, - например, пусть нам требуется составить таблицу, перечисляющую имена
сотрудников и получаемую ими зарплату.
Традиционно потребовалось бы создавать три массива, а потом обвязывать все
это кодом, гарантирующим согласованность и непротиворечивость данных.
Насколько же проще решается задача с использованием ассоциативных массивов
Кроме этого можно придумать массу других примеров.

АРХИТЕКТУРА АССОЦИАТИВНЫХ МАССИВОВ
Ассоциативные массивы представляют собой всего лишь подкласс обычных
разряженных массивов, которые уже были подробно рассмотрены выше.
366

Ассоциативные массивы создаются вызовом функции CraeteArray, точно так же как
и обычные разряженные. Кроме того, любой массив по сути одновременно является и
разряженным и ассоциативным. Точнее, масивов на самом деле все же несколько, но
идентификатор (ID) у всех может быть один.
Поэтому к ассоциативным массивам применимы описанные выше функции
RenameArray и DeleteArray.
Новым является набор менее чем из десятка функций работающий со строковыми
индексами. Вообще же в терминологии ассоциативных массивов индексы называются
ключами.
Каждый ключ предстваляет собой строковое значение без явных ограничений (во
всяком случае в контекстой помощи IDA на этот счет ничего не сказано) и может содержать
один элемент типа «строка» или «длинное целое»
Обратите внимание, тогда как каждый индекс разряженного массива может
одновременно содержать элементы двух типов, то каждый ключ ассоциативного массива
ссылается лишь на одно значение того или иного типа.
В остальном же операции с элементами ассоциативных массивов ничем не
отличаются от уже описанных выше. Собственно всеми манипуляциями заведут всего три
функции.
Элемент можно присвоить, прочитать, и, наконец, удалить. На этом
предоставляемые IDA возможности заканчиваются.
Более интересны операции, выполняемые над ключами. Поскольку теперь они
представляют собой строковые значения, то может быть не совсем понятно с первого
взгляда, как можно выполнить даже такую тривиальную операцию, как перечислить все
элементы массива.
На самом деле для этого существуют традиционные функции GetNext и GetPrev.
Очевидно, что они перечисляют ключи (то есть индексы) массива один за другим, но вот в
каком порядке?
Вообще-то это не документировано, и поэтому стоит проектировать код скрипта
так, чтобы его работоспособность не зависела от подобного рода деталей. Но как нетрудно
убедиться, все текущие версии IDA упорядочивают список индексов в алфавитном
порядке, не зависимо от очередности создания элементов.
Необходимо заметить, что некоторые языки упорядочивают индексы
ассоциативных массивов именно в порядке их создания, и никем не гарантируется, что так
не будет и в следующих версиях IDA (хотя, это конечно, очень маловероятно).
Однако, ваш скрипт не должен завистеть от этих деталей.
success SetHashLong(long id,char idx,long value);
Функция присваивает значение элементу ассоциативного массива. Ассоциативные
массивы выгодно отличаются от остальных тем, что могут индексироваться строковыми
значениями!
В остальном же с ними могут использоваться те же подходы, что и для
разряженных массивов.
Однако, в отличие от разреженных массивов один и тот же индекс не может
содержать числовое и строковое значение одновременно.
Пример использования:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
DeleteArray(a);

367

Операнд
id
idx
value
Return

Пояснения
Идентификатор массива
Индекс массива (строковой!)
Присваиваемое значение типа «длинное целое»
==return Пояснения
==1 Успешное завершение
==0 Ошибка

success SetHashString(long id,char idx,char value);
Функция присваивает значение элементу ассоциативного массива. Ассоциативные
массивы выгодно отличаются от остальных тем, что могут индексироваться строковыми
значениями!
В остальном же с ними могут использоваться те же подходы, что и для
разряженных массивов.
Однако, в отличие от разреженных массивов один и тот же индекс не может
содержать числовое и строковое значение одновременно.
Пример использования:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",”Patron”);
SetHashLong(a,"Cheputilo",”Mouse”);
SetHashLong(a,"Alushta",”Metro Station”);
DeleteArray(a);
Операнд
id
idx
value
Return

long

Пояснения
Идентификатор массива
Индекс массива (строковой!)
Присваиваемое значение типа «строка»
==return Пояснения
==1 Успешное завершение
==0 Ошибка

GetHashLong(long id,char idx);
Функция возвращает значение элемента ассоциативного массива типа «длинное

целое»
Как уже говорилось выше один и тот же элемент не может содержать значения
«строка» и «длинное целое» одновременно. Поэтому возникает возможность присвоить
значение одного типа, а попытаться прочитать другого. Эта операция завершиться
корректно и типы будут автоматически преобразованы.
При этом функция GetHaskLong всегда возвращает четыре первые байта, сколько
бы строка ни занимала на самом деле. Если она была короче, - то остаток будет
представлять собой мусор и содержать непредсказуемые значения.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
368

a=CreateArray("MyArray");
SetHashString(a,"Ivanov","Patron");
Message("%s \n",
GetHashLong(a,"Ivanov")
);
DeleteArray(a);
Patr
Необходимо помнить, что IDA учитывает регистр индексов. Так “Ivanov” и “ivanov”
это два разных индекса и при попытке прочитать присвоенное значение функция возвратит
ноль.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0х66);
Message("%x \n",
GetHashLong(a,"ivanov")
);
DeleteArray(a);
0
Возникает неоднозначность, – то ли действительно возникла ошибка, то ли такое
значение имеет элемент?
В остальном же никаких проблем с использованием этой функции не возникает.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0х66);
Message("%x \n",
GetHashLong(a,"Ivanov")
);
DeleteArray(a);
0x66
Операнд
id
idx
Return

char

Пояснения
Идентификатор массива
Индекс массива (строковой!)
==return Пояснения
!=0 Значение элемента массива
Ошибка
==0
Значение элемента массива

GetHashString(long id,char idx);
Функция возвращает значение элемента ассоциативного массива типа «строка»
369

Как уже говорилось выше один и тот же элемент не может содержать значения
«строка» и «длинное целое» одновременно. Поэтому возникает возможность присвоить
значение одного типа, а попытаться прочитать другого. Эта операция завершиться
корректно и типы будут автоматически преобразованы.
При этом функция GetHashString возвращает четыре первые байта, если ни один
из них не является нулем, в противном случае весь отрезок до первого нуля.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66776677);
Message("%s \n",
GetHashString(a,"Ivanov"));
DeleteArray(a);
Wfwf
Необходимо помнить, что IDA учитывает регистр индексов. Так “Ivanov” и “ivanov”
это два разных индекса и при попытке прочитать присвоенное значение функция возвратит
пустую строку.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov",0х66);
Message("%s \n",
GetHashLong(a,"ivanov")
);
DeleteArray(a);

Возникает неоднозначность, – то ли действительно возникла ошибка, то ли такое
значение имеет элемент?
В остальном же никаких проблем с использованием этой функции не возникает.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov",”Patron”);
Message("%s \n",
GetHashLong(a,"Ivanov")
);
DeleteArray(a);
Patron
Операнд
id
idx
Return

Пояснения
Идентификатор массива
Индекс массива (строковой!)
==return Пояснения
!=”” Значение элемента массива
370

==””

Ошибка
Значение элемента массива

success DelHashElement(long id,char idx);
Функция удаляет один элемент ассоциативного массива. Поскольку один и тот же
элемент не может содержать значения «строка» и «длинное целое» одновременно,
отпадает необходимость указывать теги (смотри описание функции DelArrayElement для
разряженных массивов)
Необходимо помнить, что IDA учитывает регистр индексов. Так “Ivanov” и “ivanov”
это два разных индекса и представляют собой два разных элемента.
В остальном же функция ничем не отличается от аналогичной, использующийся
для удаления элементов разряженных массивов.
Например:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov",”Patron”);
Message("%s \n",
DelHashElement(a,"Ivanov")
);
DeleteArray(a);
1
Операнд
id
idx
Return

char

Пояснения
Идентификатор массива
Индекс массива (строковой!)
==return Пояснения
==1 Операция выполнена успешно
==0 Ошибка

GetFirstHashKey(long id);

Функция возвращает индекс первого элемента ассоциативного массива. Поскольку
ассоциативные массивы индексируются строковыми значениями, то привычные для нас
способы обращения к элементам не подходят.
Что бы понять принципы функционирования этой (и некоторых других) функций,
необходимо познакомиться с техническими деталями архитектуры ассоциативных
массивов.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной
последовательности, не зависимо от порядка добавления в него новых элементов, что и
доказывает следующий пример:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
Message("%s \n",
371

GetFirstHashKey(a)
);
DeleteArray(a);
Alushta
Операнд
id
Return

char

Пояснения
Идентификатор массива
==return Пояснения
!=”” Индекс первого элемента массива
==”” Ошибка

GetLastHashKey(long id);

Функция возвращает индекс последнего элемента ассоциативного массива.
Поскольку ассоциативные массивы индексируются строковыми значениями, то привычные
для нас способы обращения к элементам не подходят.
Что бы понять принципы функционирования этой (и некоторых других) функций,
необходимо познакомиться с техническими деталями архитектуры ассоциативных
массивов.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной
последовательности, не зависимо от порядка добавления в него новых элементов, что и
доказывает следующий пример:
auto a;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
Message("%s \n",
GetLastHashKey(a)
);
DeleteArray(a);
Ivanov
Операнд
id
Return

char

Пояснения
Идентификатор массива
==return Пояснения
!=”” Индекс последнего элемента массива
==”” Ошибка

GetNextHashKey(long id,char idx);

Функция возвращает индекс следующего элемента ассоциативного массива.
Поскольку ассоциативные массивы индексируются строковыми значениями, то привычные
для нас способы обращения к элементам не подходят.
Что бы понять принципы функционирования этой (и некоторых других) функций,
необходимо познакомиться с техническими деталями архитектуры ассоциативных
массивов.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной
последовательности, не зависимо от порядка добавления в него новых элементов, и
372

функция GetNextHashKey последовательно возвращает элементы ассоциативного массива
один за другим.
Передаваемый ей текущий индекс не обязательно должен существовать в природе
– функция просматривает список всех элементов и возвращает следующий в алфавитном
порядке за ним.
Это дает возможность отказаться от вызовов GetFirstHashKey, поскольку
GetNextHashKey(,””) будет его полным эквивалентом.
Например:
auto a,b;
b="";
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
for(;;){
b=GetNextHashKey(a,b);
if (b=="") break;
Message("%s \n",b);}
DeleteArray(a);
Alushta
Cheputilo
Ivanov
Операнд
id
idx
Return

char

Пояснения
Идентификатор массива
Индекс массива (строковой!)
==return Пояснения
!=”” Очередной индекс массива
==”” Ошибка

GetPrevHashKey(long id,char idx);

Функция возвращает индекс предыдущего элемента ассоциативного массива.
Поскольку ассоциативные массивы индексируются строковыми значениями, то привычные
для нас способы обращения к элементам не подходят.
Что бы понять принципы функционирования этой (и некоторых других) функций,
необходимо познакомиться с техническими деталями архитектуры ассоциативных
массивов.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной
последовательности, не зависимо от порядка добавления в него новых элементов, и
функция GetPrevHashKey последовательно возвращает элементы ассоциативного массива
один за другим.
Передаваемый ей текущий индекс не обязательно должен существовать в природе
– функция просматривает список всех элементов и возвращает предшествующий в
алфавитном порядке за ним.
Это дает возможность отказаться от вызовов GetLastHashKey, поскольку
GetNextHashKey(,-1) будет его полным эквивалентом.
Например:
auto a,b;
b=-1;
373

DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
for(;;){
b=GetPrevHashKey(a,b);
if (b=="") break;
Message("%s \n",b);}
DeleteArray(a);
Ivanov
Cheputilo
Alushta
Операнд
Id
idx
Return

Пояснения
Идентификатор массива
Индекс массива (строковой!)
==return Пояснения
!=”” Очередной индекс массива
==”” Ошибка

ОПЕРАЦИИ С ГЛОБАЛЬНЫМИ НАСТРОЙКАМИ
МЕТОДЫ
Функция

Назначение

long

GetLongPrm (long offset)

Возвращает длинный целый параметр

long

GetShortPrm(long offset);

Возвращает короткий целый параметр

long

GetCharPrm (long offset)

Возвращает байтовый параметр

success SetLongPrm (long offset,long
value);

Задает длинный целый параметр

success SetShortPrm(long offset,long
value);

Задает короткий целый параметр

success SetCharPrm (long offset,long
value);

Задает байтовый параметр

success SetPrcsr
processor);

Задет тип процессора для
дизассемблирования

long

Batch

(char
(long batch);

char GetIdaDirectory ()

Устанавливает или снимает пакетный
режим работы
Возвращает путь к директории, в которой
расположена IDA
374

char GetInputFile ()

Возвращает имя дизассемблируемого
файла

long GetLongPrm (long offset);
long GetShortPrm(long offset);
long GetCharPrm (long offset);
success SetLongPrm (long offset,long value);
success SetShortPrm(long offset,long value);
success SetCharPrm (long offset,long value);
IDA предоставляет возможность не только чтения глобальных установок из
скриптов, но даже их модификации. Для этого служат целых шесть приведенных выше
функций.
Это может показаться излишне сложным, но на самом деле все они сводятся к
простому чтению \ записи непрерывного фрагмента памяти, в котором IDA и хранит свои
настройки.
Три функции чтения GetLongPrm, GetShortPrm, GetCharPrm отличаются только
возвращаемым значением. Первая возвращает длинное целое, вторая короткое целое и
последняя строку.
В каком-то смысле все они взаимозаменяемы. Т.е. можно использовать
GetLongPrm для чтения короткого целого, если потом «врачую» маскировать старшие биты
возращенного значения.
Обратите внимание, что GetCharPrm возвращает не строку, оканчивающуюся
нулем, а только один байт.
Чтение же всей строки целиком приходится осуществлять пошагово в цикле байт за
байтом. Или можно воспользоваться GetLongPrm, читая строку по четыре байта за раз (это
удобнее да и быстрее).
‘offset’ это смещение внутри структуры, в которой IDA и хранит настойки. Ниже это
будет подробно описано. А пока обратим внимание на то, что IDA не запрещает
передавать функции произвольное смещение внутри структуры.
Это часто становится источником неочевидных ошибок. Обычно такое происходит,
когда используются фиксированные смещения элементов структуры, вместо определенных
IDA значений. Ввиду того, что в следующих версиях эта структура, скорее всего, не
останется без изменений, то необходимо использовать только определения IDA, иначе за
работоспособность скрипта трудно будет поручиться.
НАСТОЙКИ IDA
Первые три байта хранят в себе слово ‘IDA’. Хотя это и не сообщается в
документации, но нетрудно убедится, что это именно так и есть.
Message("%s%s\n",
GetShortPrm(0),
GetCharPrm(2)
);
IDA
INF_VERSION
Содержит, переменную типа Short хранящую версию базы IDA. Например:
Message("%x \n",
375

GetShortPrm(INF_VERSION)
);
22
INF_PROCNAME
Хранит
восьмисимвольное
имя
выбранного
типа
процессора
дизассемблируемого текста. Для процессоров серии 80x86 предусмотрены
следующие соответствия:
Intel 8086
Intel 80286 real
Intel 80286 protected
Intel 80386 real
Intel 80386 protected
Intel 80486 real
Intel 80486 protected
Intel Pentium real with MMX
Intel Pentium protected with MMX
Intel Pentium Pro (P6) with MMX
Intel Pentium II
AMD K6-2 with 3DNow!
Intel Pentium III

8086
80286r
80286p
80386r
80386p
80486r
80486p
80586r
80586p
80686p
p262
K62p3
p3ntel

Наиболее разумным (и быстрым) способом извлечения этого поля,
вероятно, окажется чтение его с помощью GetLongPrm как показано ниже:
Message("%s%s \n",
GetLongPrm(INF_PROCNAME),
GetLongPrm(INF_PROCNAME+4)
);
p3ntel
ПРИМЕЧАНИЕ: Это поле может только считываться. Все попытки его
модификации посредством SetXXXPrm будут проигнорированы.

INF_LFLAGS
Это однобайтовое поле хранит флаги, определяемые по умолчанию в IDP
модуле для конкретной модели процессора, которые могут принимать следующие
значения:
LFLG_PC_FPP (0x1)
Декодировать
инструкции
арифметического сопроцессора.

с

плавающей

запятой

для

LFLG_PC_FLAT (0x2)
Плоская модель памяти
376

Обратите внимание, что изменение последнего параметра может повлечь
за собой непредсказуемую работу дизассемблера и привести к неверному анализу
исследуемого файла!
Пример использования:
auto a;
a=GetCharPrm(INF_LFLAGS);
Message(“%x \n”,a);
if (a & LFLG_PC_FPP)
Message ("Decode FPP \n");
if (a & LFLG_PC_FLAT)
Message ("FLAT MODEL \n");
1
Decode FPP
INF_DEMNAMES
Однобайтовое
«заманглять» имена.
модифицироваться.

поле, определяющие каким
Безболезненно может как

образом IDA будет
считываться, так и

DEMNAM_CMNT (0);
Отображать замангленные имена как комментарии. Например:
SetCharPrm(INF_DEMNAMES,DEMNAFM_CMNT);
.text:00403E79 ?sputc@streambuf@@QAEHH@Z proc near
; streambuf::sputc(int)
DEMNAM_NAME (1)
Заманглять в имена. Например:
SetCharPrm(INF_DEMNAMES,DEMNAFM_NAME);
.text:00403E79

public:

int

__thiscall

streambuf::sputc(int)

proc

near

DEMNAM_NONE (2)
Не заманглять и представлять имена как есть.
SetCharPrm(INF_DEMNAMES,DEMNAFM_NONE);
.text:00403E79 ?sputc@streambuf@@QAEHH@Z proc near
При установке нового значения IDA автоматически начнет реанализ и
внесет все изменения в дизассемблируемый текст.

377

INF_FILETYPE
Это поле содержит короткое целое, хранящее тип дизассемблируемого
файла. Видимо не используется IDA, поскольку может безболезненно
модифицироваться
произвольными
значениями,
что
не
нарушает
работоспособности.
Так же следует учитывать, что в версии 3.84 функция возвращает
неверные значения. Дело в том, что IDC.IDC не был изменен, тогда как были
расширены типы файлов, начиная с середины таблицы, от чего все типы
«посыпались».
MS-DOS exe файл стал определятся как программный файл PalmPilot, что
хотя и не нарушало работоспособности IDA, но не позволяло определить тип
текущего файла.
В IDC.IDC содержится ссылка на файл ‘core.hpp’. На самом деле это
опечатка и нужно смотреть ‘ida.hpp’, входящий в IDA SDK. Там мы обнаружим
прелюбопытную структуру, описывающую типы поддерживаемых файлов.
Сравнив ее с IDC.IDC можно обнаружить различие, которое показано ниже:
f_EXE_old,
f_COM_old,
f_BIN,
f_DRV,
f_WIN,
f_HEX,
f_MEX,
f_LX,
f_LE,
f_NLM,
f_COFF,
f_PE,
f_OMF,
f_SREC,
f_ZIP,
f_OMFLIB,
f_AR,
f_LOADER,
f_ELF,
f_W32RUN,
f_AOUT,
f_PRC,

FT_EXE_OLD
FT_COM_OLD
FT_BIN
FT_DRV
FT_WIN
FT_HEX
FT_MEX
FT_LX
FT_LE
FT_NLM
FT_COFF
FT_PE
FT_USER
FT_OMF
FT_SREC
FT_ZIP
FT_OMFLIB
FT_AR
FT_LOADER
FT_ELF
FT_W32RUN
FT_AOUT
FT_PRC
FT_EXE
FT_COM
FT_AIXAR

Что бы функция возвращала правильный результат, необходимо исключить
тип FT_USER и перенумеровать остаток списка.
Расшифровка всех значений приведена ниже:
FT_EXE_OLD

MS DOS EXE файл

FT_COM_OLD
FT_BIN

MS DOS COM файл
Двоичный файл (дамп ROM например)

378

FT_DRV
FT_WIN
FT_HEX
FT_MEX
FT_LX
FT_LE
FT_NLM
FT_COFF
FT_PE
FT_USER
FT_OMF
FT_SREC
FT_ZIP
FT_OMFLIB
FT_AR
FT_LOADER
FT_ELF
FT_W32RUN
FT_AOUT
FT_PRC
FT_EXE
FT_COM
FT_AIXAR

MS DOS драйвер (drv или sys)
New Executable (NE)
Intel Hex Object File
MOS Technology Hex Object File
Linear Executable (LX)
Linear Executable (LE)
Netware Loadable Module (NLM)
Common Object File Format (COFF)
Portable Executable (PE)
Файл,
загруженный
посредством
загрузчика IDP
Object Module Format
R-records
ZIP file (никогда не бывает загружен в базу
IDA)
Библиотека OMF Модулей
ar library
Файл загружен посредством LOADER DLL
Executable and Linkable Format (ELF)
Watcom DOS32 Extender (W32RUN)
Linux a.out (AOUT)
PalmPilot программный файл
MS DOS EXE File
MS DOS COM File
AIX ar library

Пример использования:
Message("%d \n",
GetShortPrm(INF_FILETYPE)
);
23

INF_OSTYPE
Короткое целое хранящее тип операционной системы для загруженного
файла (не среды запуска дизассемблера!)
Должна принимать следующие значения:
OSTYPE_MSDOS
OSTYPE_WIN
OSTYPE_OS2
OSTYPE_NETW

0x0001
0x0002
0x0004
0x0008

MS-DOS
MS Windows
OS/2
Novell NetWare

Да, именно должна, ибо MS-DOS файлы возвращают ноль, а не единицу и,
следовательно, OSTYPE_MSDOS не сработает.
Пример использования:
Message("%d \n",
GetShortPrm(INF_OSTYPE)
);

379

0
INF_APPTYPE
Короткое целое, содержащие информацию о типе дизассемблируемого
приложения. Часть полей (APPT_CONSOLE, APPT_GRAPHIC, APPT_1THREAD,
APPT_MTHREAD) инициализируются FLIRT. Если исследуемой программе не
соответствует ни одна библиотека сигнатур и FLIRT не сработал, то все
вышеперечисленны поля будут содержать нулевые значения.
Тип приложения (EXE\DLL\DRIVER) не актуален для MS-DOS программ, как
и разрядность (16 или 32 бит). В этом случае функция всегда возвращает нулевое
значение.
APPT_CONSOLE
APPT_GRAPHIC
APPT_PROGRAM
APPT_LIBRARY
APPT_DRIVER
APPT_1THREAD
APPT_MTHREAD
APPT_16BIT
APPT_32BIT

0x0001
0x0002
0x0004
0x0008
0x0010
0x0020
0x0040
0x0080
0x0100

Console
Graphics
EXE
DLL
DRIVER
Singlethread
Multithread
16 bit application
32 bit application

Пример использования:
Message("%x \n",
GetShortPrm(INF_APPTYPE)
);
104

INF_START_SP
Длинное целое, содержащие значение регистра SP (ESP) при запуске
программы. Для получения этой информации IDA читает соответствующие поля
заголовка файла. В противном случае (например, для com или дампов памяти) она
устанавливает SP на верхушку сегмента, то есть присваивает ему значение –1.
Для бинарных файлов и дампов памяти это оказывается не всегда
справедливо (в самом деле, откуда IDA может знать значение указателя стека в
каждом конкретном случае) Тогда рекомендуется установить требуемое значение
вручную, функцией SetLongPrm.
Однако, обычно точное значение SP (ESP) не критично и в общем случае не
влияет на правильность дизассемблирования кода.
Пример использования:
Message("%x \n",
GetShortPrm(INF_START_SP)
);
ffff

380

INF_START_AF
Это поле содержит короткое целое, управляющие настойками анализатора
IDA. Иначе к ним можно добраться через меню «Options\ Analysis options\ Kernel
analyser options 1»

Все они доступны как для чтения, так и для модификации. Назначение
битов флагов приведены ниже.
AF_FIXUP

0x0001

AF_MARKCODE

0x0002

AF_UNK
AF_CODE
AF_PROC

0x0004
0x0008
0x0010

AF_USED

0x0020

AF_FLIRT
AF_PROCPTR

0x0040
0x0080

Создавать сегменты и смещения,
используя
информацию
из
таблицы
перемещаемых
элементов
Автоматически преобразовывать
типичные
последовательности
инструкций в код
Удалять инструкции без ссылок
Трассировать выполнение
Автоматически
создавать
функции
Поверхностный
анализ
программы
Использовать FLIRT сигнатуры
Создавать функции в 32-битном
сегменте, если это
ссылка на сегмент данных
381

AF_JFUNC

0x0100

AF_NULLSUB

0x0200

AF_LVAR
AF_TRACE
AF_ASCII
AF_IMMOFF

0x0400
0x0800
0x1000
0x2000

AF_DREFOFF

0x4000

AF_FINAL

0x8000

Переименовывать jump-функции
как j_...
Переименовывать
пустые
функции как nullsub_...
Создавать стековые переменные
Отслеживать указатель стека
Автоматически создавать строки
Преобразовывать операнды 32инструкций в смещения
Преобразовывать 32-данные в
смещения
Сворачивать
все
unexplored
регионы

AF_FIXUP
если этот бит установлен, то IDA будет использовать информацию из
таблицы
перемещаемых
элементов
и
представлять
соответствующие
непосредственные операнды в виде смещений или сегментов.
Например:
Код
B8 01 00
8E D8

AF_FIXUP == 1
mov
ax, seg dseg
mov
ds, ax

AF_FIXUP == 0
mov
ax,1001h
mov
ds, ax

Значение перемещаемого элемента, выделенного красным цветом, равно
0x1. В любом случае IDA автоматически суммирует его с адресом загрузки (в
нашем примере 0x10000).
Если флаг AF_FIXUP установлен, то IDA преобразует непосредственный
операнд в сегмент, в противном же случае оставит его без изменений.
AF_MARKCODE
Установка этого флага приведет к тому, что IDA будет находить все
типичные для выбранного процессора последовательности инструкций и будет
преобразовывать их в код, даже если на него отсутствуют явные ссылки.
Такой прием не совсем безгрешен, но позволяет заметно поднять качество
дизассемблирования и переложить часть рутиной работы на плечи дизассемблера.
Например, для 80x86 процессоров типичной последовательностью
инструкций будет инициализация регистра BP (EBP) при входе впроцедуру.
.text:00401020
.text:00401021 8B EC

push
mov

ebp
ebp, esp

Обратите внимание, что этот механизм запускается только во время
загрузки файла и динамическое его изменение во время работы дизассемблера
будет проигнорировано.
AF_UNK
раз

Этот флаг будучи установленным приводит к тому, что IDA будет каждый
при пометке инструкции (инструкций) как unexplored автоматически
382

отслеживать все потерянные перекрестные ссылки,
регионы как unexplored.

помечая соответствующие

AF_CODE
IDA умеет трассировать следование инструкций, отслеживая условные
переходы и вызовы процедур. Например, если встретится:
seg000:22C3 E8 5F 00

call

sub_0_2325

то можно быть уверенным, что IDA преобразует в инструкции и код,
находящийся по смещению 0x2325. В противном случае (если бит AF_CODE
сброшен) это выполнено не будет. Более того, при загрузке IDA не
дизассемблирует ни одной инструкции, предоставляя это пользователю сделать
самостоятельно.
Этот флаг имеет смысл сбрасывать всякий раз, когда IDA неправильно
отслеживает ссылки или же вам нужно изучить только отдельно взятый фрагмент
кода и дизассемблировать весь файл не к чему.
AF_PROC
Автоматически создавать функции на месте вызова инструкцией call. В
противном случае функции будут создаваться только для библиотечных процедур.
Например:
AF_PROC == 0
Seg00:0124 call
Seg000:0284
seg000:0284
seg000:0285
seg000:0288
seg000:028A

loc_0_284

loc_0_284:
push
ds
mov
ax, 3500h
int
21h
ret

AF_PROC == 1
Seg00:0124
call
loc_0_284
Seg000:0284 sub_0_284 proc near
seg000:0284 push
ds
seg000:0285 mov
ax, 3500h
seg000:0288 int
21h
seg000:028A ret
seg000:02C6 sub_0_284 endp

AF_USED
В документации на IDA сообщается, что сброс этого бита приводит к тому,
что IDA выполняет поверхностный анализ программы. То есть примитивное
дизассемблирование, без создания перекрестных ссылок и дополнительных
проверок.
Однако, практически значение этой опции никак не влияет на
дизассемблируемый текст и в обоих случаях получаются идентичные листинги.
AF_FLIRT
Уникальная FLIRT технология позволяет IDA определять имена
библиотечных функций наиболее популярных компиляторов по их сигнатурам.
Сравните два примера:
AF_FLIRT == 1
dseg:039A push offset aHelloSailor
dseg:039D call
_printf

AF_FLIRT == 0
dseg:039A pushoffset aHelloSailor
dseg:039D call
sub_0_1035

383

dseg:03A0 pop
dseg:03A1 retn

cx

dseg:03A0 pop
dseg:03A1 retn

cx

AF_PROCPTR
Установка этого флага приведет к тому, что IDA будет проверять все
перекрестные ссылки из 32-разрядного сегмента данных в сегмент кода. Если
ссылка указывает на машинную инструкцию, то IDA автоматически преобразует ее
в код и создаст на этом месте функцию.
Например:
AF_PROCPTR == 1
.data:004085E0
.text:00405AAC
.text:00405AAC
.text:00405AAD

dd offset sub_0_405AAC
sub_0_405AAC proc near
push
ebp
mov
ebp, esp

AF_PROCPTR == 0
.data:004085E0
.text:00405AAC
.text:00405AAD
.text:00405AAE

dd 405AACh
db 55h
db 8Bh
db 0ECh

Данный метод не безгрешен и в некоторых случаях может приводить в к
ошибкам (тем более возможно предположить умышленное противодействие автора
дизассемблируемого текста против IDA) поэтому иногда его приходится отключать.
AF_JFUNC
Установка этого флага приведет к тому, что IDA будет переименовывать
функции, состоящие из одной только команды jmp somewhere в j_somewhere. Это
заметно улучшает читабельность листинга и ускоряет анализ алгоритма его
работы.
AF_JFUNC == 1
seg000:22DD j_MyJmpTrg proc near
seg000:22DD jmp
short MyJmpTrg
seg000:22DD j_MyJmpTrg
endp

AF_JFUNC == 0
seg000:22DD sub_0_22DD
proc near
seg000:22DD jmp
short MyJmpTrg
seg000:22DD sub_0_22DD
endp

AF_NULLSUB
Установка этого флага приведет к тому, что IDA будет переименовывать
«пустые», то есть состоящие только из одной инструкции возврата, процедуры в
nullsub_xx.
Это облегчает читабельность и восприятие листинга, а так же ускоряет
анализ исследуемого текста дизассемблера.
AF_NULLSUB == 1
seg000:22DF nullsub_1
seg000:22DF retn
seg000:22DF nullsub_1

proc near
endp

AF_NULLSUB == 0
seg000:22DF sub_0_22DF
seg000:22DF
seg000:22DF sub_0_22DF

proc near
retn
endp

AF_LVAR
Механизм отслеживания текущего значения регистра SP (ESP) дает
возможность поддержки локальных переменных. То есть тех, что лежат в стеке с
отрицательным смещением относительно BP (EBP).
Это становится невероятно полезным при дизассемблировании кода,
384

сгенерированного оптимизирующими компиляторами, которые уже не опираются на
BP (EBP), а адресуют локальные переменные относительно стекового регистра
ESP. Это приводит к тому, что невозможно понять к какой именно переменной
обращается та или иная инструкция, до тех пор пока не будет вычислено значение
указателя стека в конкретной точке кода.
IDA взяла на себя эту рутину работу и поддерживает оба типа стековых
переменных самостоятельно.
AF_LVAR == 1
.text:0040112A mov ecx, [esp+40h+var_1C]

AF_LVAR == 0
.text:0040112A mov ecx, [esp+24h]

AF_TRACE
Установка этого флага заставляет IDA отслеживать значение регистра
указателя стека в каждой точке кода. Главным образом это необходимо для
поддержки локальных переменных (см. AF_LVAR)
AF_PROCPTR == 1
dseg:187A off_0_187A dw offset loc_0_B45
dseg:0B45 mov
dx, 183Ch

AF_PROCPTR == 0
dseg:187A word_0_187A dw 0B45
dseg:0B45 mov
dx, 183Ch

AF_ASCII
IDA может автоматически создавать строки, если элемент на который
указывает ссылка состоит более чем из четырех символом ASCII для 16-сегмента
(и шестнадцати символов в остальных случаях).
Стиль строки определяется настойками о которых будет сказано ниже.
AF_IMMOFF
Этот флаг имеет смысл только для 32-разрядных сегментов. Если он
установлен, то IDA будет преобразовывать 32-разрядные операнды в смещения.
Для этого необходимо, что бы операнд был больше минимально возможного
адреса загрузки 0x10000.
Значительно облегчает дизассемблирование 32-разрядных приложений,
автоматически корректно распознавая большинство смещений и указателей. Поскольку
большинство приложений редко оперируют подобными величинами, то вероятность
ложных срабатываний (то есть ошибочного преобразования константы в смещение)
относительно невелика.
AF_IMMOFF == 1
.text:00401000 push offset aHeloSailor
.text:00401005 mov ecx, offset ord_0_408900

AF_IMMOFF == 0
.text:00401000 push
.text:00401005 mov

408040h
ecx, 408900h

AF_DREFOFF
Если этот флаг установлен, то IDA будет автоматически пытаться
преобразовать в смещения все двойные слова, содержащие ссылки из 32разрядных сегментов.
Преобразование в общем случае осуществляется успешно, если
содержимое двойного слова больше, чем 0x10000
385

AF_DREFOFF == 1
.data:00408330 off_0_408330 dd offset unk_0_408980 ; DATA XREF: .text:00404758o

AF_DREFOFF == 0
.data:00408330 dword_0_408330 dd 408980h

Поясним этот пример. Допустим, в 32-сегменте кода встретится следующая
инструкция:
.text:00404758

mov

eax, 408330h

Если флаг AF_IMMOFF (см. выше) установлен, то константа 0x408440 будет
автоматически преобразована в смещение, так как 0x408440 > 0x10000.
По этому смещению находится следущая ячейка:
.data:00408330 dword_0_408330

dd 408980h

Поскольку 0x408980 больше 0x10000, то, скорее всего, оно представляет
собой смещение, в которое и может быть преобразовано, если флаг AF_DREFOFF
будет установлен.
AF_FINAL
Если этот флаг установлен, то дизассемблер в последнем проходе анализа
автоматически преобразует все байты, помеченные как unexplored, в данные или
инструкции.
Правила, по которым происходит это преобразование, не документированы
и меняются от версии к версии. В идеале IDA должна была бы практически во всех
случаях «угадать» чем является каждый элемент – данными или инструкцией.
Однако, на практике она часто допускает ошибки, особенно com файлах, где
данные и код могут быть сложным образом перемешаны.
Для win32 файлов с раздельными сегментами кода и данных, эта проблема
отсутствует.
Рекомендуется сбрасывать этот флаг (по умолчанию он установлен). И вот
почему – рассмотрите пример, приведенный ниже. Очевидно, что по адресу
seg000:210D расположены строки:
seg000:210D aDir
seg000:2110 aMask

db '..',0
db '*.*',0

Но IDA, не найдя на них ссылок (поскольку невозможно для 16-разрядных
приложений отличить смещения от констант) превратила их в малоосмысленный
массив.
Очевидно, что программа была дизассемблирована не правильно. Поэтому
лучше не полагаться на автоматически алгоритмы IDA, а исследовать unexpored
байты самостоятельно.
ЗАМЕЧАНИЕ: для некоторых типов файлов (например, PE) значение
этого флага в ряде случаев игнорируется и остаются unexplored байты.
AF_FINAL == 1
seg000:210D db 2 dup(2Eh), 0, 2Ah, 2Eh, 2Ah, 0

AF_FINAL == 0
seg000:210D
seg000:210E
seg000:210F
seg000:2110
seg000:2111
seg000:2112
seg000:2113

db
db
db
db
db
db
db

2Eh
2Eh
0
2Ah
2Eh
2Ah
0

;
;
;
;
;
;
;

.
.
*
.
*

386

INF_START_IP
Это длинное поле содержит в себе значение регистра IP (EIP) при запуске
программы. Для бинарных файлов не имеет смысла и возвращает ошибку (BADADDR). В
остальных случаях IDA извлекает необходимую информацию из соответствующих полей
заголовка файла или же эмулятора загрузчика (например, для com-файлов).
Это поле доступно как на чтение, так и на запись. Однако, модификация начального
значения IP (EIP) не приведет к изменению точки входа (Entry point) файла (для этого
необходимо изменить значение INF_BEGIN_EA)
Пример использования:
Message("%x \n",
GetLongPrm(INF_START_IP)
);
401020
INF_BEGIN_EA
Это длинное поле хранит линейный адрес точки входа в файл. Доступно для
модификации, однако, все изменения не возымеют никакого эффекта для уже
существующей точки входа.
Пример использования:
Message("%x \n",
GetLongPrm(INF_BEGIN_EA)
);
401020
INF_MIN_EA
Это длинное поле хранит
программой.
Пример использования:

минимальный

линейный

адрес,

используемый

Message("%x \n",
GetLongPrm(INF_MIN_EA)
);
401000
INF_MAX_EA
Это длинное поле хранит самый старший адрес, используемый программой.
Никогда не бывает равно минус единице (не смотря на то, что это число присутствует в
IDC.IDC). Не зависимо от того, загружен файл как бинарный или нет, всегда возвращается
максимальный адрес, встретившийся в программе.
Message("%x \n",
GetLongPrm(INF_MAX_EA)
);
387

134EA
INF_LOW_OFF
Это длинное поле хранит самый младший из возможных адресов, в котором
непосредственный операнды могут трактоваться как тип void. Иными словами, начиная с
этой величины, IDA будет предполагать, что операнд может являться смещением, и
поэтому будет выделять его красным цветом, привлекая к нему внимание пользователя.
Рассмотрим это на следующем примере: пусть у в исследуемом файле
минимальный из возможных адресов равен 0x100. Следовательно, можно предположить,
что все операнды, входящие в диапазон от 0 до 0хFF окажутся константами, а свыше 0xFF
с равной степенью вероятности могут быть как смешениями, так и константами.
IDA всегда использует беззнаковые значения операндов. Поэтому [BP-2] будет
трактоваться как 0xFFFE, а не –2.
Допустима модификация этого поля, в том числе и интерактивно, через меню
«~Options\Text representation\void's low limit». По умолчанию IDA использует минимальный
линейный адрес. Как правило, первым располагается сегмент кода. Если достоверно
известно, что программа не содержит на него ссылок, то значение INF_LOW_OFF можно
изменить таким образом, что бы оно указывало на сегмент данных.
Message("%x \n",
GetLongPrm(INF_LOW_OFF)
);
401000
INF_HIGH_OFF
Это длинное поле хранит самый старший из возможных адресов, до которого
непосредственные операнды могут трактоваться как тип void. Подробнее об этом было
сказано в описании поля INF_LOW_OFF
По умолчанию INF_HIGH_OFF равно наибольшему адресу, занимаемому
программой. Часто этого оказывается недостаточным для тех программ, что организуют
буфера за пределами области статических переменных.
Рассмотрим это на примере типичного EXE файла (SMALL модель памяти Сегмент стека и заголовок для упрощения не показаны) Все переменные, кроме тех, что
инициализируются на стадии компиляции, останутся «не видимы» для IDA. Она посчитает
ссылки на них константами, а не смещениями.
Поэтому необходимо изменить значение поля INF_HIGH_OFF вручную.
Файл на диске
КОД
СТАТИЧЕСКИЕ ПЕРЕМЕННЫЕ

Исполняемая программа
Сегмент кода
Сегмент данных Статические
переменные
Сегмент данных Динамические
перемнные

INF_MAXREF
Это длинное поле хранит максимальную глубину перекрестных ссылок. По
умолчанию 10. Это значение можно изменить через меню (~Options\Cross references)
388

Пример:
Message(“%x \n”,GetLongPrm(INF_MAXREF));
10
INF_ASCII_BREAK
Это однобайтовое поле содержит в себе символ переноса конца строки. Он не
будет использоваться IDA при генерации файлов отчета или при выводе на экран. Не
влияет он и на трактовку спецификатора ‘\n’. Единственное его назначение
форматирование сток в дизассемблируемом листинге. (Ниже это будет показано на
конкретном примере для большей ясности)
Поле может, как читаться, так и модифицироваться. Изменения вступают в силу
немедленно, автоматически переформатируюя все строки в дизассемблируемом тексте.
Интерактивно это значение можно изменить, вызвав следующий диалог командой меню
«~Options\ ASCII strings options». «ASCII next line char» и есть то поле, о котором сейчас
идет речь.

Пример использования:
Message ("0x%X \n",GetCharPrm(INF_ASCII_BREAK));
0xА
.rdata:00407384 aRuntimeErrorPr db 'Runtime Error!',0Ah
.rdata:00407384
db 0Ah
.rdata:00407384
db 'Program: ',0
SetCharPrm(INF_ASCII_BREAK,0);
Message("0x%X \n",GetCharPrm(INF_ASCII_BREAK));
0x0
.rdata:00407384 aRuntimeErrorPr db 'Runtime Error!',0Ah,0Ah,'Program: ',0

389

INF_INDENT
Это однобайтовое поле содержит отступ, которым IDA предваряет все инструкции в
дизассемблируемом листинге.
INF_INDENT == 0x10
SetСharPrm(INF_INDENT,0x10);

INF_INDENT == 0
SetCharPrm(INF_INDENT,0x0);

По умолчанию отступ равен 0x10, однако, это значение можно изменять,
форматируя листинг по своему вкусу. Для этого необходимо воспользоваться функцией
SetCharPrm(INF_INDENT,
nn)
или
интерактивно
через
меню
«~Opions\Text
representation\Instructions indention»

INF_COMMENT
Это однобайтовое поле содержит отступ, которым IDA предваряет все
комментарии. По умолчанию равно 40. Может быть изменено по вкусу пользователя как
интерактивно («~Opions\Text representation\Comments indention»), так и с помощью функции
SetCharPrm(INF_COMMENT, nn)
INF_COMMENT == 40
SetCharPrm(INF_COMMENT,40);

INF_COMMENT == 0
SetCharPrm(INF_COMMENT,0);

390

INF_XREFNUM
Это однобайтовое поле хранит максимальное возможное число перекрестных
ссылок, которые IDA будет отображать в виде комментариев к инструкции. По умолчанию
равно двум. При этом, если остальные ссылки не отображаются, но IDA сигнализирует об
их наличие в виде двух точек, стоящих за последней отображаемой перекрестной ссылкой.
INF_XREFNUM == 2
SetCharPrm(INF_XREFNUM,2);

INF_XREFNUM == 4
SetCharPrm(INF_XREFNUM,4);

Может быть изменено как интерактивно («~Options\ Cross references\ Number of
xrefs to display»), так и с помощью функции SetCharPrm(INF_XREFNUM, xx)

INF_ENTAB
Это однобайтовое поле управляет генерацией выходных файлов. Если оно равно
единице, то IDA будет при форматировании использовать символы табуляции. В
противном случае все отступы будут выполнены пробелами.
Табуляция позволяет значительно, иногда в два и более раз уменьшить размер
файлов. Однако, некоторые редакторы и средства просмотра могут неправильно
интерпретировать (или же вовсе игнорировать) символы табуляции. В этих случаях
рекомендуется сбрасывать флаг INF_ENTAB (по умолчанию он установлен). Это можно
сделать как интерактивно (~Options\ Text representation\ Use tabulations in output) так и с
помощью следующего вызова:
SetCharPrm(INF_ENTAB,0);
INF_ENTAB == 1
SetCharPrm(INF_ENTAB,1);

INF_ENTAB == 0
SetCharPrm(INF_ENTAB,0);

seg000:22C0Å-------ÆcallÅ->sub_0_22DD

seg000:22C0Å-------ÆcallÅ->sub_0_22DD

391

INF_VOIDS
Это однобайтовое поле содержит флаг, указывающий IDA выводить после всех
непосредственных операндов «похожих» на смещение (т.е. попадающих в интервал
INF_LOW_OFF и INF_HIGH_OFF) комментарий «void», сигнализирующий пользователю,
что тип автоматически не был определен и должен быть уточнен вручную.
По умолчанию этот флаг сброшен, потому что IDA и без комментариев привлекает
внимание к операндам, выделяя их красным цветом. Однако, это невозможно осуществить
в выходных файлах (ASM и LST), поэтому в этом случае рекомендуется устанавливать
флаг INF_VOIDS. Это можно сделать как интерактивно (~Options\ Text representation\
Display 'void' marks), так и с помощью вызова функции SetCharPrm
INF_VOIDS == 0
SetCharPrm(INF_VOIDS,0);

INF_VOIDS == 1
SetCharPrm(INF_VOIDS,0);

INF_SHOWAUTO
Это однобайтовое поле содержит флаг, управляющий индикатором автоанализа.
По умолчанию он установлен. Если возникнет необходимость, то его можно отключить
«~Options\ Analysis options\ Indicator enabled» или вызовом функции SetCharPrm
INF_SHOWAUTO == 1
SetCharPrm(INF_SHOWAUTO,1);

INF_SHOWAUTO == 0
SetCharPrm(INF_SHOWAUTO,0);

Индикатор может принимать следующие значения:
Вид
AU:__idle__
AU:disable
FL:
PR:
AC:
LL:
L1:
L2:
L3:
FI:
??:
@:

Значение
Автоанализ завершен
Автоанализ выключен
Трассировка порядка исполнения
По указанному адресу была создана функция
Указатель на текущее положение анализатора
Был загружен файл сигнатур
Первый проход FLIRT
Второй проход FLIRT
Третий проход FLIRT
Заключительный проход автоанализа
Байт по указанному адресу помечен как unexplored
Индикатор различных действий

INF_AUTO

392

Это однобайтовое поле содержит флаг, управляющий автоанализом. То есть
автоматическим анализом программы. Именно такой режим работы установлен по
умолчанию.
Отключать его следуют только в тех случаях, когда результат работы
автоматического анализатора не устаивает или вызывает «подвисание» дизассемблера.
Такое часто случается с файлами, полученными с помощью ProcDump и подобных утилит.
Сделать это можно как интерактивно (~Options\ Background analysis\Analysis
enabled), так и вызовом функции SetCharPrm(INF_AUTO,0);

INF_BORDER
Это однобайтовое поле хранит флаг, управляющий вставкой линий, разделяющих
код и данные в дизассемблере. Значительно улучшает читабельность листинга, поэтому по
умолчанию IDA ведет себя именно так.
С другой стороны, дополнительные линии уменьшают число значащих строк,
умещающихся на дисплее, а так же приводит к излишнему перерасходу бумаги при выводе
дизассемблированного текста на принтер, поэтому в этих случаях эту опцию следует
отключить вызовом функции SetCharPrm(INF_BORDER,0) или интерактивно ~Options\ Text
representation \ Display borders between data/code.
INF_BORDER == 1
SetCharPrm(INF_BORDER,1);

INF_BORDER == 0
SetCharPrm(INF_BORDER,0);

INF_NULL
Это однобайтовое поле хранит флаг, управляющий генерацией пустых строк,
вставляемых дизассемблером в различных местах для улучшения читабельности
листинга. Однако в ряде случаев эту возможность следует отключать (например, при
выводе текста на печать). Для этого следует воспользоваться вызовом
SetCharPrm(INF_NULL,0) или сбросить флажок ~Options\ Text representation \ Display empty
lines
INF_NULL == 1
SetCharPrm(INF_NULL,1);

INF_NULL == 0
SetCharPrm(INF_NULL,0);

393

INF_SHOWPREF
Это однобайтовое поле хранит флаг, который управляет выводом префиксов в
дизассемблируемом листинге. Префикс – это адрес текущего байта.
Пример префикса:
.text:004024AC
pop edi
По умолчанию этот флаг установлен, и каждая линия предваряется префиксом.
Многоточечные структуры (например, массивы) в каждой строке содержат адрес своего
первого элемента.
Например:
.text:004023C0 dword_0_4023C0
.text:004023C0
.text:004023C0

dd 68AD123h, 468A0788h,0C102468Ah
dd 3C68302h, 8303C783h,0CC7208F9h
dd 3498D00h

При этом не зависимо от значения флага INF_SHOWPREF префиксы в
ассемблерный листинг (*.asm файл) не попадают.
Если по какой-то причине генерацию префиксов необходимо отключить, то это
можно сделать с помощью вызова функции SetCharPrm(INF_SHOWPREF,0) или
интерактивно ~ Options\ Text representation \ Line prefixes
INF_SHOWPREF == 1
SetCharPrm(INF_SHOWPREF,1);

INF_SHOWPREF == 0
SetCharPrm(INF_SHOWPREF,0);

INF_PREFSEG
Это однобайтовое поле содержит флаг, управляющий выводом имени сегмента в
префиксе стоки. По умолчанию флаг установлен и вместо полного адреса выводится имя
сегмента.
Если же возникнет необходимость видеть полный адрес, то этот флаг можно
сбросить. Сделать это можно либо интерактивно «~ Options \ Text representation \ Use
segment names», либо вызовом функции SetCharPrm(INF_PREFSEG,0)
При этом листинг будет выглядеть, как показано ниже:
INF_PREFSEG == 1
SetCharPrm(INF_PREFSEG,1);
.text:0040100F
xor
eax, eax

INF_PREFSEG == 0
SetCharPrm(INF_PREFSEG,0);
0000:0040100F
xor
eax, eax

INF_ASMTYPE
Это однобайтовое поле хранит номер, начиная с нуля, задающий целевой
ассемблер. Для PC всегда равно нулю, и указывает на «Generic for Intel 80x86»
Пример:
Message(“%x \n”,GetCharPrm(INF_ASMTYPE));
394

0
INF_BASEADDR
Это длинное поле хранит базовый параграф программы
Пример:
Message(“%x \n”,GetLongPrm(INF_BASEADDR));
1000
INF_XREFS
Это однобайтовое поле управляет представлением перекрестных ссылок в
дизассемблируемом листинге. Может быть представлено комбинацией следующего набора
битовых флагов:

SW_SEGXRF

(0x01)

Установка этого флага приводит к тому, что IDA будет указывать полный
адрес, включая сегмент, в перекрестных ссылках (по умолчанию).
Интерактивно этим значением можно управлять «~ Options \ Crossreference representation \ Display segments in xrefs»
SW_SEGXRF == 1
SetLongPrm(INF_XREF,SW_SEGXRF);
DATA XREF: .rdata:004070C0o
SW_XRFMRK

SW_SEGXRF == 0
SetLongPrm(INF_XREF,!SW_SEGXRF)
DATA XREF: 004070C0o

(0x02)

Установка этого флага приводит к тому, что IDA уточняет тип перекрестной
ссылки,– представляет ли источник собой код или данные.
Интерактивно этим значением можно управлять «~ Options \ Crossreference representation \ Display xref type mark»
SW_XRFMRK == 1
SetLongPrm(INF_XREF,SW_XRFMRK);
DATA XREF: .rdata:004070C0o

SW_XRFMRK == 0
SetLongPrm(INF_XREF,!SW_XRFMRK)
XREF: 004070C0o

395

SW_XRFFNC

(0x04)

Установка этого флага приводит к тому, что IDA выражает адрес ссылки
через смещение, относительно начла ближайшей функции.
Интерактивно этим значением можно управлять «~ Options \ Crossreference representation \ Display function offsets»
SW_XRFFNC == 1
SetLongPrm(INF_XREF,SW_XRFFNC);
CODE XREF: start+AFp

SW_XRFVAL

SW_XRFFNC == 0
SetLongPrm(INF_XREF,!SW_XRFFNC)
CODE XREF: 004010CFp

(0x08)

Установка этого флага приводит к тому, что IDA отображает значение
перекрестной ссылки в дизассемблируемом листинге. В противном же случае его
заменят три точки.
SW_XRFVAL == 1
SetLongPrm(INF_XREF,SW_XRFVAL);
CODE XREF: 004010CFp

SW_XRFVAL == 0
SetLongPrm(INF_XREF,!SW_XRFFVAL)

CODE XREF: ...

INF_BINPREF
Это короткое поле задает число байт, отображающих шестнадцатеричный оп-код
инструкции. По умолчанию равно нулю, то есть IDA дамп не отображает. Однако, в ряде
случаев потребность в нем все же возникает, кроме того, для кого-то этот может быть
вопрос удобства или привычки.
Тогда можно воспользоваться вызовом SetShortPrm(INF_BINPREF,0x10) или
изменить то же самое значение интерактивно «~ Options \ Text representation \ Number of
opcode bytes»
INF_BINPREF == 0
SetShortPrm(INF_BINPREF,0);
.text:00401000
.text:00401000
.text:00401005
.text:0040100A
.text:0040100F
.text:00401011
.text:00401011

sub_0_401000 proc near
push offset aHeloSailor
mov ecx, offset dword_0_408900
call ??6ostream@@QAEAAV0@PBD@Z
xor eax, eax
retn
sub_0_401000
endp

INF_BINPREF == 0x10
SetShortPrm(INF_BINPREF,0x10);
.text:00401000 sub_0_401000 proc near
.text:00401000
.text:00401005
.text:0040100A
.text:0040100F
.text:00401011
.text:00401011

68 40 80 40 00
B9 00 89 40 00
E8 72 2B 00 00
33 C0
C3
sub_0_401000

push offset aHeloSailor
mov ecx, offset dword_408900
call ostream@@QAEAAV0@PBD@Z
xor
eax, eax
кetn
endp

INF_CMTFLAG
Это короткое поле содержит набор флагов, манипулирующим выводом и
представлением комментариев в дизассемблируемом листинге.
SW_RPTCMT
Этот флаг управляет выводом повторяемых комментариев. По умолчанию установлен.
Если возникнет необходимость отключить генерацию повторяемых комментариев, то это
396

можно сделать как вызовом функцией SetShortPrm, так и интерактивно «~Options\ Text
representation \ Display repeatable comments»
SW_RPTCMT == 1

SW_RPTCMT == 0

SetShortPrm(INF_CMTFLAG,SW_RPTCMT);

SetShortPrm(INF_CMTFLAG,!SW_RPTCMT)
Jb short near ptr dword_0_4023AC

Jb short near ptr dword_4023AC ; repeatable comment

SW_ALLCMT
Этот флаг будучи установленным приводит к тому, что IDA комментирует
каждую стоку в дизассемблируемом тексте (обычно приводит расшифровку
мнемоник команд).
По умолчанию этот флаг сброшен, поскольку захламляет листинг
малоинтересной информацией. Однако, это может оказаться полезным для
начинающих пользователей, или любых других не знающих на память все
инструкции микропроцессора (особенно новых моделей). В этом случаи
комментарии IDA позволят сэкономить некоторое количество времени.
SW_ALLCMT == 1

SW_ALLCMT == 0

SetShortPrm(INF_CMTFLAG,SW_ALLCMT);

SetShortPrm(INF_CMTFLAG,!SW_ALLCMT)

Call sub_0_2E2 ; Call Procedure
jnb loc_0_2321 ; Jump if Not Below (CF=0)
nop
; No Operation

call
jnb
nop

sub_0_2E2
loc_0_2321

SW_NOCMT
Установка этого флага приводит к тому, что IDA вообще не будет
отображать комментариев, не зависимо от состояния остальных настоек. По
умолчанию флаг сброшен.
SW_LINNUM
Этот флаг будучи установленным приводит к тому, что IDA при наличии
необходимой отладочной информации в файле будет отображать номера строк
исходного текста программы.
SW_MICRO

INF_NAMETYPE
Это
короткое
поле
содержит
флаг,
управляющий
представлением
автогенерируемых имен (в терминологии IDA - dummy names). Эти имена автоматически
присваиваются всем созданным меткам и процедурам.
Флаг
NM_REL_OFF

значение
0

NM_PTR_OFF

1

NM_NAM_OFF

2

пояснения
Относительная база сегмента и полное смещение
loc_0_1234
Базовый адрес сегмента и смещение
loc_1000_1234
Имя сегмента и смещение (по умолчанию)
397

NM_REL_EA

3

NM_PTR_EA

4

NM_NAM_EA

5

NM_EA

6

NM_EA4

7

NM_EA8

8

NM_SHORT

9

NM_SERIAL

10

loc_dseg_1234
Сегмент, относительный базовому адресу и полный адрес
loc_0_11234
Базовый адрес сегмента и полный адрес
loc_1000_11234
Имя сегмента и полный адрес
loc_dseg_11234
Полный адрес (без нуля слева)
loc_12
Полный адрес (не менее четырех знаков)
loc_0012
Полный адрес (не менее восьми знаков)
loc_00000012
Имя сегмента и смещение без спецификатора типа
dseg_1234
Перечисленные имена (1,2,3...)
loc_1

INF_SHOWBADS
Это однобайтовое поле, будучи установленным, приводит к тому, что IDA будет
оставлять в виде дампа все инструкции, которые могут быть неправильно
ассемблированы.
Например,
в
исследуемой
программе
могут
встретиться
недокументированные команды процессора (подробнее с ними можно ознакомиться,
например, на сайте www.x86.org)
Разумеется, что распространенные ассемблеры выдадут ошибку и прекратят
работу. Однако, это не худшая ситуация. Множество команд 80x86 процессоров могут быть
ассемблированы по-разному. Например, ADD bx, 0x10 может быть представлена как
опкодом 81 C3 01 00, так и 83 C3 10
Разница здесь в том, что последняя команда добавляет к BX байт, автоматически
расширяя его до слова с учетом знака. Следовательно, возникает неоднозначность, –
часто приводящая к неработоспособности программы. Даже если не использовался
самомодифицирующийся код, изменение длины инструкции «потянет» за собой все метки
и абсолютные адреса в программе. Впрочем, при условии правильного преобразования
типов непосредственных операндов это не нарушит работоспособность программы.
Поэтому по умолчанию это опция отключена.
Если же в ней возникнет необходимость, то нужно воспользоваться функцией
SetCharPrm(INF_SHOWBADS,1) или интерактивно ~Options \ Text representation \ Display
bad instructions marks.
INF_SHOWBADS == 1
SetCharPrm(INF_SHOWBADS,1)
seg000:0220

db 0E9h,0,0 ; jmp

INF_SHOWBADS == 0
SetCharPrm(INF_SHOWBADS,0)
$+3

seg000:0220

jmp

$+3

ЗАМЕЧАНИЕ: все же часть инструкций IDA не помечает, как , хотя их
отказываются ассемблировать распространенные ассемблеры.
Например, команду JMP FAR segment:offset IDA, в отличие от большинства
ассемблеров, не считает не правильной. В результате чего приходится вручную
менять ее в листинге на последовательность:
DB 0Eah
DW offset
398

DW segment
INF_PREFFLAG
Это однобайтовое поле хранит флаг, задающий формат вывода префикса на
экран. Имеет смысл только когда вывод префиксов разрешен, (то есть флаг
INF_SHOWPREF установлен).
Флаг представляет собой возможные комбинации из трех битов. При этом бит
PREF_FNCOFF имеет приоритет над PREF_SEGADR. То есть, комбинация
(PREF_FNCOFF | PREF_SEGADR) равносильна PREF_FNCOFF. Однако, это не
документировано и возможно в последующих версиях IDA будет вести себя иначе.
Интерактивно это можно изменить с помощью следующих опций диалога настойки:
Флаг
PREF_SEGADR
PREF_FNCOFF
PREF_STACK

Options \ Text representation \ Segment addresses
Options \ Text representation \ Function offsets
Options \ Text representation \ Display stack pointer
значение
0x01
0x02
0x04

Пояснение
Представлять префикс в виде сегментного адреса
Представлять префикс в виде смещения внутри функции
Завершать префикс указателем стека

Пример
seg000:2190
Sub_0_22DD+1B
Seg000:2190
Sub_0_22DD+1B

008
008

INF_PACKBASE
Это однобайтовое поле хранит тип упаковки базы IDA, предлагаемый по
умолчанию при выходе из дизассемблера.

INF_PACKBASE == 0
SetCharPrm(INF_PACKBASE,0);

INF_PACKBASE == 1
SetCharPrm(INF_PACKBASE,1);

INF_PACKBASE == 2
SetCharPrm(INF_PACKBASE,2);

ЗАМЕЧАНИЕ: оба типа упаковки обладают слабым сжатием, поэтому при
возникновении потребности в дисковом пространстве или передачи базы по
399

коммуникационным каналам рекомендуется ее упаковать с помощью любого
подходящего архиватора (например, zip, arj) при этом выигрыш может быть более, чем
десятикратный.

INF_ASCIIFLAGS
Это однобайтовое беззнаковое поле задает стиль автогенерируемых строковых
имен (или вообще запрещает создание таковых)
Представляет собой комбинацию следующих битов.
ASCF_GEN
Если этот флаг установлен, то IDA автоматически генерирует имена для
всех ASCII строк, состоящее из читабельных символов этой строки.
Так, например, встретив строку «Hello, Word» IDA создаст имя
“aHello_Word”. В противном случае что-то наподобие «asc_0_206». Разумеется, что
генерация осмысленных имен улучшает читабельность листинга и ускоряет анализ.
По умолчанию IDA ведет себя именно так. Если по какой-то причине
возникнет желание отключить эту возможность, то можно воспользоваться
функцией SetCharPrm(INF_ASCIIFLAG,0) или интерактивно через «~ Options \ ASCII
string options \ Generate names»
SetCharPrm(INF_ASCIIFLAG,1);

SetCharPrm(INF_ASCIIFLAG,0);

seg000:2192 a123456789abcde db '123456789ABCDEFG',0

seg000:2192

db '123456789ABCDEFG',0

ASCF_AUTO
Этот флаг, будучи установленным, приводит к тому, что IDA будет помечать
все создаваемые имена, как ‘autogenerated’.
Это приведет к отображению их другим цветом и автоматическому
удалению при преобразовании имени к ‘unexplored’.
По умолчанию флаг установлен. Если возникнет необходимость его
изменить,
то
это
можно
сделать
с
помощью
функции
SetCharPrm(INF_ASCIIFLAGS,!ASCF_AUTO) или интерактивно «~Options \ ASCII
string options \ Mark as autogenerated»
ASCF_AUTO == 1
seg000:2192
seg000:2193
seg000:2194
seg000:2195
seg000:2196
seg000:2197
seg000:2198
seg000:2199
seg000:219A
seg000:219B
seg000:219C
seg000:219D
seg000:219E
seg000:219F
seg000:21A0
seg000:21A1
seg000:21A2

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

ASCF_AUTO == 0
seg000:2192
31h ; 1
seg000:2193
32h ; 2
seg000:2194
33h ; 3
seg000:2195
34h ; 4
seg000:2196
35h ; 5
seg000:2197
36h ; 6
seg000:2198
37h ; 7
seg000:2199
38h ; 8
seg000:219A
39h ; 9
seg000:219B
41h ; A
seg000:219C
42h ; B
seg000:219D
43h ; C
seg000:219E
44h ; D
seg000:219F
45h ; E
seg000:21A0
46h ; F
seg000:21A1
47h ; G
seg000:21A2
0 ;
Создается имя

seg000:2192 a123456789abcde db '123456789ABCDEFG',0

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

31h
32h
33h
34h
35h
36h
37h
38h
39h
41h
42h
43h
44h
45h
46h
47h
0

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G

seg000:2192 a123456789abcde db '123456789ABCDEFG',0

400

seg000:2192
seg000:2193
seg000:2194
seg000:2195
seg000:2196
seg000:2197
seg000:2198
seg000:2199
seg000:219A
seg000:219B
seg000:219C
seg000:219D
seg000:219E
seg000:219F
seg000:21A0
seg000:21A1
seg000:21A2

Преобразуем регион в unexplored
seg000:2192 a123456789abcde
db 31h ; 1
seg000:2193
db 32h ; 2
seg000:2194
db 33h ; 3
seg000:2195
db 34h ; 4
seg000:2196
db 35h ; 5
seg000:2197
db 36h ; 6
seg000:2198
db 37h ; 7
seg000:2199
db 38h ; 8
seg000:219A
db 39h ; 9
seg000:219B
db 41h ; A
seg000:219C
db 42h ; B
seg000:219D
db 43h ; C
seg000:219E
db 44h ; D
seg000:219F
db 45h ; E
seg000:21A0
db 46h ; F
seg000:21A1
db 47h ; G
seg000:21A2
db
0 ;

db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db
db

31h
32h
33h
34h
35h
36h
37h
38h
39h
41h
42h
43h
44h
45h
46h
47h
0

;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;
;

1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G

ASCF_SERIAL
Если этот флаг будет установлен, то IDA будет генерировать следующие (в
терминологии IDA последовательные) имена ‘pref0’,’pref1’,’pref2’… где ‘pref’ –
префикс имени, который для строк по умолчанию равен ‘a’
По умолчанию этот флаг сброшен, но если возникнет необходимость, то его
можно установить с помощью функции SetCharPrm( или же интерактивно «~Options
\ ASCII string options\ Generate serial names»
ASCF_SERIAL == 1

ASCF_SERIAL == 0

SetCharPrm(INF_ASCIIFLAGS, ASCF_SERIAL);

SetCharPrm(INF_ASCIIFLAGS, !ASCF_SERIAL);

seg000:2192 a0 db '123456789ABCDEFG',0

seg000:2192 a123456789abcde db '123456789ABCDEFG',0

INF_LISTNAMES
Это однобайтовое беззнаковое поле содержит атрибуты имен, автоматически
включаемых в Список Имен (Name List).

LN_NORMAL
LN_PUBLIC
LN_AUTO
LN_WEAK

0x01
0x02
0x04
0x08

Имя без атрибутов (по умолчанию)
Имя с атрибутом public
Автогенерируемое имя
Имя с атрибутом weak

401

INF_START_SS
Это длинное поле хранит значение регистра SS при запуске программы. Для того,
что бы его получить IDA прибегает к эмуляции загрузки и действует в соответствии с
исследуемым типом файла и декларированным правилам загрузки его операционной
системой.
Однако, это не гарантирует, что полученное значение будет тождественно
действительному. Впрочем, такая точность на практике и не трубятся. В крайнем случае
можно принудительно указать требуемый базовый адрес загрузки или изменить
непосредственно само значение INF_START_SS.
Пример:
Message(“0x%x \n”,GetLongPrm(INF_START_SS));
0x1000
SetLongPrm(INF_START_SS,0);
Message(“0x%x \n”,GetLongPrm(INF_START_SS));
0
INF_START_CS
Это длинное поле хранит значение регистра CS при запуске программы. Для того,
что бы его получить IDA прибегает к эмуляции загрузки и действует в соответствии с
исследуемым типом файла и декларированным правилам загрузки его операционной
системой.
Пример:
Message(“0x%x \n”,GetLongPrm(INF_START_CS));
0x1000

INF_MAIN
В файле определений IDC.IDC сообщается, что это длинное поле содержит адрес
процедуры main(), однако, при попытке его чтения всегда возвращается ошибка BADADDR.
Например:
Message(“0x%X \n”,GetLongPem(INF_MAIN));
0xFFFFFFFF

INF_SHORT_DN
Это длинное поле хранит короткую форму вывода «замангленных» имен.
Назначение отдельных битов не описано в файле ‘idc.idc’, в котором содержится ссылка на
‘demangle.hpp’, входящий в состав IDA SDK.
ФЛАГ

БИТ

MNG_DEFNEAR

0x00000000

MNG_DEFFAR

0x00000002

MNG_DEFHUGE

0x00000004

MNG_DEFNONE

0x00000006

MNG_NODEFINIT

0x00000008

MNG_NOUNDERSCORE

0x00000010

MNG_NOTYPE

0x00000020

MNG_NORETTYPE

0x00000040

MNG_NOBASEDT

0x00000080

ЗНАЧЕНИЕ
Подавлять в именах ключевое слово near
Подавлять в именах ключевое слово far
Подавлять в именах ключевое слово huge
Выводить все
Подавлять вывод всёго, кроме основного имени
Подавлять вывод подчерков для __ccall, __pascal...
Подавлять вывод типа параметров
Подавлять вывод типа взращаемого функцией значения
Подавлять вывод базовых типов
402

MNG_NOCALLC

0x00000100

MNG_NOPOSTFC

0x00000200

MNG_NOSCTYP

0x00000400

MNG_NOTHROW

0x00000800

MNG_NOSTVIR

0x00001000

MNG_NOECSU

0x00002000

MNG_NOCSVOL

0x00004000

MNG_NOCLOSUR

0x00008000

MNG_SHORT_S

0x00010000

MNG_SHORT_U

0x00020000

MNG_ZPT_SPACE

0x00040000

MNG_IGN_ANYWAY

0x00080000

MNG_IGN_JMP

0x00100000

MNG_MOVE_JMP

0x00200000

Подавлять вывод слов __pascal\__ccall и подобных
Подавлять вывод постфиксного const
Подавлять вывод ключевых слов public\private\protected
Подавлять вывод описания throw
Подавлять вывод ключевых слов static и virtual
Подавлять вывод ключевых слов class\struct\union\enum
Всюду подавлять вывод ключевых слов const и volatile
Подавлять вывод __closure (для компиляторов Borlnand)
Выводить signed (int) в формате s(int)
Выводить unsigned (int) в формате u(int)
Выводить пробел после запятой в аргументах функций
Игнорировать постфикс '_nn' в конце строки
Игнорировать префикс 'j_' в начале строки
Сохранять префикс 'j_' в размангленных именах

Подробнее об этом можно найти в описании функции Demangle.
INF_LONG_DN
Это длинное поле хранит полный формат вывода «замангленных» имен.
Представляет собой комбинацию флагов, назначение которых описано выше.

INF_DATATYPES
Это длинное поле хранит разрешенные к использованию типы данных, то есть
такие, что будут поочередно перебираться, скажем, при нажатии ‘D’.
Файл IDC.IDC не содержит расшифровки отдельных битов этого поля, но в
интерактивно диалоге настойки (~Options \ Setup date types ) типы данных перечислены в
порядке следования флагов в этом поле!

БИТ
0x1
0x2
0x4
0x8
0x10
0x20

ЗНАЧЕНИЕ
Байт
Слово
Двойное слово
Float
Четвертное слово
Double
403

0x40
0x80

Tbyte
Упакованное real
Пример использования:
Message("%b \n", GetLongPrm(INF_DATATYPES));
111

INF_STRTYPE
Это длинное поле хранит текущий стиль ASCII – строк. Первый байт представляет
собой один из следующих флагов.
ФЛАГ
0
ASCSTR_PASCAL
ASCSTR_LEN2

ЗНАЧЕНИЕ
Строка не претворена полем длины
Стиль Pascal – строка предваряется байтом длины
.data:00408040 aHeloSailor db 0Сh, 'Helo,Sailor!'
Стиль WinPascal – строка предваряется словом длины
.data:00408040 aHeloSailor dw 0Сh, db 'Helo,Sailor!'

ASCSTR_UNICODE

Стиль UNICODE
`H`,0,`e`,0,`l`,0,`o`,0,`,`,0,`S`,0,`a`,0,`i`,0,`l`,0,`o`,0,`r`,0,`!`

ASCSTR_LEN4

Стиль Delphi – 4 байта на длину
.data:00408040 aHeloSailor dw 0Сh, dw 0, db 'Helo,Sailor!'

Если первый байт не равен нулю, то, значит, впереди строки находится поле,
определяющие его длину. Иначе используется символ конца строки. Его определяют
второй и третий байт поля INF_STRTYPE.
Если второй символ будет равен ‘\0’, то он будет проигнорирован. Поэтому если
необходимо задать два типа завершающих символов (например, ноль и ‘$’), то ‘\0’ следует
указывать первым из них.
Пример:
Message(“%x \n”,GetLongPrm(INF_STRTTYPE));
0
SetLongPrm(INF_STRTTYPE,’$’>>0x10);

INF_AF2
Это длинное беззнаковое поле хранит дополнительные флаги анализатора.
ФЛАГ
AF2_JUMPTBL
AF2_DODATA
AF2_HFLIRT

БИТ
0x1
0x2
0x4

ЗНАЧЕНИЕ
Выявлять и создавать таблицы переходов
Сворачивать сегмент данных в последнем проходе
Автоматически скрывать стандартные библиотечные функции
(этот флаг не описан в файле IDC.IDC)

404

success SetPrcsr

(char processor);

Функция позволяет изменить тип процессора, отличный от выбранного при загрузке
дизассемблируемого файла.
Однако возможности динамической смены процессора
несколько ограничены. Допустим выбор лишь в границах текущей линейки (серии)
микропроцессоров.
Это объясняется тем, что на стадии загрузки происходят неустранимые средствами
последующих уровней изменения и настройки на конкретный процессор. (Большей частью
это относится к невозможности
IDA перезагружать микропроцессорные модули,
отвечающие за дизассемблирование. Такой модуль может быть загружен один только раз
и на весь сеанс работы окажутся доступными лишь поддерживаемые им типы
микропроцессоров)
Для линейки Intel в силу их полной обратной совместимости, динамический выбор
модели процессора некритичен, поскольку можно выбрать самого позднего из доступных
представителей, и можно быть уверенным, что все команды будут дизассемблированы.
Аналогичный результат можно получить, выбрав тип «meta pc», включающий в себя
команды всех моделей микропроцессоров.
Рассмотрим работу этой функции на следующей примере. Загрузим для
дизассемблирования бинарный или com-файл (для которого IDA по умолчанию выбирает
8086 микропроцессор), но содержащий инструкции более поздних моделей.
Разумеется, они окажутся не дизассемблированными и результат работы IDA
может выглядеть, например, так:
seg000:02E9
seg000:02EC
seg000:02ED
seg000:02EE
seg000:02EF

mov
db 0C1h
db 0E0h
db
6
cmp

ax, ds:413h
; ; р
;
ax, 0A000h

mov
shl
cmp

ax, ds:413h
ax, 6
ax, 0A000h

SetPrcsr (“metapc”);
seg000:02E9
seg000:02EC
seg000:02EF

Смена типа процессора, привела к тому, что IDA заново проанализировала
изучаемый файл и автоматически дизассемблировала интересующие нас инструкции.
Разумеется, что обратная смена на 8086 модель приведет к тому, что листинг будет
приведен к первоначальному виду
При этом IDA может так же изменять целевой ассемблер, поэтому рекомендуется
на всякий случай удостоверится в приемлемости ее выбора, вызвав диалог «~Options \
Target Assembler» Впрочем, для линии IBM PC он имеется в единственном числе –
«Generic for Intel 80x86» и беспокоиться нет никакой необходимости.

405

Операнд processor может принимать следующие значения, перечисленные ниже в
таблице. К регистру функция не чувствительна, поэтому ‘metapc’ и ‘MetaPC’ задают один и
то же тип процессора.
Отбивка слева указывает, что объединяемые ее микропроцессоры могут
динамически выбираться во время дизассемблирования файла.
Операнд
8086
80286r
80286p
80386r
80386p
80486r
80486p
80586r
80586p
80686p
k62
p2
p3
athlon
metapc
8085
z80
z8
860xr
860xp
8051
80196
m6502
m65c02
64180
pdp11
68000
68010
68020
68030
68040
68330
68882
68851
68020EX
6800
6801
6803
6301
6303
6805
6808
6809

Процессор
Intel 8086
Intel 80286 real mode
Intel 80286 protected mode
Intel 80386 real mode
Intel 80386 protected mode
Intel 80486 real mode
Intel 80486 protected mode
Intel Pentium & MMX real mode
Intel Pentium & MMX prot mode
Intel Pentium Pro & MMX
AMD K6-2 with 3DNow!
Intel Pentium II
Intel Pentium III
AMD K7
Дизассемблировать все инструкции IBM PC
Intel 8085
Zilog 80
Zilog 8
Intel 860 XR
Intel 860 XP
Intel 8051
Intel 80196
6502
65c02
Hitachi HD64180
DEC PDP/11
Motorola MC68000
Motorola MC68010
Motorola MC68020
Motorola MC68030
Motorola MC68040
Motorola CPU32 (68330)
Motorola MC68020 with MC68882
Motorola MC68020 with MC68851
Motorola MC68020 with both
Motorola MC6800
Motorola MC6801
Motorola MC6803
Hitachi HD 6301
Hitachi HD 6303
Motorola MC6805
Motorola MC6808
Motorola MC6809

Серия

Линейка IBM PC

Линейка Zilog 80
Линейка Zilog 8
Линейка Intel 860
Линейка Intel 51
Линейка Intel 80196
Линейка 65xx line
Линейка PDP line

Линейка Motorola 680x0

Линейка Motorola 8bit

406

6811
java
ppc
arm710a
arm
armb
tms320c2
tms320c5
tms320c6
sh3
sh3b
sh4
sh4b
avr
mipsl
mipsb
mipsr
h8300
h8300a
h8s300
h8s300a
pic16cxx

Motorola MC6811
java
PowerPC
ARM 7xx серия
То же самое, что и arm710a
ARM big endian
TMS320C2x серия
TMS320C5x серия
TMS320C6x серия
Hitachi SH3 (little endian)
Hitachi SH3 (big endian)
Hitachi SH4 (little endian)
Hitachi SH4 (big endian)
ATMEL AVR
MIPS little endian
MIPS big endian
MIPS & RSP
H8/300x in normal mode
H8/300x in advanced mode
H8S in normal mode
H8S in advanced mode
Michrochip PIC

Серия Java
Линейка PowerPC
Линейка ARM
Серия TMS 16bit адресации
Линейка TMS VLIW l
Hitachi SH line
Серия ATMEL
Линейка MIPS: R2000, 3000,
R4000,R4200, R4300, 4400,
R4600,R8000, R10000
Hitachi H8 line
Серия микроконтроллеров

Все вышесказанное остается верным и для интерактивного выбора типа
процессора посредством команды меню «~Options \ Processor type»

При попытке смены типа процессора IDA может выдать ошибку, например:«The
processor type "metapc" isn't included in the standard version of IDA Pro. Please check our web
site for information about ordering additional processor modules»
Это обозначает, что необходимый для дизассемблирования модуль отсутствует
или не найден. Его можно получить, обратившись к вашему поставщику IDA или на сайте
разработчика IDA (www.idapro.com)
Поскольку для DOS, OS\2 и Windows версий дизассемблера используются разные
модули, то вполне возможно, что один из них отсутствует или поврежден, когда остальные
вполне работоспособны.
Расшифровка расширений приводится ниже в таблице.
расш
d32
dll

Платформа
Процессорный модуль для OS\2 версии дизассемблера
Процессорный модуль для MS-DOS версии дизассемблера
407

w32

Процессорный модуль для Windows 95\Windows NT версий дизассемблера

Четвертая версия IDA в полной поставке, включает в себя следующие файлы:
Файл
ARM
AVR
H8
I196
I51
I860
JAVA
M65
MC8
MC68
PC
PDP11
PIC
Z8
Z80

Семейство процессоров
Семейство ARM (серия ARM 7xx)
Линейка чипов ATMEL AVR
Линейка чипов Hitachi H8 (H8/300x и H8S серии)
Микропроцессор Intel 80196
Микропроцессор Intel 8051
Микропроцессор Intel 860 XR
Java Virtual Machine
Микропроцессоры серии 65xx
Семейство 8-разрядных микропроцессоров фирмы Motorola (MC6800,
MC6801, MC6803, MC6805, MC6808, MC6809, MC6811)
Семейство 8-разрядных микропроцессоров фирмы Hitachi (HD 6301,
HD 6303)
Микропроцессоры серии Motorola 680x0
Микропроцессоры линейки IBM PC
DEC PDP-11
Микроконтроллеры Microchip серий PIC16C5x PIC16Cxx PIC17Cxx
Микропроцессорылинейки Zilog 8
Микропроцессоры линейки Zilog 80

Сравнивая эту таблицу с приведенным выше перечнем поддерживаемых IDA
процессоров, можно заметить, что часть из них в поставку не входит.
С другой стороны, если вам не нужно дизассемблировать ничего, кроме программ
для IBM PC, то все остальные модули можно удалить, освободив немного дискового
пространства.
Если в заголовке загружаемого файла отсутствует информация о типе процессора,
то IDA выбирает его, руководствуясь расширением.
Соответствия расширений и типов микропроцессора перечислены в фале IDA.CFG
в секции DEFAULT_PROCESSOR:
расширение
"com"
"exe"
"dll"
"drv"
"sys"
"bin"
"ovl"
"ovr"
"ov?"
"nlm"
"lan"
"dsk"
"obj"
"prc"
"axf"
"h68"
"i51"
"sav"

Тип процессора
"8086"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"metapc"
"68000" (PalmPilot программы)
"arm710a"
"68000" (MC68000 для *.H68 файлов)
"8051" (i8051 для *.I51 файлов)
"pdp11" (PDP-11 для *.SAV файлов)
408

"rom"
"class"
"cls"
"s19"
"*"

long

Batch

"z80"
(для *.ROM файлов)
"java"
"java"
"6811"
"metapc"

(long batch);

Функция позволяет устанавливать (или снимать) пакетный режим работы. При
этом IDA не выводит никаких диалоговых окон и не выдает предупреждений. Это может
быть полезным при автономной работе и во время выполнения скриптов.
В версии IDA 4.0 присутствуют некоторые ошибки в реализации пакетного режима.
Так, например, попытка вызова калькулятора вызовет зависание IDA, поэтому
пользоваться им следует с осторожностью и всегда обращать внимание, что бы скрипты,
использующие его, возвращались в обычный режим при возрате в IDA.
==batch
0
1

Режим
Обычный режим
Пакетный режим

Функция возвращает прежний режим работы. Следующий скрипт определяет его
текущее значение без изменений режима работы.
auto a,s;
s="нормальный";
a=Batch(0);
Batch(a);
if (a) s="пакетый";
Message("Режимм работы %s \n",s);
Режимм работы нормальный
char GetIdaDirectory ();
Функция возвращает полный путь к директории, в которой расположена IDA, без
завершающего слеша в конце.
Например:
Message (“%s \n”, GetIdaDirectory ());
D:\DEBUG\IDA384
Return

Пояснения
полный путь к директории, в которой расположена IDA

Точнее это путь к исполняемому файлу IDA.EXE (idaw.exe\ idax.exe). Расположение
остальных файлов зависит от версии.
Так, например, IDA 3.6 хранила все скрипты в базовом каталоге, а последние
версии в каталоге IDC.
Эти различия необходимо учитывать при поиске требуемых файлов. Желательно
предусмотреть возможность диалога с пользователем и «ручным» указанием путей, а не
полагаться на то, что требуемый файл окажется на месте.
409

char GetInputFile ();
Возвращает имя дизассемблируемого файла вместе с расширением. Версия под
win32 поддерживает длинные имена файлов, включая пробелы. Дозагрузка фалов не
влияет на результат работы этой функции.
Пример использования:
Message (“%s \n “,
GetInputFile ()
);
My File.exe
Return

Пояснения
Имя дизассемблируемого файла вместе с расширением

СТРОКИ
К сожалению, встроенный язык IDA не поддерживает даже основных конструкций
Си для работы со стоками. Так, например, невозможно получить посимвольный доступ к
стоке или указатель на нее же.
Зато IDA поддерживает инициализацию и контекцию, (слияние) строк, что
демонстрирует следующий пример:
auto a,b;
a="Hello";
b="IDA! \n";
a=a+","+b;
Message("%s \n",a);
Hello,IDA!
Таким образом, строки в IDA представляют собой закрытые объекты, доступные
лишь посредством набора, манипулирующих с ними функций. Их всего три.
Это определение длины строки (strlen), взятие подстроки (substr) и поиск подстроки
(srtsrt). Возможность модификации строки отсутствует, и в том случае, когда возникает
потребность изменить хотя бы один символ, приходится перестраивать всю строку
целиком.
Для этого может пригодиться две следующие функции, которые рекомендуется
включить в idc.idc, что бы сделать их доступными для всех пользовательских скриптов.
static setstr(str, pos, ch)
{
auto s0;
410

s0=substr(str,0,pos);
s0=s0+ch;
s0=s0+substr(str,pos+strlen(ch), strlen(str));
return s0;
}
static setstr(str, pos, ch)
{
auto s0;
s0=substr(str,0,pos);
s0=s0+ch;
s0=s0+substr(str,pos, strlen(str));
return s0;
}
Первая из них позволяет в строке str заместить любую подстроку ch с позиции pos,
а вторая осуществляет вставку с «раздвижкой»
Примеры использования даны ниже, но для повседневного использования обе
функции рекомендуется дополнить проверками корректности передаваемых параметров.
Message("%s \n",
setstr("Hello World!",5,",")
);
Hello, World!
Message("%s \n",
insstr("Hello, World!",7,"my ")
);
Hello, my World!
Таким образом, достаточно лишь одной функции взятия подстроки, что бы
обеспечить неограниченный доступ к объекту строка. Для этого достаточно лишь
скопировать ее в другой объект, доступный нам для чтения и записи, например,
виртуальную память, массив и так далее. К сожалению, это медленно работает, но
зачастую является возможным единственным выходом.
Другую группу строковых операций представляют функции всевозможного
преобразования форматов. Например, перевод символьной строки в двоичное
(шестнадцатеричное) число и наоборот. Однако, поскольку IDA поддерживает аналог
функции sprintf, то чаще всего пользуются одним единственным вызовом form. Это гораздо
удобнее, чем хранить в голове имена множества функций.

char

substr

(char str, long x1,long x2);

Функция осуществляет взятие подстроки. IDA не поддерживает стандартную для
Си конструкцию str[a], поэтому для любого посимвольного разбора строки приходится
вызывать 'substr'
Функция принимает следующие операнды:
операнд

пояснение
411

x1
x2
Return
char

индекс начала подстроки
индекс конца подстроки
если x2 == -1, то возвращается весь остаток строки целиком
Пояснение
Подстрока

В версии 3.84 и более ранних, эта функция не имела никакого контроля над
границами индексов, и если оказывалось, что x2 < x1, то Windows закрывала приложение
IDA, как совершившее недопустимую операцию. Так же наблюдалась непредсказуемая
работа приложения при выходе индексов за границы строки.
В версии 4.0 этот недостаток уже устранен. В случае x2 < x1 функция возвращает,
пустую строку, а при нарушении границ доступа (начальный индекс за границами строки)
хоть и выводит диалоговое окно, сообщающие о нарушении границ доступа, но не выходит
из дизассемблера, позволяя продолжить работу.
(Правда при этом попытка исполнения любого скрипта заканчивается следующей
ошибкой, вплоть до перезапуска IDA)

Если конечный индекс лежит за пределами строки, то IDA просто возвращает
остаток строки и аварийной ситуации не возникает.
Пример использования этой функции для построения
простейшего
синтаксического анализатора:
auto a,temp,c;
a="key -Hello";
for (temp=0;temp> 4;
offset = ea – (ea >> 4).
Например:
Message(“%s \n”,
atoa(0x18)
);
1:00000008

char ltoa (long n,long radix);
Функция преобразовывает длинное целое в символьное с произвольной системой
исчисления.
Функция принимает следующие операнды:
операнд
n

назначение
Задает операнд
==n Операнд
417

radix

Return

0 первый слева операнд
1 Втрой, третий и остальные
-1 все операнды
требуемая система исчисления.
ЗАМЕЧАНИЕ: В контекстной помощи IDA сообщается, что
'radix' может принимать значения 2, 8, 10, 16. Однако это
стандартная Си-функция, и она может принимать
и другие
значения, например, 3 или 11. Точнее, все кроме 0 и 1, а так же
не более 24, при которых, независимо от аргумента, функция
возвращает пустую строку.
==return пояснения
!=”” Сегментный адрес в строковом представлении
==”” Ошибка

Пример:
auto a;
for (a=0;a