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

книги К.Касперски"Образ мышления -дизассемблер IDA"

Хочется отметить,Граждане!! пользуйтесь лицензионной IDA PRO ,эта одна из немногих прог

которая действительно стоит потраченных средств!!! И поверьте- это не реклама.

Сканирование осуществил JIN (jin@pisem.net)

Содеятели

Midgard13

Nitrogen [TSRh] ( http://www.kickme.to/tsrh)


IDA - 0002.htm

Крис Касперски
1

Образ мышления —

дизассемблер
IDA

Описание функций встроенного
языка IDA Pro

Первая книга в России по знаменитому программному продукту—популярнейшему диз-
ассемблеру IDA Pro. Книга представляет собой подробный справочник по функциям встро-
енного языка, интерфейсу и архитектуре дизассемблера IDA Pro 4.01 с уточнением особен-
ностей младших версий.
Показывает приемы эффективного использования IDA Pro для исследования зашифро-
ванного кода,Pi-кода, самомодифицирующегося и защищенного кода. Содержит множество
практических примеров использования функций IDA.
Книга затрагивает вопросы внутреннего устройства IDA Pro, дана информация об ошиб-
ках реализации, неточностях, без понимания которых полноценная работа с дизассембле-
ром невозможна.
Ориентирована на системных программистов средней и высокой квалификации, в со-
вершенстве владеющих языком ассемблера микропроцессоров серии Intel 80x86 и работаю-
щих с операционными системами фирмы Microsoft, разработчиков защит, специалистов по
информационной безопасности, специалистов по PIC-контроллерам, по ремонту жестких
дисков.


IDA - 0003.htm

Введение. Об этой книге
Введение. Об этой книге
Цель этой книги — частично компенсировать информационный голод, окутывающий один из популярнейших дизассемблеров современности — IDA Pro. Сведения, содержащиеся в документации, поставляемой вместе с этим продуктом, весьма отрывочные и безнадежно устаревшие.
Самостоятельное же освоение IDA Pro требует значительных усилий, длительного времени и постоянных консультаций с ее разработчиками. Появлению этой книги предшествовал большой объем работы, проделанной автором. Первоначально, когда замышлялось написать книгу, обобщающую достижения современного реинженеринга, планировалось скромное издание страниц максимум в триста, но в течение работы над проектом выяснилось: описание одних лишь функций встроенного языка IDA Pro значительно превышает этот объем. Поэтому весь материал пришлось разбить на три тома — "Описание функций встроенного языка IDA Pro", "Приемы эффективной работы с IDA Pro" и "Технологии дизассемблирования".
Функции ядра IDA находят применение не только во встроенном языке, — сама IDA активно использует их для дизассемблирования, а большинство пунктов меню эквивалентны соответствующим функциям ядра. Поэтому любую операцию можно выполнить не только последовательным нажатием "горячих" клавиш, но и совокупностью команд встроенного языка.
Эта книга также рассказывает и об архитектуре, затрагивая вопросы внутреннего устройства IDA Pro, без понимания которых полноценная работа с дизассемблером невозможна.


IDA - 0004.htm

Версии IDA Pro
Версии IDAPro
Дизассемблер 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 ProStandard и IDA Pro Advanced заключается в количестве поддерживаемых процессоров, полный перечень которых можно найти в прилагаемой к IDA документации. Демонстрационный пакет представляет собой усеченный вариант полнофункционального продукта и обладает рядом существенных ограничений: никакие процессоры кроме семейства Intel 80x86 и типы файлов за исключением win32 РЕ не поддерживаются; в поставку входят сигнатуры всего лишь двух компиляторов — Microsoft Visual С++ 6.0 и Borland C++ Builder; функция сохранения результатов работы заблокирована, а максимальное время одного сеанса работы с дизассемблером ограничено.
В каждый пакет поставки (за исключением демонстрационного) входят две ипостаси — одна графическая под Windows-32 (в дальнейшем обозначаемая как IDAG) и три консольных для MS-DOS, OS/2 и Windows-32. B демонстрационный пакет входит лишь одна графическая ипостась. Обе ипостаси обладают сходными функциональными возможностями, поэтому в книге будет описана лишь одна из них, скомпилированная для исполнения в среде Windows-32 (в дальнейшем обозначаемая как IDAW).
Покупка IDA Pro Standard или IDA Pro Advanced дает право на бесплатное приобретение IDA SDK (Software Development Kit) — программного пакета, позволяющего пользователю самостоятельно разрабатывать процессорные модули, файловые загрузчики и плагины (внешние самостоятельные модули). Это неограниченно расширяет возможности IDA Pro, позволяя анализировать любой код, для какого бы микропроцессора и операционной системы он ни предназначался. SDK различных версий не совместимы друг с другом и созданные пользователем модули могут не работать в другой версии IDA Pro, поэтому, прежде чем переходить на очередную версию IDA Pro, настоятельно рекомендуется убедиться в сохранении работоспособности всех скриптов, модулей, плагинов и т.д.


IDA - 0005.htm

Краткое введение в дизассемблирование
Краткое введение
в дизассемблирование
Одним из способов изучения программ в отсутствие исходных текстов является дизассемблирование, — перевод двоичных кодов процессора в удобочитаемые мнемонические инструкции. С первого взгляда кажется: ничего сложного в такой операции нет, и один дизассемблер не хуже любого другого. На самом же деле, ассемблирование — однонаправленный процесс с потерями, поэтому автоматическое восстановление исходного текста невозможно.
Одна из фундаментальных проблем дизассемблирования заключается в синтаксической неотличимости констант от адресов памяти (сегментов и смещений). Потребность распознавания смещений объясняется необходимостью замены конкретных адресов на метки, действительное смещение которых определяется на этапе ассемблирования программы.
Сказанное можно проиллюстрировать следующим примером: рассмотрим исходную программу (а). При ассемблировании смещение строки s0, загружаемое в регистр s0, заменяется его конкретным значением, в данном случае равным 108h, поэтому команда "MOV DX, offset sO" приобретает вид "MOV DX, 108h". Это влечет за собой потерю информации — теперь уже нельзя однозначно утверждать как выглядел исходный текст, т.к. ассемблирование ":offset sO" и ":108h" дает одинаковый результат, т.е. функция ассемблирования не инъективна (функция f(x) = у называется инъективной, если уравнение f(y) = x имеет только один корень и, соответственно, наоборот).
Если все машинные инструкции исходного файла, перевести в соответствую-щие им символьные мнемоники (назовем такую операцию простым синтаксическим дизассемблированием), в результате получится (b). Легко видеть — программа сохраняет работоспособность лишь до тех пор, пока выводимая строка располагается по адресу 108h. Если модификация кода программы (с) нарушает такое равновесие, то на экране вместо ожидаемого приветствия появляется "мусор" — теперь выводимая строка находится по адресу 0x10C, но в регистр DX по-прежнему загружается прежнее значение ее смещения — 0x108 (d):
a) исходная программа:
mov ah,9
mov dx, offset sO
int 21h
ret
sO DB 'Hello,World!',ODh,OAh,'$'
b) дизассемблированная программа:
mov ah,0
mov dx,0108h
int 21h


IDA - 0006.htm

Краткое введение в дизассемблирование
ret
sO DB 'Helio,World!',ODh,OAh,'$'
c) модифицированная программа:
mov ah,09
mov dx,0108h
int 21h
хог ax,ax
int 16h
ret
sO DB 'Hello,World!',ODh,OAh,'$'
d) неработоспособный результат:
:0100 start proc near
:0100 mov ah, 9
:0102 mov dx, 108h————
:0105 int 21h
:0107 xor ах, ах
:0109 int 16h ——————'
:010B retn
:010B aHelloWorld db 'Hello,World!',ODh,OAh,'$'
:010C end start
Аналогичная проблема возникает и при переводе с одного языка на другой — фраза "это ключ" в зависимости от ситуации может быть переведена и как "this is key", и "this is clue", и "this is switch": Для правильного перевода мало простого словаря — подстрочечника, необходимо еще понимать о чем идет речь, т.е. осмысливать переводимый текст.
Человек легко может определить, что содержимое регистра DX в данном случае является именно смещением, а не чем-нибудь иным, поскольку его ожидает функция 0x9 прерывания 0x21. Но дизассемблеру для успешной работы мало знать одних прототипов системных и библиотечных функций, — он должен еще уметь отслеживать содержимое регистров, а, следовательно, "понимать" команды микропроцессора. Создание такого дизассемблера (часто называемого контекстным) очень сложная инженерная задача, тесно граничащая с искусственным интеллектом, которая на сегодняшний день еще никем не решена. Существует более или менее удачные разработки, но ни одна из них не способна генерировать 100%-работоспособные листинги.
Например, пусть в исходной программе имелся фрагмент (а), загружающий в регистр АХ смещение начала таблицы, а в регистр BX индекс требуемого элемента. Ассемблер, заменяя оба значения константами (b), создает неразрешимую задачу, — легко видеть, что один из регистров содержит смещение, а другой индекс, но как узнать — какой именно?
а) исходная программа:
MOV AX,offset Тablе
MOV BX,200h ; Index
ADD AX,BX
MOV AX,[BX]


IDA - 0007.htm

Краткое введение в дизассемблирование
b) машинный код:
ВВ 00 02
01 D8
8В 07
B8 10 00
c) дизассемблированный текст:
MOV AX,0010
MOV BX,0200
ADD AX,BX
MOV AX,Word ptr [BX]
Другая фундаментальная проблема заключается в невозможности определения границ инструкций синтаксическим дизассемблером. Пусть в исходной программе (а) имелась директива выравнивания кода по адресам кратным четырем, тогда ассемблер (b) вставит в этом месте несколько произвольных символов (как правило, нулей), а дизассемблер, "не зная" об этом, примет их за часть инструкций, в результате чего сгенерирует ни на что не годный листинг (с).
a) исходная программа:
JMP Label
Align 4
Label: XOR AX,AX
RET
b) машинный код:
00: E90100
03: 00
04: 33 СО
06: C3
c) дизассемблированный текст:
00: E9 01 00 jmp 04
03: 00 33 add [bp][di],dh
05: CO C3 rol bl,-070;
Контекстные дизассемблеры частично позволяют этого избежать, поскольку способны распознавать типовые способы передачи управления, но если программист использует регистровые переходы, дизассемблеру придется эмулировать выполнение программы для определения значений регистров в каждой точке программы. Это не только технически сложная, но и ресурсоемкая задача, решение которой еще предстоит найти.
Дизассемблирование — творческий процесс, развивающий интуицию и абстрактное мышление, возможно, даже особый вид искусства, позволяющий каждому проявить свою индивидуальность. На сегодняшний день не существует ни одного полностью автоматического дизассемблера, способного генерировать безупречно работоспособный листинг и доводить полученный им результат до


IDA - 0008.htm

8 Краткое введение в дизассемблирование
готовности приходится человеку. Таким образом, встает вопрос о механизмах взаимодействия человека с дизассемблером.
По типу реализации интерфейса взаимодействия с пользователем, существующие дизассемблеры можно разделить на две категории — автономные и интерактивные. Автономные дизассемблеры требуют от пользователя задания всех указаний до начала дизассемблирования и не позволяют вмешиваться непосредственно в сам процесс. Если же конечный результат окажется неудовлетворительным, пользователь либо вручную правит полученный листинг, либо указывает дизассемблеру на его ошибки и повторяет всю процедуру вновь и вновь, порой десятки раз! Такой способ общения человека с дизассемблером непроизводителен и неудобен, но его легче запрограммировать.
Интерактивные дизассемблеры обладают развитым пользовательским интерфейсом, благодаря которому приобретают значительную гибкость, позволяя человеку "вручную" управлять разбором программы, помогая автоматическому анализатору там, где ему самому не справиться — отличать адреса от констант, определять границы инструкций и т.д.
Примером автономного дизассемблера является SOURCER, а интерактивного — IDA. Преимущество SOURCER-a заключается в простоте управления, в то время как работа с IDA требует высокой квалификации и навыков системного программирования. Неопытные пользователи часто предпочитают SOURCER, лидирующий среди других дизассемблеров, на небольших проектах. Но он очень плохо справляется с анализом большого, порядка нескольких мегабайт, заковыристого файла, а с шифрованным или я-кодом не справляется вообще! И тогда на помощь приходит IDA, которая, будучи виртуальной программируемой машиной, может абсолютно все — стоит лишь разработать и ввести соответствующий скрипт. А как это сделать и рассказывает настоящая книга.


IDA - 0009.htm

Первые шаги с IDA Pro
Первые шаги с IDA Pro
С легкой руки Дениса Ричи повелось начинать освоение нового языка программирования с создания простейшей программы "Hello, World!", — и здесь не будет нарушена эта традиция. Оценим возможности IDA Pro следующим примером (для совместимости с книгой рекомендуется откомпилировать его с помощью Microsoft Visual С++ 6.0 вызовом "cl.exe first.cpp" в командной строке):
#include <iostream.h>
void main()
{
cout<<"Hello, Sailor!\n";
}
а) исходный текст программы first.cpp
Компилятор сгенерирует исполняемый файл размером почти в 40 килобайт, большую часть которого займет служебный, стартовый или библиотечный код! Попытка дизассемблирования с помощью таких дизассемблеров как W32DASM (или аналогичных ему) не увенчается быстрым успехом, поскольку над полученным листингом размером в пятьсот килобайт (!) можно просидеть не час и не два. Легко представить сколько времени уйдет на серьезные задачи, требующие изучения десятков мегабайт дизассемблированного текста.
Попробуем эту программу дизассемблировать с помощью IDA. Если все настройки оставить по умолчанию, после завершения анализа экран (в зависимости от версии) должен выглядеть следующим образом:
IDA - 0009-1.jpg
Рис. 1. Так выглядит результат работы консольной версии IDA Pro 3.6


IDA - 0010.htm

10 Первые шаги с IDA Pro
IDA - 0010-1.jpg
Рис. 2. Так выглядит результат работы консольной версии
IDA Pro 4.0
IDA - 0010-2.jpg
Рис. 3. Так выглядит результат работы графической версии
IDA Pro 4.0


IDA - 0011.htm

Первые шаги с IDA Pro 11
С версии 3.8x* в IDA появилась поддержка "сворачивания" (Collapsed) функций. Такой прием значительно упрощает навигацию по тексту, позволяя убрать с экрана не интересные в данный момент строки. По умолчанию все библиотечные функции сворачиваются автоматически.
Развернуть функцию можно, подведя к ней курсор и нажав <+>на дополнительной цифровой клавиатуре, расположенной справа. Соответственно, клавиша <—> предназначена для сворачивания.
По окончании автоматического анализа файла "first.exe" IDA переместит курсор к строке ".text:00401B2C" — точке входа в программу. Среди начинающих программистов широко распространено заблуждение — якобы программы, написанные на Си, начинают выполняться с функции "main", но в действительности это не совсем так. На самом деле сразу после загрузки файла управление передается на функцию "Stагt,вставленную компилятором. Она подготавливает глобальные переменные _osver (билд), _winmajor (старшая версия операционной системы), _winminor (младшая версия операционной системы), _winver (полная версия операционной системы), _argc(количество аргументов командной строки),_argv (массив указателей на строки аргументов), _environ (массив указателей на строки переменных окружения); инициализирует кучи (heap); вызывает функцию main, a после возращения управления завершает процесс с помощью функции Exit.
Наглядно продемонстрировать инициализацию переменных, совершаемую стартовым кодом, позволяет следующая программа.
#include <stdio.h>
#include <stdlib.h>
void main()
{
int а;
printf(">Bepcuяl OS:\t\t\t%d.%d\n\
>Билд:\t\t\t%d\n\
>Количество apryмeнтов:\t%d\n",\
_winmajor,_winminor,_osver,_ _argc);
for (a=0;a<_ _argc;a++)
printf(">\tApryмент %02d:\t\t%s\n",a+1,_ _argv[a]);
a=!a-1;
while(_environ[++a]) ;
printf(">Количество переменных окружения:%d\п",а);
while(a) printf(">\tпеременная %d:\t\t%s\n",a,_environ[--a]);
}
а) исходный текст программы CRtO.demo.c
Прототип функции main как будто бы указывает, что приложение не принимает никаких аргументов командной строки, но результат работы программы доказывает обратное и на машине автора выглядит так (приводится в сокращенном виде):
>Версия OS: 5.0
>Билд: 2195
>Количество аргументов: 1
А может и чуточку раньше.


IDA - 0012.htm

12 Первые шаги с IDA Pro
> Аргумент 01: CRtO.demo
<Количество переменных окружения: 30
> Переменная 29: windir=C:\WINNT
>...
b) результат работы программы CRtO.demo.c
Очевидно, нет никакой необходимости анализировать стандартный стартовый код приложения, и первая задача исследователя — найти место передачи управления на функцию main. K сожалению, гарантированное решение это задачи требует полного анализа содержимого функции "Start". У исследователей существует множество хитростей, но все они базируются на особенностях реализации конкретных компиляторов и не могут считаться универсальными. Например, Microsoft Visual С всегда, независимо от прототипа функции main передает ей три аргумента — указатель на массив указателей переменных окружения, указатель на массив указателей аргументов командной строки и количество аргументов командной строки.
Рекомендуется изучить исходные тексты стартовых функций популярных компиляторов, находящиеся в файлах CRtO.c (Microsoft Visual С) и cOw.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 To: KERNEL32.GetVersion, 0rd:0174h
/
:00401B52 FF1504704000 Call dword ptr [00407004]
:00401B58 33D2 xor edx, edx
:00401B5A 8AD4 mov dl, ah
:00401B5C 8915B0874000 mov dword ptr [004087BO], edx
:00401B62 8BC8 mov ecx, eax
:00401B64 81E1FFOOOOOO and ecx, OOOOOOFF
:00401B6A 890DAC874000 mov dword ptr [004087AC], ecx
:00401B70 C1E108 shl ecx, 08
:00401B73 03CA add ecx, edx
:00401B75 890DA8874000 mov dword ptr [004087A8], ecx
:00401B7B C1E810 shr eax, 10
:00401B7E A3A4874000 mov dword ptr [004087A4], eax


IDA - 0013.htm

Первые шаги с IDA Pro 13
:00401B83 6AOO push 00000000
:00401B85 E8D91BOOOO call 00403763
:00401B8A 59 pop ecx
:00401B8B 85CO test eax, eax
:00401B8D 7508 jne 00401B97
:00401B8F 6A1C push 0000001C
:00401B91 E89AOOOOOO call 00401C30
:00401B96 59 pop ecx
Referenced by a (U)nconditional ог (C)onditional Jump at Address:
|:00401B8D(C)
/
:00401B97 8365FCOO and dword ptr [ebp-04], 00000000
:00401B9B E8D70COOOO call 00402877
Reference To: KERNEL32.GetCommandLineA, Ord:OOCAh
I
:00401BAO FF1560704000 Call dword ptr [00407060]
:00401BA6 A3E49C4000 mov dword ptr [00409CE4], eax
:00401BAB E8811AOOOO call 00403631
:00401BBO A388874000 mov dword ptr [00408788], eax
:00401BB5 E82A180000 call 004033E4
:00401BBA E86C170000 call 0040332B
:00401BBF E8E1140000 call 004030A5
:00401BC4 A1C0874000 mov eax, dword ptr [004087CO]
: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
:00401BEO 83C40C add esp, OOOOOOOC
: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 894DEO 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
а) стартовый код программы "first.exe", полученный дизассемблером W32Dasm
Иначе выглядит результат работы IDA, умеющей распознавать библиотечные функции по их сигнатурам (приблизительно по такому же алгоритму работает множество антивирусов). Поэтому, способности дизассемблера тесно связаны с его версией и полнотой комплекта поставки — далеко не все версии IDA Pro в состоянии работать с программами, сгенерированными современными компиляторами.


IDA - 0014.htm

14
Первые шаги с IDA Pro
00401B2C start proc near
00401B2C
00401B2C var_20 = dword ptr -20h
00401B2C var_1C = dword ptr -1Ch
00401B2C var_18 = dword ptr -18h
00401B2C var_14 = dword ptr -14h
00401B2C var_4 = dword ptr -4
00401B2C
00401B2C push ebp
00401B2D mov ebp, esp
00401B2F push OFFFFFFFFh
00401B31 push offset stru_407170
00401B36 push offset __except_handler3
00401B3B mov eax, large fs:0
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_4087BO, edx
00401B62 mov ecx, eax
00401B64 and ecx, OFFh
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+6l^j
00401B97 and [ebp+var_4], 0
00401B9B call __ioinit
00401BAO call ds:GetCommandLineA
00401BA6 mov dword_409CE4, eax
00401BAB call _crtGetEnvironmentStringsA
00401BBO mov dword_408788, eax
00401BB5 call __setargv
00401BBA call __setenvp
00401BBF call __cinit
00401BC4 mov eax, dword_4087CO


IDA - 0015.htm

Первые шаги с IDA Pro | 15
00401BC9 mov dword_4087C4, eax 00401BCE push eax 00401BCF push dword_4087B8 00401BD5 push dword_4087B4 00401BDB call sub_401000 00401BEO add esp, OCh 00401BE3 mov [ebp+var_1C], eax 00401BE6 push eax 00401BE7 call _exit 00401BEC : ---------------------------------------------------- 00401BEC 00401BEC loc_401BEC: : DATA XREF: _rdata:00407170io 00401BEC mov eax, [ebp-14h] 00401BEF mov ecx, [eax] 00401BF1 mov ecx, [ecx] 00401BF3 mov [ebp-20h], ecx 00401BF6 push eax 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" в окне сообщений.
IDA - 0015-1.jpg
Рис. 4. Загрузка библиотеки сигнатур


IDA - 0016.htm

16 Первые шаги с IDA Pro
Дизассемблер сумел определить имена всех функций,вызываемых стартовым кодом, за исключением одной, расположенной по адресу 0x0401BDB. Учитывая передачу трех аргументов и обращение к _exit, после возвращения функцией управления, можно предположить, что это main и есть.
Перейти по адресу 0x0401000 для изучения содержимого функции main можно несколькими способами — прокрутить экран с помощью стрелок управления курсором, нажать клавишу <G> и ввести требуемый адрес в появившемся окне диалога, но проще и быстрее всего воспользоваться встроенной в IDA Pro системой навигации. Если подвести курсор в границы имени, константы или выражения и нажать <Enter>, IDA автоматически перейдет на требуемый адрес.
В данном случае требуется подвести к строке "sub_401000" (аргументу команды call) и нажать на <Enter>, если все сделано правильно, экран дизассемблера должен выглядеть следующим образом:
00401000 ; —— ——— S U В R 0 U Т I N E ———————————
00401000
00401000 ; Attributes: bp-based frame
00401000
00401000 sub_401000 proc near ; CODE XREF: start+AF ! p
00401000 push ebp
00401001 mov ebp, esp
00401003 push offset aHelloSailor ; "Hello, Sailor!\n"
00401008 mov ecx, offset dword_408748
0040100D call ??6ostream@@QAEAAVO@PBD@Z ; ostream::operator<<(char const *)
00401012 pop ebp
00401013 retn
00401013 sub_401000 endp
Дизассемблер сумел распознать строковую переменную и дал ей осмысленное имя "aHelloSailor", a в комментарии, расположенном справа, для наглядности привел оригинальное содержимое "Hello, Sailor!\n". Если поместить курсор в границы имени "aHelloSailor" и нажать <Enter>, IDA автоматически перейдет к требуемой строке:
00408040 aHelloSailor db 'Hello, Sailor!',OAh,0 ; DATA XREF: sub_401000+3^o •
Выражение "DATA XREF: sub_401000+3to" называется перекрестной ссылкой и свидетельствует о том, что в третьей строке процедуры sub_401000 произошло обращение к текущему адресу по его смещению ("о" от offset), a стрелка, направленная вверх, указывает на относительное расположение источника перекрестной ссылки.
Если в границы выражения "sub_401000+3" подвести курсор и нажать <Enter>, IDA Pro перейдет к следующей строке:
00401003 push offset aHelloSailor ; "Hello, Sailor!\n"
Нажатие клавиши <Ecs> отменяет предыдущее перемещение, возвращая курсор в исходную позицию. (Аналогично команде "back" в web-бpayзepe). Смещение строки "Hello, Sailor!\n", передается процедуре "??6ostre-am@@QAEAAVO@PBD@Z", представляющей собой оператор "«" языка


IDA - 0017.htm

Первые шаги с IDA Pro 17
Странное имя объясняется ограничениями, наложенными на символы, допустимые в именах библиотечных функций. Поэтому компиляторы автоматически преобразуют (замангляют) такие имена в "абракадабру", пригодную для работы с линкером, и многие начинающие программисты даже не догадываются об этой скрытой "кухне".
Для облегчения анализа текста IDA Pro в комментариях отображает "правильные" имена, но существует возможность заставить ее везде показывать незаман-гленные имена. Для этого необходимо в меню "Options" выбрать пункт "Deman-gled names" и в появившемся окне диалога переместить радио-кнопку на "Names", после этого вызов оператора "«" станет выглядеть так:
0040100D call ostream::operator<<(char const *)
На этом анализ приложения "first.cpp" можно считать завершенным. Для полноты картины остается переименовать функцию "sub_401000" в main. Для этого необходимо подвести курсор к строке 0x0401000 (началу функции) и нажать клавишу <N>, в появившемся диалоге ввести "main". Конечный результат должен выглядеть так:
00401000 ; —— ——— S U В R 0 U Т I N E —————————— — — ——————
00401000
00401000 ; Attributes: bp-based frame
00401000
00401000 main proc near ; CODE XREF: start+AF ! p
00401000 push ebp
00401001 mov ebp, esp
00401003 push offset aHelloSailor ; "Hello, Sailor!\n"
00401008 mov ecx, offset dword_408748
0040100D call ostream::operator<<(char const *)
00401012 pop ebp
00401013 retn
00401013 main endp
Для сравнения, результат работы W32Dasm выглядит следующим образом (ниже приводится лишь содержимое функции main):
:00401000 55 push ebp
:00401001 8BEC mov ebp, esp
Possible StringData Ref from Data Obj ->"Hello, Sailor!"
I
:00401003 6840804000 push 00408040
:00401008 B948874000 mov ecx, 00408748
;0040100D E8ABOOOOOO call 004010BD
:00401012 5D pop ebp
:00401013 C3 ret
Другое важное преимущество IDA — способность дизассемблировать зашиф-рованные программы. В демонстрационном примере ("Crypt.com" — исходный текст см. в приложении) использовалась статическая шифровка, часто встречающаяся в "конвертных" защитах. Этот простой прием полностью "ослепляет"


IDA - 0018.htm

18 Первые шаги c lDA Pro
большинство дизассемблеров. Например, результат обработки файла "Crypt.com"
SOURCER-oм выглядит так:
Crypt proc far
7E5B:0100 start:
7E5B:0100 83 C6 06 add si,6
7E5B:0103 FF E6 jmp si ;*
;*No entry point to code
7E5B:0105 B9 14BE mov cx,14BEh
7E5B:0108 01 AD 5691 add ds:data_1e[di],bp ; (7E5B:5691=0)
7E5B:010C 80 34 66 xor byte ptr [si],66h ; 'f'
7E5B:010F 46 inc si
7E5B:0110 E2 FA loop $-4 ; Loop if cx > 0
7E5B:0112 FF E6 jmp si ;*
;* No entry point to code
7E5B:114 18 00 sbb [bx+si],al
7E5B:116 D2 6F DC shr byte ptr [bx-24h],cl ; Shift w/zeros fill
7E5B:119 6E 67 AB 47 A5 2E db 6Eh, 67h,OABh, 47h,OA5h, 2Eh
7E5B:11F 03 OA OA 09 4A 35 db 03h, OAh, OAh, 09h, 4Ah, 35h
7E5B:125 07 OF OA 09 14 47 db 07h, OFh, OAh, 09h, 14h, 47h
7E5B:12B 6В 6C 42 E8 00 00 db 6Bh, 6Ch, 42h, E8h, 00h, 00h
7E5B:131 59 5E BF 00 01 57 db 59h, 5Eh, BFh, 00h, 01h, 57h
7E5B:137 2В СЕ F3 A4 C3 db 2Bh, CEh, F3h, A4h, C3h
Crypt endp
SOURCER половину кода вообще не смог дизассемблировать, оставив ее в виде дампа, а другую половину дизассемблировал неправильно! Команда "JMP SI" в строке :Oxl03 осуществляет переход по адресу :Oxl06 (значение регистра SI после загрузки com-файла равно 0x100, поэтому после команды "ADD SI,6" регистр SI равен 0x106). Но следующая за "JMP" команда расположена по адресу 0x105! В исходном тексте в это место вставлен байт-пустышка, сбивающий дизассемблер с толку.
Start:
ADD SI,6
JMP SI
DB OB9h ;
LEA SI,_end ; На начало зашифрованного фрагмента
SOURCER не обладает способностью предсказывать регистровые переходы и, встретив команду "JMP SI", продолжает дизассемблирование, молчаливо предполагая, что команды последовательно расположены вплотную друг к другу. Существует возможность создать файл определений, указывающий, что по адресу :Oxl05 расположен байт данных, но подобное взаимодействие с пользователем очень неудобно.
Напротив, IDA изначально проектировалась как дружественная к пользователю интерактивная среда. В отличие от SOURCER-подобных дизассемблеров, IDA не делает никаких молчаливых предположений, и при возникновении затрудне-


IDA - 0019.htm

Первые шаги с IDA Pro | 19
ний обращается за помощью к человеку. Поэтому, встретив регистровый переход по неизвестному адресу, она прекращает дальнейший анализ, и результат анализа файла "Crypt.com" выглядит так:
segOOO:0100 start proc near segOOO:0100 add si, 6 segOOO:0103 jmp si segOOO:0103 start endp segOOO:0103 segOOO:0103 ; ——————————-———. segOOO:0105 . db OB9h | segOOO:0106 db OBEh - segOOO:0107 db 14h segOOO:0108 db 1 segOOO:0109 db OADh н segOOO:010A db 91h C
Необходимо помочь дизассемблеру, указав адрес перехода. Начинающие пользователи в этой ситуации обычно подводят курсор к соответствующей строке и нажимают клавишу <C>, заставляя IDA дизассемблировать код с текущей позиции до конца функции. Несмотря на кажущуюся очевидность, такое решение ошибочно, ибо по-прежнему остается неизвестным куда указывает условный переход в строке :Oxl03 и откуда код, расположенный по адресу :Oxl06 получает управление. Правильное решение — добавить перекрестную ссылку, связывающую строку :Oxl03, со строкой :Oxl06. Для этого необходимо в меню "View" выбрать пункт "Cross references" и в появившемся окне диалога заполнить поля "from" и "to" значениями segOOO:0103 и segOOO:0106 соответственно. После этого экран дизассемблера должен выглядеть следующим образом (в IDA версии 4.01.300 содержится ошибка, и добавление новой перекрестной ссылки не всегда приводит к автоматическому дизассемблированию):
segOOO:0100 public start segOOO:0100 start proc near segftOO:0100 add si, 6 segOOO:0103 jmp si segOOO:0103 start endp segOOO:0103 segOOO:0103 ; ——————————————————————————————————————————— segOOO:.0105 db OB9h ; | segOOO:0106 ; ——————————————————————————————————————————— segOOO:0106 segOOO:0106 locJM06: ; CODE XREF: start+s ^ u segOOO:0106 mov si, 114h segOOO:0109 lodsw segOOO:010A xchg ax, cx segOOO:010B push si segOOO:010C segOOO:010C loc_0_10C: ; CODE XREF: segOOO:0110 ! j segOOO:010C xor byte ptr [si], 66h


IDA - 0020.htm

20 Первые шаги с IDA Pro
segOOO:010F inc si
segOOO:0110 loop loc_0_10C
segOOO:0112 jmp si
segOOO:0112 ; — — — — — — — — — — — — — ——— — — — — — — — — —
segOOO:0114 db 18h
segOOO:0115 db 0
segOOO:0116 db OD2h Т
segOOO:0117 db 6Fh о
Поскольку IDA Pro не отображает адреса-приемника перекрестной ссылки, то рекомендуется выполнить это самостоятельно. Такой прием улучшит наглядность текста и упростит навигацию. Если подвести курсор к строке :Oxl03, нажать клавишу <:> и ввести в появившемся диалоговом окне любой осмысленный комментарий (например "переход по адресу 0106"), то экран примет следующий вид:
segOOO:0103 jmp si ; Переход по адресу 0106
Ценность такого приема заключается в возможности быстрого перехода по адресу, на который ссылается "JMP SI", — достаточно лишь подвести курсор к числу "0106" и нажать <Enter>. Важно соблюдать правильность написания — IDA Pro не распознает шестнадцатеричный формат ни в стиле Си (0x106), ни в стиле MASM\TASM (0106h).
Что представляет собой число "114h" в строке :Oxl06 — константу или смещение? Чтобы узнать это, необходимо проанализировать следующую команду — "LODSW", поскольку ее выполнение приводит к загрузке в регистр АХ слова, расположенного по адресу DS:SI, очевидно, в регистр SI заносится смещение.
segOOO:0106 mov si, 114h
segOOO:0109 lodsw
Однократное нажатие клавиши <O> преобразует константу в смещение и дизассемблируемый текст станет выглядеть так:
segOOO:0106 mov si, offset unk_0_114
segOOO:0109 lodsw
segOOO:0114 unk_0_114 db 18h ; DATA XREF: segOOO:0106To
segOOO:0115 db 0
segOOO:0116 db OD2h Т
segOOO:0117 db 6Fh о
IDA Pro автоматически создала новое имя "unk_0_114", ссылающееся на переменную неопределенного типа размером байт, но команда "LODSW" загружает в регистр АХ слово, поэтому необходимо перейти к строке :0144 и дважды нажать <D> пока экран не станет выглядеть так:
segOOO:0114 word_0_114 dw 18h ; DATA XREF: segOOO:0106^o
segOOO:0116 db OD2h ; Т


IDA - 0021.htm

Первые шаги с IDA Pro 21
Но что именно содержится в ячейке "word_0_144"? Понять это позволит
изучение следующего кода:

segOOO:0106 mov si,offset word_0_114
segOOO:0109 lodsw
segOOO:010A xchg ax, cx
segOOO:010B push si
segOOO:010C
segOOO:010C loc_0_1OC: ; CODE XREF: segOOO:0110
segOOO:010C xor byte ptr [si], 66h
segOOO:010F inc si
segOOO:0110 loop loc_0_10C

В строке :OxlOA значение регистра АХ помещается в регистр CX и затем он
используется командой "LOOP LOC_OlOC" как счетчик цикла. Тело цикла
представляет собой простейший расшифровщик — команда "XOR" расшифровы-
вает один байт, на который указывает регистр SI, а команда "INC SI" перемещает
указатель на следующий байт. Следовательно, в ячейке "word__0_144" содержит-
ся количество байт, которые необходимо расшифровать. Подведя к ней курсор,
нажатием клавиши <N> можно дать ей осмысленное имя, например "BytesTo-
Decrypt".
После завершения цикла расшифровщика встречается еще один безусловный
регистровый переход.

segOOO:0112 jmp si

Чтобы узнать, куда именно он передает управление, необходимо проанализи-
ровать код и определить содержимое регистра SI. Часто для этой цели прибегают
к помощи отладчика — устанавливают точку останова в строке 0x112 и дождав-
шись его "всплытия" просматривают значения регистров. Специально для этой
цели IDA Pro поддерживает генерацию map-файлов, содержащих символьную
информацию для отладчика.
В частности, чтобы не заучивать численные значения всех "подопытных"
адресов, каждому из них можно присвоить легко запоминаемое символьное имя.
Например, если подвести курсор к строке "segOOO:0112", нажать <N> и ввести
"BreakHere", отладчик сможет автоматически вычислить обратный адрес по его
имени.
Для создания map-файла в меню "File" необходимо кликнуть по "Produce
output file" и в развернувшемся подменю выбрать "Produce MAP file" или вместо
всего этого нажать на клавиатуре "горячую" комбинацию "Shift-F10". Независимо
от способа вызова на экран должно появиться диалоговое окно следующего вида.
Оно позволяет выбрать какого рода данные будут включены в map-файл —
информация о сегментах, имена автоматически сгенерированные IDA Pro (такие
как, например, "loc_0_106", "sub_OxllO" и т.д.) и "размангленные" (т.е. приве-
денные в читабельный вид) имена.
Подробнее о сегментах рассказывается в главе "Сегменты и селекторы", об
автогенерируемых и замангленных именах — в главе "Настройки IDA".
Содержимое полученного map-файла должно быть следующим:


IDA - 0022.htm

22 Первые шаги с IDA Pro
Start Stop Length Name Class
00100H 0013BH 0003CH segOOO CODE
Address Publics by Value
0000:0100 start
0000:0112 BreakHere
0000:0114 BytesToDecrypt
Program entry point at 0000:0100

Такой формат поддерживают большинство отладчиков, в том числе и
популярнейший Soft-Ice, в поставку которого входит утилита "msym", запуска-
емая с указанием имени конвертируемого map-файла в командной строке.
Полученный sym-файл необходимо разместить в одной директории с отлажи-
ваемой программой, загружаемой в загрузчик без указания расширения,
т.е., например, так "WLDR Crypt". B противном случае символьная информа-
ция не будет загружена!
Затем необходимо установить точку останова командой "bpx BreakHere" и
покинуть отладчик командной "x". Спустя секунду его окно вновь появится на
экране, извещая о достижении процессором контрольной точки. Посмотрев на
значения регистров, отображаемых по умолчанию вверху экрана, можно выяс-
нить, что содержимое SI равно 0x12E.
С другой стороны, это же значение можно вычислить "в уме", не прибегая к
отладчику. Команда MOV в строке 0x106 загружает в регистр SI смещение 0x114,
откуда командой LODSW считывается количество расшифровываемых байт —
0x18, при этом содержимое SI увеличивается на размер слова — два байта.
Отсюда, в момент завершения цикла расшифровки значение SI будет равно
0x114 + 0x18 + 0x2 = Oxl2E.
Вычислив адрес перехода в строке 0x112, рекомендуется создать соответ-
ствующую перекрестную ссылку (from: 0x122; to: 0x12E) и добавить коммен-
тарий к строке 0x112 ("Переход по адресу 012E"). Создание перекрестной
ссылки автоматически дизассемблирует код, начиная с адреса segOOO:012E и
до конца файла.

segOOO:012E loc_0_12E: ; CODE XREF: segOOO:0112u
segOOO:012E call $+3
segOOO:0131 pop cx
segOOO:0132 pop si
segOOO:0133 mov di, 100h
segOOO:0136 push di
segOOO:0137 sub cx, si
segOOO:0139 repe movsb
segOOO:013B retn

Назначение команды "CALL $+3" (где $ обозначает текущее значение
регистра указателя команд IP) состоит в заталкивании в стек содержимого
регистра IP, откуда впоследствии оно может быть извлечено в любой регистр
общего назначения. Необходимость подобного трюка объясняется тем, что в
микропроцессорах серии Intel 80x86 регистр IP не входит в список непосредст-
венно адресуемых и читать его значение могут лишь команды, изменяющие ход
выполнения программы, в том числе и call.


IDA - 0023.htm

Первые шаги с IDA Pro 23
Для облегчения анализа листинга можно добавить к строкам 0x12E и 0x131
комментарий — "MOV CX, IP", или еще лучше — сразу вычислить и подставить
непосредственное значение — "MOV CX,Oxl31".
Команда "POP SI" в строке Ох132снимает слово из стека и помещает его в
регистр SI. Прокручивая экран дизассемблера вверх в строке 0x10B можно
обнаружить парную ей инструкцию "PUSH SI", заносящую в стек смещение
первого расшифровываемого байта. После этого становится понятным смысл
последующих команд "MOV DI, OxlOO\SUB CX,SI\REPE MOVSB". Они пере-
мещают начало расшифрованного фрагмента по адресу, начинающегося со сме-
щения 0x100.
Такая операция характерна для "конвертных" защит, накладывающихся на
уже откомпилированный файл, который перед запуском должен быть размещен
по своим "родным" адресам.
Перед началом перемещения в регистр CX заносится длина копируемого
блока, вычисляемая путем вычитания смещения первого расшифрованного байта
от смещения второй команды перемещающего кода. В действительности, истин-
ная длина на три байта короче и по идее от полученного значения необходимо
вычесть три. Однако, такое несогласование не нарушает работоспособности,
поскольку содержимое ячеек памяти, лежащих за концом расшифрованного
фрагмента, не определено и может быть любым.
Пара команд "Oxl36:PUSH DI" и "Oxl3B:RETN" образуют аналог инструкции
"CALL DI" — "PUSH" заталкивает адрес возврата в стек, a "RETN" извлекает
его оттуда и передает управление по соответствующему адресу. Зная значение DI
(оно равно 0x100), можно было бы добавить еще одну перекрестную ссылку
("from:Oxl3B; to:OxlOO")n комментарий к строке :Oxl3B — "Переход по адресу
0x100", но ведь к этому моменту по указанным адресам расположен совсем другой
код! Поэтому логически правильнее добавить перекрестную ссылку "from:Oxl3B;
to:Oxll6" и комментарий "Переход по адресу 0x116".
Сразу же после создания новой перекрестной ссылки IDA попытается дизас-
семблировать зашифрованный код, в результате чего получится следующее:
segOOO:0116 locJM16: ; CODE XREF: segOOO:013Bu
segOOO:0116 shr byte ptr [bx-24h], cl
segOOO:0119 outsb
segOOO:011A stos word ptr es:[edi]
segOOO:011C inc di
segOOO:011D movsw
segOOO:011E add cx, cs:[bp+si]
segOOO:0121 or cl, [bx+di]
segOOO:0123 dec dx
segOOO:0124 xor ax, OF07h
segOOO:0127 or cl, [bx+di]
segOOO:0129 adc al, 47h
segOOO:0129;—————————-—————————————
segOOO:012B db 6Bh ; k
segOOO:012C db 6Ch ;L
segOOO:012D db 42h ; В
segOOO:012E; —————————————————————————————————


IDA - 0024.htm

24_______________Первые шаги с IDA Pro________________

Непосредственное дизассемблирование зашифрованного кода невозможно —
предварительно его необходимо расшифровать. Подавляющее большинство дизас-
семблеров не могут модифицировать анализируемый текст налету и до загрузки
в дизассемблер исследуемый файл должен быть полностью расшифрован. На
практике, однако, это выглядит несколько иначе — прежде чем расшифровывать
необходимо выяснить алгоритм расшифровки, проанализировав доступную часть
файла. Затем необходимо выйти из дизассемблера, тем или иным способом
расшифровать "секретный" фрагмент, вновь загрузить файл в дизассемблер
(причем предыдущие результаты дизассемблирования окажутся утеряны) и про-
должить его анализ до тех пор, пока не встретится еще один зашифрованный
фрагмент, после чего описанный цикл "выход из дизассемблера — расшифров-
ка — загрузка — анализ" повторяется вновь.
Достоинство IDA заключается в том, что она позволяет выполнить ту же
задачу значительно меньшими усилиями, никуда не выходя из дизассемблера.
Это достигается за счет наличия механизма виртуальной памяти, подробно
описанного в главе "Виртуальная память". Если не вдаваться в технические
тонкости, упрощенно можно изобразить IDA в виде "прозрачной" виртуальной
машины, оперирующей с физической памятью компьютера. Для модификации
ячеек памяти необходимо знать их адрес, состоящий из пары чисел — сегмента
и смещения.
Слева каждой строки указывается ее смещение и имя сегмента, например
"segOOO:0116". Узнать базовый адрес сегмента по его имени можно, открыв окно
"Сегменты" выбрав в меню "View" пункт "Segments".
IDA - 0024-1.jpg
Рис. 5. Окно "Сегменты"

Искомый адрес находится в столбце "Base" и для наглядности на приведенной
копии экрана выделен жирным шрифтом. Обратиться к любой ячейке сегмента
поможет конструкция "[segment:offset]", а для чтения и модификации ячеек
предусмотрены функции Byte и PatchByte соответственно. Их вызов может
выглядеть, например, так: a=Byte([OxlOOO,OxlOO]) — читает ячейку, расположен-
ную по смещению 0x100 в сегменте с базовым адресом 0x1000; PatchBy-
te([OxlOOO,OxlOO],Ox27) — присваивает значение 0x27 ячейке памяти, располо-
женной по смещению 0x100 в сегменте с базовым адресом 0x1000. Как следует
из названия функций, они манипулируют с ячейками размером в один байт.
Существуют также функции, манипулирующие целыми словами, подробнее о них
можно прочесть в главе "Виртуальная память".
Знания этих двух функций вполне достаточно для написания скрипта —рас-
шифровщика при условии, что читатель знаком с языком Си. Реализация IDA-Си
не полностью придерживается стандарта — подробнее об этом рассказывается в
главе "Язык скриптов IDA Си", здесь же достаточно заметить, что IDA не
позволяет разработчику задавать тип переменной и определяет его автоматически


IDA - 0025.htm

Первые шаги с IDA Pro 25

по ее первому использованию, а объявление осуществляется ключевым словом
"auto". Например, "auto MyVar, sO" объявляет две переменных — MyVar и sO.
Для создания скрипта необходимо нажать комбинацию клавиш <Shift-F2> или
выбрать в меню "File" пункт "IDC Command" и в появившемся окне диалога
ввести исходный текст программы:

auto а;
for (a=Ox116;a<Ox12E;a++)
PatchByte([Ox1000,a],Byte([Ox1000,a])^Ox66);
а) исходный текст скрипта-расшифровщика
IDA - 0025-1.jpg
Пояснение. Как было показано выше, алгоритм расшифровщика сводится к
последовательному преобразованию каждой ячейки зашифрованного фрагмента
операцией XOR 0x66, (см. ниже — выделено жирным шрифтом)

segOOO:010C xor byte ptr [si], 66h
segOOO:O10F inc si
segOOO:Ol10 loop loc_O_10C

Сам же зашифрованный фрагмент начинается с адреса segOOO:Oxll6 и продолжа-
ется вплоть до segOOO:Oxl2E. Отсюда — цикл расшифровки на языке Си выглядит
так: for (a=Oxll6;a<Oxl2E;a++) PatchByte([Ox1000,a],Byte([OxlOOO,a])^Ox66);

В зависимости от версии IDA для выполнения скрипта необходимо нажать
либо <Enter> (версия 3.8x и старше), либо <Ctrl-Enter> в более ранних версиях.
Если все сделано правильно, после выполнения скрипта экран дизассемблера
должен выглядеть так (b).
Возможные ошибки — несоблюдение регистра символов (IDA к этому чувст-
вительна), синтаксические ошибки, базовый адрес вашего сегмента отличается от
0x1000 (еще раз вызовите окно "Сегменты" чтобы узнать его значение). В про-
тивном случае необходимо подвести курсор к строке "segOOO:0116", нажать
клавишу <U> для удаления результатов предыдущего дизассемблирования за-


IDA - 0026.htm

26______________Первые шаги с IDA Pro________________
шифрованного фрагмента и затем клавишу <C> для повторного дизассемблиро-
вания расшифрованного кода.
segOOO:0116 loc_0_116: ; CODE XREF: segOOO:013Bu
segOOO:0116 mov ah, 9
segOOO:0118 mov dx, 108h
segOOO:011B int 21h ; DOS - PRINT STRING
segOOO:011B ; DS:DX -> string terminated by "$"
segOOO:011D retn
segOOO:011D ; ————————————————————————————————————————————
segOOO:011E db 48h H
segOOO:011F db 65h e
segOOO:0120 db 6Ch 1
segOOO:0121 db 6Ch 1
segOOO:0122 db 6Fh o
segOOO:0123 db 2Ch ,
segOOO:0124 db 53h S
segOOO:0125 db 61h a
segOOO:0126 db 69h i
segOOO:0127 db 6Ch 1
segOOO:0128 db 6Fh o
segOOO:0129 db 72h r
segOOO:012A db 21h !
segOOO:012B db ODh
segOOO:012C db OAh
segOOO:012D db 24h $
segOOO:012E ; ————————————————————————————————————————————
b) результат работы скрипта расшифровщика
Цепочку символов, расположенную начиная с адреса "segOOO:OllE" можно преобразовать в удобочитаемый вид, подведя к ней курсор и нажав клавишу "<A>". Теперь экран дизассемблера будет выглядеть так:
segOOO:0116 loc_0_116: ; CODE XREF: segOOO:013Bu
segOOO:0116 mov ah, 9
segOOO:0118 mov dx, 108h
segOOO:011B int 21h ; DOS - PRINT STRING
segOOO:011B ; DS:DX -> string terminated by "$"
segOOO:011D retn
segOOO:011D ; ————————————————————————————————————————————
segOOO:011EaHelloSailor db 'Hello,Sailor!',ODh,OAh,'$'
segOOO:012E ; ————————————————————————————————————————————
c) создание ASCII-строки
Команда "MOV AH,9" в строке :0116 подготавливает регистр АН перед вызовом прерывания 0x21, выбирая функцию вывода строки на экран, смещение которой заносится следующей командой в регистр DX. Т.е. для успешного ассемблирования листинга необходимо заменить константу 0x108 соответствующим смещением. Но ведь выводимая строка на этапе ассемблирования (до перемещения кода) расположена совсем в другом месте! Одно из возможных решений этой проблемы заключается в создании нового сегмента с последующим


IDA - 0027.htm

________________Первые шаги с IDA Pro_______________27

копированием в него расшифрованного кода — в результате чего достигается
эмуляция перемещения кода работающей программы.
Для создания нового сегмента можно выбрать в меню "View" пункт "Seg-
ments" и в раскрывшемся окне нажать клавишу <Insert>. Появится диалог
следующего вида (см. рис. 7):
IDA - 0027-1.jpg
Рис. 7. IDAC: создание нового сегмента

Подробнее о значении каждого из полей рассказано в главе "Сегменты :
селекторы".

Пояснение. Базовый адрес сегмента может быть любым если при этом не
происходит перекрытия сегментов segOOO и MySeg; начальный адрес сегмента
задается так, чтобы смещение первого байта было равно 0x100; разница между
конечным и начальным адресом равна длине сегмента, вычислить которую можно
вычитанием смещения начала расшифрованного фрагмента от смещения его
конца — 0x13B - 0x116 = 0x25.

Скопировать требуемый фрагмент в только что созданный сегмент можнo
скриптом следующего содержания.

auto а;
for (a=OxO;a<Ox25;a++) PatchByte([Ox2000,a+Ox100],Byte([Ox1000,a+Ox116]));
а) исходный текст скрипта-копировщика

Для его ввода необходимо вновь нажать <Shift-F2>, при этом предыдущш
скрипт будет утерян (IDA позволяет работать не более чем с одним скриптом
одновременно). После завершения его работы экран дизассемблера будет выгля
деть так:

MySeg:0100 MySeg segment byte public '' use16
MySeg:0100 assume cs:MySeg
MySeg:0100 ;org 100h
MySeg:0100 assume es:nothing, ss:nothing, ds:nothing, fs:nothing,
gs:nothing


IDA - 0028.htm

28 Первые шаги с IDA Pro
MySeg:0100 db OB4h ^
MySeg:0101 db 9
MySeg:0102 db OBAh ||
MySeg:0103 db 8
MySeg:0104 db 1
MySeg:0105 db OCDh -
MySeg:0106 db 21h !
MySeg:0107 db OC3h ^
MySeg:0108 db 48h H
MySeg:0109 db 65h e
MySeg:010A db 6Ch l
MySeg:010B db 6Ch 1
MySeg:010C db 6Fh о
MySeg:010D db 2Ch ,
MySeg:010E db 53h S
MySeg:010F db 61h а
MySeg:0110 db 69h i
MySeg:0111 db 6Ch 1
MySeg:0112 db 6Fh о
MySeg:0113 db 72h г
MySeg:0114 db 21h !
MySeg:0115 db ODh
MySeg:0116 db OAh
MySeg:0117 db 24h $
MySeg:0117 MySeg ends
b) результат работы скрипта-копировщика

Теперь необходимо создать перекрестную ссылку "from:segOOO:013B;
to:MySeg:OxlOO", преобразовать цепочку символов в удобочитаемую строку,
подведя курсор к строке MySeg:0108 и нажав клавишу <A>. Экран дизассембле-
ра должен выглядеть так:

MySeg:0100 loc_1000_100: ; CODE XREF: segOOO:013Bu
MySeg:0100 mov ah, 9
MySeg:0102 mov dx, 108h
MySeg:0105 int 21h ; DOS - PRINT STRING
MySeg:0105 ; DS:DX -> string terminated by "$"
MySeg:0107 retn
MySeg:0107 ; ——————————————————————————————————————————————
MySeg:0108 aHelloSailorS db 'Hello,Sailor!',ODh,OAh
MySeg:0108 db '$'
MySeg:0118 MySeg ends
с) результат дизассемблирования скопированного фрагмента

Результатом всех этих операций стало совпадение смещения строки со
значением, загружаемым в регистр DX (в тексте они выделены жирным шриф-
том). Если подвести курсор к константе "108h" и нажать клавишу <Ctrl-O> она
будет преобразована в смещение:

MySeg:0102 mov dx, offset aHelloSailorS ; "Hello,Sailor!\r\n$"
MySeg:0105 int 21h ; DOS - PRINT STRING
MySeg:0105 ; DS:DX -> string terminated by $
MySeg:0107 retn


IDA - 0029.htm

Первые шаги с IDA Pro 29
MySeg:0107 ; —————————————————————————
MySeg:0108aHelloSailorS db 'Hello,Sailor!',ODh,OAh ; DATA XREF: MySeg:0102o
d) преобразование константы в смещение

Полученный листинг удобен для анализа, но все еще не готов к ассемблиро-
ванию, хотя бы уже потому, что никакой ассемблер не в состоянии зашифровать
требуемый код. Конечно, эту операцию можно выполнить вручную, после компи-
ляции, но IDA позволит проделать то же самое не выходя из нее и не прибегая к
помощи стороннего инструментария.
Демонстрация получится намного нагляднее, если в исследуемый файл внести
некоторые изменения, например, добавить ожидание клавиши на выходе. Для
этого можно прибегнуть к интегрированному в IDA ассемблеру, но прежде,
разумеется, необходимо несколько "раздвинуть" границы сегмента MySeg, дабы
было к чему дописывать новый код.
Выберете в меню "View" пункт "Segments" и в открывшемся окне подведите
курсор к стоке "MySeg". Нажатие <Ctrl-E> открывает диалог свойств сегмента,
содержащий среди прочих полей конечный адрес, который и требуется изменить.
Не обязательно указывать точное значение — можно "растянуть" сегмент с
небольшим запасом от предполагаемых изменений.
Если попытаться добавить к программе код "XOR AX,AX; INT 16h" он
неминуемо затрет начало строки "Hello, Sailor!", поэтому, ее необходимо забла-
говременно передвинуть немного "вниз" (т.е. в область более старших адресов),
например, с помощью скрипта следующего содержания:

, for(a=Ox108;a<Ox11A;a++) PatchByte([Ox2000,a+Ox20],Byte([Ox2000,a]);

Пояснение. Объявление переменной "a" для краткости опущено, длина строки
берется с запасом, чтобы не утомлять себя лишними вычислениями, и перемещение
происходит справа налево, поскольку исходный и целевой фрагменты заведомо не
пересекаются.

Подведя курсор к строке :0128 нажатием <A> преобразуем цепочку символов
к удобочитаемому виду; подводим курсор к строке :0102 и, выбрав в меню "Edir"
пункт "Path program", "Assembler", вводим команду "MOV DX,128h", где
"128h" — новое смещение строки, и тут же преобразуем его в смещение
нажатием <Ctrl-O>.
Вот теперь можно вводить новый текст — переместив курсор на инструкцию
"ret", вновь вызовем ассемблер и введем "XOR AX,AX<ENTER>INT 16h<Enter>RET
<Enter><Esc>". Напоследок рекомендуется произвести "косметическую" чист-
ку — уменьшить размер сегмента до необходимого и переместить строку "Hello,
Sailor" вверх, прижав ее вплотную к коду.

Пояснение. Удалить адреса, оставшиеся при уменьшении размеров сегмента за
его концом можно взводом флажка "Disable Address" в окне свойств сегмента,
вызываемом нажатием <Alt-S>

Если все было сделано правильно конечный результат должен выглядеть как
показано ниже:


IDA - 0030.htm

30 Первые шаги с IDA Pro

segOOO:0100 File Name : F:\IDAN\SRC\Crypt.com
segOOO:0100 Format : MS-DOS COM-file
segOOO:0100 Base Address: 1000h Range: 10100h-1013Ch Loaded length: 3Ch
segOOO:0100
segOOO:0100
segOOO:0100 ;

segOOO:0100
segOOO:0100 ; Segment type: Pure code
segOOO:0100 segOOO segment byte public 'CODE' use16
segOOO:0100 assume cs:segOOO
segOOO:0100 org 100h
segOOO:0100 assume es:nothing, ss:nothing, ds:segOOO, fs:nothing, gs:nothing
segOOO:0100
segOOO:0100 ; ------------ SUBROUTINE —— ————— — ————
segOOO:0100
segOOO:0100
segOOO:0100 public start
segOOO:0100 start proc near
segOOO:0100 add si, 6
segOOO:0103 jmp si ; Переход no адресу 0106
segOOO:0103 start endp
segOOO:0103
segOOO:0103 ; ————————————————- ——————— -—————— ————————
segOOO:0105 db OB9h ; ш
segOOO:0106 ; ————————————————————————————————————————————
segOOO:0106 mov si, offset BytesToDecrypt
segOOO:0109 lodsw
segOOO:010A xchg ax, cx
segOOO:010B push si
segOOO:010C
segOOO:010C loc_0_10C: ; CODE XREF: segOOO:0110j
segOOO:010C xor byte ptr [si], 66h
segOOO:010F inc si
segOOO:0110 loop loc_0_10C
segOOO:0112
segOOO:0112 BreakHere: ; Переход по адресу 012E
segOOO:0112 jmp si
segOOO:0112 ; — — — — — ——— ——————— — ——— — — — — — — — — —
segOOO:0114 BytesToDecrypt dw 18h ; DATA XREF: segOOO:0106o
segOOO:0116 ; —————----- ———————— - ———— ----- — ————— — —— ——
segOOO:0116
segOOO:0116 loc_0_116: ; CODE XREF: segOOO:013Bu
segOOO:0116 mov ah, 9
segOOO:0118 mov dx, 108h ; "Hello,Sailor!\r\nS"
segOOO:011B int 21h ; DOS - PRINT STRING
segOOO:011B ; DS:DX -> string terminated by "$"
segOOO:011D retn
segOOO:011D ; ————————————————————————
segOOO;011EaHelloSailor db 'Hello,Sailor!',ODh,OAh,'S' ; DATA XREF; segOOO:0118o
segOOO:012E ; ——————————————————————————
segOOO:012E
segOOO:012E loc_OJ2E: ; CODE XREF: segOOO;0112u


IDA - 0031.htm

________________Первые шаги с IDA Pro______________31

segOOO:012E call $+3
segOOO:0131 pop cx
segOOO:0132 pop si
segOOO:0133 mov di, 100h
segOOO:0136 push di
segOOO:0137 sub cx, si
segOOO:0139 гере movsb
segOOO:013B retn
segOOO:013B segOOO ends
segOOO:013B
MySeg:0100 ; ——————— ———— — — — — — — — — — — — — — —
MySeg:0100 ;

MySeg:0100
MySeg:0100 ; Segment type: Regular
MySeg:0100 MySeg segment byte public '' use16
MySeg:0100 assume cs:MySeg
MySeg:0100 ;org 100h
MySeg:0100 assume es:nothing, ss:nothing, ds:nothing, fs:nothing,
gs:nothing
MySeg:0100
MySeg:0100 loc_1000_100: ; CODE XREF: segOOO:013Bu
MySeg:0100 mov ah, 9
MySeg:0102 mov dx, offset aHelloSailor_0 ; "Hello,Sailor!\r\n$"
MySeg:0105 int 21h ; DOS - PRINT STRING
MySeg:0105 ; DS:DX -> string terminated by "$"
MySeg:0107 xor ax, ax
MySeg:0109 int 16h ;KEYBOARD-READ CHAR FROM BUFFER,WAIT IF EMPTY
MySeg:0109 ; Return: AH = scan code, AL =
character
MySeg:010B retn
MySeg:010B ; ------------------------------------------------------------------------
MySeg:010CaHelloSailor_0 db 'Hello,Sailor!',ODh,OAh,$ ; DATA XREF: MySeg:0102o
MySeg:010C MySeg ends
MySeg:010C
MySeg:010C
MySeg:010C end start
a) окончательно дизассемблированный текст

Структурно программа состоит из следующих частей — расшифровщика,
занимающего адреса segOOO:OxlOO—segOOO:Oxll3, переменной размером в слово,
содержащей количество расшифровываемых байт, занимающей адреса
segOOO:Oxll4—segOOO:Oxll6, исполняемого кода программы, занимающего цели-
ком сегмент MySeg и загрузчика, занимающего адреса segOOO:Oxl2E—
segOOO:Oxl3B.
Все эти части должны быть в перечисленном порядке скопированы в целевой
файл, причем исполняемый код программы необходимо предварительно зашифро-
вать, произведя над каждым его байтом операцию XOR 0x66.
Ниже приведен пример скрипта, автоматически выполняющего указанные
действия. Для его загрузки достаточно нажать <F2> или выбрать в меню "File"
пункт "Load file", "IDC file".


IDA - 0032.htm

32 Первые шаги с IDA Pro
|| Компилятор для файла Crypt
//
static main()
{
auto a,f;

// Открывается файл Crtypt2.com для записи в двоичном режиме
f=fopen("crypt2.com","wb");

// В файл Crypt2 копируется расшифровщик
for (a=Ox100;a<Ox114;a++) fputc(Byte([Ox1000,a]),f);
// Определяется и копируется в файл слово, содержащее число
// байтов для расшифровки
fputc( SegEnd([Ox2000,Ox100]) - SegStart([Ox2000,Ox100]),f);
fputc(0,f);

// Копируется и "на лету" шифруется расшифрованный фрагмент
for(a=SegStart([Ox2000,Ox100]);a!=SegEnd([Ox2000,Ox100]);a++)
fputc(Byte(a) ^ Ox66,f);

// Дописывается загрузчик
for(a=Ox12E;a<Ox13C;а++)
fputc(Byte([Ox1000,a]),f);

// Закрывается файл.
fclose(f);
}
а) исходный код скрипта-компилятора
Подробное объяснение каждой функции, встретившейся в скрипте, можно
найти в главах "Сегменты и селекторы", "Файловый ввод-вывод" и других.
Выполнение скрипта приведет к созданию файла "Crypt2.com", запустив
который, можно убедиться в его работоспособности — он выводит строку на
экран и, дождавшись нажатия любой клавиши, завершает свою работу.
Огромным преимуществом такого подхода является "сквозная" компиляция
файла, т.е. дизассемблированный листинг в действительности не ассемблировал-
ся! Вместо этого из виртуальной памяти байт-за-байтом читалось оригинальное
содержимое, которое за исключением модифицированных строк доподлинно иден-
тично исходному файлу. Напротив, повторное ассемблирование практически
никогда не позволяет добиться полного сходства с дизассемблируемым файлом.
IDA — очень удобный инструмент для модификации файлов, исходные тексты
которых утеряны или отсутствуют; она практически единственный дизассемблер,
способный анализировать зашифрованные программы, не прибегая к сторонним
средствам; она обладает развитым пользовательским интерфейсом и удобной
системой навигации по исследуемому тексту; она дает возможность справиться с
любой мыслимой и немыслимой задачей.
...но эти, и многие другие возможности невозможно реализовать в полной
мере без владения языком скриптов, что и подтвердил приведенный выше пример.


IDA - 0033.htm

Язык скриптов IDA Cu 33
Язык скриптов IDA Си
IDA поддерживает Си-подобный скрипт-язык, в целом напоминающий Си Кери-
*гана и Ричи, но значительно упрощенный, не поддерживающий типов, массивов,
указателей и в том числе не обладающий возможностью отладки приложений. Язык
скриптов изначально задумывался как средство выполнения простейших операций,
укладывающихся в десяток-другой строк кода, а для "серьезных" задач предусмотрен
механизм плагинов — внешних модулей, написанных на Borland C++ или Microsoft
Visual С++ и подключаемых к IDA по мере необходимости. Впрочем, в подавляющем
большинстве случаев нет никакой нужды прибегать к плагинам и вполне можно
обойтись встроенным скрипт-языком. Ниже приводится краткое описание языка
скриптов IDA Си, рассчитанное на читателя, владеющего "классическим" Си.

Консоль
Простейшие скрипты могут быть набраны "не отходя от кассы" в диалоге,
вызываемом нажатием <Shif-F2>, в дальнейшем именуемым консолью. Достоин-
ство этого метода — оперативность, а недостатки — жесткое ограничение мак-
симально допустимого размера вводимого текста (порядка одного килобайта) и
невозможность работать более чем с одним скриптом одновременно.
Более сложные скрипты, как правило, создаются в отдельном файле, загружа-
емом нажатием <F2> или заданием ключа "-Sfilename" в командной строке,
причем между "-S" и именем файла не должно присутствовать символа пробела.
Результаты работы скрипта (и служебные сообщения самой IDA) выводятся
в специальную область экрана, напоминающую собой бесконечную телетайпную
ленту, условно именуемую "консолью". Поскольку одна консоль связана с вво-
дом (исходного текста скрипта), а другая с выводом (результатов работы),
никакого противоречия не возникает, и по контексту легко понять, о какой
именно консоли идет речь (рис. 8, 9).

Функции, объявление функций, аргументы
функций, возвращаемое значение
IDA поддерживает функции двух типов — встроенные (build-in) функции и
функции, определяемые пользователем. Объявление пользовательских функций
происходит следующим образом — перед именем функции указывается ключевое
слово "static", a за именем в круглых скобках через запятую перечисляются
аргументы (если они есть) без указания их типа, например, так:
static MyFunc() static MyOtherFunc(x,y,sO)
{ {
// тело функции; // тело функции;
} }


IDA - 0034.htm

34 Язык скриптов IDA Cu
IDA - 0034-1.jpg
Рис. 8. IDAC: область вывода результатов работы скрипта "консоль"
IDA - 0034-2.jpg
Рис. 9. IDAG: область вывода результатов работы скрипта "консоль"


IDA - 0035.htm

Язык скриптов IDA Cu 35
Ограничения
Максимально допустимая длина имени функции равна 16 символам; возмож-
ность организовывать вложенные функции отсутствует.
Консоль не поддерживает объявления функций, сообщая при этом о синтак-
сической ошибке, функции могут быть объявлены только в idc-файлах.
Все функции, объявленные как "static" (т.е. все функции вообще, поскольку,
"локальные функции IDA не поддерживает) будучи однажды загруженными,
резидентно остаются в памяти на протяжении всего сеанса работа с дизассемб-
лером и в любой момент доступны для вызова из консоли или других программ.
Например, если создать idc-файл следующего содержания: "static Demo(sO)
{Warning(sO);}", загрузить его нажатием <F2>, нажать <Shift-F2> для вызова
консоли и набрать нечто наподобие "Demo("Hello, ШАГ);" появится диалоговое
окно, выводящее указанную строку на экран.
—54а{}€-функции не могут быть выгружены из памяти, но могут перекрываться
повторным объявлением. Встроенные в IDA функции перекрыть нельзя.
Если в тексте скрипта объявлена функция main(), она автоматически выпол-
няется при его загрузке.
Возврат значения функции осуществляется посредством "return" с последую-
щим указанием имени переменной или константы, например, так:
static MyFunc(x,y) staic MyOtherFunc()
{' {
return x-y; return "Hello, IDA Pro\\n";
} }
Пустой "return", равно как и его отсутствие, возвращает нулевое значение
(пустую строку).

Объявления переменных, типы переменных,
преобразования переменных
Для объявления переменных используется ключевое слово auto за которым
последовательно перечисляются декларируемые переменные, отделенные друг от
друга запятой. Инициализация при объявлении не поддерживается.
Например:
auto x; auto x,y,z;
а)верно

auto x=1;
b)неверно

Все переменные, объявленные таким образом, локальные; поддержка глобаль-
ных переменных, доступных всем функциям отсутствует. Вместо этого IDA
поддерживает виртуальные массивы, доступные всем функциям всех загру-
женных скриптов одновременно (см. главу "Виртуальные массивы").
Имя переменной не должно совпадать ни с одним зарезервированным ключе-
вым словом. Зарезервированные ключевые слова в IDA те же самые, что и в Си.


IDA - 0036.htm

36 Язык скриптов IDA Cu
IDA поддерживает два типа переменных — переменные типа строка и
переменные типа число.
Строковые переменные ограничены максимальной длиной в 255 символов и
заканчиваются нулем. Над ними доступны три операции — присвоение, кон-
текция (слияние) и сравнение.
Переменные типа число в свою очередь делятся на два подтипа: long —
32-разрядное знаковое целое и float — число с плавающей запятой до 25
разрядов. Над ними обоими доступны операции — присвоение, сравнение,
сложение, вычитание, умножение, деление. Над целочисленными перемен-
ными доступны битовые операции — циклический сдвиг вправо и влево,
битовое И, битовое НЕ, битовое ИЛИ, битовое НЕТ и битовое ИЛИ-ИСКЛЮ-
ЧАЮЩЕЕ-И.
Преобразования типов переменных осуществляются автоматически и проис-
ходят в следующих случаях:

• если левая переменная — строка, то и правая переменная преобразуется в
строку;
• если левая переменная — длинное целое, а правая — строка, правая
переменная преобразуется в длинное целое;
• если одна из переменных имеет тип float, все остальные переменные
преобразуются в тип float;
• если над переменной выполняется одна из битовых операций, она преобра-
зуется в длинное целое.

Преобразование "строка -> длинное целое" осуществляется по следующей
схеме: если левая часть строки представляет собой число, записанное в десяти-
чной нотации, результат преобразования равен этому числу, в противном слу-
чае — нулю.
Примеры:

auto sO,s1,s2,s3;
sO="1234"; s1="1234mystring","Ox1234","MyString";
Message(">%d,%d,%d,%d\n",sO,s1,s2,s3);

>1234,1234,0,0

Преобразование "длинное целое -> строка" осуществляется путем дописыва-
ния завершающего нуля к преобразуемому значению.
Примеры:

auto a,b;
a=0x21;b=0x22232425;
Message(">%s,%s\n",a,b);

> !,%$#"

Преобразование "строка -- float" осуществляется аналогично преобразованию
"строка —> длинное целое", включая дробную часть; если же преобразование
невозможно, результат равен нулю.
Например:


IDA - 0037.htm

Язык скриптов IDA Cu 37
auto sO,s1;
sO="1.1"; s1="MyString";
Message(">%f,%f\n",sO,s1);
> 1.1, 0.

Преобразование "float -> строка" в отличие от преобразования "длинное
целое -> строка" осуществляет действительно корректное преобразование, срав-
ните:
auto f; auto а;
f="1.2"; a=Ox21;
Message(">%s\n",f); Message(">%s\n",a);

> 1.2 >!

При сложении двух строковых переменных происходит их конкатенация
(слияние); при вычитании одной строки из другой левый операнд игнорируется,
а над каждым символом правого выполняется операция NEG (дополнение до
нуля); при делении и умножении строк друг на друга они преобразуются к
длинному целому. Операции вычитания, деления и умножения строк недокумен-
тированны и по-разному могут вести себя в зависимости от версии IDA. Рекомен-
дуется воздержаться от использования их в собственных программах.
Смешивание операндов различных типов заставляет более внимательно отно-
ситься к порядку расстановки их в выражении — результат может зависеть от
перемены мест слагаемых, например:
auto x,sO;x=1;sO="3h"; auto x,sO;x=1;sO="3h";
Message(">%x\n",x+sO); Message(">%x\n",sO+x);

>4 >3

Пояснение. В первом случае левая переменная — длинное целое, поэтому, строка
"3h" преобразуется к длинному целому, в результате чего получается: 1+3=4; во
втором случае левая переменная — строка, поэтому, длинное целое #1 преобразу-
ется к строке, в результате чего получается: "3h"+'\xl' = "3h\xl", но затем
"3h\xl" вновь преобразуется к длинному целому, ибо того требует спецификатор
"%x", a "3h\xl" = #3. Так что от перемены мест слагаемых сумма меняется!

Смещение типов — частый источник трудноуловимых ошибок, и лучше
использовать явное преобразование вызовом следующих функций:

• long (переменная) — преобразует переменную в длинное целое;
• char (переменная) — преобразует переменную в строку;
• float(переменная) — преобразует переменную в тип float.

Например:
auto x,sO;x=1;sO="3h"; auto x,sO;x=1;sO="3h";
Message(">%d\n",long(sO)+x); Message(">%d\n",x+long(sO));

>4 >4

На этот раз от перемены мест слагаемых сумма не изменяется!


IDA - 0038.htm

38 Язык скриптов IDA Cu
Директивы

IDA поддерживает следующие стандартные директивы препроцессора:

• #define;
• #undef;
• #include;
• #error;
• #ifdef\#ifndef\#else\#endif.

Внимание! Консоль не поддерживает никаких директив препроцессора — их
можно использовать только в IDC-файлах.

Замечание. Для использования определений, констант, приводимых в этой книге
по ходу описания функций, в исходный код скрипта необходимо включить директи-
вы "#include <idc.idc>".
Вызов функций возможен и без включения файла <idc.idc> в исходный листинг —
это необходимо только для использования определений, например, таких, как
BADADDR.
До вызова консоли IDA самостоятельно подключает к ней этот файл, делая
доступными все определения.
Предписания
IDA поддерживает следующие стандартные конструкции, изменяющие нор-
мальный ход выполнения программы:

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

Замечание. Цикл "for (exprl; expr2; expr3 ) statement" в отличие от стандарт-
ного языка Си не поддерживает более одного счетчика.

Математические и битовые операторы
Поддерживаются следующие математические операторы — сложение: "+",
вычитание: "-", умножение: "*", деление "/", приращение на единицу
"++". Операторы "+=" и "-=" не поддерживаются.
Поддерживаются следующие битовые операторы — битовое И: "&", битовое
ИЛИ: |, битовое НЕТ: "!", битовое ИЛИ-ИСКЛЮЧАЮЩЕЕ-И: "^".

Массивы
Встроенной языковой поддержки массивов, наподобие той, что присутствует
в Си, у IDA нет.


IDA - 0039.htm

Виртуальная память 39
Виртуальная память
Архитектура виртуальной памяти
В отличие от многих других дизассемблеров, IDA работает не с исследуе-
мым файлом, а с его образом, загруженным в виртуальную память. IDA
эмулирует загрузчик операционной системы, благодаря чему образ загружен-
ного в дизассемблер файла идентичен образу того же файла, загруженного
операционной системой.
IDA использует плоскую 32-разрядную модель виртуальной памяти, предо-
ставляя в распоряжение пользователя немногим менее четырех гигабайт адресно-
го пространства. Наибольший возможный адрес равен 0xFF000000 и для удобства
определен в файле <idc.idc> через константу MAXADDR. Попытка задания
больших адресов приведет к ошибке.
Адресное пространство виртуальной памяти может быть разбито на один или
более сегментов (см. главу "Сегменты и селекторы"), однако, подавляющее
большинство встроенных функций IDA ожидают не сегментных, а линейных
адресов.
Адресное пространство виртуальной памяти не непрерывно — в нем могут
присутствовать "выключенные" адреса, попытка обращения к которым приведет
к ошибке. IDA не предоставляет функций, позволяющих манипулировать "вклю-
чением" — "выключением" адресов. Эта операция может быть выполнена только
косвенно — адреса "включаются" при загрузке бинарного файла и создании
нового сегмента, а выключаются при его удалении (см. описание функции
SegCreate).
С каждым "включенным" адресом виртуальной памяти связана специальная
структура данных, включающая в себя 8-разрядную ячейку виртуальной памяти
и 24-разрядное поле атрибутов этой ячейки.
Атрибуты, в частности, указывают, следует ли отображать ячейку как
инструкцию или как данные, в каком виде следует представлять операнды и
т.д. Назначение каждого бита атрибутов подробно описано в главах "Элемен-
ты", "Типы элементов", "Операнды" и "Объекты". Поле атрибутов доступно
не только для косвенного (посредством вызова встроенных в IDA функций), но
и непосредственного чтения и изменения. Однако, разработчики IDA настоя-
тельно рекомендуют избегать непосредственной модификации, поскольку, на-
значение тех или иных битов атрибутов в последующих версиях дизассемблера
может измениться!
Ячейка может иметь как инициализированное, так и неинициализиро-
ванное значение. Попытка чтения неинициализированной ячейки возвращает
ошибку. Определить инициализирована ячейка или нет можно по состоянию
младшего бита поля атрибутов — если ячейка инициализирована, он равен


IDA - 0040.htm

40 Виртуальная память
одному, а если не инициализирована — нулю. 32-разрядное целое, содержащее
ячейку и связанные с ней атрибуты, называется флагом виртуальной памяти
(см. рис. 10).
IDA - 0040-1.jpg
Рис. 10. Строение флага виртуальной памяти

Флаги виртуальной памяти хранятся в виртуальном массиве, подробнее об
организации которого рассказано в главе "Виртуальные массивы".
Сами же виртуальные массивы хранятся в страничной физической памяти.
Совокупность страниц физической страничной памяти в документации и контек-
стовой помощи IDA называется виртуальной памятью. Такая терминологическая
путаница требует постоянного уточнения о чем собственно идет речь — образе
загруженного файла или виртуальном массиве, поэтому, во избежание двусмыс-
ленного понимания, здесь и далее память, хранящая виртуальные массивы, будет
называется страничной, а память, хранящая образ загруженного файла — вирту-
альной.

Технические детали
Виртуальное адресное пространство представляет собой совокупность индек-
сов всех существующих элементов виртуального массива, а "выключенные"
адреса являются отсутствующими индексами виртуального массива.
Виртуальный массив располагается в файле *.idl, формат заголовка которого
приведен в табл. 1.

Таблица 1. Структура файла *.id1

Смещение | длина | значение
0x0 | 0x4 | Сигнатура "Va4" — "Virtual Array version 4"
0x4 | 0x2 (Word) | Количество непрерывных регионов памяти
0x6 | 0x2 (Word) | Количество страниц всего (одна страница равна 4 кб)
0x8 | 0x4 (long) | Индекс (он же линейный адрес) первого кванта региона
0xC | 0x4 (long) | Индекс (он же линейный адрес) последнего кванта региона
0x10 | 0x4 (long) | Смещение индекса первого кванта региона в файле *.idl
+0x4 | 0x4 (long) | Индекс (он же линейный адрес) первого кванта следующего региона
+0x4 | 0x4 (long) | Индекс (он же линейный адрес) последнего кванта региона
+0x4 | 0x4 (long) | Смещение индекса первого кванта следующего региона в файле *.idl
Если открыть файл "tutor.idl" в любом шестнадцатиричном редакторе (вни-
мание, это необходимо сделать до выхода из IDA, т.к. при выходе он будет
автоматически удален), его начало должно выглядеть так:


IDA - 0041.htm

________________Виртуальная память_______________41
IDA - 0041-1.jpg
1) Три страницы виртуальной памяти израсходовано (размер страницы 4 кб)
2 Два непрерывных адресных пространства 0x10666-x10677 и 0x10777 и 0x10788
3 Начальный адрес первого непрерывного региона виртуальной памяти
4 Конечный адрес первого непрерывного региона виртуальной памяти
5 Сигнатура, позволяющая распознать тип файла
6 Смещение, по которому хранится первый регион виртуальной памяти в этом файле
7 Начальный адрес второго непрерывного региона виртуальной памяти
8 Конечный адрес второго непрерывного региона виртуальной памяти
9 Смещение, по которому хранится второй регион виртуальной памяти в этом файле

По смещению 0x003998 (помним об обратном порядке байтов) в файле
"tutor.idl" находится следующее содержимое:
00003998: 68 21 00 00 45 01 00 00 4С 01 00 00 4С 01 00 00 Н! €® 1® ДО
000039A8: 4F 01 00 00 ОС 01 00 00 00 01 00 00 69 01 00 00 о© ,© © I©
000039В8: 64 01 00 00 61 01 00 00 00 01 00 00 70 01 00 00 D© A@ © P@
000039С8: 52 01 00 00 4F 01 00 00 21 01 00 00 20 01 00 00 г© о© !© ©
000039D8: OD 01 00 00 FF 01 00 00 00 00 00 00 00 00 00 00 J© ©
Это и есть флаги виртуальной памяти, содержащие, ячейки и атрибуты
виртуальной памяти, читая которые через каждый четвертый байт, можно разо-
брать "Hello, IDA Pro!"

Архитектура страничной памяти
Размещение виртуальной памяти в дисковом файле требует некоторое коли-
чество оперативной памяти компьютера под кэш-буферы, поскольку побайтовый
обмен с диском крайне непроизводителен.
Кэш IDA имеет страничную организацию. Вся виртуальная память разбита на
множество блоков одинакового размера, называемых страницами. Если проис-
ходит обращение к одной ячейке виртуальной памяти, страница, в которой эта
ячейка расположена, загружается в оперативную память целиком. Соответствен-
но, модификация одной ячейки виртуальной памяти влечет перезапись всей
страницы.
Разбивка на страницы осуществляется маскированием n младших битов
целевых адресов, по этой причине размер страницы всегда представляет собой
степень двойки. Если размер региона, выделенный под кэш буфер (также называ-
емый окном), превышает размер одной страницы, IDA может загружать в
оперативную память несколько страниц одновременно. Соответственно, размер
окна должен быть кратен размеру страницы.


IDA - 0042.htm

42 Виртуальная память

Если окно целиком заполнено, но требуется загрузить еще одну страницу, IDA
просматривает буфер на предмет поиска немодифицированной страницы, к кото-
рой дольше всего не было обращения. Отыскав такую, замещает ее прочитанной
с диска. В противном случае (если все страницы со времени последней загрузки
были модифицированы), IDA "сбрасывает" на диск самую старую страницу и
только затем замещает ее новой.
Во избежание потерь информации при сбоях питания, зависаниях дизассемб-
лера и т.д., предусмотрен автоматический "сброс" модифицированных страниц
через определенные промежутки времени, длительность которых задается значе-
нием поля "AUTOSAVE" файла <idatui.cfg> и <idagui.cfg> — консольной и
графической версий соответственно. По умолчанию буферы сохраняются после
совершения пользователем ста любых действий или по истечении пяти минут, при
этом в окне сообщений появится поясняющая надпись "Flushing buffers, please
wait...ok".

Замечание. IDA не учитывает "популярность" страницы (т.е. частоту ее
использования или количество обращений к ней), а только время последнего
обращения (чтения или записи).
IDA - 0042-1.jpg
Рис. 11. Окно страничной памяти
Увеличение размера страниц увеличивает вероятность того, что очередной
запрашиваемый байт окажется уже загруженным в оперативную память, но в то
же время, повышает накладные расходы на сброс модифицированных страниц и
уменьшает количество страниц в окне. Поскольку размер окна сверху ограничен
объемом доступной оперативной памяти, возникает задача вычисления оптималь-
ного соотношения между размером и количеством страниц.
Размеры и количество страниц задаются полями "VPAGES" и "VPAGESIZE"
файла <ida.cfg> соответственно. Если VPAGES == 0, IDA пытается самостоя-
тельно вычислить оптимальное значение, исходя из количества требующейся для
анализа загруженного файла виртуальной памяти.
Поскольку каждый флаг виртуальной памяти требует четыре байта странич-
ной памяти (т.к. помимо 8 бит содержимого ячейки хранит 24 бита атрибутов),
грубо оценить потребности страничной памяти можно умножением размера
загружаемого файла на четыре. Разумеется, в зависимости от формата файла,


IDA - 0043.htm

Виртуальная память 43

истинное значение может значительно отличаться от расчетного, и предсказанная
оценка окажется неверной. Это не нарушит работоспособности дизассемблера,
поскольку недостающая память будет выделена автоматически по мере необходи-
мости, однако, неоптимальное соотношение размера и количества страниц ухуд-
шат производительность, поэтому в некоторых случаях его выгоднее вычислять
вручную.
По умолчанию размер страницы (VPAGESIZE) в зависимости от версии IDA
равен либо 4096, либо 8192 байт, а поскольку количество страниц выражается
16-разрядным целым числом, доступное адресное пространство виртуальной па-
мяти равно 64 и 128 мегабайт соответственно. Дальнейшее увеличение размера
страниц расширяет границы доступного адресного пространства виртуальной
памяти, но одновременно с этим ухудшает производительность за счет большей
грануляции, поэтому прибегать к нему рекомендуется в тех и только в тех
случаях, когда требуется свыше 128 мегабайт виртуальной памяти.
Ввиду страничной адресации базы, IDA не позволяет "на лету" изменять размер
страниц и при загрузке файлов *.idb величина размера страниц берется оттуда, а
значения поля VPAGESIZE игнорируется. Поэтому, необходимо внимательно отне-
стись к выбору размера страницы — в дальнейшем изменить его не удастся!
Существует возможность необратимо уничтожить результаты своей работы, выбрав
слишком малый размер страниц — в этом случае адресного пространства виртуаль-
ной памяти может не хватить и попытка выделения очередного блока виртуальной
памяти провалится, а сеанс работы с IDA аварийно завершится!
Поле VPAGES, задающее размер буфера окна в страницах, по умолчанию
равно нулю, и его значение IDA самостоятельно вычисляет по следующему
алгоритму (см. табл. 2).

Таблица 2. Алгоритм автоматического выделения памяти
IDA - 0043-1.jpg
Важно понять — размер буфера окна не ограничивает количество доступной
страничной памяти! Окно — всего лишь кэш-буфер, и IDA будет работать даже в
том случае, если уменьшить его до размера одной страницы (правда, это чрезвычайно
снизит производительность). Операционные системы Windows и OS/2 позволяют
выделить под окно больше памяти, чем ее имеется в наличии, сбрасывая избыток
данных на диск в файл подкачки. В результате количество обращений к диску
удваивается и производительность дизассемблера резко падает. Рекомендуется
выбирать размер окна таким образом, чтобы он полностью умещался в физической
памяти компьютера. Значения, вычисляемые IDA по умолчанию, рассчитаны на
компьютеры, обладающем 16-ю или менее мегабайтами оперативной памяти, при
наличии же большего ее количества (на конец 2000 года компьютер типичной


IDA - 0044.htm

44 Виртуальная память

конфигурации содержал 32—64 мегабайт оперативной памяти) можно значи-
тельно улучшить производительность, увеличив размер буфера окна.
Помимо виртуальной памяти, содержащей образ загруженного файла, дизас-
семблеру требуется какое-то количество страничной памяти для хранения меток,
имен функций, комментариев и т.д. В терминологии IDA такая память именуется
DATEBASE_MEMORY или Memory for b-tree — память базы данных двоич-
ного дерева. Поле "DATEBASE_MEMORY" конфигурационного файла <ida.cfg>
позволяет изменять выделенное количество памяти под буфер базы данных.
Объем памяти измеряется в байтах, но округляется до целого числа страниц,
размер которых в текущих версиях IDA равен 8 килобайт (8192 байт). Для
нормальной работы дизассемблеру необходимо по крайней мере 5 страниц (40
килобайт), в противном случае IDA сообщит о нехватке памяти "bTree error: not
enough memory" и аварийно завершит работу.
Если DATEBASE_MEMORY = 0, IDA самостоятельно определяет оптималь-
ный размер буфера по следующему алгоритму (см. табл. 3):
Таблица 3. Алгоритм автоматического определения размера окна
IDA - 0044-1.jpg
С целью увеличения производительности IDA динамически создает список
указателей на имена меток, для работы с которым также требуется некоторое
количество страничной памяти. Размер буфера в страницах задается значением
поля NPAGES файла <ida.cfg>, а размер страницы — значением поля NPAGES-
SIZE. По умолчанию 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...


IDA - 0045.htm

________________Виртуальная память_______________4i

Взаимодействие с физической
памятью
Взаимодействие с физической памятью становится возможным благодаря
наличию четырех недокументированных функций _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 а;
SegCreate(OxFOOOO,0xFFFFF, 0x0F000,0,0,0);
Меззаgе('Ждите... читаю BIOS...");
for (a=0;a<OxFFFF;a++)
PatchByte(OxFOOOO+a, _peek(OxFOOOO+a));
Message("OK \n Дизассемблирую обработчик Int 0x13");
MakeCode(OxFEC59);
Message("OK \n");
Jump(OxFEC59);
По окончании работы скрипта экран дизассемблера должен выглядеть так:
seg001:EC59loc_EOOO_EC59: ; CODE XREF: seg001:0188J
seg001:EC59 ; seg001:019BJ
seg001:EC59 jmp loc_EOOO_EE3B
seg001:EC5C ; ———————————————————————————————————————
seg001:EC5C mov al, OCOh ; '„'
seg001:EC5E call sub_EOOO_EC8E
seg001;EC61 jmp short loc_EOOO_EC66
seg001:EC63 ; ————————-—————————————————————————————
seg001:EC63 call loc_EOOO_EC8A
seg001:EC66
seg001:EC66 loc_EOOO_EC66: ; CODE XREF: seg001:EC61j


IDA - 0046.htm

46 Виртуальная память

seg001:EC66 call sub_EOOO_EC6C
seg001:EC69 cmp al, ah
seg001:EC6B retn

Навигатор по функциям
Начать изучение виртуальной памяти лучше всего с загрузки двоичного
(бинарного) файла. Сначала необходимо создать сам файл — это можно сделать
командой "echo Hello, IDA Pro! > tutor.bin".
IDA - 0046-1.jpg
Рис. 12. Диалог загрузки бинарного файла

Дождавшись появления диалога загрузки консольной версии IDA Pro,
запомните базовый адрес сегмента, записанный в поле "Loading segment ...
(in paragraphs)", измените адрес загрузки на любой отличный от нуля
(нулевое смещение крайне ненаглядно для иллюстрации), скажем, на 0x666, и
сбросьте флажок "Create segment" для предотвращения автоматического со-
здания сегмента (работа с сегментами будет рассмотрена позже, в главе
"Сегменты и селекторы").

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

После окончания загрузки файла экран дизассемблера должен выглядеть
следующим образом:
0:00010666 ; File Name : F:\IDAN\SRC\1\tutor.bin
0:00010666 ; Format : Binary File
0:00010666 ; Base Address: 1000h Range: 10666h - 10678h Loaded length: 0012h
0:00010666


IDA - 0047.htm

________________Виртуальная память_______________47

0:00010666
0:00010666 db 48h H
0:00010667 db 65h e
0:00010668 db 6Ch 1
0:00010669 db 6Ch 1
0:0001066A db 6Fh о
0:0001066B db 2Ch ,
0:0001066C db 20h
0:0001066D db 49h I
0:0001066E db 44h D
0:0001066F db 41h А
0:00010670 db 20h
0:00010671 db 50h P
0:00010672 db 72h r
0:00010673 db 6Fh о
0:00010674 db 21h !
0:00010675 db 20h
0:00010676 db ODh
0:00010677 db OAh
0:00010677
0:00010677 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), определенным в <idc.idc>,
который следует вызывать так:

if(hasValue(GetFlags(ea)))
// значение ячейки определено, можно читать;
else
// значение ячейки не определено либо ячейка не существует


IDA - 0048.htm

48 Виртуальная память

Более короткий путь предоставляет макрос isLoaded(ea), определенный там
же с аналогичным назначением:

if(isLoaded(ea))
// значение ячейки определено, можно читать',
else
// значение ячейки не определено либо ячейка не существует\

Замечание. Макрос byteValue(F), определенный в файле <idc.idc>, при правиль-
ном употреблении позволяет сократить количество вызов GetFlags, а, следова-
тельно, увеличить производительность программы. Вызывать его следует так:

F = GetFlags(ea);
.if (hasValue(F)) val = byteValue(F);

Использование функции Byte обычно требует двух вызов GetFlags — один раз для
проверки значения .ячейки, второй — для ее чтения. Если же ячейка заведомо
существует и гарантированно содержит инициализированное значение, то провер-
ку можно опустить — в этом случае макрос byteValue не будет иметь никаких,
преимуществ перед функцией Byte.

Пример использования:

auto а;
Message(">");
for (a=0x10666;a<0x10676;a++)
if (isLoaded(a)) Message("%c",Byte(a));
else Message("!ОШИБКА!\n");

>Hello, IDA Pro!

Модификация ячеек виртуальной памяти осуществляется функциями Patch-
Byte (long ea, long value), PatchWord (long ea, long value) и PatchDword
(long ea, long value) записывающих байт, слово и двойное слово соответственно.
Попытка модификации несуществующей ячейки виртуальной памяти заканчи-
вается провалом.
Следующий пример меняет регистр всех символов на противоположный:

0:00010666 db 48h H
0:00010667 db 65h e
0:00010668 db 6Ch 1
0:00010669 db 6Ch 1
0:0001066A db 6Fh о
0:0001066B db 2Ch ,
0:0001066C db 20h
0:0001066D db 49h I
0:0001066E db 44h D
0:0001066F db 41h А
0:00010670 db 20h
0:00010671 db 50h P
0:00010672 db 72h r
0:00010673 db 6Fh о
0:00010674 db 21h !


IDA - 0049.htm

Виртуальная память 49

0:00010675 db 20h ;
0:00010676 db ODh ;
0:00010677 db OAh ;
а) исходные данные — требуется заменить регистр всех символов на противоположный

auto а;
for(a=Ox10666;a<0x10674;a++)
PatchByte(a, Byte(a) ^ 0x20);
b) скрипт, меняющий регистр всех символов на противоположный посредством вызова
функции PatchByte

0:00010666 db 68h h
0:00010667 db 45h E
0:00010668 db 4Ch L
0:00010669 db 4Ch L
0:0001066A db 4Fh 0
0:0001066B db OCh
0:0001066C db 0
0:0001066D db 69h i
0:0001066E db 64h d
0:OOQ1066F db 61h а
0:00010670 db 0
0:00010671 db 70h p
0:00010672 db 52h R
0:00010673 db 4Fh 0
,0:00010674 db 21h !
0:00010675 db 20h
0:00010676 db ODh
0:00010677 db OFFh
с) результат — регистр всех символов изменен на противоположный

Пара функций NextAddr(long ea) и PrevAddr(long ea) позволяют трас-
сировать адресное пространство виртуальной памяти, "проходясь" по цепочке
"включенных" адресов.
Функция NextAddr(long ea) возвращает первый "включенный" адрес, следую-
щий за "ea", соответственно PrevAddr(long ea) возвращает первый "включенный"
адрес, предшествующий "ea".

Пример использования:

auto а;
a=0;
while(1)
{
a=NextAddr(a);
if (a==BADADDR) break;
Message("0:%08X\tdb %x;",a,Byte(a));
if (Byte(a)>Ox20)
Message("'%c'",Byte(a));
Message("\n");
}
а) скрипт, демонстрирующий трассировку адресов


IDA - 0050.htm

50 Виртуальная память
0:00010666 db 68;'h'
0:00010667 db 45;'E'
0:00010668 db 4c;'L'
0:00010669 db 4c;'L'
0:0001066A db 4f;'0'
0:0001066B db с;
0:0001066C db 0;
0:0001066D db 69;'i'
0:0001066E db 64;'d'
0:0001066F db 61;'a'
0:00010670 db 0;
0:00010671 db 70;'p'
0:00010672 db 52;'R'
0:00010673 db 4f;'0'
0:00010674 db 21;'!'
0:00010675 db 20;
0:00010676 db d;
0:00010677 db ff;' '
b) результат — отображены только существующие адреса
Сводная таблица функций

Функции, возвращающие значение ячейки виртуальной памяти
long Byte (long ea) | Возвращает содержимое ячейки виртуальной памяти, располо- женной по адресу еа
long Word (long ea) | Возвращает содержимое ячеек виртуальной памяти, располо- женных по адресам еа и еа+1, располагая их в младшем и старшем байте машинного слова соответственно
long Dword (long ea) | Возвращает содержимое ячеек виртуальной памяти, расположен- ных по адресам еа, еа+1, еа+2 и еа+3, располагая их в младших и старших байтах младшего и старшего слова соответственно
Функции, модифицирующие значение ячейки виртуальной памяти
void PatchByte (long ea, long value) | Записывает в ячейку виртуальной памяти, расположенную по адресу еа, значение value
void PatchWord (long ea, long value) | Записывает в ячейки виртуальной памяти, расположенные по адресам еа и еа+1, младший и старший байт значения value соответственно
void PatchDword (long еа, long value) | Записывает в ячейки виртуальной памяти, расположенные по адресам еа, еа+1, еа+2 и еа+3 младшие и старшие байты младшего и старшего слова соответственно
Функции, трассирующие адреса виртуальной памяти
long NextAddr (long ea) | Возвращает следующий линейный адрес, если он существует, в противном случае — ошибку
long PrevAddr (long ea) | Возвращает предыдущий линейный адрес, если он существует, в противном случае — ошибку
Функции поиска
long FindBinary (long еа, long flag, char str) | Ищет заданную подстроку в виртуальной памяти


IDA - 0051.htm

Виртуальная память 51

Функции, манипулирующие с флагами
long GetFlags (long ea) | Возвращает | значения флагов виртуальной памяти
long SetFlags (long еа, long flags) | Задает новые значения флагов виртуальной памяти
long Byte (long еа)
Функция возвращает значение байта виртуальной памяти, расположенного по адресу еа. Если указанный адрес не существует, функция возвращает 0xFF, сигнализируя об ошибке, такое же значение возвратится и в том случае если ячейка имеет не инициализированное значение.
Поэтому, до вызова функции Byte следует убедиться, что ячейка действительно существует и имеет определенное значение. Проверить это можно, проанализировав младший бит поля атрибутов, определенный в файле <idc.idc> константой FF_INV, — если он не равен нулю, то все о'кей:
if (FF_INV & GetFlags(ea))) value=Byte(ea);
else // ячейка не существует или имеет неинициализированное значение
В файле <idc.idc> определены два макроса hasValue(F) и isLoaded(ea),
выполняющие такие проверки. Макрос hasValue(F) ожидает флаг виртуальной
памяти, a isLoaded(ea) линейный адрес ячейки, т.е.:
if(hasValue(GetFlags(ea))) value=Byte(ea);
else //ячейка не существует или имеет неинициализированное значение
if(isLoaded(ea)) value=Byte(ea);
else //ячейка не существует или имеет неинициализированное значение
Альтернативой функции Byte служит вызов GetFlags с последующей маскировкой старших 24 бит, содержащих поле атрибутов. Маской может служить определенная в файле <idc.idc> константа MS_VAL или непосредственное значение — 0xFF. Это может выглядеть так: value = (MS_VAL & GetFlags(ea)).
Для увеличения производительности скрипта можно использовать макрос byteValue(F), определенный в файле <idc.idc>, передавая ему значение флагов, ранее полученное для выполнения проверки существования ячейки: "F=Get-Flags(ea); if (hasValue(F)) value=byteValue(F);" — это позволяет избавиться от вызова функции Byte, экономя тем самым некоторое количество процессорного времени. :
Замечание. Если читаемые ячейки заведомо существуют и гарантированно содержат инициализированные значения, в дополнительных, проверках никакой необходимости нет и использование макроса byteValue будет ничуть не быстрее вызова функции Byte.
Пример использования:
0:00010000 db 48h ; H
0:00010001 db 65h ; e
0:00010002 db 6Ch ; 1


IDA - 0052.htm

52 Виртуальная память

0:00010003 db 6Ch 1
0:00010004 db 6Fh о
0:00010005 db 2Ch
0:00010006 db 20h
0:00010007 db 49h I
0:00010008 db 44h D
0:00010009 db 41h А
0:0001000A db 20h
0:0001000B db 50h P
0:0001000C db 72h r
0:0001000D db 6Fh о
0:0001000E db 21h !
0:0001000F db 20h
0:00010010 db ODh
0:00010011 db OAh
а) исходные данные — требуется вывести на консоль содержимое отображаемых
ячеек памяти

auto a; Message(">"); for (a=0x10000;a<0x10011;a++) Message("%c",Byte(a)); Messa-
ge("\n");
b) последовательный вызов Byte для каждой ячейки

> Hello, IDA Pro!
с) результат

Другие примеры использования функции Byte можно найти в главе "Первые
шаги с IDA Pro" и в файле "memcpy.idc", входящем в комплект поставки IDA.
Аргумент Пояснения
еа Линейный адрес ячейки виртуальной памяти
return =return Пояснения
== Содержимое байта ячейки виртуальной памяти
==OxFF Ошибка
Родственные функции: Word, Dword.
Интерактивный аналог: нет.

long Word (long еа)

Функция возвращает значение слова (одно слово равно двум байтам) вирту-
альной памяти, расположенного по адресу еа. При попытке чтения 6auma,
расположенного по несуществующему адресу, равно как и имеющего неопреде-
ленное значение, функция возвращает значение 0xFF, сигнализируя об ошибке.
Наглядно продемонстрировать работу функции позволяет следующий пример:

0:00010000 db 48h H
0:00010001 db 65h e
0:00010002 db 6Ch 1
0:00010003 db 6Ch 1
0:00010004 db 6Fh о
0:00010005 db 2Ch ,


IDA - 0053.htm

Виртуальная память 53
0:00010006 db 20h
0:00010007 db 49h I
0:00010008 db 44h D
0:00010009 db 41h А
0:0001000A db 20h
0:0001000B db 50h P
0:0001000C db 72h г
0:0001000D db 6Fh о
0:0001000E db 21h
0:0001000F db 20h
0:00010010 db ODh
0:00010011 db OAh

Message(">%X\n", Word (0x10000));
>6548

Message(">%X\n", Word (OxO));
>FFFF

Message(">%X\n", Word (0x10011));
>FFOA

Message(">%X\n", Word (OxFFFF));
>48FF

В первом случае существуют оба адреса (т.е. 0x10000 и 0x10001) и функция
отрабатывает успешно; во втором — не существует ни одного из них — ни 0x0,
ни 0x1, в результате чего возвращается 0xFFFF.
Но попытка прочитать слово, расположенное по адресу 0x10011, приводит к
тому, что в младшем байте возвращается значение соответствующей ячейки, а в
старшем — 0xFF! Аналогично и в последнем примере — несуществующий млад-
ший байт дает 0xFF, в то время как старший читается успешно.
Поэтому, до вызова функции Word следует убедиться, что обе ячейки дейст-
вительно существуют и имеют определенные значения. Проверить это можно,
проанализировав младший бит поля атрибутов каждой из ячеек — если он не
равен нулю, то все о'кей. О том как это сделать, подробно рассказано в описании
функции Byte.

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
return | ==return | Пояснения
 | == | Содержимое слова виртуальной | памяти
 | ==FF?? | ==??FF | Ошибка
Родственные функцни: Byte, Dword.
Интерактивный аналог: нет.

long Dword (long еа)

Функция возвращает значение двойного слова виртуальной памяти по указан-
ному адресу.


IDA - 0054.htm

54 Виртуальная память
В остальном она полностью аналогична функции Word.

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
 | ==return | Пояснения
return |  | Содержимое двойного слова | виртуальной памяти
 | ==(FF) | Ошибка
Родственные функции: Byte, Word.
Интерактивный аналог: нет.

void PatchByte (long ea, long value)

Функция модифицирует содержимое байта виртуальной памяти, расположен-
ного по линейному адресу еа, на значение value.
По замыслу разработчика предназначалась для "падченья" программы (напри-
мер, замене 7x на ЕВ, т.е. инструкций условного перехода на безусловный
переход — операция, часто сопутствующая снятию защит), чем и объясняется ее
название. Однако, она нашла применение в решении широкого круга различных
задач, в частности копировании фрагментов виртуальной памяти.
Функция не позволяет модифицировать несуществующие ячейки памяти и не
сигнализирует об ошибках записи, поэтому перед ее вызовом рекомендуется
проверить передаваемый ей линейный адрес на существование вызовом GetFlags
(подробнее об этом рассказывается в описании функции Byte).
Пример ее использования можно найти в файле "memcpy.idc", поставляемом
вместе с IDA.

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
value | Записываемое значение (байт)
Родственные функции: PatchWord, PatchDword.
Интекративный аналог: "^EDIT\Patch program\Change byte".

void PatchWord (long ea, long value)

Функция модифицирует содержимое слова виртуальной памяти, расположен-
ного по адресу еа на значение value. B остальном аналогична PatchByte
(см. описание PathByte).

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
value | Записываемое значение (слово)
Родственные функции: PatchByte, PatchDword.
Интерактивный аналог: "^EDIT\ Patch program\Change word'


IDA - 0055.htm

Виртуальная память 55
void PatchDword (long ea, long value)

Функция модифицирует содержимое двойного слова виртуальной памяти,
расположенного по адресу еа на значение value. B остальном аналогична
PatchByte (см. описание PatchByte).

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
value | Записываемое значение (слово)
Родственные функции: PatchByte, PatchWord.
Интерактивный аналог: нет.

long NextAddr (long еа)

Функция возвращает следующий существующий виртуальный адрес, и
BADADDR в том случае, если такого адреса не существует. Вызов NextAddr
(BADADDR) равносилен NextAddr (0x0).
Пример использования:
0:00010000 db 48h
0:00010001 db 65h
0:00010002 db 6Ch
0:00010003 db 6Ch
0:00010004 db 6Fh
0:00010005 db 2Ch
0:00010006 db 20h
0:00010007 db 49h
0:00010008 db 44h
0:00010009 db 41h
0:0001000A db 20h
0:0001000B db 50h
0:0001000C db 72h
0:0001000D db 6Fh
0:0001000E db 21h
0:0001000F db 20h
0:00010010 db ODh
0:00010011 db OAh
а) исходные данные — требуется получить список адресов виртуальной памяти

auto а;
a=0;
while(1)
{
a=NextAddr(a);
if (a==BADADDR) break;
Message(">%x\n",a);
}
b) трассировка адресов последовательными вызовами функции NextAddr


IDA - 0056.htm

56 Виртуальная память
>10000
>10001
>10002
>10003
>10004
>10005
>10006
>10007
>10008
>10009
>1000a
>1000b
>1000c
>1000d
>1000e
>1000f
>10010
>10011
с) результат — получение перечня существующих адресов виртуальной памяти

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
return | =return | Пояснения
 | !=BADADDR | Следующий за еа адрес виртуальной | памяти
 | ==BADADDR | Ошибка
Родственные функции: PrevAddr.
Интерактивный аналог: нет.

long PrevAddr (long еа)

Функция возвращает предшествующий еа существующий виртуальный адрес,
и BADADDR в том случае, если такого адреса не существует.
Пример использования:

0:00010000 db 48h H
0:00010001 db 65h e
0:00010002 db 6Ch 1
0:00010003 db 6Ch 1
0:00010004 db 6Fh о
0:00010005 db 2Ch ,
0:00010006 db 20h
0:00010007 db 49h I
0:00010008 db 44h D
0:00010009 db 41h А
0:0001000A db 20h
0:0001000B db 50h P
0:0001000C db 72h г
0:0001000D db 6Fh о
0:0001000E db 21h !


IDA - 0057.htm

Виртуальная память 57
0:0001000F db 20h ;
0:00010010 db ODh ;
0:00010011 db OAh ;
а) исходные данные — требуется получить список адресов виртуальной памяти

auto а;
a=BADADDR;
while(1)
{
a=PrevAddr(a);
if (a==BADADDR) break;
Message(">%X\n",a);
}
b) трассировка адресов последовательными вызовами функции PrevAddr

>10011
>10010
>1000F
>1000E
>1000D
>1000C
>1000B
>1000A
>10009
>10008
>10007
>10006
>10005
>10004
>10003
>10002
>10001
>10000
с) результат — получение перечня существующих адресов виртуальной памяти

Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
return | =return | Пояснения
 | !=BADADDR | Предшествующий еа адрес | виртуальной памяти
 | ==BADADDR | Ошибка
Родственные функции: NextAddr.
Интерактивный аналог: нет.

long GetFlags (long еа)
Функция возвращает значение флагов виртуальной памяти, ассоциированных
с виртуальным адресом еа. Если указанного виртуального адреса не существует,
функция возвращает ноль.
О назначении каждого бита флагов рассказано при описании связанных с ним
функций.


IDA - 0058.htm

58 Виртуальная память

!Аргумент | Пояснения
еа | Линейный адрес ячейки виртуальной памяти
return | =return | Пояснения
 | !=0 | Значение флагов
 | ==0 | Ошибка
Родственные функции: SetFlags.
Интерактивный аналог: нет.

void SetFlags (long еа)

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

Внимание! Настоятельно рекомендуется no возможности избегать непосредст-
венной модификации флагов — допущенная ошибка может привести к зависанию
дизассемблера!

Аргумент | Пояснения
еа | Линейный адрес | ячейки виртуальной | памяти
Родственные функции: GetFlags.
Интерактивный аналог: нет.

long FindBinary (long ea, long ffag, char str)

Функция ищет заданную подстроку в виртуальной памяти и в случае успеш-
ного поиска возвращает ее линейный адрес, иначе возвращает значение BAD-
ADDR, сигнализируя об ошибке.
В зависимости от флага направления поиск может идти как вперед (от
младших адресов к старшим), так и назад (от старших адресов к младшим),
регистр символов может как различаться, так и нет.
Аргумент еа задает линейный адрес начала поиска и не обязательно должен
существовать.
Аргумент str задает подстроку поиска, выраженную в шестнадцатеричных
кодах символов (точнее — в системе исчисления, установленной системой исчис-
ления по умолчанию), разделенных между собой пробелами. Суффикс "h", равно
как и префикс "x" при этом указывать не нужно.
Аргумент flag задает направление поиска и определяет чувствительность к
регистру символов: если его младший бит установлен, то поиск идет от младших
адресов к старшим и, соответственно, наоборот; если первый справа бит (считая
от нуля) установлен — прописные и строчные буквы различаются и, соответст-
венно, наоборот.


IDA - 0059.htm

Виртуальная память 59
Пример использования:

segOOO:0000 db 48h
segOOO:0001 db 65h
segOOO:0002 db 6Ch
segOOO:0003 db 6Ch
segOOO:0004 db 6Fh
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 49h
segOOO:0008 db 44h
segOOO:0009 db 41h
segOOO:OOOA db 20h
segOOO:OOOB db 50h
segOOO:OOOC db 72h
segOOO:OOOD db 6Fh
segOOO:OOOE db 21h
segOOO:OOOF db 0
Message(">%s\n",atoa(FindBinary( SegByName("segOOO"),1,"49 44 41")));

Аргумент | Пояснения
ea | Линейный адрес начала поиска
flag | =flag |  | Пояснения
 | бит | # |
 | 0 | 0 | Поиск от старших адресов к младшим
 |  | 1 | Поиск от младших адресов к старшим
 | 1 | 0 | Не различать регистр символов
 |  | 1 | Различать регистр символов
return | =return | Пояснения
 | !=BADADDR |  | Линейный адрес найденной подстроки
 | ==BADADDR |  | Ошибка
Родственные функции: нет.
Интерактивный аналог: "~Search\Text", <Alt-T>.


IDA - 0060.htm

60 Сегменты и селекторы
Сегменты и селекторы
#Definition
Сегментом называется непрерывная область памяти, адресуемая относитель-
но базового адреса сегмента.
Каждый сегмент характеризуется базовым адресом сегмента, адресом
начала сегмента и адресом конца сегмента.
Базовый адрес сегмента обычно выражается в параграфах, адреса начала и
конца сегмента — в байтах.
Адрес начала сегмента задает наименьший адрес, принадлежащей сегменту;
адрес конца сегмента — адрес, на единицу превышающий наибольший адрес,
принадлежащий сегменту.
Никакой линейный адрес не может принадлежать более чем одному сегменту
одновременно — т.е. сегменты не могут пересекаться.
В дальнейшем, если не оговорено другое, адрес начала сегмента обозначается
"startea", адрес конца сегмента — "endea", а базовый адрес — "BASE".
Смещение первого байта в сегменте обозначается "startoffset" и связано с
адресом начала и базовым адресом следующим соотношением:
| startoffset = startea - BASE * 0x10 |

Формула 1. Смещение первого байта в сегменте

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

Подавляющее большинство функций IDA работают не с сегментными, а с
линейными адресами. Пользователь же, напротив, видит на экране дизассемблера
в основном сегментные адреса, а линейные от его взора скрыты.
Для облегчения преобразования сегментных адресов в линейные предусмот-
рен специальный макрос MK_FP(long BASE, long offset), возвращающий
значение BASE*OxlO+offset, а также оператор "квадратные скобки" — "[BASE,
offset]" аналогичного назначения.
Линейные адреса начала и конца сегмента представляют собой 32-битовые
значения, ограничивающие максимальный размер сегмента четырьмя гигабай-
тами.


IDA - 0061.htm

Сегменты и селекторы 61
Базовый адрес представляет собой 16-битовое значение, ограничивающее
адресуемую память одним мегабайтом, которого в ряде случаев оказывается
недостаточно.

Замечание. Размер сегмента ограничен разрядностью линейных адресов его
начала и конца и составляет 4 гигабайта, но выбор адреса начала сегмента,
первый байт которого имеет нулевое смещение, ограничен разрядностью базового
адреса, и равен BASEmax * 0x10 = 0xFFFF * 0x10 = 0xFFFF0, т.е. немногим менее
одного мегабайта.

Выход состоит в использовании селекторов, ссылающихся на 32-разрядные
адреса, что позволяет адресовать с их помощью до 4 гигабайт.
К селектору можно обратиться по его индексу в таблице селекторов.
Индексы представляют собой 16-разрядные целые значения, увеличивающиеся с
каждым очередным элементом на единицу.
Элементы таблицы — 32-разрядные базовые адреса сегмента, измеряемые в
параграфах.
Таблица селекторов представляет собой разряженный массив, допуская созда-
ние элементов с несмежными индексами. Например, 0x5, 0x07, 0x16, 0x88...
Если при создании сегмента в качестве базового адреса указать индекс
созданного ранее селектора, его значение будет автоматически использовано для
базирования данного сегмента.
Аналогично, если создать селектор, совпадающий с базовым адресом некото-
рого сегмента, для его базирования станет использоваться значение селектора, а
не базовый адрес.
С каждым сегментом связан ряд атрибутов — имя сегмента, кратность
выравнивания, разрядность и объединение. Никакой из атрибутов, включая имя,
уникальной характеристикой сегмента не является и вполне допустимо сущест-
вование двух и более сегментов с одинаковыми именами (однако, ассемблеры не
смогут откомпилировать исходный тест, содержащий несколько одноименных
сегментов).

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

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

Навигатор по функциям
Для изучения организации сегментов и селекторов рекомендуется загрузить
полученный в главе "Виртуальная память" файл "tutor.idb" в дизассемблер, при
этом экран IDA должен выглядеть так:


IDA - 0062.htm

62 Сегменты и селекторы
0:00010000 db 48h H
0:00010001 db 65h e
0:00010002 db 6Ch 1
0:00010003 db 6Ch 1
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 ODh
Для создания нового сегмента можно воспользоваться вызовом функции
SegCreate (long startea, long endea, long base, long use32, long align,
long comb), последовательно передав ей адрес начала сегмента, адрес конца
сегмента, базовый адрес сегмента, разрядность сегмента, кратность выравнивания
и атрибуты (о трех последних аргументах подробно рассказано в описании
функции SegCreate, сейчас их можно принять равными нулю), например, так:
"SegCreate (0x10000, 0x10012, 0x1000, 0, 0, 0);"
segOOO:0000 ; Segment type: Regular
segOOO:0000 segOOO segment at 1000h private " use16
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh o
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 49h I
segOOO:0008 db 44h D
segOOO:0009 db 41h A
segOOO:OOOA db 20h
segOOO:OOOB db 50h P
segOOO:OOOC db 72h r
segOOO:OOOD db 6Fh o
segOOO:OOOE db 21h !
segOOO:OOOF db 20h
segOOO:0010 db ODh
segOOO:0011 db OAh
segOOO:0011 segOOO ends
Создав новый сегмент, IDA автоматически присвоила ему имя "segOOO", где
"000" порядковый номер (считая от нуля) созданного сегмента. Последующие
сегменты будут названы "segOOl", "seg002" и т.д.


IDA - 0063.htm

Сегменты и селекторы 63
Функция "long SegByName (char segname)" позволяет узнать линейный
адрес базового адреса сегмента* по его имени. Ее вызов может выглядеть,
например, так:

Message(">%X\n", SegByName("segOOO"));
> 10000

Переименовать сегмент можно с помощью функции "success SegRena-
me (long ea, char name)" принимающей в качестве первого аргумента
любой линейный адрес, принадлежащий указанному сегменту, а вторым —
его новое имя.
Например:

SegRename(SegByName("segOOO"),"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 1
MySeg:0003 db 6Ch 1
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:OOOA db 20h
MySeg:OOOB db 50h P
MySeg:OOOC db 72h r
MySeg:OOOD db 6Fh o
MySeg:OOOE db 21h !
MySeg:OOOF db 20h
MySeg:0010 db ODh
MySeg:0011 db OAh
MySeg:0011 MySeg ends
Для удаления сегментов предусмотрена функция "success SegDelete (long
ea, long disable)". Если флаг "disable" равен нулю, будет удален лишь сам
сегмент, а содержимое принадлежащих ему ячеек сохранится, в противном случае
вместе с сегментом будет удалены и все принадлежащие ему адреса виртуальной
памяти. Сравните:

SegDelete(Ox10000, 0); SegDelete(Ox10000, 0);
0:00010000 db 48h H Пустой экран дизассемблера
0:00010001 db 65h e
0:00010002 db 6Ch 1
* Т.е. функция возвращает базовый адрес, тут же умножая его на 0x10 для перевода в
линейный.


IDA - 0064.htm

64 Сегменты и селекторы
0:00010003 db 6Ch 1
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 г
0:0001000D db 6Fh o
0:0001000E db 21h !
0:0001000F db 20h
0:00010010 db ODh
0:00010011 db OAh
Описанные выше операции можно выполнять и интерактивно — с помощью
горячих клавиш и системы меню. Для последующих экспериментов потребуется
перезагрузить исследуемый файл "tutor.bin", поскольку удаление виртуальной
памяти необратимо.
Для интерактивного создания сегмента достаточно в меню "View" выбрать
пункт "Segments" и дождавшись появления списка существующих сегментов (в
данном случае — пустого), нажать клавишу <Insert> и надлежащим образом
заполнить соответствующие поля появившегося диалога. Если предварительно
выделить некоторую область курсорными клавишами, удерживая <Shift>, IDA
автоматически подставит линейные адреса начала и конца этой области в поля
адреса начала и адреса конца сегмента соответственно. Если поле "Segment
Name" оставить пустым, IDA присвоит имя сегменту самостоятельно.
По умолчанию базовый адрес равен наибольшему возможному значению, т.е.
Startea
BASEdef = , при этом offsetdef = Startea + 0xF, где offset^ef — смещение
первого байта в сегменте. Очевидно, что offsetdef^OxF.
Например, если дозагрузить бинарный файл ("File\Load file\Additional binary
file), скажем, Crypt.com, по виртуальному адресу 0x20100 и, выделив его, попытаться
создать сегмент, IDA по умолчанию выберет базовый адрес, равный 0x2010, в
результате чего смещение первого байта в сегменте будет равно 0x0, а не 0x100!
Следует очень внимательно относиться к значениям, предлагаемым по умол-
чанию — они не всегда соответствуют требуемым.
В действительности, базовый адрес должен быть равен 0x2000, тогда после
создания сегмента экран дизассемблера будет выглядеть так:

=[•] Program Secpnentation ———з=[т]=т)
Name Start End Align Base Type Cls 32es ss ds fs gs A
segOOO 00000000 00000012 byte 1000 pub N FFFF FFFF FFFF FFFF FFFF 00010000 00010012 1
segOOl 00000100 0000013C byte 2000 pub N FFFF FFFF FFFF FFFF FFFF 00020100 0002013C •
________________________________________________ Т
г/2 4HHHHHHHHHHHHHHHHHHHHHHHHHHH '
Для изменения свойств сегмента достаточно подвести к нему курсор и нажать
клавишу <Ctrl-E>. Появится диалоговое окно следующего вида:


IDA - 0065.htm

Сегменты и селекторы 65

= [•]=== change segment attributes =====

Segment name segOOl |i|
Segment class |l|
Start address 0x20100 |i|
End address 0x2013C |i|

(•) 16-bit segment Combination (public)
( ) 32-bit segment ------------
Alignment (byte)
[X] Move adjacent segments -------------
[ ] Disable addresses

OK Cancel F1 - Help

Интерактивно можно изменять все характеристики и атрибуты сегмента за
исключением его базового адреса. На модификацию адресов начала и конца
сегмента наложены некоторые ограничения: (BASE * 0x10)=< Startea > Endea, т.е.
адрес начала должен быть больше либо равен базовому адресу, умноженному на
шестнадцать, а адрес конца сегмента должен по крайней мере на единицу
превышать адрес начала.
При изменении границ одного из смежных сегментов, IDA автоматически
расширит или сузит другой сегмент, если флаг "Move adjacent segments" уста-
новлен. Графически это можно изобразить так:
IDA - 0065-1.jpg
Рис. 13. Изменение границ сегментов

На рис. 13, а показано уменьшение нижней границы сегмента segOOO, к концу
которого вплотную примыкает сегмент seg001. Если флажок "Move adjacent seg-
ment" взведен, IDA автоматически изменит адрес начала сегмента "segOOl" как
показано на рис. 13, b; напротив, сброс этого флажка приведет к тому, что между
сегментами образуется дыра "ничейной памяти", изображенная на рис. 13, с.


IDA - 0066.htm

66 Сегменты и селекторы
Установка флажка "Disable addresses" автоматически уничтожит "дыру",
удалив принадлежащие ей адреса памяти, вместе с их содержимым.
Внимание! Эта операция необратима и вернуть утерянные данные
обратно уже не удастся!
Для закрепления всего вышесказанного рекомендуется провести несколько
простых экспериментов. Для начала попробуйте расширить границы сегмента
"segOOO" до адреса 0x20120 (или любого другого, принадлежащего сегменту
"segOOl"). Одним лишь изменением атрибутов сегмента "segOOO" это сделать не
удастся — IDA сообщит об ошибке "set_segm_end(10000) -> 20120: areas
overlap" и, независимо от состояния флага "Move adjacent segment", прервет
выполнение операции.
Причина в том, что автоматическое изменение границ работает со смежными
и только со смежными сегментами, a "segOOO" и "segOOl" таковыми, очевидно,
не являются, поскольку адрес конца сегмента "segOOO" равен 0x10012, а адрес
начала "segOOl" — 0x20100.
Выполнить поставленную задачу можно, по меньшей мере, двумя путями:
первый — предварительно увеличить адрес начала сегмента "seg001" и повто-
рить операцию; второй — увеличить адрес конца сегмента "segOOO" до макси-
мально возможного значения (т.е. 0x20100), добившись слияния сегментов, что
даст возможность расширить "segOOO" до требуемого адреса установкой флага
"Move adjacent segment".
Последовательность нажатий клавиш для первого способа: переместить
курсор в пределы сегмента "seg001" и нажать <Alt-S>, в появившемся диалого-
вом окне изменить значение поля "Start address" на 0x20120; затем переместится
в границы сегмента "segOOO", нажать <Alt-S> и изменить значение поля "End
Address" на "0x20120". Задача выполнена.
Последовательность нажатий клавиш для второго способа: переместить
курсор в пределы сегмента "segOOO" и нажать <Alt-S>, в появившемся диалого-
вом окне изменить значение поля "End address" на 0x20100, затем, повторно
вызвав тот же диалог, распахнуть сегмент до требуемого адреса, изменив значе-
ние поля "End address" на 0x20120. Задача выполнена.
Программно изменить атрибуты сегмента можно вызовами функций SegCrea-
te, SegBounds, SegRename, SegClass, SegAlign, SegComb, SegAddrng.
Функция success SegBounds (long ea, long startea, long endea, long
disable) принимает в качестве первого аргумента любой адрес, принадлежащий
сегменту, startea, endea — новые адреса начала и конца сегмента соответственно,
ненулевое значение флага disable удаляет виртуальную память освободившуюся
при уменьшении сегмента (при расширении сегмента его значение игнорируется).
Следует помнить, что startea > BEGIN_ADDRES * 0x10, т.е. верхнее расширение
сегмента жестко ограничено его базовым адресом.
Например, для восстановления сегмента "segOOO" до его прежних размеров
можно воспользоваться следующим кодом: "SegBounds (0x10000, 0x10000,
0x10012,1);"
Если базовый адрес создаваемого сегмента больше или равен 0x10000, IDA
автоматически создает селектор для его адресации.


IDA - 0067.htm

Сегменты и селекторы 67

Например, результат выполнения "SegCreate (0x100000, 0x100100, 0x10000,
0, 0, 0);" приведет к следующему:
IDA - 0067-1.jpg
Базовый адрес сегмента "seg002" равен 0x1, но несмотря на это, смещение
первого байта в сегменте равно 0x0, а не 0x1000000 — 0x1 * 0x10 = 0xFFFFF0.
Причина в том, что 0x1 — это селектор, а не сегмент. Чтобы убедиться в этом
достаточно просмотреть список селекторов, вызвать который можно нажатием
"<Alt-V>,<L>":
IDA - 0067-2.jpg
В отличие от базового адреса, значение селектора легко изменить, для чего
достаточно выделить его курсором и нажать <Ctrl-E>. Появится диалоговое окно
следующего содержания:
IDA - 0067-3.jpg
Значение селектора может совпадать с базовым адресом любого из существую-
щих сегментов, на него не действует ограничение startea => BASE_ADDRES * 0x10,
но если присвоить селектору значение численно равное его индексу, он будет
немедленно удален.
Присвоение селектору базового адреса, превышающего адрес начала сегмента,
приводит к появлению байт с отрицательными смещениями, которые автоматиче-
ски дополняются до нуля, т.е. offset = NEG (|startea - SEL_VALUE|).
Например, после увеличения значения селектора 0x1 на один параграф экран
дизассемблера будет выглядеть так:

seg002:FFFFFFFO seg002 segment at 10001h private '' use16
seg002:FFFFFFFO assume cs:seg002
seg002:FFFFFFFO ;org OFFFFFFFOh
seg002:FFFFFFFO assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
seg002:FFFFFFFO db ? unexplored
seg002:FFFFFFF1 db ? unexplored
seg002:FFFFFFF2 db ? unexplored


IDA - 0068.htm

68 Сегменты и селекторы

seg002:FFFFFFF3 db ? unexplored
seg002:FFFFFFF4 db ? unexplored
seg002:FFFFFFF5 db ? unexplored
seg002:FFFFFFF6 db ? unexplored
seg002:FFFFFFF7 db ? unexplored
seg002:FFFFFFF8 db ? unexplored
seg002:FFFFFFF9 db ? unexplored
seg002:FFFFFFFA db ? unexplored
seg002:FFFFFFFB db ? unexplored
seg002:FFFFFFFC db ? unexplored
seg002:FFFFFFFD db ? unexplored
seg002:FFFFFFFE db ? unexplored
seg002:FFFFFFFF db ? unexplored
seg002:0000 db ? unexplored
seg002:0001 db ? unexplored
seg002:0002 db ? unexplored
seg002:0003 db ? unexplored
seg002:0004 db ? unexplored
seg002:0005 db ? unexplored
seg002:0006 db ? unexplored
seg002:0007 db ? unexplored

Исправить ситуацию можно соответствующим увеличением адреса начала
сегмента. После вызова "SegBounds (0x100000, 0x100010, 0x100100, 1);" экран
дизассемблера должен выглядеть так:
IDA - 0068-1.jpg
Смещение первого байта в сегменте "seg002" равно нулю, чего и требовалось
достичь.
Программно создать новый селектор или изменить значение уже существующего
селектора можно с помощью функции "void SetSelector (long sel, long value)",
где sel — индекс селектора, a value — его значение, измеряемое в параграфах.
Например, вызов "SetSelector (0x1, 0x10000);" изменит значение селектора
0x1 и смещение первого байта в сегменте "seg002" будет равно нулю, а вызов
"SetSelector (0x4, 0x500000);" создаст новый селектор с индексом 0x4 (индексы
селекторов не обязательно должны следовать друг за другом).
————————————————————————— Program Segmentation ——————————————————————4———
Name Start End Align Base Type Cls 32es ss ds fs gs
segOOO 00000000 00000012 byte 1000 pub CODE N FFFF FFFF FFFF FFFF FFFF 00010000 00010012
segOOl 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 ————————————————————————————————————————————————————————————————I
IDA - 0068-2.jpg


IDA - 0069.htm

Сегменты и селекторы 69

Удалить селектор можно вызовом функции "void DelSelector (long sel)"
либо присвоением ему собственного индекса — SetSelector (sel, sel).
Если удалить используемый селектор, то смещение первого байта в сегменте,
который на него ссылался станет равным: offset = startea - sel * 0x10 , где sel —
индекс (не значение!) селектора.
Например, после вызова "SelDelete (0x1);" экран дизассемблера будет таким:
IDA - 0069-1.jpg
Операция удаления обратима и повторное создание уничтоженного селектора
все вернет на свои места.
Функция long FirstSeg() возвращает линейный адрес сегмента, имеющего
наименьший адрес начала, соответственно функция long NextSeg (long еа)
возвращает линейный адрес сегмента, следующего за еа.
Пример использования:
auto а;
a=FirstSeg();
while(a!=BADADDR)
{
Message(">%08x\n",a,SegName(a));
a=NextSeg(a);
}
>00010000
>00020100
>00100010
Зная адрес начала сегмента можно получить его имя вызовом функции
SegName, а зная имя сегмента можно получить его базовый адрес при помощи
функции SegByName; более быстрого способа вычисления базового адреса,
по-видимому, не существует.
Передав функциям SegStart и SegEnd любой принадлежащий сегменту линей-
ный адрес можно получить линейный адрес его начала и конца соответственно.
Скрипт, приведенный ниже, распечатывает список всех существующих сег-
ментов, с указанием их основных характеристик.
auto а;
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))/Ox10);
a=NextSeg(a);
}
Message(">—————————------————\n\n");


IDA - 0070.htm

70 Сегменты и селекторы
>Name | Start |End |BASE

>segOOO|00010000|00010012|00001000
>seg001|00020100|0002013c|00002000
>seg002|00100010|00100100|00010000
>_____...-._________._..___..._...

Узнать обо всех остальных атрибутах сегмента можно при помощи функции
GetSegmentAttr.

Сводная таблица функций

Функции 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 еа) | Возвращает линейный адрес начала сегмента
long SegEnd (long ea) | Возвращает линейный адрес конца сегмента
long SegByName (char segname) | По имени сегмента определяет его базовый адрес
long SegByBase (long base) | По базовому адресу сегмента определяет линейный адрес его начала
Функции изменения атрибутов сегмента
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) | Изменяет значение сегментных регистров


IDA - 0071.htm

Сегменты и селекторы 71
success SetSegmentType (long segea, Изменяет тип сегмента
long type)
Функции получения атрибутов сегмента
long GetSegmentAttr (long segea, Возвращает атрибуты сегмента
long attr) :
char SegName (long ea) Возвращает имя сегмента
Функции трассировки сегментов
long FirstSeg 0 Возвращает линейный адрес начала первого сегмента
long NextSeg (long еа) ! Возвращает линейный адрес начала следующего сег-
мента
Функции, работающие с селекторами
Функции создания и удаления селекторов
void SetSelector (long sel,l ong value) Создает новый селектор или изменяет значение уже
существующего селектора
void DelSelector (long sel) Удаляет селектор
Утилиты
long AskSelector (long sel) (Возвращает значение селектора в параграфах
long FindSelector (long val) .Возвращает селектор с указанным значением
long MK_FP (long seg, long off)

Функция преобразует сегментный адрес в линейный по следующей схеме
еа = seg * 0x10 + off. Перед ее использованием необходимо убедиться что "seg"
представляет собой именно базовый адрес сегмента, выраженный в парагра-
фах, а не селектор, иначе полученный результат будет неверен.
Оператор "квадратные скобки" полностью аналогичен функции MK_FP, но
обладает более компактной формой записи (7 символов "MK_FP()" вместо двух
"П").

Замечание. В комментариях, содержащихся в файле <idc.idc>, часто использу-
ется конструкция /"имя сегмента", смещение/. Попытка использования этой
схематической конструкции в коде скриптов приведет к появлению синтаксиче-
ской ошибки, но если передать имя сегмента в строковой переменной, оператор
"квадратные скобки" автоматически подставит его базовый адрес — если, конеч-
но, такой сегмент существует; в противном случае строка будет преобразована
в число, согласно правилам преобразования "строка—>число" в /DA-Cu (см. "Язык
скриптов IDA Си" — "Объявления переменных, типы переменных, преобразования
переменных").
Напротив, макрос MK_FP всегда преобразует переданное ему имя сегмента в
строку, даже если сегмент с таким именем существует.

Пример использования:

Message(">[seg %X,off%X]=%X=%X\n~,Ox1000,Ox6,MK.FP(Ox1000,Ox6),[Ox1000,Ox6]);

>[seg lOOO,off6]=10006=10006


IDA - 0072.htm

72 Сегменты и селекторы

Аргумент | Пояснения |
seg | Базовый адрес сегмента (не селектор!), выраженный | в параграфах
off | Смещение ячейки в сегменте |
return | Пояснения |
long | 32-битный линейный адрес ячейки |
Родственные функции: оператор [].
Интерактивный аналог: "~View\Calculate" <?>.

char atoa (long еа)

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

• если линейный адрес еа принадлежит некоторому сегменту, смещение
вычисляется относительно его базового адреса, а сам адрес записывается в
виде "имя сегмента:смещение";
• если линейный адрес еа не принадлежит ни одному сегменту, преобразова-
ние выполняется по формуле seg = еа / 0x10; off = еа - seg.

Пример использования:
Message(">%s\n",atoa(Ox2000lO));
>0:00200010

SegCreate(Ox200000,0x201000,0x20000,0,0,0);
0. Creating a new segment (00200000-00201000) ... ... OK
Message(">%s\n",atoa(Ox200010));
>segOOO:OOlO

Аргумент |  | Пояснения |
еа | 32-разрядный линей | ный адрес |
return | =return | Пояснения |
 | ! = "" | Сегментный адрес в строковом | представлении
 | =="" | Ошибка |
Родственные функции: нет.
Интерактивный аналог: нет.

success SegCreate (long startea, long endea, long base, long use32,
long align, long comb)

Функция создает новый сегмент. Сегмент задается линейным адресом начала
(startea), линейным адресом конца (endea) и базовым адресом (BASE), исполь-
зующимся для адресации ячеек внутри сегмента.
Адрес начала задает наименьший линейный адрес, принадлежащий данному
сегменту, напротив, адрес конца, задает линейный адрес на единицу превышающий
наибольший адрес, принадлежащий указанному сегменту. Такая мера необходима
для поддержки сегментов нулевой длинны — в силу архитектурных ограничений


IDA - 0073.htm

Сегменты и селекторы 73
IDA, один линейный адрес не может является и началом, и концом сегмента
одновременно, поэтому, приходится "искусственно" увеличивать адрес конца сегмента.
Смещение первого байта внутри сегмента вычисляется по формуле startoffset =
= startea - BASE * 0x10 . Поддержка базирования позволяет создавать сегменты
с произвольным начальным смещением, например, равным 0x100. Поскольку, сме-
щения выражаются неотрицательными величинами, начальный адрес должен быть
не меньше базы сегмента, измеряемой в байтах.
Напротив, базовый адрес по известному адресу начала сегмента и смещению
первого байта в сегменте вычисляется по формулеBAS=startea-startoffset/0x10
Атрибут use32 задает разрядность сегмента — нулевое значение соответст-
вует 16-разрядному сегменту, любое другое — 32-разрядному. Разрядность ока-
зывает влияние только на способ дизассемблирования машинных инструкций,
принадлежащих этому сегменту, но независимо от разрядности смещения в
сегменте всегда задаются 32-битовыми значениями.
Атрибут align задает кратность выравнивания сегмента, помещая соответст-
вующую директиву в ассемблерный листинг. Никакого влияния на размещение
сегмента в виртуальной памяти атрибут align не оказывает!
Атрибут comb задает флаг комбинирования, разрешающий (запрещающий)
линкеру объединять несколько сегментов в один. Никакого влияния на объедине-
ние сегментов в виртуальной памяти атрибут comb не оказывает — независимо
от его значения смежные сегменты не будут автоматически объединены.
Детали
а) Базовый адрес задается неотрицательным 16-разрядным значением и может
(OxlOOOO*OxlO )
адресовать не более одного мегабайта памяти ———————— = 1 Если этого
( 1024*1024 )
недостаточно, следует указать вместо базового адреса селектор, который необ-
ходимо предварительно создать вызовом функции SetSelector.
Если базовый адрес, переданный функции SegCreate, больше 0x10000, IDA автома-
тически создаст новый селектор и использует его для адресации данного сегмента.
б) Если передать линейный адрес, принадлежащий некоторому сегменту, но
отличный от адреса начала (т.е. попытаться создать вложенный сегмент), функция
автоматически ускорит или разобьет данный сегмент и создаст новый (см. рис. 14).
IDA - 0073-1.jpg


IDA - 0074.htm

74 Сегменты и селекторы

Пример использования:
Пусть необходимо создать сегмент с (задача вычисления базового адреса)
SegCreate(Ox1000,0x4000,0x100,0,0,0);
0. Creating a new segment (00001000-00004000) ... ... OK
IDA - 0074-1.jpg
SegCreate(Ox2000,Ox3000,Ox200,0,0,0);
1. Creating a new segment (00002000-00003000) ...
Additional segment (00003000-00004000) ...
2. Creating a new segment (00003000-00004000) ... ... OK
... OK
IDA - 0074-2.jpg
Аргумент |  | Пояснения
startea | 32-разрядный линей | ный адрес начала сегмента
endea | Величина на единиц | у большая последнего принадлежащего сегменту адреса
Base | 16-разрядный базов | ый адрес в параграфах или указатель на селектор
use32 | =use32 | Пояснения
 | ==0 | 16-разрядный сегмент
 | ==1 | 32-разрядный сегмент
aling | Кратность выравнив | ания начала сегмента
comb | Комбинирование сег | мента
return | =return | Пояснения
 | ==1 | Операция завершилась успешно
 | ==0 | Ошибка
Родственные функции: SetSelector; SegClass; SegAlign; SegComb;
SegAddrng.
Интерактивный аналог: "~View\Segments", <Insert>.

success SegDelete (long еа, long disable)

Функция удаляет сегмент вместе с результатами дизассемблирования (метка-
ми, функциями, переменными и т.д.) и адресным пространством виртуальной
памяти, принадлежащей сегменту.


IDA - 0075.htm

Сегменты и селекторы 75
Аргумент еа — представляет собой любой линейный адрес, принадлежащий
сегменту, но не адрес его конца (см. описание функции SegCreate — адрес конца
сегмента на единицу больше наибольшего адреса, принадлежащего сегменту).
Аргумент disable будучи неравным нулю указывает на необходимость удале-
ния адресного пространства виртуальной памяти, принадлежащей сегменту, в
противном случае удаляется лишь сам сегмент вместе с результатами дизассемб-
лирования, а содержимое ячеек виртуальной памяти остается неизменным.

Внимание! Операция удаления сегментов необратима — повторное создание
уничтоженного сегмента не восстановит удаленные вместе с сегментом метки,
функции, переменные, комментарии и другие результаты дизассемблирования.

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

Пример использования:

segOOO:0000 segOOO segment byte public " use16
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing,
gs:nothing
segOOO:0000 aHelloIdaPro db 'Hello, IDA Pro! ',ODh,OAh ; Test
segOOO:0000 seg000 ends
a) исходный сегмент, содержащий строку "aHelloldaPro" и комментарий "Test"

SegDelete(SegByBase(SegByName("segOOO")>>4),0);
b) удаление сегмента без уничтожения виртуальной памяти
0:00010000 db 48h H
0:00010001 db 65h e
0:00010002 db 6Ch 1
0:00010003 db 6Ch 1
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 ODh
с) результат — сегмент удален, содержимое виртуальной памяти — нет

SegCreate(Ox10000,0x10012,0x1000,0,0,0);
d) попытка создания нового сегмента для восстановления удаленного

segOOO:0000
segOOO:0000 ; Segment type: Regular


IDA - 0076.htm

76 Сегменты и селекторы

segOOO:0000 segOOO segment at 1000h private " use16
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh o
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 49h I
segOOO:0008 db 44h D
segOOO:0009 db 41h A
segOOO:OOOA db 20h
segOOO:OOOB db 50h P
segOOO:OOOC db 72h r
segOOO:OOOD db 6Fh o
segOOO:OOOE db 21h !
segOOO:OOOF db 20h
segOOO:0010 db ODh
segOOO:0011 db OAh;
segOOO:0011 segOOO ends
e) результат — сегмент восстановлен, а строка и комментарий — нет

SegDetele(Ox10000,1);
f) удаление сегмента вместе с принадлежащей ему виртуальной памятью
1 пустей экран дизассемблера

j) результат — виртуальная память удалена

SegCreate(Ox10000,0x10012,0x1000,0,0,0);
k) попытка создания нового сегмента для восстановления виртуальной памяти

segOOO:0000 ; Segment type: Regular
segOOO:0000 segOOO segment at 1000h private " use16
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
segOOO:0000 db ? unexplored
segOOO:0001 db ? unexplored
segOOO:0002 db ? unexplored
segOOO:0003 db ? unexplored
segOOO:0004 db ? unexplored
segOOO:0005 db ? unexplored
segOOO:0006 db ? unexplored
segOOO:0007 db ? unexplored
segOOO:0008 db ? unexplored
segOOO:0009 db ? unexplored
segOOO:OOOA db ? unexplored
segOOO:OOOB db ? unexplored
segOOO:OOOC db ? unexplored
segOOO:OOOD db ? unexplored
segOOO:OOOE db ? unexplored


IDA - 0077.htm

Сегменты и селекторы 77

segOOO:OOOF db ? unexplored
segOOO:0010 db ? unexplored
segOOO:0011 db ? unexplored
segOOO:0011 segOOO ends
I) результат — адресное пространство восстановлено, но содержимое виртуальной
памяти — нет

Аргумент |  | Пояснения
еа | Линейный адрес, пр | инадлежащий сегменту
disable | =disable | Пояснения
 | ==0 | Не уничтожать адреса, принадлежащие сегменту
 | ==1 | Уничтожать адреса, принадлежащие сегменту
return | =return |
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: SegCreate.
Интерактивный аналог: "^Edit\Segments\Delete segment"; "^View\Seg-
ments", <Del>.

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

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
startea | 32-разрядный линейный адрес нового начала сегмента


IDA - 0078.htm

78 Сегменты и селекторы

Аргумент |  | Пояснения |
endea | Величина на единиц | у большая последнего принадлежащего | сегменту адреса
disable | Уничтожать освободи | вшиеся адреса |
return | =return | Пояснения |
 | ==1 | Успешное завершение операции |
 | ==0 | Ошибка |
Родственные функции: SegCreate.
Интерактивный аналог: <Alt-S>; "~View\Segments", <Ctrl-E>.

long SegStart (long еа)

Функция принимает любой линейный адрес, принадлежащий сегменту, и
возвращает линейный адрес начала данного сегмента.
Если переданный адрес не принадлежит ни одному сегменту, функция возвра-
тит BADADDR, сигнализируя об ошибке.
Пример использования:
SegCreate(Ox10000,Ox20000,Ox1000,0,0,0);
а) создаем сегмент с адресом начала 0x10000 и адресом конца 0x20000
0. Creating a new segment (00010000-00020000) ... ... OK
b) сегмент успешно создан
Message(">%X\n",SegStart(Ox10100));
с) вызываем функцию SegStart, передавая ей один из адресов, принадлежащих сегменту
>10000
d) результат — функция вернула адрес начала сегмента

Аргумент |  | Пояснения
еа | Линейный адрес, пр | инадлежащий сегменту
return | =return | Пояснения
 | !=BADADDR | Линейный адрес начала сегмента
 | ==BADADDR | Ошибка
Родственные функции: SegEnd.
Интерактивный аналог: "^View\Segments".
IDA - 0078-1.jpg


IDA - 0079.htm

Сегменты и селекторы 79

long SegEnd (long еа)
Функция принимает любой линейный адрес, принадлежащий сегменту, и
возвращает линейный адрес конца данного сегмента.
Если указанный адрес не принадлежит ни одному сегменту, функция возвра-
тит значение BADADDR, сигнализируя об ошибке.
Линейный адрес конца сегмента на единицу больше наибольшего адреса,
принадлежащего сегменту. Подробнее об этом рассказывается в описании функ-
ции SegCreate.
Пример использования:
SegCreate(Ox1000,0x2000,0x100,0,0,0);
SegCreate(Ox2000,0x3000,0x200,0,0,0);
а) создаем два смежных сегмента с адресами начала и конца 0x1000; 0x2000 и 0x2000;
0x3000 соответственно
0. Creating a new segment (00001000-00002000) ... ... OK
1. Creating a new segment (00002000-00003000) ... ... OK
b) сегменты успешно созданы
Message(">%X\n",SegEnd(Ox1100));
с) вызываем функцию SegEnd, передавая ей один из адресов, принадлежащих первому
сегменту
>2000
d) результат — адрес конца первого сегмента
Message(">%X\n",SegStart(Ox2000));
e) вызываем функцию SegStart, передавая ей адрес конца первого сегмента
>2000
f) результат — адрес начала второго сегмента. Таким образом, наглядно продемонст-
рировано — адрес конца сегмента не принадлежит самому сегменту

Аргумент |  | Пояснения
еа | Любой 32-разрядный | линейный адрес, принадлежащий сегменту
return | =return | Пояснения
 | !=BADADDR | Линейный адрес конца сегмента
 | ==BADADDR | Ошибка
Родственные функции: SegStart.
Интерактивный аналог: "^View\Segments".
IDA - 0079-1.jpg


IDA - 0080.htm

80 __ Сегменты и селекторы _______

long SegByName (char segname)
Функция по имени сегмента (с учетом регистра) определяет его базовый
адрес.
Если базовый адрес представляет собой селектор, то функция автоматически
возвратит его значение.
Если сегмента с указанным именем не существует, функция возвратит значе-
ние BADADDR, сигнализируя об ошибке.
Детали:
а) вызов SegByName — единственный способ определения базового адреса
сегмента.
Чтобы определить базовый адрес сегмента по некоему линейному адресу,
принадлежащему сегменту, необходимо воспользоваться конструкцией BASE =
SegByName(SegName(ea)).
b) поскольку IDA допускает совместное существование двух и более одно-
именных сегментов, определение базового адреса сегмента по ему имени позво-
ляет определить базовый адрес лишь одного из них.

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

Пример использования:
SegCreate(Ox1000,0x2000,0x100,0,0,0); SegRename(Ox1000, "MySeg");
а) создаем новый сегмент с базовым адресом 0x1000 и тут же переименовываем его в
"MySeg"
Message(">%X\n",SegByName("MySeg"));
b) вызываем функцию SegByName передавая ей имя только что созданного сегмента
>1000
с) результат — базовый адрес сегмента MySeg
SegCreate(Ox2000,0x3000,0x200,0,0,0); SegRename(Ox2000,"MySeg");
d) создаем второй сегмент с именем "MySeg", но на этот раз с базовым адресом, равным
0x2000
IDA - 0080-1.jpg
e) это получилось! Теперь существуют два одноименных сегмента

Message(">%X\n",SegByName("MySeg"));
f) вызываем функцию SegByName передавая ей имя "MySeg"

>1000
g) результат — базовый адрес первого сегмента


IDA - 0081.htm

Сегменты и селекторы 81

Аргумент | Пояснения
segname | Имя сегмента (регистр учитывается)
return | =return | Пояснения
 | !=BADADDR | Базовый адрес сегмента или | значение селектора
 | ==BADADDR | Ошибка
Родственные функции: SegRename, SegName.
Интерактивный аналог: "~View\Segments".
IDA - 0081-1.jpg
long SegByBase (long base)

Функция по базовому адресу (селектору) сегмента определяет линейный адрес
его начала. Если передать базовый адрес, не принадлежащий ни одному сегменту,
или существующий, но не использующийся ни одним сегментом селектор, функ-
ция возвратит значение BADADDR, сигнализируя об ошибке.
Поскольку IDA допускает совместное существование двух и более сегментов
: одинаковыми базовыми адресами, определение адреса начала сегмента по его
эазовому адресу позволяет определить адрес начала лишь одного из них.
Пример использования:

SegCreate(Ox1100,0x2000,0x100,0,0,0);
а) создаем сегмент с базовым адресом 0x100 и линейным адресом начала 0x1100

Message(">%X\n",SegByBase(Ox100));
b) вызываем функцию SegByBase для получения линейного адреса начала сегмента по
его базе

>1100
с) результат — линейный адрес начала сегмента

Аргумент | Пояснения
base | Базовый адрес сегмента или значение селектора
return | =return | Пояснения
 | !=BADADDR | Линейный адрес начала сегмента
 | ==BADADDR | Ошибка
Родственные функции: SegByName.
Интерактивный аналог: "^View\Segments".


IDA - 0082.htm

82 Сегменты и селекторы
IDA - 0082-1.jpg
success SegRename (long ea, char name)
Функция переименовывает сегмент, возвращая при удачном завершении
операции ненулевое значение и ноль в противном случае.
Аргумент еа представляет собой любой линейный адрес, принадлежащий
сегменту. Если передать адрес не принадлежащий никакому сегменту, функция
возвратит ошибку.
Аргумент name задает новое имя сегмента с учетом регистра. Если имя
начинается с цифры, функция автоматически дополнит его знаком прочерка. Если
в имени содержатся недопустимые символы, они будут автоматически заменены
знаком прочерка.
Допустимые символы перечисляются в полях NameChars конфигурационного
файла <ida.cfg>, значение которых по умолчанию приведено в табл. 4.
Попытка присвоить сегменту пустое имя (аргумент name равен "") приводит
к ошибке.
IDA допускает существование двух и более сегментов с одинаковыми имена-
ми, однако, использование этой возможности влечет невозможность определения
базового адреса сегмента. Подробнее — см. описание функции SegByName.
Таблица 4

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


IDA - 0083.htm

Сегменты и селекторы 83

Пример использования:

SegCreate(Ox1000,0x2000,0x100,0,0,0);
Message(">%s\n",SegName(Ox1000));
а) создаем сегмент и тут же определяем его имя

>segOOO
b) имя сегмента — "segOOO"

SegRename(Ox1000,"666");
Message(">%s\n",SegName(Ox1000));
с) вызываем функцию SegRename для переименования сегмента в "666" и тут же
получаем имя сегмента при помощи SegName

>_666
d) результат — новое имя сегмента "_666", — функция автоматически добавила спере-
ди знак прочерка, поскольку имя начиналась с цифры

SegRename(Ox1000,"Русский квас");
Message(">%s\n",SegName(Ox1000));
e) вызываем функцию SegRename для переименования сегмента в "Русский квас" и тут
же получаем имя сегмента при помощи SegName

>______
f) результат — все запрещенные символы были заменены знаками прочерка

Аргумент | Пояснения |
еа | Линейный адрес, принадлежащий сегменту |
name | Новое имя сегмента I
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: SegName, SegByName.
Интерактивный аналог: "~View\Segments", <Ctrl-E>; <Alt-S>.

success SegAddrng (long ea, long use32)

Функция изменяет разрядность сегмента, определяя, каким образом будут
дизассемблироваться машинные инструкции.
На платформе Intel 386+ префикс 0x66 перед инструкцией в 16-разрядном
сегменте указывает на использование 32-битных операндов и, соответственно, в
32-разрядном сегменте наоборот. Изменение адресации также затрагивает интер-
претацию префикса предопределения адреса — 0x67, видов адресации и т.д.
Неверный выбор разрядности сегмента приводит к некорректному дизассемб-
лированию — появлению бессмысленного "мусора" и бессвязных инструкций.
IDA определяет разрядность сегмента на основе информации, содержащейся
в заголовках файлов. При загрузке бинарных файлов, равно как и MS-DOS
ехе-файлов разрядность созданных сегментов по умолчанию равна нулю. Если


IDA - 0084.htm

84 Сегменты и селекторы
IDA неправильно определила разрядность одного или нескольких сегментов, ее
следует исправить вручную.
Внимание! Изменение разрядности сегмента уничтожает все результаты ди-
зассемблирования — метки, функции, переменные и т.д.
Аргумент еа задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Нулевое значение аргумента use32 указывает на 16-битовую разрядность сегмента и приводит к помещению в ассемблерный листинг атрибута размера сегмента usel6; в противном случае сегмент считается 32- разрядным, а атрибут размера сегмента — use32.
Независимо от разрядности сегмента, IDA всегда выражает смещения 32-битовыми значениями и допустимо создание 16-разрядного сегмента размером более 64 килобайт, однако, следует помнить, что в дальнейшем такой "большой" сегмент не сможет быть ассемблирован.
Функция не учитывает выбранный тип процессора и допускает использование 32-разрядного режима даже с 8086 (!) процессором.
Пример использования:
SegCreate(Ox1000,Ox2000,Ox100,0,0,0);
a) Создание 16-разрядного сегмента
segOOO:0000 segOOO segment at 100h private " use16
b) Сегмент успешно создан. Атрибут размера сегмента выделен жирным шрифтом
SegAddrng(Ox1000,1);
c) Вызов функции SegAddrng для изменения разрядности сегмента
segOOO:00000000 segOOO segment at 100h private " use32
d) Разрядность сегмента успешно изменена

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
use32 | =use32 | Пояснения
 | ==0 | 16-разрядный сегмент
 | ==1 | 32-разрядный сегмент
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: SegCreate.
Интерактивный аналог: "~View\Segments", <Ctrl-E>; <Alt-S>.


IDA - 0085.htm

Сегменты и селекторы
85
success SegAlign (long ea, long alignment)
Функция управляет выравниванием сегмента, помещая в ассемблерный текст соответствующий атрибут выравнивания (byte, word, dword, para, page, mempa-ge). Подробнее о каждом из этих атрибутов можно прочитать в документации, прилагаемой к используемому линкеру.
Никакого влияния на дизассемблируемый образ файла функция не оказывает и не выравнивает сегмент в виртуальной памяти. Это подтверждает эксперимент:
SegCreate(Ox1003,Ox2000,Ox100,0,0,0);
a) создание нового сегмента с адресом начала равным 0x1003
segOOO:0003 segOOO segment at 100h private " use16
b) сегмент создан; смещение первого байта в сегменте равно трем
Message(">1%x\n",SegAlign(Ox1003,saRelWord));
c) вызов функции SegAlign для выравнивания сегмента по границе слова
segOOO:0003 segOOO segment word private '' use16
>1
d) результат — функция поместила в ассемблерный текст атрибут выравнивания 'word' (он выделен жирным шрифтом) и сигнализировала об успешном завершении. Но линейный адрес начала сегмента не был изменен (он выделен жирным шрифтом)!
Атрибут выравнивания возымеет действие только на стадии компоновки объектного файла, расположив сегмент по адресу, кратном двум. Это приведет к несоответствию смещений в дизассемблируемом и целевом файле, что, скорее всего, вызовет невозможность корректного выполнения откомпилированной программы. Для выравнивания сегмента в виртуальной памяти следует воспользоваться функцией SegBounds для перемещения его начала по заданному адресу.
Аргумент еа задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент alignment представляет собой атрибут выравнивания сегмента и может принимать одно из значений, перечисленных в табл. 5.
Таблица 5. Значения аргумента alignment

Определение | # | Пояснения
saAbs | 0 | Безусловное выравнивание
saRelByte | 1 | Выравнивание по границе байта (8 бит)
saRelWord | 2 | Выравнивание по границе слова (16 бит)
saRelPara | 3 | Выравнивание по границе параграфа (16 байт)
saRelPage | 4 | Выравнивание по границе страницы (256-байт для чипов Intel)
saRelDble | 5 | Выравнивание по границе двойного слова (4 байта)
saRel4K | 6 | Выравнивание по границе страницы (4 килобайта для | PharLapOMF)
saGroup | 7 | Segment group
saRel32Bytes | 8 | Выравнивание по границе 32 байта
saRel64Bytes | 9 | Выравнивание по границе 64 байта
saRelQword | 10 | Выравнивание по границе 8 байт


IDA - 0086.htm

Сегменты и селекторы

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
alignment | Кратность выравнивания (смотри определения в табл. |  | 5)
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: SegCreate.
Интерактивный аналог: "~View\Segments", <Ctrl-E>; <Alt-S>.
success SegComb (long segea, long comb)
Функция управляет объединением сегментов, помещая в ассемблерный листинг атрибут комбинирования (private, public, common, at, stack).
Атрибут комбинирования указывает компоновщику, как следует комбинировать сегменты различных модулей, имеющих одно и то же имя. Подробную информацию о каждом из атрибутом можно найти в документации, прилагаемой к используемому линкеру.
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент comb представляет собой атрибут выравнивания и может принимать одно из значений, приведенных в табл. 6.
Пример использования:
SegCreate(Ox1000,0x2000,0x100,0,0,scPub);
a) создаем новый сегмент с атрибутом комбинирования public
segOOO:0000 segOOO segment at 100h public "
b) сегмент успешно создан, атрибут комбинирования выделен жирным шрифтом
SegComb(Ox10000,scStack);
c) вызываем функцию SegComb для изменения атрибута комбинирования сегмента
segOOO:0000 segOOO segment at 100h stack "
d) атрибут комбинирования изменен
Таблица 6. Значения аргумента comb

Определение | # | Пояснения
scPriv | 0 | Атрибут private. He может быть соединен ни с одним другим сегментом
scPub | 2 | Атрибут public. Может быть объединен с другими сегментами с уче-
 |  | том требуемого выравнивания
scPub2 | 4 | Атрибут, определенный Microsoft, то же самое, что и scPub
scStack | 5 | Атрибут stack. Может быть объединен с public-сегментами, но с вы-
 |  | равниванием в один байт
scCommon | 6 | Атрибут common
scPub3 | 7 | Атрибут, определенный Microsoft, то же самое, что и scPub


IDA - 0087.htm

Сегменты и селекторы
87

Аргумент | Пояснения
еа | Любой линейный адрес, принадлежащий сегменту
comb | Атрибут (смотри определения в табл. 6)
return | =return | Пояснения
 | 1 | Операция завершилась успешно
 | 0 | При выполнении операции произошла ошибка
Родственные функции: SegCreate.
Интерактивный аналог: "~View\Segments", <Ctrl-E>; <Alt-S>.
success SegClass (long ea, char class)
Функция изменяет атрибут класса сегмента, помещая в ассемблерный листинг атрибут класса.
Атрибут класса представляет собой текстовую строку, указывающую линкеру порядок следования сегментов. Если это не запрещено комбинаторным атрибутом (см. описание SegComb), линкер объединяет вместе сегменты с одинаковым именем. Рекомендуется назначать имена так, чтобы они отображали функциональное назначение сегментов, например, "CODE", "DATA", "STACK" и т.д. Общепринятые имена перечислены в табл. 7.
Замечание. Большинство линкеров требуют, чтобы в объективном файле присутствовал хотя бы один сегмент с именем "CODE", в противном случае, они могут отказаться обрабатывать такой файл или обработают его неправильно.
Пример использования:
SegCreate(Ox1000,0x2000,0,0,scPub);
a) создаем сегмент с атрибутом public
segOOO:0000 segOOO segment at 100h public "
b) сегмент создан, по умолчанию атрибут класса сегмента отсутствует
SegClass(Ox1000,"MySegment");
c) вызываем функцию SegClass для изменения атрибута класса сегмента
segOOO:0000 segOOO segment at 100h public 'MySegment'
d) атрибут класса сегмента изменен (в тексте он выделен жирным шрифтом)
Таблица 7. Общепринятые наименования классов сегментов

Класс | Пояснения
CODE | Pure code | Сегмент кода
DATA | Pure data |
 |  | Сегмент данных
CONST | Pure data |
BSS | Uninitialized data | Неинициализированные данные
STACK | Uninitialized data | Сегмент стека
XTRN | Extern definitions segment |


IDA - 0088.htm

88 Сегменты и селекторы

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
class | Класс сегмента
return | =return | Пояснения
 | 1 | Операция завершилась успешно
 | 0 | При выполнении операции произошла | ошибка
Родственные функции: SegCreate.
Интерактивный аналог: "~View\Segments", <Ctrl-E>; <Alt-S>.
success SegDefReg (long ea, char reg, long value)
Функция определяет значение сегментных регистров, помещая в ассемблерный текст директиву ASSUME. Это указывает дизассемблеру (ассемблеру) к какому именно сегменту обращается тот или иной адресный операнд. Более подробную информацию по этому вопросу можно получить, обратившись к описанию директивы ASSUME в документации, прилагаемой к используемому ассемблеру, или к справочной (учебной) литературе по языку ассемблера.
Аргумент еа задает любой линейный адрес, принадлежащий сегменту, в который необходимо поместить директиву ASSUME.
Аргумент reg задает сегментный регистр в символьном представлении, например, "DS","ES","SS" и т.д. Строчные и прописные символы не различаются. Тип процессора при назначении сегментного регистра учитывается.
Аргумент value содержит базовый адрес сегмента, загружаемый в регистр и выраженный в параграфах. Если сегмента с указанным базовым адресом не существует, регистр приобретает неопределенное ("nothing") значение.
В реализации этой функции допущена одна ошибка — независимо от успешности выполнения, возвращаемое значение никогда не сигнализирует об ошибке.
Пример использования.
Пусть имеется один сегмент кода ("segOO") и два сегмента данных ("segOOl" и "seg002"), содержащих переменные "My666" и "My777" соответственно. По умолчанию значения сегментных регистров неопределенны, и IDA не может определить, к каким именно сегментам происходит обращение в командах "mov ax,ds:[0]" и "mov dx,es:[0]", поэтому, вынуждена оставить операнды в виде непосредственных смещений (в тексте они выделены жирным шрифтом).
segOOO:0000 segOOO segment byte public 'CODE'
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing
segOOO:0000 mov ax, ds:0
segOOO:0003 mov dx, es:0
segOOO:0003 segOOO ends
segOOO:0003
seg001:0000 ; =============================================================
seg001:0000
seg001:0000 ; Segment type: Pure data
seg001:0000 seg001 segment byte public 'DATA'


IDA - 0089.htm

Сегменты и селекторы
89
seg001:0000
seg001:0000 My666
seg001:0000 seg001
seg001:0000
seg002:0000 ; =============
seg002:0000
seg002:0000 ; Segment type:
seg002:0000 seg002
seg002:0000
seg002:0000 My777
seg002:0000 seg002
assume cs:seg001
dw 6666h
ends
Pure data
segment byte public 'DATA'
assume cs:seg002
dw 7777h
ends
Задать значения сегментных регистров можно с помощью функции DefSegReg, вызов которой может выглядеть так:
DefSegReg(SegByName("segOOO"), "DS", SegByName("seg001")>>4);
DefSegReg(SegByName("segOOO"), "ES", SegByName("seg002")>>4);
В результате ее выполнения IDA смогла отследить обращения к переменным, автоматически подставив их имена вместо смещений (в тексте они выделены жирным шрифтом). Поместив курсор в границы того или другого имени, нажатием клавиши <Enter> можно перейти к соответствующей ячейке памяти.
segOOO:0000 segOOO segment byte public 'CODE'
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:seg002, ss:nothing, ds:seg001
segOOO:0000 mov ax, My666
segOOO:0003 mov dx, es:My777
segOOO:0003 segOOO ends
segOOO:0003
seg001:0000 ; =============================================================
seg001:0000
seg001:0000 ; Segment type: Pure data
seg001:0000 seg001 segment byte public 'DATA'
seg001:0000 assume cs:seg001
seg001:0000 My666 dw 6666h ; DATA XREF: segOOO:OOOOr
seg001:0000 seg001 ends
seg001:0000
seg002:0000 ; =============================================================
seg002:0000
seg002:0000 ; Segment type: Pure data
seg002:0000 seg002 segment byte public 'DATA'
seg002:0000 assume cs:seg002
seg002:0000 My777 dw 7777h ; DATA XREF: segOOO:0003r
seg002:0000 seg002 ends
seg002:0000

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
reg | Сегментный регистр в строковом представлении | (например, "DS' | ')
val | Базовый адрес сегмента, загружаемого в регистр | , выраженный в | параграфах
return | Всегда единица


IDA - 0090.htm

Сегменты и селекторы
Родственные функции: нет.
Интерактивный аналог: "~Edit\Segments\Change segment register
value", <Alt-G>.
success SetSegmentType (long segea, long type)
Функция изменяет тип сегмента, оказывая влияние на его дизассемблирование.
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент type указывает на тип сегмента и может принимать одно из значений, перечисленных в табл. 8. При создании сегмента функцией SegCreate ему присваивается тип "неизвестный" — SEG_NORM.
Таблица 8. Значения аргумента type

Определение | # | Пояснения
SEG_NORM | 0 | Неизвестный тип
SEG_XTRN | 1 | Внешний ('extern') сегмент. Инструкции исключены
SEG_CODE | 2 | Сегмент кода
SEG_DATA | 3 | Сегмент данных
SEG_JMP | 4 | Сегмент Java implementation
SEG_GRP | 6 | Group of segments
SEG_NULL | 7 | Сегмент нулевой длины
SEG_UNDF | 8 | Сегмент неопределенного типа (не используется)
SEG_BSS | 9 | Неинициализированный сегмент
SEG_ABSSYM | 10 | Сегмент с определением абсолютных символов
SEG_COMM | 11 | Сегмент с общими определениями
SEG_IMEM | 12 | Внутренняя память процессора 8051
Пример использования:
SegCreate(Ox1000,Ox2000,Ox100,0,0,0);
a) создаем новый сегмент (по умолчанию неизвестного типа)
segOOO:0000 ; Segment type: Regular
segOOO:0000 segOOO segment at 100h private "
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing
b) сегмент создан (тип выделен жирным шрифтом), автоматически внедрена директива
ASSUME для определения сегментных регистров
SetSegmentType(Ox1000,SEG_DATA);
c) вызов функции SetSegnetType для установки типа "сегмент данных"
segOOO:0000 ; Segment type: Pure data
segOOO:0000 segOOO segment at 100h private "
segOOO:0000 assume cs:segOOO
d) тип сегмента изменен, директива ASSUME, задающая значение регистров DS, ES и
SS, удалена


IDA - 0091.htm

Сегменты и селекторы
91

Аргумент | Пояснения
еа | Любой линейный адрес, принадлежащий сегменту
type | Тип сегмента (возможные значения приведены в табл. |  | 8) !
return | =return | Пояснения
 | !=0 | Успешное завершение операции
 | 0 | Ошибка
Родственные функции: GetSegmentAttr.
Интерактивный аналог: нет.
long GetSegmentAttr (long segea, long attr)
Функция позволяет узнать следующие атрибуты сегмента: кратность выравнивания, комбинацию, привилегии доступа, разрядность, флаги сегмента, селектор, использующийся для базирования сегмента, тип сегмента и значения сегментных регистров, определенных функцией DefSegReg.
Об атрибуте выравнивания можно подробнее прочитать в описании функции SegAlign, об атрибутах комбинации — в описании функции SegComb, o типе сегмента рассказывается в описании функции "SetSegmentType". Об использовании селекторов для базирования сегментов подробно рассказано в главе "Нази-гатор по функциям" (см. описание функций "SegCreate" и "SetSelector").
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент attr указывает функции — содержимое какого атрибута необходимо возвратить. Возможные значения аргумента attr приведены в табл. 9.
Таблица 9. Типы сегментов

Константа | # | Пояснения |  |  | Функция
SEGATTR_ALIGN | 20 | Получить выравнивание сегмента |  |  | SegAlign
SEGATTR_COMB | 21 | Получить атрибуты комбинации |  |  | SegComb
 |  | Привилегии доступа |  |  |
SEGATTR_PERM | 22 | SEGPERM_EXEC | 1 | Исполнение | Внутренняя, исполь- зуемая только в
 |  | SEGPERM_WRITE | 2 | Запись | IDA SDK
 |  | SEGPERM_READ | 4 | Чтение |
SEGATTR_USE32 | 23 | 32-разрядный сегмент |  |  | SegAddrnd
 |  | Флаги сегмента |  |  |
 |  |  | Все сегментные
SEGATTR_FLAGS | 24 | ADDSEG_NOSREG | регистры, заданные по умолчанию, |  | Add_seg (из IDA SDK)
 |  |  | неопределенны |  |
 |  | ADDSEG_OR_DIE | Невозможно добавить сегмент |  |
SEGATTR_SEL | 26 | Селектор сегмента |  |  | SetSelector


IDA - 0092.htm

92
Сегменты и селекторы

Константа | # | Пояснения | Функция
SEGATTR_DEF_ES | 28 | Значение регистра ES по умолчанию |
SEGATTR_DEF_CS | 30 | Значение регистра CS по умолчанию |
SEGATTR_DEF_SS | 32 | Значение регистра SS по умолчанию | DefSegReg
SEGATTR_DEF_DS | 34 | Значение регистра DS по умолчанию |
SEGATTR_DEF_FS | 36 | Значение регистра FS по умолчанию |
SEGATTR_DEF_GS | 38 | Значение регистра GS по умолчанию |
SEGATTR_TYPE | 40 | Тип сегмента | SetSegmentType

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
Туре | Тип сегмента (возможные значения приведены в табл. 9)
return | =return | Пояснения
 | !=0 | Операция завершилась успешно
 | 0 | При выполнении операции произошла | ошибка
Родственные функции: SegAddrng, SegAling, SegComb, SegClass,
SegDefReg, SetSegmentType.
Интерактивный аналог: нет.
char SegName (long ea)
Функция возвращает имя сегмента, заданного любым принадлежащим ему линейным адресом еа. Если передать адрес не принадлежащий никакому сегменту, функция возвратит пустую строку.
Пример использования:
SegCreate(Ox1000,0x2000,0x100,0,0,0);
SegRename(Ox1000,"MySeg");
a) создаем сегмент и тут же переименовываем его в "MySeg"
MySeg:0000 MySeg segment at 100h private "
b) сегмент успешно создан (имя выделено жирным шрифтом)
Message(">%s\n",SegName(Ox1000));
c) вызываем функцию SegName для получения имени сегмента
>MySeg
d) результат — имя сегмента

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий сегменту
return | =return |
 | !="" | Имя сегмента
 | "" | Ошибка


IDA - 0093.htm

_______________Сегменты и селекторы______________93
Родственные функции: SegRename, SegByName.
Интерактивный аналог: по умолчанию имя сегмента отображается в ад-
ресе каждой ячейки.
long FirstSeg()
Функция возвращает линейный адрес начала сегмента с наименьшим линейным адресом начала. Если не существует ни одного сегмента, функция возвратит значение BADADDR, сигнализируя об ошибке.
Замечание. FirstSeg обычно используется в паре с NextSeg для получения списка
адресов начала всех существующих сегментов.
Пример использования:
SegCreate(Ox1000,0x2000,0x9,0,0,0);
SegCreate(Ox100,0x200,0x10,0,0,0);
a) создаем два сегмента с адресами начала 0x100 и 0x1000
Message(">%X\n",FirstSeg());
b) вызываем функцию FirstSeg для получения наименьшего линейного адреса сегмента
>100
c) результат — линейный адрес сегмента с наименьшим линейным адресом начала

Аргумент | Пояснения
Нет | Нет
return | =return |
 | !=BADADDR | Линейный адрес начала сегмента | с наименьшим линей-
 |  | ным адресом |
 | ==BADADDR | Ошибка
Родственные функции: NextSeg.
Интерактивный аналог: "~View\Segments".
IDA - 0093-1.jpg
long NextSeg (long ea)
Функция возвращает линейный адрес начала сегмента, линейный адрес начала которого больше чем переданный линейный адрес еа. Если сегмента удовлетворяющего такому условию не существует, функция возвращает значение BA-DADDDR, сигнализируя об ошибке.


IDA - 0094.htm

94______________Сегменты и селекторы_______________
Передаваемый функции линейный адрес не обязательно должен быть адресом начала какого-то сегмента, более того, он вообще может не принадлежать ни одному сегменту — это ничуть не помешает функции возвратить линейный адрес следующего сегмента, при условии, что такой сегмент есть.
Замечание. NextSeg обычно используется в паре с FirstSeg для получения списка
адресов начала всех существующих сегментов.
Конструкция NextSeg(0), аналогичная FirstSeg(), при условии, что начальные адреса
всех сегментов отличны от нуля (как правило, так практически всегда и бывает).
Замечание. Функции, возвращающей линейный адрес начала сегмента, линейный адрес которого меньше переданного линейного адреса еа, не существует. Тем не менее, эта задача может быть решена на основе имеющихся функций FirstSeg u NextSeg. Ниже приводится пример реализации функции PrevSeg, работающей по следующему алгоритму: вызовами NextSeg сегменты перебираются один за другим до тех пор, пока линейный адрес начала очередного сегмента не превысит переданное значение еа. Затем — возвращается линейный адрес начала предыдущего сегмента.
static PrevSegEx(ea)
{
auto а;
a=0;
while (SegEnd(NextSeg(a))<ea) a=NextSeg(a);
return a;
}
Пример использования NextSeg для получения списка адресов начала всех существующих сегментов:
SegCreate(Ox100,.0x200,0x10,0,0,0);
SegCreate(Ox1000,0x2000,0x100,0,0,0);
SegCreate(Ox10000,.0x20000,0x1000,0,0,0);
a) создаем три сегмента с адресами начала 0x100, 0x1000 и 0x1000
0. Creating a new segment (00000100-00000200) ... ... OK
1. Creating a new segment (00001000-00002000) ... ... OK
2. Creating a new segment (00010000-00020000) ... ... OK
b) сегменты успешно созданы
auto a;
a=FirstSeg();
while(a!=BADADDR)
{
Message(">%X\n",a);
a=NextSeg(a);
}
c) вызываем функцию NextSeg в цикле для получения линейных адресов начала всех
существующих сегментов.
>100
>1000
>10000
d) результат — линейные адреса начала всех существующих сегментов


IDA - 0095.htm

Сегменты и селекторы 95

Аргумент | Пояснения
еа | Линейный адрес не обязательно принадлежащий какому-нибудь |  | сегменту
return | =return |
 | !=BADADDR | Линейный адрес начала следующего сегмента
 | ==BADADDR | Ошибка
Родственные функции: FirstSeg.
Интерактивный аналог: "~View\Segments".
IDA - 0095-1.jpg
void SetSelector (long sel, long value)
Функция создает новый селектор или изменяет значение уже существующего селектора. Создаваемый селектор не должен совпадать с базовым адресом ни одного сегмента, иначе для базирования этого сегмента будет автоматически использоваться созданный селектор, а не его базовый адрес, что приведет к искажению всех смещений внутри сегмента.
Максимальное допустимое количество селекторов равно 4096 (0x1000 в шестнадцатеричной системе исчисления), а индексы селекторов могут принимать любые значение от 0x0 до 0xFFFF включительно.
Аргумент sel задает 16-разрядный индекс создаваемого или модифицируемого селектора. Старшее слово, передаваемого 32-битного значения автоматически обрезается, в результате чего существует угроза непреднамеренного искажения другого селектора, что подтверждает следующий эксперимент:
SetSelector(Ox1,0x666); Message(">%X\n",AskSelector(Ox1));
a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его значение
>666
b) селектор имеет значение 0x666
SetSelector(Ox10001,Ox777);
c) пытаемся создать селектор с индексом 0x10001 и значением 0x777
Message(">%X\n",AskSelector(Ox1));
d) проверяем значение селектора 0x1
>777
e) результат — значение селектора 0x1 искажено! Старшие 16 бит индекса 0x10001 были
обрезаны, в результате чего был модифицирован селектор 0x10001 AND 0xFFFF == 0x1
Аргумент value содержит 32-разрядное значение базы сегмента в параграфах. Оно будет автоматически использовано для базирования сегмента, базовый адрес которого равен индексу данного селектора.


IDA - 0096.htm

96 Сегменты и селекторы
Замечание. Функция DeleteAll (см. описание DeleteAll), удаляющая все сегменты,
не уничтожает селекторов и их приходится удалять "вручную" вызовом DelSelector.

Аргумент | Пояснения
sel | 16-разрядный индекс селектора
val | 32-разрядное значение селектора в параграфах
return | =return | Пояснения
 | void | Функция не возвращает никакого | значения
Родственные функции: AskSelector, DelSelector.
Интерактивный аналог: "~View\Selector", <Insert> — создает новый;
<Ctrl-E> изменяет значение уже существующе-
го селектора.
void DelSelector (long sel)
Функция удаляет селектор, если он существует, в противном случае ничего не происходит. Если данный селектор использовался для базирования сегмента, новый базовый адрес сегмента будет равен base = sel * 0x10, а смещение первого байта в сегменте соответственно: startoffset = startea - sel * 0x10. Создав заново уничтоженный селектор вызовом SetSelector, можно все вернуть на свои места.
Аргумент sel задает 16-разрядный индекс удаляемого селектора. Старшее слово, передаваемого 32-битного значения автоматически обрезается, в результате чего существует угроза непреднамеренного уничтожения другого селектора, что подтверждает следующий эксперимент:
SetSelector(Ox1,Ox666);
Message(">%X\n",AskSelector(Ox1));
a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его значение
>666
b) селектор имеет значение 0x666
DelSelector(Ox10001,Ox777);
c) пытаемся удалить селектор с индексом 0x10001
Message(">%X\n",AskSelector(Ox1));
d) проверяем значение селектора 0x1
>FFFFFFFF
e) результат — селектор 0x1 был уничтожен! Старшие 16 бит индекса 0x10001 были
обрезаны, в результате чего был удален селектор 0x10001 AND 0xFFFF == 0x1

, Аргумент | Пояснения
sel | 16-разрядный индекс уничтожаемого селектора
return | =return | Пояснения
 | void | Функция не возвращает никакого значения
Родственные функции: SetSelector.
Интерактивный аналог: "~View\Selectors", <Del>.


IDA - 0097.htm

_______________Сегменты и селекторы______________97
long AskSelector (long sel)
Функция возвращает значение селектора в параграфах. Если запрошенный селектор отсутствует, функция возвратит переданное ей значение, сигнализируя об ошибке (внимание! не BADADDR). Поскольку, значение селектора не может быть равно его индексу, никакой неоднозначности не возникает, однако, автору книги совершенно непонятно чем вызвано такое решение — не лучше ли при неуспешном завершении функции возвращать BADADDR — значение, которое не может иметь ни один селектор?
Аргумент sel задает 16-разрядный индекс запрашиваемого селектора. Старшее слово передаваемого 32-битного значения автоматически обрезается, в результате существует возможность обращения совсем к другому селектору, чем предполагалось.
Ввиду всего вышесказанного проверка успешности завершения функции должна выглядеть так:
if ((selvalue=AskSelector(sel)) == (sel & OxFFFF))
// ошибка
else
// успешное завершение функции
Аргумент Пояснения
sel 16-разрядный индекс запрашиваемого селектора
return =return Пояснения
sel & 0xFFFF Ошибка
!=(sel & 0xFFF) 32-разрядное значение селектора в параграфах
Родственные функции: SetSelector, FindSelector.
Интерактивный аналог: "~View\Selectors".
IDA - 0097-1.jpg
long FindSelector (long val)
Функция возвращает индекс селектора с указанным значением val, выраженном в параграфах.
Если существуют два и более селектора с идентичными значениями, функция возвращает индекс первого из них в порядке создания. Если же ни одного селектора с таким значением не существет, функция возвращает младшие 16 бит переданного ей аргумента обратно.


IDA - 0098.htm

98 Сегменты и селекторы
Поскольку, значение селектора не может быть равно его индексу, никакой неоднозначности не возникает, однако, автору книги совершенно непонятно чем вызвано такое решение — не лучше ли при неуспешном завершении функции возвращать BADADDR — значение, которое не может иметь ни один селектор?
Ввиду всего вышесказанного, проверка успешности завершения функции должна выглядеть так:
if ((sel=FindSelector(selvalue)) == (selvalue & OxFFFF))
// ошибка
else
// успешное завершение функции
Замечание. Поскольку с данным базовым адресом может существовать только один сегмент, в создании двух, и более селекторов с одинаковыми значениями никакой необходимости нет. Тем не менее, функция SetSelector не препятствует этому и существования двух и более селекторов с одинаковыми индексами в принципе возможно, — один селектор может использоваться для базирования некоторого сегмента, а остальные простаивать. В такой ситуации функция FindSelector может возвратить неверный результат, поэтому, перед ее вызовом следует убедиться, что существует не более одного селектора с каждым значением. Единственный документированный способ решения этой задачи заключается в последовательном переборе всех возможных селекторов в интервале от 0x0 до 0xFFFF.

Аргумент | Пояснения
val | 32-разрядное значение селектора в параграфах для поиска
return | =return |
 | ==(val & 0xFFFF) | Ошибка
 | !=(val & 0xFFF) | 16-разрядный индекс селектора с указанным | значением
Родственные функции: SetSelector, AskSelector.
Интерактивный аналог: "~View\Selectors".
IDA - 0098-1.jpg


IDA - 0099.htm

Элементы 99
Элементы
#Definition
С каждым адресом виртуальной памяти связано 32-разрядное поле флагов (см. главу "Виртуальная память"). Флаги хранят содержимое ячейки, описывают ее представление и указывают на наличие связанных с ней объектов.
Содержимое ячейки: флаги хранят содержимое ячейки виртуальной памяти (в восьми младших битах) или указывают на то, что ячейка содержит неинициализированное значение (см. главу "Виртуальная память").
Представление: флаги определяют будет ли отображаться данный байт в виде данных или машинной инструкции. Они также позволяют уточнить в виде каких именно данных (массива, строки, переменной, непосредственного значения или смещения) он должен отображаться (см. главу 'Типы элементов").
Связанные объекты: флаги указывают на наличие связанных с ячейкой объектов — меток, имен, комментариев и т.д. Сами объекты хранятся в отдельном виртуальном массиве, проиндексированном линейными адресами. В принципе без флагов, ссылающихся на объекты можно было бы и обойтись, но тогда бы пришлось при отображении каждой ячейки просматривать все виртуальные массивы на предмет поиска объектов, ассоциированных с данным линейным адресом, что отрицательно сказалось бы на производительности дизассемблера. Напротив, перенос этой информации в флаги позволяет ускорить работу — обращение к виртуальному массиву происходит только в тех случаях, когда с ячейкой заведомо связан какой-то объект (см. главу "Объекты").
Размер машинных инструкций и типов данных различен, и это не дает возможности быстро определить к какой инструкции (переменной) принадлежит ячейка с таким-то линейным адресом. Можно провести следующую аналогию — текст со словами не отделенными друг от друга пробелами из произвольной точки читать невозможно, поскольку неизвестно, чем является та или иная буква — началом слова или его серединой.
Образно говоря, при дизассемблировании IDA разбивает исследуемый файл на "слова", в терминологии разработчиков — элементы.
Элементом называется последовательность смежных ячеек виртуальной памяти, содержащих одну самостоятельную конструкцию языка ассемблера, с которой можно оперировать как с одним целым, — инструкцию, строку, переменную и т.д.
Первая ячейка элемента называется головой, а все, последующие за ней — хвостом. Элементы единичной длины состоят только из головы и не имеют хвоста.
Признаком головы является ненулевое значение бита FF_DATA (см. табл. 10) поля флагов. Соответственно признаком хвоста является нулевое значение бита FF_DATA и ненулевое значение бита FF_TAIL.


IDA - 0100.htm

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

Бит |  | А | 9 |  |  | 8 |  | 7 | 1 | 0
 | Поле флагов |  |  |  |  |  |  | Поле содержимого
Обозначение |  | FF_DATA | FF_TAIL |  |  | FF_IVL |  | MS_VAL
Назначение |  | Голова | Голова |  | Хвост | ==1 | ==0 | Содержимое ячейки
 |  |  | Тип элемента |  | При- знак хвоста | Инициализированный байт | Неинициализированный байт
 |  |  | ==0 | ==1 | ==1
 |  |  | DATA | CODE
Маска |  | 0x400 | 0x200 |  |  | 0x100 |  | 0xFF
Таблица 11. Определение типа элемента
FF_DATA
FF_TAIL

 | 0 | 1
0 | Неопределенный элемент FF_UNK | Элемент данных, голова FF_DATA
1 | Хвост элемента FF_FAIL | Элемент кода, голова FF_CODE
Хвостовые флаги в двенадцати старших битах содержат смещения относительно начала и конца элемента. Флаги, расположенные по четному линейному адресу — относительно конца, а флаги расположенные по нечетному линейному адресу — относительно его начала.
Это позволяет легко определить, к какому элементу принадлежит такая-то ячейка, а также узнать линейный адрес начала и конца элемента, на основании которых легко вычислить его длину.
Замечание. В файле <INCLUDE\Bytes.hpp>, входящем в lDA SDK определена функция, возвращающая значение хвостовых бит "Mine ushort gettof(flags_t F) {return ushort((F & TL_TOFF) » TL_TSFT); /'


IDA - 0101.htm

Элементы 101
Внимание! Создавать новые объекты, использовать или изменять хвостовые биты может только ядро lDA, но не пользовательские модули! В противном случае это может привести к некорректной работе и зависанию дизассемблера.
Элементы могут существовать только внутри сегментов — попытка создания
элемента по адресу, не принадлежащему ни одному сегменту, обречена на провал.
Навигатор по функциям
Созданием элементов всецело занимается ядро IDA; пользовательские скрипты хотя и имеют достаточные для "ручного" создания элементов привилегии, пользоваться этими привилегиями категорически не рекомендуется — даже незначительная ошибка способна вызвать непредсказуемое поведение дизассемблера вплоть до его полного "зависания".
Напротив, работая с уже созданными ядром элементами, пользователь может решить ряд задач, облегчающих дизассемблирование исследуемого файла. Пусть, например, требуется отыскать в тексте все условные переходы, стоящие после инструкции "test", следующей за вызовом функции, иначе говоря, произвести поиск по шаблону "call *\test *,*\j? *" и вывести протокол отчета.
Это можно осуществить с помощью следующего листинга, сердцем которого является цикл, вызывающий функцию NextHead, последовательно переходящей от одной машинной инструкции к другой:
#include <ida.idc>
static main()
{
auto а;
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(а,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));
a=NextHead(a,BADADDR);
}
}


IDA - 0102.htm

102 Элементы
Результат его работы может быть следующим (в данном примере использовал-
ся файл first.exe — см. главу "Первые шаги с IDA Рго").
>004010CO call ostream::opfx(void)
>004010C5 test eax,eax
>004010C7 jz loc_4010EO
>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 <idc. idc>
static MyGetHead(ea)
{
auto off, F;
F=GetFlags(ea);
if (!F) return -1; //Адрес не принадлежит ни одной ячейке
if (!(F & FF_TAIL)) //Это голова, возвращает ее адрес
return ea;


IDA - 0103.htm

Элементы 103
if (ea & 1) // ...нечетный линейный адрес
return (еа - (F » 20));
// ...четный линейный адрес
return MyGetHead(ea-l);
}
Недостатком такого подхода является его потенциальная несовместимость с последующими версиями IDA Рго, но обойтись для решения проблемы одними лишь высокоуровневыми функциями, встроенными в IDA, не получается.
Задача могла бы быть решена при помощи вызовов функций ItemSize и ItemEnd, предназначенных для вычисления длины и адреса конца элемента соответственно, очевидно, адрес начала элемента равен ItemEnd(ea) — ItemSi-ze(ea), но ItemSize возвращает не размер элемента, а смещение до его конца!
Ниже приведен другой вариант реализации функции MyGetItemHeadEA, вместо низкоуровневых манипуляций с флагами, использующий вызов PrevHead, однако, полностью отказаться от обращений к флагам не удается — приходится вызывать макрос isTail, определенный в файле <idc.idc> для проверки, — не является ли переданный адрес адресом головы элемента.
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 еа) | Возвращает значение на единицу превышающее линей-
 | ный адрес конца элемента
Функции трассировки элементов
long NextHead (long ea) | Возвращает линейный адрес следующей головы эле-
long NextHead (long еа, long maxea) | мента
long PrevHead (long ea) | Возвращает линейный адрес предыдущей головы эле-
long PrevHead (long ea, long minea) | мента


IDA - 0104.htm

104 Элементы

long NextNotTail (long ea) | Возвращает линейный адрес следующей головы эле-
 | мента или неопределенного байта
long PrevNotTail (long ea) | Возвращает линейный адрес предыдущей головы эле-
 | мента или неопределенного байта
long ItemSize (long еа)
В контекстной помощи IDA сообщается, что эта функция определяет размер элемента, как и следует из ее названия. На самом же деле она возвращает расстояние в байтах до конца элемента, что доказывает следующий эксперимент:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Рго!'
segOOO:OOOE a1234 db '1234'
a) исходные данные
auto a,b;
a=SegByName("segOOO");
for(b=0;b<Ox12;b++)
Message(">ea:%X -%c-> %d\n",a+b,Byte(a+b),ItemSize(a+b));
b) скрипт, последовательно передающий функции различные адреса от начала объекта
>ea:1000 -H-> 14
>ea:1001 -е-> 13
>ea:1002 -1-> 12
>ea:1003 -1-> 11
>ea:1004 -о-> 10
>ea:1005 -,-> 9
>ea:1006 -I-> 8
>ea:1007 -D-> 7
>ea:1008 -А-> 6
>ea:1009 - -> 5
>ea:100A -P-> 4
>ea:100B -г-> 3
>ea:100C -о-> 2
>ea:100D -!-> 1
>ea:100E -1-> 4
>ea:100F -2-> 3
>ea:1010 -3-> 2
>ea:1011 -4-> 1
c) результат: функция ItemSize возвращает расстояние до конца объекта в байтах
Минимальное значение, возвращаемое функцией, равно единице. Это же значение возвращается при возникновении ошибки — если функции передать адрес, не принадлежащий ни одному элементу.
Исходный код альтернативной реализации функции ItemSize (см. MyGetItemSize):
static MyGetItemHeadEA(ea)
{
if (GetFlags(ea) & FF_DATA) // голова
return еа;
if (GetFlags(ea) & FF_TAIL) // хвост
return PrevHead(ea,0);


IDA - 0105.htm

Элементы 105
return -1; // если не голова и не хвост - ошибка
}
static MyGetItemSize(ea)
{
if (GetFlags(ea) & MS_CLS) // элемент?
return ItemEnd(ea) - MyGetItemHeadEA(ea);
return -1;
}
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Pro!'
segOOO:OOOE a1234 db '1234'
a) исходные данные
auto a,b;
a=SegByName("segOOO");
for(b=0;b<Ox12;b++)
Message(">ea:%X -%c-> %d\n",a+b,Byte(a+b),MyGetItemSize(a+b));
b) скрипт, последовательно передающий функции MyGetItemSize различные адреса от
начала объекта
Замечание. Перед исполнением скрипта файл должен быть загружен в память.
Это можно осуществить нажатием клавиши <F2>.
>ea:1000 -H-> 15
>ea:1001 -е-> 15
>ea:1002 -1-> 15
>ea:1003 -1-> 15
>ea:1004 -о-> 15
>ea:1005 -,-> 15
>ea:1006 -I-> 15.
>ea:1007 -D-> 15*
>ea:1008 -А-> 15
>ea:1009 - -> 15
>ea:100A -P-> 15
>ea:100B -г-> 15
>ea:100C -о-> 15
>ea:100D -!-> 15
>ea:100E -1-> 5
>ea:100F -2-> 5
>ea:1010 -3-> 5
>ea:1011 -4-> 5
c) результат — корректное выполнение функции

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


IDA - 0106.htm

106 Элементы
Родственные функции: нет.
Интерактивный аналог: нет.
long ItemEnd (long еа)
Функция возвращает значение на единицу превышающее линейный адрес конца элемента, заданного линейным адресом еа.
Если переданный адрес не принадлежит ни одному сегменту, функция возвратит единицу, сигнализируя об ошибке.
Замечание. Если по указанному адресу расположен бестиповой байт (т.е. переданный адрес не принадлежит ни одному элементу), функция возвратит не ошибку, а линейный адрес следующей ячейки.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Pro!'
segOOO:OOOE a1234 db '1234'
a) исходные данные
Message(">%s\n",atoa(ItemEnd(SegByName("segOOO"))));
b) вызываем функцию ItemEnd, передавая ей адрес, принадлежащий элементу "Hello,
IDA Pro!"
>segOOO:OOOE
c) результат — функция вернула значение, на единицу превышающее адрес конца
данного элемента

Аргумент | Пояснения
еа | Линейный адрес, принадлежащий элементу или бестиповому байту
return | =return | Пояснения
 | !=1 | Значение, на единицу превышающее линейный | адрес
 |  | конца элемента |
 | ==1 | Ошибка
Родственные функции: нет.
Интерактивный аналог: нет.
long NextHead (long ea)
(версия IDA 3.85 и младше)
Возвращает линейный адрес головы следующего элемента, в противном случае —BADADDR, сигнализируя об ошибке. Переданный функции линейный адрес еа не обязательно должен принадлежать какому-то элементу — он может даже вообще не существовать.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Pro!'
segOOO:OOOE a1234 db '1234'
а) исходные данные


IDA - 0107.htm

Элементы 107
Message(">%s\n",atoa(NextHead(SegByName("segOOO"))));
b) вызываем функцию NextHead, передавая ей адрес, принадлежащий элементу "Hello,
IDA РгоГ
>segOOO:OOOE
c) результат — функция вернула адрес головы следующего элемента
Другой пример использования функции NextHead приведен в файле
<href.idc>, распространяемом вместе с IDA.
Замечание. Линейный адрес начала следующего элемента будет возвращен даже
в том случае, если элемент находится в другом сегменте.

Аргумент | Пояснения
еа | Линейный адрес, не обязательно принадлежащий какому-то |  | элементу
return | =return | Пояснения
 | !=BADADDR | Линейный адрес головы следующего элемента
 | ==BADADDR | Ошибка
Родственные функции: PrevHead.
Интерактивный аналог: нет.
long NextHead (long ea, long maxea)
(версия IDA 4.0 и старше)
В версии 4.0 прототип функции NextHead(long еа) (см. ее описание) был изменен добавлением еще одного аргумента — maxea, ограничивающего диапазон адресов, доступных функции.
Функция возвращает адрес головы следующего элемента, при условии, что он меньше, чем maxea, т.е. отвечает следующему условию еа < return value < maxea.
Изменение прототипа повлекло за собой неработоспособность всех ранее созданных скриптов, использующих эту функцию и необходимость внесения в них исправлений — замены NextHead(ea) на NextHead(ea, BADADDR).
Замечание. Ограничение максимального адреса облегчает написание скриптов,
работающих с выделенными регионами (см. описание функций SelStart и SelEnd).

Аргумент | Пояснения
еа | Линейный адрес, не обязательно принадлежащий какому-то элементу
mахеа | Значение на единицу превышающее наибольший адрес, доступный функции
return | =return | Пояснения
 | !=BADADDR | Линейный адрес головы следующего элемента
 | ==BADADDR | Ошибка
Родственные функции: PrevHead.
Интерактивный аналог: нет.


IDA - 0108.htm

108 Элементы
long PrevHead (long ea)
(версия IDA 3.85 и младше)
Функция возвращает линейный адрес предыдущей головы элемента (не головы предыдущего элемента!). Если указать на хвост элемента, функция возвратит адрес его головы.
Переданный функции линейный адрес еа не обязательно должен принадлежать какому-то элементу — он может даже вообще не существовать.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Pro!'
segOOO:OOOE a1234 db '1234'
a) исходные данные
Message(">%s\n",atoa(PrevHead(SegByName("segOOO")^Ox2)));
b) вызываем функцию PrevHead, передавая ей адрес принадлежащий элементу "Hello,
IDA Рго!"
>segOOO:OOOE
c) результат — функция вернула адрес предыдущей головы элемента (этого элемента)

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


IDA - 0109.htm

Элементы
109

Аргумент | Пояснения
еа | Линейный адрес, не обязательно принадлежащий какому-то элементу
minea | Минимальный адрес, доступный функции
return | =return | Пояснения
 | !=BADADDR | Линейный адрес предыдущей головы элемента (не голо-
 |  | вы предыдущего элемента!)
 | ==BADADDR | Ошибка
Родственные функции: NextHead.
Интерактивный аналог: нет.
long NextNotTail (long еа)
Функция возвращает линейный адрес головы следующего элемента или бестипового байта. Переданный функции линейный адрес еа не обязательно должен принадлежать какому-то элементу — он может даже вообще не существовать.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Pro!'
segOOO:OOOE a1234 db '1234'
a) исходные данные
Message(">%s\n",atoa(NextNotTail(0)));
b) вызываем функцию NextNotTail, передавая ей нулевой адрес
>segOOO:0000
c) результат — функция вернула адрес головы первого элемента

Аргумент | Пояснения
еа | Линейный адрес, не обязательно принадлежащий какому-то элементу
return | =return | Пояснения
 | !=BADADDR | Линейный адрес головы следующего элемента или | бести-
 |  | пового байта |
 | ==BADADDR | Ошибка
Родственные функции: PrevNotTail.
Интерактивный аналог: нет.
long PrevNotTail (long еа)
Функция возвращает линейный адрес головы предыдущего элемента (не предыдущей головы элемента!). Переданный функции линейный адрес еа не обязательно должен принадлежать какому-то элементу — он может даже вообще не существовать.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello,IDA Рго!'
segOOO:OOOE a1234 db '1234'
а) исходные данные


IDA - 0110.htm

110 Элементы
Message(">%s\n",atoa(NextNotTail(BADADDR)));
b) вызываем функцию PrevNotTail, передавая ей значение BADADDR
>segOOO:OOOE
c) результат — функция вернула адрес головы самого последнего из существующих
элементов
Замечание. В отличие от функции NextNotTail, функция PrevNotTail игнорирует
бестиповые байты.

Аргумент | Пояснения
еа | Линейный адрес, не обязательно принадлежащий какому-то элементу
return | ==return | Пояснения
 | !=BADADDR | Линейный адрес головы предыдущего элемента
 | ==BADADDR | Ошибка
Родственные функции: PrevNotTail.
Интерактивный аналог: нет.


IDA - 0111.htm

Типы элементов 111
Типы элементов
#Definition
Поле флагов головы элемента определяет является ли данный элемент элементом кода или элементом данных (см. главу "Элементы"), а также уточняет его тип. Например, одна и та же цепочка из четырех байт может быть двойным словом, типом float, ASCII-строкой, массивом байт или двойных слов и т.д.
Замечание. Типизация данных — одно из немаловажных достоинств IDA Pro, ощутимо повышающее качество дизассемблирования. В полностью дизассемблиро-ванной программе не должно быть ни одного байта данных неопределенного типа.
IDA Рго поддерживает следующие типы данных — байт, слово, двойное слово, четверное слово, восьмерное слово, float, double, packed real, ASCII-строка, массив, состоящих из любых вышеперечисленных типов, а также тип align — байты, использующиеся для выравнивания кода (данных) по кратным адресам (см. табл. 12).
Таблица 12. Поддерживаемые типы данных

Константа | # | Тип
FF_BYTE | 0x00000000 | Байт
FF_WORD | 0x10000000 | Слово
FF_DWRD | 0x20000000 | Двойное слово
FF_QWRD | 0x30000000 | Четверное слово
FF_TBYT | 0x40000000 | Восьмерное слово
FF_ASCI | 0x50000000 | ASCII-строка
FF_STRU | 0x60000000 | Структура
FF_XTRN | 0x70000000 | Внешние данные неизвестного размера
FF_FLOAT | 0x80000000 | Float
FF_DOUBLE | 0x90000000 | Double
FF_PACKREAL | 0xA0000000 | Упакованное десятичное целое
FF_ALIGN | 0xB0000000 | Директива выравнивания
Цепочка бестиповых байт может быть преобразована в любой поддерживаемый IDA Pro тип данных, при условии что она имеет достаточный для такой операции размер. Уже существующий элемент данных также может быть преобразован в данные другого типа, если он имеет достаточный для такой операции размер, либо за его хвостом следует цепочка бестиповых байт необходимой длины.
Если в результате преобразования, размер элемента уменьшается, его остаток преобразуется в один или несколько бестиповых байт.


IDA - 0112.htm

112 Типы элементов ___
Примеры:
segOOO:0000 Var db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh о
segOOO:0005 Var2 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 49h I
segOOO:0008 db 44h D
segOOO:0009 db 41h А
segOOO:OOOA db 20h
Бестиповая переменная "Var" может быть преобразована в байт, слово, двойное слово, float, ASCII-строку, но попытка преобразовать ее в четверное, восьмерное слово, double, packed real приведет к ошибке — поскольку тому препятствует элемент данных, расположенный по адресу "segOOO:0005". Если же его уничтожить, преобразование пройдет успешно. Аналогично:
a) преобразование Var в двойное слово:
segOOO:0000 Var dw 6548h
segOOO:0002 db 6Ch
segOOO:0003 db 6Ch
b) преобразование Var в двойное слово невозможно — требуется предварительно уничтожить следующий за ним элемент:
segOOO:0000 Var dw 6548h
segOOO:0002 dw 6C6Ch
Два и более последовательных элементов одного типа могут быть объединены в массив — как бы макроэлемент, собирающий их всех под одну крышу. С массивом IDA Рго работает как с единым целым, в частности, в начале каждой строки указывает не адрес текущего элемента, а адрес начала массива:
segOOO:0000 db 6Ch, 6Fh, 2Ch, 20h, 49h, 44h, 41h, 20h, 50h, 72h, 6Fh
segOOO:0000 db 21h, 0
Функции трассировки элементов (см. главу "Элементы") будут возвращать только адреса начала массива и следующего за массивом элемента, но не адреса элементов самого массива! Аналогично, функции изменения представления операндов (см. главу "Операнды") будут изменять представление всех элементов массива одновременно, но никак не выборочно.
Поэтому, разумно объединять в массив данные лишь тогда, когда они имеют одинаковый тип, одинаковое представление и нет никакой необходимости в индивидуальной работе ни с одним элементом. В противном случае создание массива доставит больше неудобств, чем пользы.
Важно понять — массив в IDA Рго это один элемент, а не совокупность множества элементов другого типа.


IDA - 0113.htm

Типы элементов 113
Максимальная длина элемента ограничена четырьмя килобайтами — это связано с тем, что под хвостовые биты, содержащие смещения относительно начала (конца) элемента отведено 12 разрядов, отсюда и ограничение на длину.
Если требуется создать строку или массив большего размера, единственный выход — создать два (или более) массивов (строк), расположив их последовательно друг за другом.
Элемент данных может иметь тип align — указывающий, что эти байты используются для выравнивания кода (данных) по кратным адресам. Формально тип align — такой же точно тип как, например, слово или двойное слово, и с ним можно выполнять точно те же операции, что и над любым другим элементом данных. Единственная его особенность состоит в том, что принадлежащие ему байты в ассемблерный листинг не попадают:
segOOO:0000 db 48h ; H segOOO:0000 db 48h ; H
segOOO:0001 db 65h ; e segOOO:0001 align 2
segOOO:0002 db 6Ch ; 1 segOOO:0002 db 6Ch ; 1
Элемент кода не имеет никаких типов, разрядность инструкций определяется разрядностью сегмента, а логика работы дизассемблера — типом выбранного процессора.
Элемент данных не может быть непосредственно преобразован в элемент кода и, соответственно, наоборот. Элемент кода может быть создан только из цепочки бестиповых байт достаточной длины.
Если после создания элемента кода IDA может определить адрес следующей выполняемой инструкции, она автоматически пытается создать в соответствующем месте очередной элемент кода — так продолжается до тех пор, пока ей не встретится инструкция, передающая управление по адресу, который IDA вычислить не в состоянии. Это может быть, например, регистровый переход, команда выхода из подпрограммы (прерывания) и т.д. Возможности создания одного элемента кода в IDA Рго нет.
Строго говоря, элементы кода и данных не являются неделимым целым — IDA Рго предоставляет возможность выборочной работы с их операндами (если они есть) — см. главу "Операнды".
Навигатор по функциям
Группа функций MakeByte, MakeWord, MakeDword, MakeQword, MakeFloat, MakeDouble, MakePackedReal, MakeTbyte предназначена для преобразования цепочки бестиповых байт (или уже существующего элемента данных) в элемент данных типа байт, слово, двойное слово, четверное слово, float, double, PackedReal и восьмерное слово соответственно.
Интерактивный аналог первых трех функций — пункт "Data" меню "~Edit" (или "горячая клавиша "D"">), циклично преобразующий тип элемента, находящегося под курсором в байт, слово и двойное слово. При необходимости в эту последовательность можно включить и другие типы данных, вызвав диалог "Setup data types" из меню "Options" ("горячая клавиша — "Alt-D").


IDA - 0114.htm

114 Типы элементов
Функция MakeStr преобразует цепочку бестиповых байт в ASCII-строку заданной длины или попытается определить ее автоматически. Автоматически распознаются длины следующих типов строк — ASCIIZ-строк, заканчивающихся символом нуля; PASCAL-строк, начинающихся с байта, содержащего длину строки и DELPHI-строк, начинающихся со слова (двойного слова), содержащего длину строки. Если строка не принадлежит ни одному из этих трех типов, концом строки считается:
a) первый нечитабельный ASCII-символ;
b) неинициализированный байт;
c) голова элемента кода или данных;
d) конец сегмента.
Функция MakeArray создает массив, состоящий из данных одного типа — байтов, слов, двойных слов, четверных слов, двойных слов в формате float, четверных слов в формате double, packed real, tbyte. Бестиповые байты могут стать частью массива любого типа. Строки не могут быть элементами никакого массива.
Тип массива определяется типом его первого элемента. Все остальные элементы массива на момент его создания должны быть представлены бестиповыми байтами, — последовательность типизированных данных не может быть преобразована в массив.
Элементы массива записываются в строку, отделяясь друг от друга знаком запятой. Если два или более подряд идущих элемента имеют одно и то же значение (в том числе и неинициализированное), то для сокращения ассемблерного листинга используется конструкция "DUP".
Функция Align помещает в ассемблерный файл директиву выравнивания align и исключает из дизассемблируемого листинга байты, используемые для выравнивания.
Функция MakeCode создает по указанному адресу элемент кода, выполняя дизассемблирование первой машинной инструкции. Если это возможно, автоматически дизассемблируются и другие инструкции. Это происходит в следующих случаях:
a) текущая инструкция не изменяет нормального выполнения программы и за ее концом расположены бестиповые байты;
b) текущая инструкция изменяет нормальное выполнение программы, осуществляя переход по непосредственному адресу, тогда IDA продолжит дизассемблирование с этого адреса.
Если встречается инструкция, изменяющая адрес перехода непредсказуемым образом (например, RET), то IDA прекращает дизассемблирование.
Во время дизассемблирования IDA при необходимости создает перекрестные ссылки и автогенерируемые метки.
Функция MakeUnkn разрушает элемент, заданный любым принадлежащим ему адресом, превращая его содержимое в бестиповые байты. Объекты, связанные с элементом (например, метки, комментарии) при этом не уничтожаются.


IDA - 0115.htm

Типы элементов 115
Сводная таблица функций

Функции создания новых элементов, преобразования и уничтожения элементов
success MakeByte (long ea) ' | Создает (преобразует) ячейку в байт
success MakeWord (long ea) | Создает (преобразует) ячейку в слово (2 байта)
success MakeDword (long ea) | Создает (преобразует) ячейку в двойное слово (4 байта)
success MakeQword (long ea) | Создает (преобразует) ячейку в четверное слово (8 байт)
success MakeFloat (long ea) | Создает (преобразует) ячейку в тип float (представление
 | с плавающей запятой 4 байта)
success MakeDouble (long ea) | Создает (преобразует) ячейку в тип Double (представле-
 | ние с плавающей запятой 8 байт)
success MakePackReal (long ea) | Создает (преобразует) ячейку в тип PackReal (от 10 до
 | 12 байт)
success MakeTbyte (long ea) | Создает (преобразует) ячейку в тип Tbyte (10 байт)
success MakeStr (long ea, | Создает ASCII строку
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)
Функция создает по переданному ей линейному адресу еа элемент данных типа байт.
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в байт, а хвост элемента (если он есть) — в бестиповые байты.
Если по данному адресу находится хвост элемента данных, голова или хвост элемента кода, функция возвратит ошибку.
Наличие неинициализированных байт в создаваемом или преобразуемом элементе не является препятствием для выполнения этой функции.


IDA - 0116.htm

116 Типы элементов
Пример использования:
Эксперимент 1
segOOO:0000 db ? ; unexplored
a) исходные данные
Message(">%x\n",MakeByte(SegByName("segOOO")));
b) вызываем функцию MakeByte для создания нового элемента данных типа байт,
передавая ей адрес бестипового байта
segOOO:0000 db ? >1
c) результат — элемент данных типа байт успешно создан
Замечание. В ассемблерном листинге бестиповые байты помечаются комментарием "unexplored" (или ASCII кодом содержимого), сигнализирующим о неисследо-ваности данной ячейки. В полностью дизассемблированной программе не должно остаться ни одного неисследованного байта — тип каждой ячейки должен быть задан явно.
Эксперимент 2
segOOO:0000 aHelloSailor db 'Hello, Sailor'
a) исходные данные
Message(">%x\n",MakeByte(SegByName("segOOO")));
b) вызываем функцию MakeByte для преобразования элемента типа "строка" в элемент
типа "байт", передавая ей линейный адрес головы данного элемента
segOOO:0000 aHelloSailor db 48h segOOO:0001 db 65h e segOOO:0002 db 6Ch 1 segOOO:0003 db 6Ch 1 segOOO:0004 db 6Fh о segOOO:0005 db 2Ch , segOOO:0006 db 20h segOOO:0007 db 53h S segOOO:0008 db 61h а segOOO:0009 db 69h i segOOO:OOOA db 6Ch 1 segOOO:OOOB db 6Fh о segOOO:OOOC db 72h г segOOO:OOOD db 66h f >1
c) результат — успешное преобразование; хвост элемента типа строка преобразован в
бестиповые байты
Эксперимент 3
segOOO:0000 aHelloSailor db 'Hello, Sailor'
a) исходные данные
Message(">%x\n",MakeByte(1+SegByName("segOOO")));
b) вызываем функцию MakeByte для преобразования ячейки, принадлежащей хвосту


IDA - 0117.htm

Типы элементов 117
segOOO:0000 aHelloSailor db 'Hello, Sailor'
>0
с) результат — функция возвратила ошибку, не выполнив преобразования
Эксперимент 4
segOOO:0000 PUSH AX
a) исходные данные
Message(">%x\n",MakeByte(1+SegByName("segOOO")));
b) вызываем функцию MakeByte для преобразования элемента кода в элемент данных
типа байт
segOOO:0000 PUSH AX
>0
c) результат — функция возвратила ошибку, не выполнив преобразования

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeWord, MakeDword, MakeQword,
MakeFloat, MakeDouble, MakePAckReal,
MakeTbyte.
Интерактивный аналог: "~Edit\Data"; <D>.
success MakeWord (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа слово, длиной два байта. Порядок следования младших и старших байт зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в слово, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу.
В противном случае (если размер элемента недостаточен для преобразования, а следом за его хвостом находится другой элемент или не находится ничего) функция возвратит ошибку не выполнив преобразования. Для выполнения такого преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае, если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все слово приобретает неинициализированное значение.


IDA - 0118.htm

118 Типы элементов
Пример использования:
Эксперимент 1
segOOO:0000 db ? ; unexplored
segOOO:0001 db ? ; unexplored
a) исходные данные
Message(">%x\rT,MakeWord(SegByName("segOOO")));
b) вызываем функцию MakeWord для создания нового элемента данных типа слово, передавая ей адрес цепочки из двух бестиповых байтов
segOOO:0000 dw ?
>1
c) результат — элемент данных типа слово успешно создан
Эксперимент 2
segOOO:0000 db ? ; unexplored
segOOO:0001 db ?
a) исходные данные
Message(">%x\n",MakeWord(SegByName("segOOO")));
b) вызываем функцию MakeWord для создания нового элемента данных типа слово, передавая ей адрес бестипового байта
segOOO:0000 db ? ; unexplored
segOOO:0001 db ?
>0
c) результат — функция завершилась с ошибкой, т.к. для создания нового элемента недостаточно места — по адресу segOOO:0001 находится элемент данных типа байт, который не может быть автоматически присоединен к слову. Для выполнения преобразования его необходимо уничтожить вызовом MakeUnkn
MakeUnkn(SegByName("segOOO")+1,0);
d) вызываем функцию MakeUnkn для удаления элемента данных, расположенного по адресу"sедООО:ООО1"
segOOO:0000 db ? ; unexplored
segOOO:0001 db ? ; unexplored
e) результат — элемент данных успешно разрушен
Message(">%x\n",MakeWord(SegByName("segOOO")));
f) повторно вызываем функцию MakeWord, на этот раз передавая ей адрес цепочки из двух бестиповых байт
segOOO:0000 dw ?
>1
g) результат — элемент данных типа слово успешно создан

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


IDA - 0119.htm

Типы элементов 119
Родственные функции: MakeByte, MakeDword, MakeQword, MakeFloat,
MakeDouble, MakePAckReal, MakeTbyte.
Интерактивный аналог: "^Edit\Data"; <D>.
success MakeDword (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа двойное слово, длиной четыре байта. Порядок следования младших и старших байт зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в двойное слово, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу.
В противном случае (если следом за его хвостом находится другой элемент не находится ничего) функция возвратит ошибку не выполнив преобразования. Для выполнения такого преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все двойное слово приобретает неинициализированное значение.
Пример использования:
Эксперимент 1
segOOO:0000 db ? ; unexplored segOOO:0001 db ? ; unexplored segOOO:0002 db ? ; unexplored segOOO:0003 db ? ; unexplored
a) исходные данные
Message(">%x\n",MakeDword(SegByName("segOOO")));
b) вызываем функцию MakeDword для создания нового элемента данных типа двойное
слово, передавая ей адрес цепочки из четырех бестиповых байт
segOOO:0000 dd ?
>1
c) результат — элемент данных типа двойное слово успешно создан
Эксперимент 2
segOOO:0000 db ? ; unexplored
segOOO:0001 db ? ; unexplored
segOOO:0002 dw ?
а) исходные данные


IDA - 0120.htm

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

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeQword, MakeFloat,
MakeDouble, MakePackReal, MakeTbyte.
Интерактивный аналог: "^Edit\Data"; <D>.
success MakeQword (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа четверное слово, длиной восемь байт. Порядок следования младших и старших байт зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных, то функция преобразует его в четвертное слово, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразо-


IDA - 0121.htm

Типы элементов 121
вания, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу. В противном случае, если следом за его хвостом находится другой элемент или не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для выполнения преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из двух байт имеет неинициализированное значение, все двойное слово приобретает неинициализированное значение.
Пример использования:
segOOO:0000 db ? unexplored
segOOO:0001 db ? unexplored
segOOO:0002 db ? unexplored
segOOO:0003 db ? unexplored
segOOO:0004 db ? unexplored
segOOO:0005 db ? unexplored
segOOO:0006 db ? unexplored
segOOO:0007 db ? unexplored
a) исходные данные
Message(">%x\n",MakeQword(SegByName("segOOO")));
b) вызываем функцию MakeQword для создания нового элемента данных типа четвер-
ное слово, передавая ей адрес цепочки из восьми бестиповых байт
segOOO:0000 dq ? >1
c) результат — элемент данных типа четверное слово успешно создан

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeDword, MakeFloat,
MakeDouble, MakePackReal, MakeTbyte.
Интерактивный аналог: ("^Options\Setup data types"; <Alt-D>), <Q>.
Замечание. Для включения типа "четверное слово" в список типов данных, пролистываемых нажатием клавиши <D>, необходимо, вызвав диалог "Setup data types\ установить галочку напротив пункта "Quadro word".
success MakeFloat (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа float, длиной четыре байта. Представление типа float зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 он имеет следующее строение (см. рис. 15):


IDA - 0122.htm

122
Типы элементов

3 | Порядок (8 бит) | Мантисса (23 бита)
н а к |  |
Рис. 15. Представление типа float на микропроцессорах серии Intel 80x86
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в двойное слово типа float, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу. В противном случае, если следом за его хвостом находится другой элемент или не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для выполнения преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из четырех байт имеет неинициализированное значение, все двойное слово приобретает неинициализированное значение.
Пример использования:
segOOO:0000 db 48h ; Н segOOO:0001 db 65h ; e segOOO:0002 db 6Ch ; 1 segOOO:0003 db 6Ch ; 1 segOOO:0004 db 6Fh ; о
a) исходные данные
Message(">%x\n",MakeFloat(SegByName("segOOO")));
b) вызываем функцию MakeFloat для создания нового элемента данных типа float,
передавая ей адрес цепочки из четырех бестиповых байта
segOOO:0000 dd 1.1431391e27
>1
с) результат — элемент данных типа float успешно создан

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeDword, MakeQword,
MakeDouble, MakePackReal, MakeTbyte.
Интерактивный аналог: ("^Options\Setup data types"; <Alt-D>), <F>.


IDA - 0123.htm

Типы элементов
123
Замечание. Для включения типа "четверное слово' в список типов данных, пролистываемых нажатием клавиши <D>, необходимо, вызвав диалог "Setup data types", установить галочку напротив пункта "Float ".
success MakeDouble (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа double, длиной восемь байт. Представление типа double зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 он имеет следующее строение (см. рис. 16):

3 | Порядок (11 | бит) | Мантисса (52 бита)
н |  |  |
а |  |  |
к |  |  |
Рис. 16. Представление типа double на микропроцессорах серии Intel 80x86
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в четверное слово типа double, a хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу. В противном случае, если следом за его хвостом находится другой элемент или не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для выполнения преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если хотя бы один из восьми байт имеет неинициализированное значение, все четверное слово приобретает неинициализированное значение.
Пример использования: -
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh о
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 53h
a) исходные данные
Message(">%x\n",MakeDouble(SegByName("segOOO")));
b) вызываем функцию MakeDouble для создания нового элемента данных типа double,
передавая ей адрес цепочки из восьми бестиповых байта
segOOO:0000
>1
dq 2.635692361932979e92
с) результат — элемент данных типа double успешно создан


IDA - 0124.htm

124 Типы элементов

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeDword, MakeQword,
MakeQword, MakePackReal, MakeTbyte.
Интерактивный аналог: ("~Options\Setup data types"; <Alt-D>), <u>.
Замечание. Для включения типа "четверное слово" в список типов данных, пролистываемых нажатием клавиши <D>, необходимо, вызвав диалог "Setup data types", установить галочку напротив пункта "Double".
success MakePackReal (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа packed real, занимающий в результате от обстоятельств от десяти до двенадцати байт.
Если по данному адресу находится голова ранее созданного элемента данных, функция преобразует его в packed real, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу. В противном случае, если следом за его хвостом находится другой элемент или не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для выполнения преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае, если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если один или более байт имеют неинициализированное значение, они никак не влияют на содержимое остальных и вся цепочка байт packed real не принимает неинициализированного значения.
Пример использования:
segOOO:0000 db ? unexplored
segOOO:0001 db ? unexplored
segOOO:0002 db ? unexplored
segOOO:0003 db ? unexplored
segOOO:0004 db ? unexplored
segOOO:0005 db ? unexplored
segOOO:0006 db ? unexplored
segOOO:0007 db ? unexplored
segOOO:0008 db ? unexplored
segOOO:0009 db ? unexplored
a) исходные данные


IDA - 0125.htm

Типы элементов 125
Message(">%x\n",MakePackReal(SegByName("segOOO")));
b) вызываем функцию MakePackReal для создания нового элемента данных типа pac-
ked real, передавая ей адрес цепочки из десяти бестиповых байтов
segOOO:0000 db ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
>1
c) результат — элемент данных типа packed real успешно создан

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeDword, MakeFloat,
MakeQword, MakeDouble, MakeTbyte.
Интерактивный аналог: ("~Options\Setup data types"; <Alt-D>), <P>.
Замечание. Для включения типа "четверное слово" в список типов данных, пролистываемых нажатием клавиши <D>, необходимо, вызвав диалог "Setup data types\ установить галочку напротив пункта "Packeed rеal.
success MakeTbyte (long ea)
Функция создает по переданному ей линейному адресу еа элемент данных типа tbyte, длиной десять байт. Порядок следования младших и старших байт зависит от выбранного процессора. На микропроцессорах серии Intel 80x86 младший байт располагается по меньшему адресу и, соответственно, наоборот.
Если по данному адресу находится голова ранее созданного элемента данных, то функция преобразует его в tbyte, а хвост элемента (если он есть) — в бестиповые байты. Если размер элемента недостаточен для преобразования, но следом за его хвостом находятся бестиповые байты, они будут автоматически присоединены к новому элементу. В противном случае, если следом за его хвостом находится другой элемент или не находится ничего, функция возвратит ошибку, не выполнив преобразования. Для выполнения преобразования необходимо предварительно уничтожить мешающие элементы вызовом MakeUnkn (см. описание функции MakeUnkn).
Ошибка возвратится и в том случае, если по переданному функции линейному адресу находится хвост элемента данных, голова или хвост элемента кода.
Если один или более байт имеют неинициализированное значение, они никак не влияют на содержимое остальных и вся цепочка байт tbyte не принимает неинициализированного значения.
Пример использования:
segOOO:0000 db ? ; unexplored
segOOO:0001 db ? ; unexplored
segOOO:0002 db ? ; unexplored
segOOO:0003 db ? ; unexplored


IDA - 0126.htm

126 Типы элементов
segOOO:0004 db ? unexplored
segOOO:0005 db ? unexplored
segOOO:0006 db ? unexplored
segOOO:0007 db ? unexplored
segOOO:0008 db ? unexplored
segOOO:0009 db ? unexplored
a) исходные данные
Message(">%x\n",MakeQword(SegByName("segOOO")));
b) вызываем функцию MakeTbyte для создания нового элемента данных типа tbyte,
передавая ей адрес цепочки из десяти бестиповых байтов
segOOO:0000 db ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
>1
c) результат — элемент данных типа tbyte успешно создан

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы |  | элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение
i | ==0 | Ошибка
Родственные функции: MakeByte, MakeWord, MakeDword, MakeQword,
MakeFloat, MakeDouble, MakePackReal.
Интерактивный аналог: ("^Options\Setup data types"; <Alt-D>), <T>.
Замечание. Для включения типа "четверное слово" в список типов данных, пролистываемых нажатием клавиши <D>, необходимо, вызвав диалог "Setup data types", установить галочку напротив пункта "Tbyte".
success MakeStr (long ea, long endea)
Функция преобразует последовательность бестиповых байт в ASCII-строку, автоматически назначая ей метку, стиль которой задается вызовом "Set-LongPrm(INF_STRTYPE)" (см. описание функции SetLongPrm в главе "Глобальные настройки").
Аргумент еа задает линейный адрес начала цепочки бестиповых байт или головы элемента данных, преобразуемого в строку. Если по данному адресу находится хвост элемента данных, голова или хвост элемента кода, то функция возвратит ошибку, не выполнив преобразования.
Аргумент endea задает линейный адрес конца строки. Если передать функции значение BADADDR, то IDA предпримет попытку вычислить адрес конца автоматически. Поддерживаются следующие типы строк — ASCIIZ-строки, заканчивающиеся символом нуля; PASCAL-строки, начинающиеся с байта, содержащего длину строки и DELPHI-строки, начинающиеся со слова (двойного слова), содержащего длину строки. Если строка не принадлежит ни к одному из этих трех типов, концом строки считается:
а) первый нечитабельный ASCII-символ. Перечень читабельных символов содержится в поле "AsciiStringChars" конфигурационного файла <ida.cfg>.


IDA - 0127.htm

__________________Типы элементов________________127
Любой символ, не входящий в этот список, трактуется ограничителем длины строки. По умолчанию содержимое поля "AsciiStringChars" для кодировки cp866 следующее:
b) неинициализированный байт;
c) голова элемента кода или данных;
d) конец сегмента.
IDA - 0127-1.jpg
Если на пути от адреса начала строки до адреса ее конца встретится хотя бы один неинициализированный байт, элемент кода или данных, функция возвратит ошибку без преобразования строки.
Замечание. Вплоть до версии 3.85 эта функция была реализована с ошибкой и передача значения BADADDR не приводила к автоматическому определению конца строки.
Пример использования:
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh о
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 53h S
segOOO:0008 db 61h а
segOOO:0009 db 69h i
segOOO:OOOA db 6Ch 1
segOOO:OOOB db 6Fh о
segOOO:OOOC db 72h г
segOOO:OOOD db 0
a) исходные данные — ASCIIZ-строка
MakeStr(SegByName("segOOO"),BADADDR);
b) вызываем функцию MakeStr для преобразования цепочки бестиповых байтов в
ASCII-строку с автоматическим определением ее конца
segOOO:OOOOaHelloSailor db 'Hello, Sailor',0
c) результат — успешное создание строки с автоматическим назначением метки, состо-
ящей из допустимых в имени символов


IDA - 0128.htm

128 Типы элементов

Аргумент | Пояснения
еа | Линейный адрес начала цепочки бестиповых байт или |  | головы элемента дан-
 | ных |  |
endea | !=BADADDR | Линейный адрес конца строки
 | ==BADADDR | Указание вычислять адрес конца | строки автоматически
return | =return | Пояснения
 | ==l | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "~Edit\ASCII"; <A>.
Замечание. При нажатии клавиши <A>, IDA пытается создать по текущему адресу ASCII-строку, автоматически определяя ее длину. Для явного задания требуемой длины, необходимо предварительно выделить соответствующую область курсорными клавишами, удерживая нажатым <shift> или мышью, удерживая нажатой правую кнопку.
success MakeArray (long ea, long nitems)
Функция создает массив состоящий из данных одного типа — байтов, слов, двойных слов, четверных слов, двойных слов в формате float, четверных слов в формате double, packed real, tbyte. Бестиповые байты могут стать частью массива любого типа. Строки не могут быть элементами никакого массива.
Тип массива определяется типом его первого элемента. Все остальные элементы массива на момент его создания должны быть представлены бестиповыми байтами, — последовательность типизированных данных не может быть преобразована в массив.
Элементы массива записываются в строку, отделяясь друг от друга знаком запятой. Если два или более подряд идущих элемента имеют одно и то же значение (в том числе и неинициализированное) для сокращения ассемблерного листинга используется конструкция "DUP".
Аргумент еа задает линейный адрес первого элемента массива или линейный адрес начала цепочки бестиповых байт.
Аргумент nitems задает размер массива, выраженный в количестве элементов. Массив создается даже в том случае, когда nitems равен единице.
Пример использования:
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh о
segOOO:0005 db 2Ch
segOOO:0006 db 20h
segOOO:0007 db 53h S
segOOO:0008 db 61h а


IDA - 0129.htm

Типы элементов 129
segOOO:0009 db 69h ; 1 segOOO:OOOA db 6Ch ; 1 segOOO:OOOB db 6Fh ; о segOOO:OOOC db 72h ; г
a) исходные данные
MakeArray(SegByName("segOOO"),14);
b) вызываем функцию МаКеАггау для преобразования последовательности бестиповых
байтов в массив байтов
segOOO:0000 db 48h, 65h, 2 dup(6Ch), 6Fh, 2Ch, 20h, 53h, 61h, 69h
segOOO:0000 db 6Ch, 6Fh, 72h, 0
c) результат — успешно созданный массив
Внимание! Если все элементы массива не умещаются на одной строке, они автоматически переносятся на следующую, но слева от них указывается не адрес данного элемента массива, а адрес головы массива!
Замечание. Для изменения размеров массива (усечения или расширения) достаточно передать функции адрес его начала и новую длину.

Аргумент | Пояснения
еа | Линейный адрес первого элемента массива или линейный адрес | головы уже
 | существующего массива |
nitems | Размер массива, выраженный в количестве элементов
return | =return* Пояснения
 | ==1 Успешное завершение операции
 | ==0 Ошибка
Родственные функции: нет.
Интерактивный аналог: "^Edit\Array"; <*>.
success MakeAlign (long ea, long count, long align)
Функция помещает в ассемблерный файл директиву выравнивания align и исключает из дизассемблируемого листинга байты, используемые для выравнивания.
Замечание. Микропроцессоры серии Intel 80x86 используют выравнивание для ускорения доступа к данным и инструкциям (подробнее об этом можно узнать в технической документации фирмы Intel), но существуют процессоры, которые требуют обязательного выравнивания, и при обращении к невыровненным данным (инструкциям) генерируют исключение.
Аргумент еа задает линейный адрес первого байта, использующегося для выравнивания. Если по этому адресу расположен хвост элемента данных, голова или хвост элемента кода, функция возвратит ошибку.
Аргумент count задает количество байт, использующихся для выравнивания. Значение count должно быть больше нуля и меньше кратности выравнивания, т.е. 2align > count > 0, в противном случае функция возвратит ошибку.


IDA - 0130.htm

130 Типы элементов
Аргумент align задает кратность выравнивания и представляет собой показатель степени с основанием два. То есть, если align равен четырем, то кратность выравнивания — шестнадцати, т.к. 24=16. Если align равен нулю, функция определяет необходимую степень выравнивания автоматически, используя наибольшее возможное значение.
Для изменения величины выравнивания достаточно передать функции MakeAlign линейный адрес уже существующей директивы Align и новые значения count и align.
Пример использования:
segOOO:0000 db 48h ; H segOOO:0001 db 65h ; e segOOO:0002 db 6Ch ; 1 segOOO:0003 db 6Ch ; 1 segOOO:0004 db 6Fh ; о
a) исходные данные
MakeAlign(SegByName("segOOO")+1,3,2);
b) вызываем функцию MakeAlign для помещения по адресу segOOO:0001 директивы
align 4. Для выравнивания используются три байта — seg0001, seg0002 и seg0003
segOOO:0000 db 48h ; H
segOOO:0001 align 4
segOOO:0004 db 6Fh ; о
c) результат — успешное создание директивы выравнивания

Аргумент | Пояснения
еа | Линейный адрес первого байта, использующегося для выравнивания или |  | уже
 | существующей директивы align |  |
count | Число байт, использующихся для выравнивания
align | =align | Пояснения
 | ==[1..5] | Показатель степени выравнивания с основанием два
 | ==0 | Автоматическое определение кратности выравнивания
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "^Edit\Structs\Other\ Create alignment directive";
<L>.
long MakeCode (long ea)
Функция создает по указанному адресу элемент кода, выполняя дизассемблирование первой машинной инструкции. Если это возможно, автоматически дизассемблируются и другие инструкции. Это происходит в следующих случаях:
а) текущая инструкция не изменяет нормального выполнения программы и за ее концом расположены бестиповые байты;


IDA - 0131.htm

Типы элементов 131
b) текущая инструкция изменяет нормальное выполнение программы, осуществляя переход по непосредственному адресу, тогда IDA продолжит дизассемблирование с этого адреса.
Если встречается инструкция, изменяющая адрес перехода непредсказуемым образом (например, RET) IDA прекращает дизассемблирование.
Во время дизассемблирования IDA при необходимости создает перекрестные ссылки и автогенерируемые метки. Подробнее об этом можно прочитать в главах "Перекрестные ссылки" и "Глобальные настройки" соотвественно.
Замечание. IDA эмулирует выполнение кода на виртуальном процессоре с целью отслеживания изменения регистра указателя команд и дизассемблирует все инструкции, на которые он указывает или может указывать при определенных обстоятельствах.
Благодаря этому дизассемблируются все вызываемые функции, условные переходы и все косвенные ссылки, которые IDA в состоянии распознать (например, если это не запрещено настройками, она может автоматически преобразовывать 32-разрядные непосредственные операнды по модулю больше OxlOOOO в смещения на код — см. главу "Глобальные настройки").
При успешном завершении функция возвращает длину первой дизассемблированной инструкции, выраженную в байтах, в противном случае возвращается ноль, сигнализируя об ошибке.
Передача линейного адреса головы уже существующего элемента кода приведет к повторному анализу инструкции; будут заново созданы перекрестные ссылки, автогенерируемые метки и т.д., а функция возвратит длину инструкции, расположенной по линейному адресу еа.
Пример использования:
segOOO:0100 start db 83h Г
segOOO:0101 db OC6h
segOOO:0102 db 6
segOOO:0103 db OFFh
segOOO:0104 db OE6h ц
segOOO:0105 db OB9h
segOOO:0106 db OBEh
segOOO:0107 db 14h
segOOO:0108 db 1
segOOO:0109 db OADh н
segOOO:010A db 91h С
segOOO:010B db 56h V
segOOO:010C db 80h А
segOOO:010D db 34h 4
segOOO:010E db 66h f
segOOO:010F db 46h F
segOOO:0110 db OE2h т
segOOO:0111 db OFAh
segOOO:0112 db OFFh
segOOO:0113 db OE6h ц


IDA - 0132.htm

132 Типы элементов
segOOO:0114 db 18h ;
segOOO:0115 db 0 ;
a) исходные данные
Message(">%X\n",MakeCode(SegByName("segOOO")+Ox100));
b) вызываем функцию MakeCode для дизассемблирования кода
segOOO:0100 add si, 6
segOOO:0103 jmp si
segOOO:0103 ; —————————
segOOO:0105 db OB9h
segOOO:0106 db OBEh
segOOO:0107 db 14h
segOOO:0108 db 1
segOOO:0109 db OADh н
segOOO:010A db 91h С
segOOO:010B db 56h V
segOOO:010C db 80h А
segOOO:010D db 34h 4
segOOO:010E db 66h f
segOOO:010F db 46h F
segOOO:0110 db OE2h т
segOOO:0111 db OFAh
segOOO:0112 db OFFh
segOOO:0113 db OE6h ц
segOOO:0114 db 18h
segOOO:0115 db 0
>3
c) результат — функция дизассемблировала две инструкции и остановилась, встретив регистровый переход, целевой адрес которого она предсказать не в силах; по завершению дизассемблирования функция возвратила длину первой инструкции, равную трем байтам
Message(">%X\n",MakeCode(SegByName("segOOO")+Oxl06));
d) повторно вызываем функцию MakeCode, передавая ей адрес следующей инструкции (значение регистра SI при загрузке com-файла равно 0x100, а после выполнения инструкции ADD SI, 6 — 0x106, следовательно целевой адрес перехода JMP SI равен 0x106)
segOOO:0100 add si, 6
segOOO:0103 jmp si
segOOO:0103 ; ————————————————————————————
segOOO:0105 db OB9h ;
segOOO:0106 ; —————————————————————————
segOOO:0106 mov si, 114h
segOOO:0109 lodsw
segOOO:010A xchg ax, cx
segOOO:010B push si
segOOO:010C
segOOO:010C loc_0_10C: ; CODE XREF: segOOO:0110j
segOOO:OlOC xor byte ptr [si], 66h
segOOO:010F inc si
segOOO:0110 loop loc_0_10C
segOOO:0112 jmp si
segOOO:0112 ; ————————————————————————————————————————


IDA - 0133.htm

Типы элементов 133
segOOO:0114 db 18h ; segOOO:0115 db 0 ; >3
e) результат — функция продолжила дизассемблирование, автоматически создавая перекрестные ссылки и автогенерируемые метки, до тех пор пока не встретила инструкцию регистрового перехода, целевой адрес которого предсказать не в силах

Аргумент | Пояснения
еа | Линейный адрес бестипового байта или головы уже существующего элемента кода
return | =return | Пояснения
 | !=0 | Длина первой дизассемблируемой инструкции
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "^Edit\Code'" <C>.
char GetMnem (long еа)
Функция возвращает символьную мнемонику инструкции элемента кода, расположенного по линейному адресу еа. Для получения операндов (если они есть) следует воспользоваться функцией GetOpnd (см. главу "Операнды").
Пример использования:
segOOO:0000 mov ah, 9
a) исходные данные — требуется получить символьную мнемонику инструкции
Message(">%s\n",GetMnem(SegByName("segOOO")));
b) вызов функции GetMnem
>mov
c) результат — мнемоника инструкции в символьном представлении

Аргумент | Пояснення
еа | Линейный адрес элемента кода
return | =return | Пояснения
 | ! = "" | Мнемоника | в символьном представлении
 | =="" | Ошибка
Родственные функции: GetOpnd.
Интерактивный аналог: нет.
void MakeUnkn (long ea, long expand)
Функция разрушает элемент, заданный любым принадлежащим ему адресом, превращая его содержимое в бестиповые байты. Объекты, связанные с элементом (например, метки, комментарии) при этом не уничтожаются.
Замечание. Автогенерируемые метки, назначаемые ASCII-строкам при их раз-
рушении, удаляются.


IDA - 0134.htm

134 Типы элементов
Аргумент еа задает любой линейный адрес, принадлежащий разрушаемому элементу.
Аргумент expand будучи неравным нулю указывает на необходимость разрушения всей цепочки элементов, связанных друг с другом перекрестными ссылками типа "ссылка на следующую инструкцию" (см. главу "Перекрестные ссылки").
Пример использования:
Эксперимент 1
segOOO:0000 aHelloSailor db 'Hello, Sailor',0
a) исходные данные
MakeUnkn(SegByName("segOOO")+Ox1,0);
b) вызов функции MakeUnkn для разрушения элемента данных типа "ASCII-строка"
segOOO:0000 db 48h H
segOOO:0001 db 65h e
segOOO:0002 db 6Ch 1
segOOO:0003 db 6Ch 1
segOOO:0004 db 6Fh о
segOOO:0005 db 2Ch ,
segOOO:0006 db 20h
segOOO:0007 db 53h S
segOOO:0008 db 61h а
segOOO:0009 db 69h i
segOOO:OOOA db 6Ch 1
segOOO:OOOB db 6Fh о
segOOO:OOOC db 72h г
c) результат — элемент данных разрушен
Эксперимент 2
segOOO:0100 add si, 6
segOOO:0103 jmp si
a) исходные данные
MakeUnkn(SegByName("segOOO"),0);
b) вызов функции MakeUnkn для разрушения только одного элемента кода
segOOO:0100 start db 83h Г segOOO:0101 db OC6h ^ segOOO:0102 db 6 segOOO:0103 ; —————————
c) разрушен один элемент кода
Эксперимент 3
segOOO:0100 add si, 6
segOOO:0103 jmp si
a) исходные данные
MakeUnkn(SegByName("segOOO"), 1);
b) вызов функции MakeUnkn для разрушения всей цепочки элементов кода


IDA - 0135.htm

Типы элементов
135
segOOO:0100 start db 83h Г
segOOO:0101 db OC6h
segOOO:0102 db 6
segOOO:0103 db OFFh
segOOO:0104 db OE6h ц
segOOO:0105 db OB9h
с) результат — вся цепочка элементов кода разрушена

Аргумент | Пояснения
еа | Любой линейный адрес, принадлежащий разрушаемому элементу
expand | ==0 | Разрушение только одного элемента кода или данных
 | !=0 | Разрушение всей цепочки элементов кода или только од-
 |  | ного элемента данных
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "~Edit\Undefine"; <U>.
Замечание. Нажатие <U> равносильно вызову MakeUnk(ScreenEA()J) и разрушает всю цепочку элементов кода. При необходимости разрушения одного элемента, его следует предварительно выделить курсорными клавишами, удерживая нажатым <Shift> или мышью, удерживая нажатой левую кнопку.
long FindCode (long ea, long flag)
Функция ищет ближайший к переданному ей линейному адресу еа элемент кода, возвращая в случае успешного завершения поиска адрес его головы. В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0100 mov ax, 9
segOOO:0103 mov dx, 133h
a) исходные данные — требуется получить линейный адрес первого элемента кода
Message(">%s\n",atoa(FindCode(0,1)));
b) вызов функции FindCode — адрес начала поиска равен нулю, единичное значение
флага направления указывает вести поиск с увеличением адресов
>segOOO:0100
c) результат — линейный адрес первого элемента кода


IDA - 0136.htm

136 Типы элементов

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==l | Прямое направление поиска
 | ==0 | Обратное направление поиска
return | =return | Пояснения
 | !=BADADDR | Линейный адрес элемента кода
 | ==BADADDR | Ошибка
Родственные функции: FindData, FindExplored, FindUnexplored.
Интерактивный аналог: "~Nabigate\Search for\Next Code"; <Ctrl-C>.
long FindData (long ea, long flag)
Функция ищет ближайший к переданному ей линейному адресу еа элемент кода, возвращая в случае успешного завершения поиска адрес его головы. В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен, то поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0000 mov ah, 9
segOOO:0002 mov dx, 108h
segOOO:0005 int 21h
segOOO:0005
segOOO:0007 retn
segOOO:0007 ; ————————————
segOOO:0008 aHelloIda db 'Hello, IDA'
a) исходные данные — требуется получить линейный последнего элемента данных
Message(">%s\n",atoa(FindData(BADADDR,0)));
b) вызов функции FindData
>segOOO:0108
c) результат — линейный адрес последнего элемента данных

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==1 | Прямое направление поиска
 | ==0 | Обратное направление поиска


IDA - 0137.htm

Типы элементов 137

Аргумент | Пояснения
return | =return | Пояснения
 | !=BADADDR | Линейный адрес элемента | данных
 | ==BADADDR | Ошибка
Родственные функции: FindCode, FindExplored, FindUnexplored.
Интерактивный аналог: "~Nabigate\Search for\Next Data"; <Ctrl-D>.
long FindExplored (long ea, long flag)
Функция ищет ближайший к переданному ей линейному адресу еа элемент кода или данных, возвращая в случае успешного завершения поиска адрес его головы. В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен, то поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0100 DB 99h ; Щ
segOOO:0101 DW 666h
a) исходные данные — требуется получить линейный адрес первого элемента кода или
данных
Message(">%s\n",atoa(FindExplored(0,1)));
b) вызов функции FindExplored — адрес начала поиска равен нулю, единичное значение
флага направления указывает вести поиск с увеличением адресов
>segOOO:0101
c) результат — линейный адрес первого элемента

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==1 | Прямое направление поиска
 | ==0 | Обратное направление поиска
return | =return | Пояснения
 | !=BADADDR | Линейный адрес элемента любого вида
 | ==BADADDR | Ошибка
Родственные функции: FindCode, FindData, FindUnexplored.
Интерактивный аналог: "~Navigate\Search for\Next explored";
<Ctrl-A>.


IDA - 0138.htm

138 Типы элементов
long FindUnexplored (long ea, long flag)
Функция ищет ближайший к переданному ей линейному адресу еа бестиповой байт, возвращая в случае успешного завершения поиска его адрес. В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0100 DW 666h
segOOO:0102 DB 99h ; Щ
a) исходные данные — требуется получить линейный адрес первого бестипового байта
Message(">%s\n",atoa(FindUnexplored(0,1)));
b) вызов функции FindUnexplored — адрес начала поиска равен нулю, единичное значе-
ние флага направление указывает вести поиск с увеличением адресов
>segOOO:0102
c) результат — линейный адрес первого бестипового байта

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==1 | Прямое направление поиска
 | ==0 | Обратное направление поиска
return | =return | Пояснения
 | !=BADADDR | Линейный адрес бестипового байта
 | ==BADADDR | Ошибка
Родственные функцни: FindCode, FindData, FindExplored.
Интерактивный аналог: "^Nabigate\Search for\Next Unexplored";
<Ctrl-U>.


IDA - 0139.htm

Операнды 139
Операнды
#Definition
Элементы могут содержать один и более операндов, включающих в себя непосредственные значения. Одно и то же непосредственное значение может являться и константой, и смещением в сегменте, и базовым адресом сегмента. Константа, в свою очередь, может отображаться в двоичной, восьмеричной, десятичной и шестнадцатеричной системе счисления или в виде символьной ASCII-строки.
Тип и представление каждого операнда определяется значением соответствующих битов флагов (см. табл. 13). Две группы битов определяют представление первого и второго операнда; если же элемент содержит более двух операндов, третий и все последующие операнды имеют тот же самый тип и представление, что и второй операнд.
Если ни один бит атрибутов операнда не установен, операнд не имеет никакого типа, (т.е. имеет тип "void") и отображается в системе исчисления принятой по умолчанию. В зависимости от настроек IDA Рго бестиповые операнды могут отображаться другим цветом (по умолчанию красным).
Определить имеет ли некий элемент кода непосредственный операнд или нет можно анализом бита FF_IMMD флагов. Если он установлен — элемент кода имеет непосредственный операнд, и, соответственно, наоборот. Значение бита FF_IMMD не зависит от типа непосредственного операнда — чем бы он ни был: смещением, константой, базовым адресом сегмента или имел тип void, — флаг FF_IMMD указывает на сам факт наличия (отсутствия) непосредственного операнда, но никак не связан с его типом.
В полностью дизассемблированной программе не должно быть ни одного операнда с типом void, — типы всех операндов должны заданы явно в соответствии с их назначением, которое можно узнать путем анализа программы.
В некоторых случаях IDA Рго позволяет автоматически отличить смещения от констант:
a) используя информацию о перемещаемых элементах (fixup info} IDA Рго может автоматически преобразовать бестиповые операнды в базовые адреса сегментов и смещений;
b) в 32-разрядном сегменте, инструкция, имеющая непосредственный операнд, содержащий значение OxlOOOO и выше, автоматически преобразуется в смещение, если это разрешено настройками;
c) то же, что и в пункте b, но для данных;
d) непосредственный операнд инструкции push, следующей за инструкцией, заносящей в стек базовый адрес сегмента, автоматически преобразуется в смещение, если это разрешено настройками;


IDA - 0140.htm

140 Операнды
e) непосредственный операнд, копируемый инструкцией MOV в один из регистров общего назначения, автоматически преобразуется в смещение, если он предшествует инструкции MOV, заносящей в один из сегментных регистров базовый адрес сегмента;
f) непосредственный операнд, копируемый инструкцией MOV в ячейку памяти независимо от способа ее адресации, автоматически преобразуется в смещение, если он предшествует инструкции MOV, заносящей в ячейку памяти базовый адрес сегмента.
Автоанализатор IDA Рго непрерывно совершенствуется, и новые версии становятся способными автоматически распознавать смещения все в большем и большем числе случаев, — однако, вместе с этим увеличивается и процент ложных срабатываний, поэтому окончательная доводка дизассемблируемого листинга ложится на плечи пользователя.
Таблица 13. Возможные представления непосредственных операндов элементов
типа данные и код

Представление первого слева операнда
Флаг | # Представление
FF_OVOID | 0x00000000 Тип void
FF_ONUMH | 0x00100000 Шестнадцатеричное представление
FF_ONUMD | 0x00200000 !Десятичное представление
FF_OCHAR | 0x00300000 Символьное представление
FF_OSEG | 0x00400000 ! Представление в виде базового адреса сегмента
FF_OOFF | 0x00500000 Представление в виде смещения в сегменте
FF_ONUMB | 0x00600000 | Бинарное представление
FF_ONUMO | 0x00700000 | Восьмеричное представление
FF_OENUM | 0x00800000 | Представление в виде перечисления
FF_OFOP | 0x00900000 | Пользовательское представление
FF_OSTRO | 0x00A00000 Представление в виде смещения в структуре
FF_OSTK | 0x00B00000 Представление в виде стековой переменной
Представление второго слева операнда
Флаг | # | Представление
FF_1VOID | 0x00000000 | Тип void
FF_1NUMH | 0x00100000 | Шестнадцатеричное представление
FF_1NUMD | 0x00200000 | Десятичное представление
FF_1CHAR | 0x00300000 | Символьное представление
FF_1SEG | 0x00400000 | Представление в виде базового адреса сегмента
FF__10FF | 0x00500000 | Представление в виде смещения в сегменте
FF_1NUMB | 0x00600000 | Бинарное представление
FF_1NUMO | 0x00700000 | Восьмеричное представление
FF_1ENUM | 0x00800000 | Представление в виде перечисления
FF 1FOP | 0x00900000 | Пользовательское представление
FF_1STRO | 0x00A00000 | Представление в виде смещения в структуре
FF_1STK | 0x00B00000 | Представление в виде стековой переменной


IDA - 0141.htm

Операнды 141
Сводная таблица функций

Функции, изменяющие отображение операндов
success OpBinary (long еа, int п) | Отображает операнд (операнды) в двоичном виде
success OpOctal (long еа, int п) | Отображает операнд (операнды) в восьмеричном виде
success OpDecimal (long еа, int п) | Отображает операнд (операнды) в десятичном виде
success OpHex (long еа, int п) | Отображает операнд (операнды) в шестнадцатеричном виде
success OpChr (long еа, int п) | Отображает операнд (операнды) в символьном виде
success OpNumber (long еа, int п) | Отображает операнд (операнды) в системе счисления,
 | принятой по умолчанию
success OpOff (long еа, int п, | Отображает операнд (операнды) в виде смещения, отсчи-
long base) | тываемого относительно начала сегмента
success OpOffEx (long ea, int п, | Отображает операнд (операнды) в виде смещения, отсчи-
long reftype, long target, | тываемого относительно любого адреса, принадлежащего
long base, long tdelta) | сегменту
success OpSeg (long еа, int n) | Отображает операнд (операнды) в виде имени сегмента,
 | базовый адрес которого равен значению операнда
success OpAlt (long еа, 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 п) | Возвращает тип операнда
long GetOperandValue (long еа, | Возвращает значение операнда
long п) |
char AltOp (long ea, long п) | Возвращает операнд, определенный пользователем
Функции, обеспечивающие поиск операндов
long FindVoid (long ea, long flag) | Возвращает линейный адрес очередного операнда неопре-
 | деленного типа
long Findlmmediate (long ea, long | Возвращает линейный адрес очередного элемента с опе-
flag, long value) | рандами, имеющими указанное значение
char Demangle (char name. | Возвращает незамангленное имя метки
long disable_mask) |
success OpBinary (long ea, int n)
Функция отображает операнд (операнды) в двоичном виде, добавляя в его
конце суффикс 'b'.
Пример использования:
segOOO:0000 mov ax,41h
а) исходные данные


IDA - 0142.htm

142 Операнды
OpBinary(SegByName("segOOO"),1);
b) вызов функции OpBinary для преобразования второго слева операнда в двоичный
вид
segOOO:0000 mov ax, 1000001b
c) результат — второй слева операнд преобразован в двоичный вид

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Операнд
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 | Успешное завершение операции
==о |  | Ошибка
Родственные функции: OpOctal, OpDeciminal, OpHex, OpChr, OpNumber.
Интерактивный аналог: "~Edit\Operand types\Binary"; <B>.
success OpOctal (long ea, int n)
Функция отображает операнд (операнды) в восьмеричном виде, добавляя в
конце суффикс 'o'.
Пример использования:
segOOO:0000 mov ax,41h
a) исходные данные
OpOctal(SegByName("segOOO"),1);
b) вызов функции OpOctal для преобразования второго спева операнда в восьмеричный
вид
segOOO:0000 mov ax, 101o
c) результат — второй слева операнд преобразован в восьмеричный вид

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


IDA - 0143.htm

Операнды 143
Родственные функции: OpBinary, OpDeciminal, OpHex, OpChr,
OpNumber.
Интерактивный аналог: "~Edit\Operand types\Octal".
success OpDecimal (long ea, int n)
Функция отображает операнд (операнды) в десятичном виде. Пример исполь-
зования:
segOOO:0000 mov ax,41h
a) исходные данные
OpDecimal(SegByName("segOOO"). 1);
b) вызов функции OpDecimal для преобразования второго слева операнда в десятичный
вид
segOOO:0000 mov ax, 65
c) результат — второй слева операнд преобразован в десятичный вид

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Операнд
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные операнды
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: OpBinary, OpOctal, OpHex, OpChr, OpNumber.
Интерактивный аналог: "Edit\Operand types\Decimal"; <H>.
success OpHex (long ea, int n)
Функция отображает операнд (операнды) в шестнадцатеричном виде, добав-
ляя в его конце суффикс 'h'.
Пример использования:
segOOO:0000 mov ax,65
a) исходные данные
OpHex(SegByName("segOOO").l);
b) вызов функции OpHex для преобразования второго слева операнда в шестнадцате-
ричный вид
segOOO:0000 mov ax. 41h
c) результат — второй слева операнд преобразован в шестнадцатеричный вид


IDA - 0144.htm

144 Операнды

Аргумент | Пояснения
еа 'Линейный адрес элемента, которому принадлежит операнд
п | =п | Операнд
 | ==0 | Первый слева операнд
 | ==l | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: OpBinary, OpOctal, OpDeciminal, OpChr,
OpNumber.
Интерактивный аналог: "~Edit\Operand types\Hexadeciminal"; <Q>.
success OpChr (long ea, Int n)
Функция отображает операнд (операнды) в символьном виде, заключая его в кавычки. Если операнд содержит один или больше нечитабельных байт, функция возвратит ошибку. Перечень читабельных символов содержится в поле "AsciiStringChars" конфигурационного файла <ida.cfg>.
По умолчанию содержимое поля "AsciiStringChars" для кодировки cp866 следующее:
IDA - 0144-1.jpg
Замечание. Порядок следования старших и младший байтов зависит от выбранного типа процессора. У микропроцессоров серии Intel 80x86 младший байт располагается по меньшему адресу, а старший, соответственно, наоборот.
Пример использования:
Эксперимент 1
segOOO:0000 mov ax,65
a) исходные данные
OpChr(SegByName("segOOO"),1);
b) выэов функции ОрСhг для преобразования второго слева операнда в символьный вид
segOOO:0000 mov ax, 'A'
c) результат — второй слева операнд преобразован в шестнадцатеричный вид


IDA - 0145.htm

Операнды 145
Эксперимент 2
segOOO:0000 dq 4944412050726F21h
a) исходные данные
OpChr(SegByName("segOOO"),0);
b) вызов функции OpChr для преобразования первого слева операнда в символьный
вид
segOOO:0000 dq 'IDA Pro''
c) результат — успешное преобразование

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Операнд
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные операнды
==-1 |  | Все операнды
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функцни: OpBinary, OpOctal, OpDeciminal, OpHex,
OpNumber.
Интерактивный аналог: "Edit\Operand types\Chaster"; <R>.
success OpNumber (long ea, int n)
Функция отображает операнд (операнды) в форме исчисления принятой по умолчанию.
По умолчанию системой исчисления по умолчанию назначена шестнадцате-ричная система исчисления.
Пример использования:
segOOO:0000 mov ax,65
a) исходные данные
OpNumber(SegByName("segOOO"),1);
b) вызов функции OpNumber для преобразования второго слева операнда в систему
исчисления по умолчанию
segOOO:0000 mov ax, 41h
c) результат — второй слева операнд преобразован в шестнадцатеричный вид


IDA - 0146.htm

146 Операнды

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Операнд
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 ! Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: OpBinary, OpOctal, OpDeciminal, OpHex, OpChr.
Интерактивный аналог: "Edit\Operand types\ Number"; <#>.
success OpOff(long ea, int n, long base)
Функция отображает операнд (операнды) в виде смещения относительно заданного сегмента, автоматически создавая автогенерируемую метку по целевому адресу (если целевой адрес не имеет метки) и перекрестную ссылку соответствующего типа (см. главу "Перекрестные ссылки"). Разрядность операнда, представляемого в виде смещения, должна быть равна разрядности соответствующего сегмента, иначе функция возвратит ошибку.
Аргумент еа задает линейный адрес элемента, которому принадлежит операнд.
Аргумент base задает базовый адрес сегмента, выраженный в байтах (не параграфах!) относительного которого отсчитывается смещение.
Аргумент n задает операнд, отображаемый в виде смещения (см. табл. далее).
Для выполнения обратной операции, т.е. преобразования смещения к непосредственному значению, достаточно передать функции нулевой базовый адрес сегмента.
Пример использования:
segOOO:0100 start proc near
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, 108h
segOOO:0105 int 21h
segOOO:0107 retn
segOOO:0107 start endp
segOOO:0107
segOOO:0107 ; ————————————————
segOOO:0108 Ob 'Hello,World!',ODh,OAh,'S
segOOO:0108 segOOO ends
a) исходные данные
OpOff(SegByName("segOOO")+Ox102,1,SegByName("segOOO"));
b) вызов функции ОрОff для отображения константы, загружаемой в регистр DX в виде
смещения относительно текущего сегмента


IDA - 0147.htm

Операнды 147
segOOO:0100 start proc near
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset asc_0_108 ; "Hello,World!\r\n$"
segOOO:0105 int 21h
segOOO:0107 retn
segOOO:0107 start endp
segOOO:0107
segOOO:0107 ; ——————————————————————————————————————
segOOO:0108asc_0_108 db'Hello,World!',ODh,OAh,'S' : DATA XREF: start+2o
segOOO:0108 segOOO ends
с) результат — константа, загружаемая в регистр DX отображена в виде смещения, предваренного директивой "offset, автоматически создана метка и перекрестная ссылка (в тексте они выделены жирным шрифтом)
Ближайший аналог — (~Edit\Operad types\Offset by any segment).

Аргумент Пояснения
ea | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды
base | Базовый адрес сегмента, выраженный в байтах (не параграфах!), относитель-
 | но которого отсчитывается смещение
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: OpOffEx.
Интерактивный аналог: ""Edit\Operad types\Offset by any segment";
<Alt-R>.
success OpOffEx (long ea, int n, long reftype, long target, long base, long tdelta)
Функция отображает операнд (операнды) в виде смещения, отсчитываемого от любого заданного адреса, не обязательно совпадающего с базовым адресом сегмента. Такая необходимость возникает, например, в случае обращения к элементу структуры, смещение которого требуется отсчитывать относительно начала этой структуры.
Данная функция является усовершенствованным вариантом функции OpOff и поддерживает не только смещения, разрядность которых равна разрядности соответствующего сегмента, но и смещения, записанного в восьми или шестнадцати младших (старших) битах шестнадцати и тридцатидвухразрядных операндов соответственно (см. табл. 14). При этом остальные биты операнда маскируются операцией "логического И" AND.
Аргумент еа задает линейный адрес элемента, которому принадлежит операнд.


IDA - 0148.htm

148 Операнды
Аргумент n задает операнд, отображаемый в виде смещения (см. табл. 14-a).
Аргумент reftype задает тип смещения и может принимать одно из значений, перечисленных в табл. 14-a.
Аргумент target задает линейный адрес целевого смещения выраженный в байтах, относительного которого будет отсчитываться смещение операнда. Если в качестве целевого смещения передать значение BADADDR, целевое смещение будет вычислено автоматически по следующей формуле: target = operand_value -- tdelta +base
Аргумент base задает базовый адрес сегмента, выраженный в байтах, относительно которого задается целевое смещение.
Аргумент tdelta задает относительное смещение, отсчитываемое относительно целевого смещения. Относительное смещение может быть как положительным, так и отрицательным. Если оно равно нулю, то данная функция становится эквивалентна функции OpOff (см. описание функции OpOff).
Значение операнда должно соответствовать следующему соотношению: operand_value = target + tdelta - base, в противном случае функция вернет ошибку.
Таблица 14

Определение | # | Тип смещения
REF_OFF8 | 0 | 8-битное смещение
REF_OFF16 | 1 | 16-битное смещение
REF_OFF32 | 2 | 32-битное смещение
REF_LOW8 | 3 | Смещение представлено 8 младшими битами 16-битного | непосред-
 |  | ственного значения |
REF_LOW16 | 4 | Смещение представлено 16 младшими битами 32-битного | непосред-
 |  | ственного значения |
REF_HIGH8 | 5 | Смещение представлено 8 старшими битами 16-битного непосредст-
 |  | венного значения |
REF_HIGH16 | 6 | Смещение представлено 16 старшими битами 32-битного | непосред-
 |  | ственного значения |
Пример использования:
segOOO:0100 start:
segOOO:0100 mov ax, 105h
segOOO:0103 retn
segOOO:0103 ; —————————————
segOOO:0104 MyStruc ob 0
segOOO:0105 dw 6666h
segOOO:0107 dw 9999h
segOOO:Ol07 segOOO ends
segOOO:0107
a) исходные данные — требуется представить непосредственное значение, загружаемое в
регистр АХ в виде смещения, отсчитываемого относительного начала структуры MyStruc
OpOffEx(SegByName("segOOO")+Ox100,1,REF_OFF16,
SegByName("segOOO")+Oxl04,SegByName("segOOO"),1);
b) вызов функции OpOffEx для представления непосредственного значения в виде
смещения, отсчитываемого относительно начала структуры MyStruc


IDA - 0149.htm

Операнды 149
Пояснение. Линейный адрес структуры MyStruc равен SegByNa-me("segOOO")+Oxl04, следовательно, целевой адрес tagreg равен SegByNa-me("segOOO")+OxW4;
базовый адрес сегмента, которому принадлежит структура, будучи выраженным в байтах равен SegNyName("segOOO"), следовательно, аргумент base равен SegBy-Name("segOOO");
смещение искомого элемента относительно начала структуры равно ope-rand_value — offset MyStruc, т.е. в непосредственных значениях — 0x105 - 0x104 = = 1, следовательно, аргумент tdelta равен 1;
операнд представляет собой 16-разрядное непосредственное значение, поэтому тип смещения — REF_OFFl6.
segOOO:0100 start:
segOOO:0100 mov ax, offset MyStruc+1
segOOO:0103 retn
segOOO:0103 ; ———————————————
segOOO:0104 MyStruc db 0 ; DATA XREF: segOOO:0100o
segOOO:0105 dw 6666h
segOOO:0107 dw 9999h
segOOO:0107 segOOO ends
с) результат — непосредственное значение теперь представлено в виде смещения,
отсчитываемого от начала структуры MyStruc
Замечание. В данном примере было допустимо использовать автоматическое определение целевого адреса, однако, для большей ясности оно было вычислено вручную.
Таблица 14-а

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные операнды
 | ==-1 | Все операнды
reftype | ==reftype |  | п | Тип смещения
 | ==REF_OFF8 |  | 0 | 8-битное смещение
 | ==REF_OFF16 |  | 1 | 16-битное смещение
 | ==REF_OFF32 |  | 2 | 32-битное смещение
 | ==REF_LOW8 |  | 3 | Смещение представлено 8 младшими битами 16-битно-
 |  |  |  | го непосредственного значения
 | ==REF_LOW16 |  | 4 | Смещение представлено 16 младшими битами 32-битно-
 |  |  |  | го непосредственного значения
 | ==REF_HIGH8 |  | 5 | Смещение представлено 8 старшими битами 16-битного
 |  |  |  | непосредственного значения
 | ==REF_HIGH16 |  | 6 | Смещение представлено 16 старшими битами 32-битно-
 |  |  |  | го непосредственного значения


IDA - 0150.htm

150 Операнды

Аргумент | Пояснения
target | ==target | Пояснения
 | !=BADADDR | Целевое смещение
 | ==BADADDR | Вычислять целевое смещение автоматически по следую-
 |  | щей формуле target = operand_value - tdelta + base
base | Базовый адрес сегмента, выраженный в байтах (не параграфах!)
tdelta | Относительное смещение, вычисляемое относительно целевого смещения; мо-
 | жет быть как положительным, так и отрицательным
return | =return | Пояснения
 | ==l | Успешное завершение операции
 | ==0 | Ошибка
Родственные функции: OpOff.
Интерактивный аналог: "~Edit\Operad types\User-defined offset";
<Ctrl-R>.
success OpSeg (long ea, int n)
Функция отображает операнд (операнды) в виде имени сегмента, базовый адрес которого равен значению операнда. Если сегмента с таким базовым адресом не существует, функция возвращает ошибку.
Замечание. В процессе загрузки файла IDA автоматически преобразует все
перемещаемые элементы в базовые адреса соответствующих сегментов.
Пример использования:
segOOO:0000 mov ax, 1000h
a) исходные данные — требуется представить непосредственный операнд, загружае-
мый в регистр ах в виде имени сегмента
OpSeg(SegByName("segOOO"),1);
b) вызов функции OpSeg для преобразования непосредственного операнда в имя
сегмента с соответствующим базовым адресом
segOOO:0000 mov ax, seg segOOO
c) результат — непосредственный операнд теперь представлен в виде имени сегмента
с соответствующим базовым адресом

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды


IDA - 0151.htm

Операнды 151

Аргумент | Пояснения
return | =return | Пояснения
 | ==l | Операция выполнена успешно
 | ==0 | Ошибка j
Родственные функции: нет.
Интерактивный аналог: "^ Edit\Operand types\ Segment" <S>.
success OpAlt (long ea, long n, char str)
Функция, отображающая операнды в виде символьной строки, заданной пользователем. Никаких ограничений на переданную строку не налагается — она может содержать любые символы, кроме символа с кодом нуля, который является признаком конца строки.
Пример использования:
segOOO:0000 mov ax, 9
a) исходные данные
OpAlt(SegByName("segOOO"),0,"Регистр AX");
b) вызов функции ОрАlt для переименования первого слева операнда в строку "Регистр
АХ"
segOOO:0000 mov Регистр АХ, 9
c) результат — операнд успешно переименован

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==l | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-l | Все операнды
return | =return | Пояснения
 | ==l | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: AltOp.
Интерактивный аналог: "~Edit\Operand types\ Enter operand
manually";<Alt-Fl >.
success OpSign (long ea, int n)
Функция отображает операнд в знаковой или целочисленной форме, работая как триггер — если до ее вызова операнд отображался в целочисленной форме, после станет отображаться в знаковой и, соответственно, наоборот.


IDA - 0152.htm

152___________________
Операнды
_____________________
Пример использования:
segOOO:0000 mov ax, OFFFFh
a) исходные данные — требуется отобразить непосредственное значение, загружаемое
в регистр АХ в знаковой форме
OpSign(SegByName("segOOO"),1);
b) вызов функции OpSign для отображения непосредственного значения, загружаемого
в регистр АХ в знаковой форме
segOOO:0000 mov ax,-1
c) результат — непосредственное значение, загружаемое в регистр АХ теперь отобра-
жается в знаковой форме

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные
 |  | операнды
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "~Edit\Operand types\ Change Sign"; <->.
success OpStkvar (long ea, Int n)
Функция отображает непосредственное значение, используемое для базовой адресации относительно регистров BP (EBP) и SP (ESP) в виде стековой переменной. Сама стековая переменная должна быть предварительно создана вызовом MakeLocal (см. описание функции MakeLocal).
Значение регистров BP (EBP) и SP (ESP) IDA в каждой точке программы IDA по возможности определяет автоматически, облегчая тем самым анализ кода, генерируемого оптимизируемыми компиляторами, использующими для адресации локальных переменных регистр SP (ESP), значение которого подвержено частым изменениям. Для ручного задания значения регистра SP (ESP) предусмотрена функция SetSpDiff, к вызову которой приходится прибегать в случае невозможности определить значение стекового регистра автоматическим анализатором.
Замечание. IDA эмулирует выполнение некоторых наиболее употребляемых инструкций, таких как PUSH, POP, ADD, SUB u т.д., для отслеживания изменения значения регистра SP (ESP). Более сложные операции с регистрами пока не поддерживаются.


IDA - 0153.htm

Операнды 153
Пример использования:
segOOO:0000 start proc near
segOOO:0000 mov bp, sp
segOOO:0002 sub sp, 10h
segOOO:0005 mov word ptr [bp-2], 666h
segOOO:OOOA add sp, 10h
segOOO:OOOD retn
segOOO:OOOD start endp
a) исходные данные — требуется представить непосредственное значение, вычитае-
мое из регистра bp в виде имени локальной переменной
MakeLocal(SegByName("segOOO"),SegByName("segOOO")+OxD,"[BP-2]","MyVar");
b) вызов функции MakeLocal (см. описание MakeLocal) для создания локальной пере-
менной MyVar, расположенной двумя байтами "выше" конца кадра стека
OpStkvar(SegByName("segOOO"),0);
c) вызов функции OpStkvar для отображения непосредственного значения в виде имени
ранее созданной локальной переменной
segOOO:0100 start proc пеаг
segOOO:0100
segOOO:0100 MyVar = word ptr -2
segOOO:0100
segOOO:0100 mov bp, sp
segOOO:0102 sub sp, 10h
segOOO:0105 mov [bp+MyVar], 666h
segOOO:010A add sp, 10h
segOOO:010D retn
segOOO:010D start endp
d) результат — непосредственное значение отображено в виде имени локальной пере-
менной MyVar (в тексте она выделена жирным шрифтом)
Замечание. Подробнее о поддержке локальных переменных можно прочитать в
главе "Функции".

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева операнд
 | ==-1 | Все операнды
return | =return | Пояснения
 | ==1 | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: нет.
Интерактивный аналог: "Edit\Operand types\ Stack variable"; <K>.


IDA - 0154.htm

154 Операнды
char GetOpnd (long ea, long n)
Функция возвращает операнд в строковом виде, т.е. в том виде, в каком
дизассемблер отображает его на экране.
Пример использования:
segOOO:0000 mov ах, 9
a) исходные данные — требуется получить операнды в том виде, в котором они отобра-
жены на экране.
Message(">%s,%s\n",GetOpnd(SegByName("segOOO"),0),
GetOpnd(SegByName("segOOO"),1));
b) вызов функции GetOpnd для получения операндов в том виде, в котором они отобра-
жены на экране
>ax, 0
c) результат

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева операнд
return | =return | Пояснения
 | ==1 | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: GetOpType, GetOperandValue.
Интерактивный аналог: нет.
char AltOp (long ea, long n)
Функция возвращает операнд, определенный пользователем (см. описание
функции OpAlt).
segOOO:0000 mov Регистр АХ, 9
a) исходные данные
Message(">%s\n",AltOp(SegByName("segOOO"), 1));
b) вызов функции AltOp для получения операнда, опеределенного пользователем
>Регистр АХ
c) результат — получен операнд, определенный пользователем

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева операнд


IDA - 0155.htm

Операнды 155

Аргумент | Пояснения
return | =return | Пояснения
 | ==l | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: OpAlt.
Интерактивный аналог: нет.
long GetOpType (long ea, long n)
Функция возвращает тип операнда (см. табл. 15), принадлежащего элементу кода (не данных!). Тип операнда, за исключением типов определенных для всех процессоров, зависит от выбранного микропроцессора.
Тип операнда определяется не его представлением на экране, а инструкциями, в состав которых он входит. Так, например, при попытке определения второго слева операнда конструкции "mov dx,offset MyLabel" функция вернет тип "непосредственное значение", несмотря на то, что он представлен в виде смещения.
Таблица 15

# | Тип операнда
Общие для всех процессоров
1 | Регистр общего назначения
2 | Ячейка памяти
3 | Базовый регистр + [индексный]
4 | Базовый регистр + [индексный] + смещение
5 | Непосредственное значение
6 | Непосредственный far-адрес
7 | Непосредственный nеаг-адрес
Intel 80x86
8 | 386+ трассировочный регистр
9 | 386+ отладочный регистр
10 | 386+ контрольный регистр
11 | Регистр FPP (сопроцессора)
12 | ММХ регистр
8051
8 |
9 | Бит
10 |
80196
8 | [Внутренняя память]
9 |
10 | Смещение [внутренняя память]


IDA - 0156.htm

156 Операнды

# | Тип операнда
ARM
8 | Регистр сдвига
9 | MLA-операнд
10 | Регистр (для LDM/STM)
II |  | СОР
12 | Регистр сопроцессора | LDC/STC
Power PC
8 | Регистр указателя стека
9 | Регистры с плавающей запятой
10 | SH & MB & ME
11 |  | Поле
 | CR |
 |  | Бит
TMS320C5
8 Cпapкa регистров (Al:AO..Bl5: |  | В14)
Z8
8 | ©внутренняя память
9 | @Rx
Z80
8 | Условие
Пример использования:
segOOO:0000 mov ax, 9
a) исходные данные — требуется определить тип обоих операндов
Message(">%x, %x\n",GetOpType(SegByName("segOOO"),0),
GetOpType(SegByName("segOOO"),1));
b) вызов функции GetOpType для определения типов операндов
>1,5
c) результат — по табл. 15 определяем тип операндов — регистр общего назначения и
непосредственное значение соответственно

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
п | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева, третий (если он есть) и все остальные операнды
return | =return | Пояснения
 | >1 | Тип операнда (см. табл. 15)
 | ==0 | Элемент не имеет операндов
 | ==BADADDR | Ошибка


IDA - 0157.htm

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

Аргумент | Пояснения
еа | Линейный адрес элемента, которому принадлежит операнд
n | =п | Пояснения
 | ==0 | Первый слева операнд
 | ==1 | Второй слева операнд
return | =return | Пояснения
 | ==1 | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: GetOpnd, GetOpType.
Интерактивный аналог: нет.
long FindVoid (long ea, long flag)
Функция ищет ближайший к переданному ей линейному адресу еа операнд типа "void", возвращая в случае успешного завершения поиска адрес головы элемента кода, которому он принадлежит. В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен, то поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0100 mov ax, 9
segOOO:0103 mov dx, 133h
а) исходные данные — требуется получить линейный адрес элемента, содержащего
операнд типа "void"


IDA - 0158.htm

158 Операнды
Message(">%s\n",atoa(FindVoid(0,1)));
b) вызов функции FindVoid — адрес начала поиска равен нулю, единичное значение
флага направления указывает вести поиск с увеличением адресов
>segOOO:0103
c) результат — линейный адрес элемента, содержащего операнд типа void, найден

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==1 | Прямое направление поиска
 | ==0 | Обратное направление поиска
return | =return | Пояснения
 | !=BADADDR | Линейный адрес элемента, которому принадлежит най-
 |  | денный операнд
 | ==BADADDR | Ошибка
Родственные функции: FindImmediate.
Интерактивный аналог: "~Nabigate\Search for\Next void"; <Ctrl-V>.
long FindImmediate (long ea, long flag, long value)
Функция ищет ближайший к переданному ей линейному адресу еа операнд типа константа со значением равным value. B случае успешного поиска возвращается адрес головы элемента кода, которому этот операнд принадлежит.
В зависимости от флага направления поиск может идти как вперед (от младших адресов к старшим), так и назад (от старших адресов к младшим). Переданный функции линейный адрес в этот диапазон поиска не входит и не обязательно должен принадлежать какому-нибудь сегменту.
Аргумент flag задает направление поиска — если его младший бит установлен, то поиск идет от младших адресов к старшим и, соответственно, наоборот.
Пример использования:
segOOO:0100 mov ax, 9
segOOO:0103 mov dx, 133h
a) исходные данные — требуется получить линейный адрес элемента, содержащего
операнд типа константа, значение которой равно 9
Message(">%s\n",atoa(FindImmediate(0,1,9)));
b) вызов функции FindImmediate — адрес начала поиска равен нулю, единичное значе-
ние флага направления указывает вести поиск с увеличением адресов
>segOOO:0100
c) результат — линейный адрес элемента, содержащего операнд типа "константа",
значение которой равно 9


IDA - 0159.htm

Операнды 159

Аргумент | Пояснения
еа | Линейный адрес начала поиска, не обязательно принадлежащий какому-ни-
 | будь сегменту
flag | =flag | Пояснения
 | ==l | Прямое направление поиска
 | ==0 | Обратное направление поиска
value | Искомое значение константы
return | =return | Пояснения
 | !=BADADDR | Линейный адрес элемента, которому принадлежит най-
 |  | денный операнд
 | ==BADADDR | Ошибка
Родственные функции: FindVoid.
Интерактивный аналог: "~Nabigate\Search for\Immediate"; <Alt-I>,
"^Nabigate\Search for\Next Immediate";
<Ctrl-I>.


IDA - 0160.htm

160 Объекты
Объекты
#Definition
С каждым элементом (бестиповым байтом) могут быть связаны три объекта — метка, перекрестная ссылка и комментарий. IDA поддерживает два типа меток — метки, определенные пользователем и метки, автоматически генерируемые IDA, а также четыре типа комментариев — постоянный комментарий, отображаемый справа от элемента и отделяемый от него знаком "точка с запятой" (обычный ассемблерный комментарий), повторяемый комментарий, отображаемый справа от комментируемого элемента и возле всех ссылок на данный элемент, и два вида многострочных комментариев предваряющих и замыкающих комментируемый элемент. О перекрестных ссылках подробно рассказано в главе "Перекрестные ссылки".
Каждый элемент может иметь не более одной метки и до четырех комментариев различного типа одновременно. Метки и комментарии хранятся в отдельном виртуальном массиве, проиндексированным линейными адресами, а на наличие связанных с элементом (бестиповым байтом) объектов указывают флаги (см. табл. 16).
В принципе без флагов, ссылающихся на объекты можно было бы и обойтись, но тогда бы пришлось при отображении каждой ячейки просматривать все виртуальные массивы на предмет поиска объектов, ассоциированных с данным линейным адресом, что отрицательно сказалось бы на производительности дизассемблера. Напротив, перенос этой информации в флаги позволяет ускорить работу — обращение к виртуальному массиву происходит только в тех случаях, когда с ячейкой заведомо связан какой-то объект
Разрушение элемента не вызывает автоматического уничтожения связанных с ним объектов — каждый объект должен быть удален по отдельности соответствующими функциями.
Таблица 16. Флаги, указывающие на наличие связанных объектов

Константа | # | Пояснения
FF_COMM | 0x00000800 | Комментарий
FF_REF | 0x00001000 | Перекрестная ссылка
FF__LINE | 0x00002000 | Многострочный комментарий
FF_NAME | 0x00004000 | Метка, определенная пользователем
FF_LABL | 0x00008000 | Метка, автоматически сгенерированная IDA
FF_FLOW | 0x00010000 | Перекрестная ссылка с предыдущей инструкции
FF_VAR | 0x00080000 | Переменная


IDA - 0161.htm

Объекты 161
Сводная таблица функций

Функции, создающие и уничтожающие объекты
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) |
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. Переданный линейный адрес должен быть либо адресом головы элемента любого вида, либо адресом бестипового байта; в противном случае функция возвратит ошибку. Имя метки должно состоять только из допустимых символов, перечень которых для каждой платформы содержится в поле "NameChars" конфигурационного файла <ida.cfg>.
Если по указанному адресу расположена уже существующая метка, в результате работы функции она будет переименована.
Удалить метку можно, переименовав ее в пустую строку. Удаление возможно только в том случае, если во всем дизассемблируемом тексте на данную метку


IDA - 0162.htm

162__________________Объекты_____________________
нет ни одной ссылки, в противном случае IDA Рго тут же создаст новое
автогенерирумое (dummy) имя.
Таблица 17. Перечень символов, допустимых в именах меток

Платформа | Перечень символов, допустимых в именах меток
PC | "$?@"1 "_01 23456789"
 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
 | "abcdefghijklmnopqrstuvwxyz";
 | "$_@?!"2
 | "0123456789<>"
Java | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | "abcdefghijklmnopqrstuvwxyz"
 | "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ"3
 | "абвгдежзийклмнопрстуфхцчшщъыьэюя";
 | " $_01 23456789"
TMS320C6 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | "abcdefghijklmnopqrstuvwxyz"
 | ".0123456789."
PowerPC | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 | "abcdefghijklmnopqrstuvwxyz"
Замечание. "MakeName" помимо переименования меток, также изменяет имена
функций, если ей передать адрес начала функции (см. главу "Функции').
Пример использования:
segOOO:0000 mov ah, 9
a) исходные данные — требуется создать метку с именем "NoName" по адресу
segOOO:000
MakeName(SegByName("segOOO"),"NoName");
b) вызов функции MakeName для создания метки
segOOO:0000 NoName mov ah, 9
c) результат — метка успешно создана

Аргумент | Пояснения
еа | Линейный адрес головы элемента любого вида или бестипового |  | байта
name | Имя метки
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | ==0 | Ошибка
1Служебные символы ассемблера.
2Символы, определенные только для специальных режимов Java-ассемблера.
3 Национальные (российские) символы.


IDA - 0163.htm

_____________________Объекты___________________163
Родственные функции: GetTrueName.
Интерактивный аналог: "~Edit\Name" <N>.
success MakeComm (long ea,char comment)
Функция создает комментарий comment, размещая его справа от элемента, расположенного по линейному адресу еа. Переданный линейный адрес должен быть либо адресом головы элемента любого вида, либо адресом бестипового байта; в противном случае функция возвратит ошибку.
Комментарий автоматически отделяется от элемента символом "точка с запятой" и в самой строке комментария его указывать не нужно. Величина отступа задается настройками IDA (см. главу "Глобальные настройки").
Строка комментария может содержать как символы латиницы, так и символы кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси IDA, в которой они были созданы.
Удалить комментарий можно задав в качестве нового пустую строку. Удаляются в том числе, и некоторые комментарии, автоматически создаваемые IDA.
Функция поддерживает спецификатор переноса строки '\n', автоматически создавая новую строку и перенося на нее хвост комментария.
Пример использования:
segOOO:0000 mov ah, 9
a) исходные данные — требуется вставить комментарий
МаКеСоmm(Ох1275С,"Функция 0x9 - печать строки");
b) вызов функции MakeComm для вставки комментария
segOOO:0000 mov ah, 9 ; Функция 0x9 - печать строки
c) результат — вставленный комментарий

Аргумент | Пояснения
еа | Линейный адрес головы элемента любого вида или бестипового |  | байта
comment | Строка комментария
return | =return | Пояснения
 | ==1 | Успешное завершение операции
 | =0 | Ошибка
Родственные функции: MakeRptCmt, ExrLinA, ExtLinB.
Интерактивный аналог: "^Edit\Comments\Enter comment"; <:>.
success MakeRptCmt (long ea, char comment)
Функция создает повторяемый комментарий comment, размещая его справа от элемента, расположенного по линейному адресу еа. Переданный линейный адрес должен быть либо адресом головы элемента любого вида, либо адресом бестипового байта; в противном случае функция возвратит ошибку.


IDA - 0164.htm

164___________________Объекты_____________________
Комментарий автоматически отделяется от элемента символом "точка с запятой" и в самой строке комментария его указывать не нужно. Величина отступа задается настройками IDA (см. главу "Глобальные настройки").
Строка комментария может содержать как символы латиницы, так и символы кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси IDA, в которой они были созданы.
Удалить комментарий можно задав в качестве нового пустую строку. Функция поддерживает спецификатор переноса строки '\n', автоматически создавая новую строку и перенося на нее хвост комментария.
Отличие повторяемого комментария от постоянного заключается в том, что повторяемый комментарий автоматически отображается около всех элементов, ссылающихся на элемент, помеченный повторяемым комментарием.
Замечание. Повторяемый комментарий может оказаться очень полезным на начальной стадии анализа программы, когда осмысленные имена переменным и функциям дать еще затруднительно, но какие-то мысли по поводу их назначения уже имеются, которые и можно высказать в комментарии, автоматически повторяемом возле всех ссылок на эту переменную (функцию), облегчая тем самым исследование кода.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 int 21h ;
segOOO:0107 retn
segOOO:0107 ; ———————————————————————————————————————————————
segOOO:0108aHello db'Hello,',0 ; DATA XREF: segOOO:0102o
segOOO:0108 ;
a) исходные данные — требуется вставить комментарий к метке aHello, автоматически повторяемый возле всех инструкций, ссылающихся на эту метку
MakeRptCmt(SegByName("segOOO")+Ox108,"Это повторяемый комментарий");
b) вызов функции MakeRptCmt для создания повторяемого комментария
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello ; Это повторяемый комментарий
segOOO:0105 int 21h ; DOS - PRINT STRING
segOOO:0105 ; DS:DX -> string terminated by "$"
segOOO:0107 retn
segOOO:0107 ; ————————————————————————
segOOO:0108aHello db'Hello,',0 ; DATA XREF: segOOO:0102o
segOOO:0108 ; Это повторяемый комментарий
c) результат — повторяемый комментарий создан — теперь он будет отображаться возле всех элементов, ссылающихся на метку aHello (обратите внимание на текст, выделенный в листинге жирным шрифтом)

Аргумент | Пояснения
еа | Линейный адрес головы элемента любого вида | или бестипового байта
comment | Строка повторяемого комментария


IDA - 0165.htm

Объекты 165

Аргумент | Пояснения
return | =return | Пояснения
 | ==l | Операция выполнена успешно
 | ==0 | Ошибка
Родственные функции: MakeComm, ExrLinA, ExtLinB.
Интерактивный аналог: "Edit\Comments\Enter repeatable comment".
void ExtLinA (long ea, long n, char line)
Функция создает строку (или несколько строк) комментариев, отображаемых перед элементом (бестиповым байтом), расположенном по переданному функции линейному адресу еа.
Комментарий располагается в начале строки и не предваряется символом иточка с запятой", поэтому этот символ необходимо указать самостоятельно.
Аргумент n задает номер строки комментария и может принимать значения от 0 до 500 включительно. IDA отображает комментарии начиная с нулевой до первой пустой строки. Т.е. если создать нулевую, первую и третью строки комментария, IDA отобразит лишь первые две из них.
Строка комментария может содержать как символы латиницы, так и символы кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси IDA, в которой они были созданы.
Для удаления всех строк комментария достаточно присвоить нулевой строке пустое значение — остальные строки хотя физически и останутся в базе, на экране отображаться не будут.
Замечание. Отсутствие автоматического предварения комментария символом "точка с запятой", позволяет, используя данную функцию, помещать в ассемблерный листинг директивы и инструкции, заданные пользователем.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 int 21h ;
a) исходные данные — требуется разместить комментарий перед инструкцией INT 21h
ExtLinA(SegByName("segOOO")+Ox105,0,"; Строка 1");
ExtLinA(SegByName("segOOO")+Ox105,1,"; Строка 2");
b) вызов функции ExtLinA для создания двух строк комментария
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 ; Строка 1
segOOO:0105 ; Строка 2
segOOO:0105 int 21h ;
c) результат


IDA - 0166.htm

166 Объекты

Аргумент | Пояснения
еа | Линейный адрес головы элемента (бестипового байта) пе-
 | ред которым должен быть размещен комментарий
п | Номер строки комментария от 0 до 500 включительно
line | Строка комментария
Родственные функции: MakeComm, MakeRptCmt, ExtLinB, DelExtLnA.
Интерактивный аналог: "~Edit\Comments\Edit extra anterior lines";
<Ins>.
void ExtLinB (long ea, long n, char line)
Функция создает строку (или несколько строк) комментариев, отображаемых после элемента (бестипового байта), расположенного по переданному функции линейному адресу еа.
Комментарий располагается сначала строки и не предваряется символом "точка с запятой", поэтому этот символ необходимо указать самостоятельно.
Аргумент n задает номер строки комментария и может принимать значения от 0 до 500 включительно. IDA отображает комментарии начиная с нулевой до первой пустой строки. Т.е. если создать нулевую, первую и третью строки комментария, IDA отобразит лишь первые две из них.
Строка комментария может содержать как символы латиницы, так и символы кириллицы, однако, нормальное отображение кириллицы возможно только в той ипостаси IDA, в которой они были созданы.
Для удаления всех строк комментария достаточно присвоить нулевой строке пустое значение — остальные строки хотя физически и останутся в базе, на экране отображаться не будут.
Замечание. Отсутствие автоматического предварения комментария символом "точка с запятой", позволяет, используя данную функцию, помещать в ассемблерный листинг директивы и инструкции, заданные пользователем.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 int 21h ;
a) исходные данные — требуется разместить комментарий после инструкции MOV DX,
offset aHello
ExtLinB(SegByName("segOOO")+Ox102,0,"; Строка 1"); ExtLinB(SegByNa-
me("segOOO")+Ox102,1,"; Строка 2");
b) вызов функции ExtLinB для создания двух строк комментария
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0102 ; Строка 1
segOOO:0102 ; Строка 2
segOOO:0105 int 21h ;
c) результат


IDA - 0167.htm

Объекты 167

Аргумент | Пояснения
еа | Линейный адрес головы элемента (бестипового байта) пе-
 | ред которым должен быть размещен комментарий
п | Номер строки комментария от 0 до 500 включительно
line | Строка комментария
Родственные функции: MakeComm, MakeRptCmt, ExtLinA, DelExtLnB.
Интерактивный аналог: "^Edit\Comments\Edit extra posterior lines";
<Shift-Ins>.
void DelExtLnA (long ea, long n)
Функция удаляет строку n многострочного комментария, ранее помещенного перед элементом (бестиповым байтом), расположенным по линейному адресу еа. При этом, все строки с номерами, превосходящими n (если они существуют) отображаться не будут, но физически по-прежнему будут присутствовать в базе.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 ; Строка 1
segOOO:0105 ; Строка 2
segOOO:0105 ; Строка 3
segOOO:0105 int 21h ;
a) исходные данные — требуется удалить вторую (считая от одного) строку многостроч-
ного комментария
DelExtLnA(SegByName("segOOO")+Ox105,1);
b) вызов функции DelExtLnA
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 ; Строка 1
segOOO:0105 int 21h ;
c) результат — все строки, с номерами больше двух (считая от одного) не отображают-
ся на экране
ExtLinA(SegByName("segOOO")+Ox105,1,"; 2");
d) вызов функции ExtLinA для восстановления второй строки двух строк комментария
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 Строка 1
segOOO:0105 2
segOOO:0105 Строка 3
segOOO:0105 int 21h ;
e) результат — все строки вновь отображаются на экране

Аргумент | Пояснения
еа | Линейный адрес элемента (бестипового байта)
n | Удаляемая строка комментария (от 0 до 500 включительно)


IDA - 0168.htm

168__________________Объекты_____________________
Родственные функции: DelExtLnB.
Интерактивный аналог: "^Edit\Comments\Edit extra anterior lines";
<Ins>.
void DelExtLnB (long ea, long n)
Функция удаляет строку n многострочного комментария, ранее помещенного после элемента (бестипового байта), расположенного по линейному адресу еа. При этом, все строки с номерами, превосходящими n (если они существуют) отображаться не будут, но физически по-прежнему будут присутствовать в базе.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0102 ; Строка 1
segOOO:0102 ; Строка 2
segOOO:0102 ; Строка 3
segOOO:0105 int 21h ;
a) исходные данные — требуется удалить вторую (считая от одного) строку много
строчечного комментария
DelExtLnB(SegByName("segOOO")+Ox102,1);
b) вызов функции DelExtLnB
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0102 ; Строка 1
segOOO:0105 int 21h ;
c) результат — все строки, с номерами больше двух (считая от одного) не отображают-
ся на экране
ExtLinB(SegByName("segOOO")+Ox102,1,"; 2");
d) вызов функции ExtLinB для восстановления второй строки двух строк комментария
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0102 Строка 1
segOOO:0102 2
segOOO:0102 Строка 3
segOOO:0105 int 21h ;
e) результат — все строки вновь отображаются на экране

Аргумент | Пояснения
еа | Линейный адрес элемента (бестипового |  | байта)
n | Удаляемая строка | комментария (от 0 до | 500 включительно)
Родственные функции: DelExtLnB.
Интерактивный аналог: "~Edit\Comments\Edit extra posterior lines";
<Shift-Ins>.


IDA - 0169.htm

____________________Объекты__________________169
void MakeVar (long ea)
Функция помечает элемент символом "звездочка", помещая его в начало строки. Повторный вызов функции не снимает пометку, и автору книги вообще не известно ни одного программного способа, позволяющего, эту пометку убрать. Интерактивно она снимается вызовом пункта "Mark item as variable" меню "^Edit\Other", который действует как триггер.
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello, IDA Рго! ',ODh,OAh
a) исходные данные — требуется установить пометку
MakeVar(SegByName("segOOO"));
b) вызов функции MakeVar для пометки
segOOO:0000*aHelloIdaPro db'Hello, IDAPro! ',ODh,OAh
c) результат — пометка установлена

Аргумент | Пояснения
еа | Линейный адрес | элемента (бестипового | байта)
Родственные функции: нет.
Интерактивный аналог: "^Edit\Other \Mark item as variable".
char Name (long ea)
Функция возвращает имя метки или функции, расположенной по линейному адресу еа. Если с данным линейный адресом не связано ни одно имя, функция возвращает пустую строку, сигнализируя об ошибке.
Функция выполняет проверку на наличие недопустимых символов в имени метки (функции) и при наличии таковых, заменяет их символом, заданным в поле "SubstChar" конфигурационного файла <ida.cfg>. По умолчанию недопустимые символы заменяются знаком "прочерка". Перечень допустимых символов в именах меток определяется значением поля "NameChars" конфигурационного файла <ida.cfg> (см. табл. 18).
Замечание. При отображении имен меток, (функций) в окне дизассемблера, IDA Pro всегда заменяет запрещенные символы знаком "прочерка". Т.е. функция Name возвращает имена в том виде, в каком они отображаются на экране. Для получения подлинного имени метки (функции) следует воспользоваться функцией GetTrueName.
Пример использования:
segOOO:OOOO aHelloIdaPro db'Hello, IDAPro! ',ODh,OAh
a) исходные данные — требуется получить имя метки
Message(">%s\n", Name(SegByName("segOOO")));
b) вызов функции Name для получения имени метки
> aHelloIdaPro
c) результат — имя метки получено


IDA - 0170.htm

170 Объекты
Таблица 18. Перечень символов, допустимых в именах меток

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

Аргумент | Пояснения
ea | Линейный адрес
return | =return Пояснения
 | !="" Имя метки, | в том виде, в котором оно отображено на экране
 | =="" Ошибка
Родственные функции: MakeName, GetTrueName.
Интерактивный аналог: имя метки (функции) отображается справа от
адреса.
char GetTrueName (long еа)
Функция возвращает полное имя метки (функции), расположенной по линейному адресу еа, не проверяя его на наличие недопустимых символов и не производя их автоматической замены (см. описание функции Name).
Пример использования:
segOOO:0000_HelloIdaPro db'Hello, IDAPro! ',ODh,OAh
a) исходные данные — требуется получить подлинное имя метки
Message(">%s\rT, GetTrueName(SegByName("segOOO")));
b) вызов функции GetTrueName для получения имени метки
Служебные символы ассемблера.
Символы, определенные только для специальных режимов Java-ассемблера.
Национальные (российские) символы.


IDA - 0171.htm

Объекты 171
>%HelloIdaPro
с) результат — подлинное имя метки получено (сравните его с отображаемым на
экране)

Аргумент | Пояснения
еа | Линейный адрес
return | =return | Пояснения
 | !="" | Подлинное имя метки (функции)
 | =="" | Ошибка
Родственные функции: MakeName, Name.
Интерактивный аналог: нет.
char Comment (long еа)
Функция возвращает строку постоянного комментария, расположенного по линейному адресу еа. Если с данным адресом не связан никакой комментарий, функция возвращает пустую строку, сигнализируя об ошибке.
Пример использования:
segOOO:0000 mov ah, 9 ; Функция 0x9 - печать строки
a) исходные данные — требуется получить постоянный комментарий
Message(">%s\n",Comment(SegByName("segOOO")));
b) вызов функции Comment для получения постоянного комментария
> Функция 0x9 - печать строки
c) результат

Аргумент | Пояснения
еа | Линейный адрес
return | =return | Пояснения
 | !="" | Строка постоянного комментария
 | =="" | Ошибка
Родственные функции: MakeComment.
Интерактивный аналог: постоянный комментарий отображается справа
от элемента.
char RptCmt (long еа)
Функция возвращает строку повторяемого комментария, расположенного по линейному адресу еа. Если с данным адресом не связан никакой комментарий, функция возвращает пустую строку, сигнализируя об ошибке.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello ; Это повторяемый комментарий


IDA - 0172.htm

172 Объекты __________
segOOO:0105 int 21h ; DOS - PRINT STRING
segOOO:0105 ; DS:DX -> string terminated by "$"
segOOO:0107 retn
segOOO:0107 ; ——————————————————————————
segOOO:0108aHello db'Hello,',0 ; DATA XREF: segOOO:Ol02o
segOOO:0108 ; Это повторяемый комментарий
a) исходные данные — требуется получить строку повторяемого комментария
Message(">%s\n",RptCmt(SegByName("segOOO")+Ox108)));
b) вызов функции RptCmt для получения повторяемого комментария
> Это повторяемый комментарий
c) результат — строка повторяемого комментария
Внимание! Функция RptCmt ожидает именно адрес повторяемого комментария, а не адрес элементов, ссылающихся на элемент, связанный с повторяемым комментарием. Т.е. в приведенном выше примере вызов RptCmt(SegByNa-me("segOOO")+Oxl02)) вернул бы пустую строку.

Аргумент | Пояснения
еа | Линейный адрес
return | =return | Пояснения
 | !="" | Строка повторяемого комментария
 | =="" | Ошибка
Родственные функции: MakeRptCmt.
Интерактивный аналог: повторяемый комментарий отображается справа
от элемента и всех ссылок на данный элемент.
char LineA (long ea, long num)
Функция возвращает строку num многострочного комментария, помещенного
перед элементом, расположенным по линейному адресу еа.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0105 ; Строка 1
segOOO:0105 ; Строка 2
segOOO:0105 int 21h ;
a) исходные данные — требуется получить первую строку многострочного комментария
Message(">%s\n",LineA(SegByName("segOOO")+Ox105,0)));
b) вызов функции LineA для получения первой строки многострочного комментария
> ; Строка 1
c) результат

Аргумент | Пояснения
еа | Линейный адрес
num | Номер строки комментария | от 0 до 500 включительно


IDA - 0173.htm

Объекты 173

/Аргумент | Пояснения
return | =return | Пояснения
 | !="" | Строка повторяемого комментария
 | =="" | Ошибка
Родственные функцни: LineB.
Интерактивный аналог: многострочный комментарий отображается пе-
ред комментируемым элементом.
char LineB (long ea, long num)
Функция возвращает строку num многострочечного комментария, помещен-
ного за элементом, расположенным по линейному адресу еа.
Пример использования:
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHello
segOOO:0102 ; Строка 1
segOOO:0102 ; Строка 2
segOOO:0105 int 21h ;
a) исходные данные — требуется получить первую строку многострочного коммен-
тария
Message(">%s\n",LineB(SegByName("segOOO")+Ox102,0)));
b) вызов функции LineB для получения первой строки многострочного комментария.
> ; Строка 1
c) результат

Аргумент | Пояснения
еа | Линейный адрес
num | Номер строки комментария от 0 до 500 включительно
return | =return | Пояснения
 | !="" | Строка повторяемого комментария
 | =="" | Ошибка
Родственные функции: LineA.
Интерактивный аналог: многострочный комментарий отображается пе-
ред комментируемым элементом.
long LocByName (char пате)
Функция возвращает линейный адрес метки (имени функции) с именем name. Если ни одной метки (функции) с указанным именем не существует, функция возвращает значение BADADDR, сигнализируя об ошибке.
Функция чувствительна к регистру символов и различает имена, набранные строчными и прописными буквами.


IDA - 0174.htm

174 Объекты
Внимание! Функции требуется передавать подлинные имена меток, а не имена, отображаемые на экране, прошедшие через фильтр замены недопустимых символов (см. описание функции GetTrueName).
Пример использования:
segOOO:0000 aHelloIdaPro db 'Hello, IDA Рго! ',ODh,OAh
a) исходные данные — требуется получить адрес метки "aHelloIdaPro"
Message(">%s\n",atoa(LocByName("aHelloIdaPro")));
b) вызов функции LocByName для получения адреса метки
>segOOO:0000
c) результат — адрес метки "aHelloIdaPro"

Аргумент | Пояснения
name | Имя метки (функции) с учетом регистра
return | =return | Пояснения
 | !=BADADDR | Линейный адрес метки (функции)
 | ==BADADDR | Ошибка
Родственные функции: нет
Интерактивный аналог: "~View\Names".


IDA - 0175.htm

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


IDA - 0176.htm

176 Функции
вычисления делает ассемблер). Эту конструкцию создатели языка окрестили процедурой, чем окончательно всех запутали.
Ассемблер не может автоматически присваивать переменным и регистрам результат, возвращенный процедурой, но это не означает, что процедура не может ничего возвращать. Воистину, "называемое дао не есть дао". Впрочем, ассемблер, как и любой другой язык, имеет свои соглашения и может не интересоваться, как та же конструкция называется у других.
Но IDA — это не просто дизассемблер, это еще и декомпилятор, еще и мотивом ее использования зачастую служит необходимость получить представление об исходном тексте программы, пусть и в другой форме.
Поддерживать отдельные языковые конструкции, следовательно, нецелесообразно, в ряде случаев невозможно, поэтому остановились на одной универсальной конструкции, которая подходит для любого языка. Названа она функцией. Но в ассемблере не существует такого понятия! Поэтому для выражения функции приходится пользоваться средствами ассемблера, в котором она (функция) называется процедурой (различие только в названии).
Иногда это приводит к путанице. Так, например, если дизассемблировать программу, написанную на Turbo-Pascal, то IDA автоматически распознает все процедуры, но называться они будут функциями, а выделяться в ассемблерном листинге ключевым словом PROC (сокращение от procedure).
Пусть исходная программа выглядела так:
Procedure MyProc;
begin
WriteLn('Hello');
end;
BEGIN
MyProc;
End.
Тогда результат работы IDA может выглядеть следующим образом:
segOOO:0006 ; Attributes: bp-based frame
segOOO:0006
segOOO:0006 sub_0_6 proc near ; CODE XREF: PROGRAM+14p
segOOO:0006 push bp
segOOO:0007 mov bp, sp
segOOO:0009 xor ax, ax
segOOO:OOOB call @__StackCheck$q4Word ; Stack overflow check (AX)
segOOO:0010 mov di, offset unk_E1_166
segOOO:0013 push ds
segOOO:0014 push di
segOOO:0015 mov di, offset asc_0_0 ; "\x05Hello"
segOOO:0018 push cs
segOOO:0019 push di
segOOO:001A xor ax, ax
segOOO:001C push ax
segOOO:OOlD call @Write$qm4Textm6String4Word ; Write(var f; s: String; width:
segOOO:0022 call @WnteLn$qm4Text ; WriteLn(var f: Text)
segOOO:0027 call @__IOCheck$qv ; Exit if error


IDA - 0177.htm

Функции 177
segOOO:002C pop bp
segOOO:002D retn
segOOO:002D sub_0_6 endp
segOOO:002D
segOOO:002E assume ss:seg004
segOOO:002E PROGRAM proc near
segOOO:002E call @__SystemInit$qv ; __SystemInit(void)
segOOO:0033 call sub_5_D
segOOO:0038 push bp
segOOO:0039 mov bp, sp
segOOO:003B xor ax, ax
segOOO:003D call @__StackCheck$q4Word ; Stack overflow check (AX)
segOOO:0042 call sub_0_6
segOOO:0045 pop bp
segOOO:0046 xor ax, ax
segOOO:0048 call @Halt$q4Word ; Halt(Word)
segOOO:0048 PROGRAM endp
На самом деле противоречия тут нет. Компиляция — однонаправленный процесс с потерями, поэтому можно забыть о существующих конструкциях в языке-источнике.
Код, сгенерированный компилятором, похож как на процедуру, так и на функцию:
segOOO:0006 sub_0_6 proc near
segOOO:0006 push bp
segOOO:0007 mov bp, sp
segOOO:0027 call @__IOCheck$qv
segOOO:002C pop bp
segOOO:002D retn
Поэтому при дизассемблировании не акцентируют внимание на подобных различиях и говорят о подпрограммах.
Подпрограмма, оформленная особым образом, в IDA называется функцией. Под этим понимается не только совокупность кода и данных, но и действия, которые над ними можно совершить.
Чувствуете разницу? Функцию можно создать, дать ей имя, потом удалить ее, внутри функции IDA может отслеживать значение регистра указателя на верхушку стека и автоматически поддерживать локальные переменные.
При этом в ассемблерном листинге функции оформляются в виде процедур:
segOOO:0006 sub_0_6 proc near
segOOO:002D sub_0_6 endp
Противоречия не возникнет, если понять, что в данном случае под процедурой понимается один из возможных вариантов представления функции средствами выбранного языка (в данном случае ассемблера MASM).
Таким образом, мы будем говорить о функции не как о совокупности кода и данных, а именно как о методах взаимодействия с ней.


IDA - 0178.htm

178 Функции
Методы класса "функция"

Имя функции | Назначение
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 GetFrameArgsSize (long ea) | Возвращает размер аргументов функции в байтах
long GetFrameSize (long ea) | Возвращает полный размер стекового фрейма в байтах
long MakeFrame (long ea, long | Создает фрейм функции или модифицирует уже сущест-
lvsize, long frregs, long argsize) | вующий
long GetSpd (long ea) | Возвращает значение регистра SP в произвольной точке
 | функции
long GetSpDiff (long ea) | Возвращает относительное изменение регистра SP ука-
 | занной инструкцией
success SetSpDiff | Корректирует изменение регистра SP указанной командой
(long ea, long delta) |
long FindFuncEnd (long ea) | Определяет линейный адрес конца функции
success MakeFunction (long start, long end)
Вызов MakeFunction позволяет создавать функцию. IDA не различает функций и процедур — в ее терминологии все это функции.
Каждая функция обладает принадлежащим ей непрерывным диапазоном адресов. В его границах может отслеживаться значение указателя стека, создаются ссылки на следующие инструкции и так далее, т.е. ряд вызовов API работает исключительно с функциями.
Для создания функции достаточно указать линейный адрес ее начала и конца. Функции могут создаваться только внутри сегментов и располагаться только в


IDA - 0179.htm

Функции 179
начале, а не в середине машинных инструкций. В то же время допускается в качестве конца задавать адрес, приходящийся на середину инструкции — IDA округлит его до адреса конца предыдущей инструкции. Например:
копия экрана IDA:
segOOO:002A mov si, 211h segOOO:002D call sub_O_DD segOOO:0030 mov si, 2BAh segOOO:0033 call sub_O_DD segOOO:0036 retn
> ввод пользователя:
MakeFunction(Oxl002A,Oxl0037);
G копия экрана IDA:
segOOO:002A ; _________ S U B R 0 U Т I N E
segOOO:002A
segOOO:002A
segOOO:002A sub_0_2A proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 sub__0_2A endp
segOOO:0036
segOOO:0037
segOOO:0037 ; _________ S U B R 0 U Т I N E
При этом функции автоматически дается имя, вид которого зависит от настроек. По умолчанию оно предваряется префиксом 'sub' (от subroutine — процедура, хотя в терминологии IDA она называется функцией) и последующим смещением внутри сегмента. Если вместо адреса конца функции указать константу BADADDR, то IDA попытается самостоятельно определить его. Этот механизм довольно бесхитростен (концом функции считается инструкция ret или jmp) и довольно часто приводит к ошибкам и занижает адрес.
Разберем, например, такой случай. Путь некая функция имеет более одной точки выхода. В этом случае IDA часто принимает за конец функции первую встретившуюся из них, а вторая так и остается в "хвосте". Это не упрек в несовершенстве алгоритма, а предостережение: машинный анализ не сможет полностью заменить человека.
Обратите внимание, что вызов MakeFunction провалится, если выделенная под функцию область будет помечена как undefined, что видно из следующего примера:
G копия экрана IDA:
segOOO:002A db OBEh segOOO:002B db 11h segOOO:002C db 2 segOOO:002D db OE8h segOOO:002E db OADh


IDA - 0180.htm

180____________ __ Функции
segOOO:002F db 0
segOOO:0030 db OBEh
segOOO:0031 db OBAh
segOOO:0032 db 2
segOOO:0033 db OE8h
segOOO:0034 db OA7h
segOOO:0035 db 0
segOOO:0036 db OC3h
> ввод пользователя:
Message("Ox%X \n",MakeFunction(Oxl002A,0x10037));
< ответ IDA:
0
Но в то же время, если в качестве адреса конца указать константу BADADDR,
то функция будет успешно создана!
G копия экрана IDA:
segOOO:002A - db OBEh
segOOO:002B db 11h
segOOO:002C db 2
segOOO:002D db OE8h
segOOO:002E db OADh
segOOO:002F db 0
segOOO:0030 db OBEh
segOOO:0031 db OBAh
segOOO:0032 db 2
segOOO:0033 db OE8h
segOOO:0034 db OA7h
segOOO:0035 db 0
segOOO:0036 db OC3h
> ввод пользователя:
Message("Ox%X \n",MakeFunction(Oxl002A,-l));
< ответ IDA:
1
копия экрана IDA:
segOOO:002A ; _________ S U B R 0 U Т I N E _______________________________
segOOO:002A
segOOO:002A
segOOO:002A sub_0_2A proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 sub_0_2A endp
segOOO:0036
segOOO:0037
segOOO:0037 ; _________ S U B R 0 U Т I N E ______________________


IDA - 0181.htm

Функции 181

Операнд | Пояснения
Start | Линейный адрес начала функции. Функция не может начинаться с |  | середины
 | инструкции |  |
End | ==end | Пояснения
 | !=-l | Линейный адрес конца функции. Может приходиться | на середину
 |  | инструкции. IDA его округлит до адреса конца предыдущей инструкции
 | ==-l | IDA автоматически вычисляет адрес конца функции и при | необходимо-
 |  | сти преобразует undefined в инструкции |
Return | Завер- | Пояснения
 | шение
 | 0 | Вызов завершился не успешно. Функция не была создана
 | 1 | Вызов завершился успешно
success DelFunction (long ea)
Вызов DelFunction позволяет удалять функцию, указав любой принадлежащий ей адрес. Вместе с функцией уничтожаются все локальные переменные и аргументы, если они есть. Все остальное (инструкции, перекрестные ссылки, метки) остается нетронутым. Например:
G копия экрана IDA:
.text:00400FFF ; _________ S U B R 0 U Т 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]
.text:00401011 call __NMSG_WRITE
.text:00401016 push OFFh
.text:0040101B call off_0_408050
.text:00401021 pop ecx
.text:00401022 pop ecx
.text:00401023 retn
.text:00401023 __amsg_exit endp
> ввод пользователя:
DelFuncton(Ox400FFF);
копия экрана IDA:
.text:00400FFF __amsg_exit: ; CODE XREF: __setenvp+4Ep
.text:00400FFF ; __setenvp+7Dp ...


IDA - 0182.htm

182 Функции
.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: .text:00401006j
.text:0040100D push dword ptr [esp+4]
.text:00401011 call __NMSG_WRITE
.text:00401016 push OFFh
.text:0040101B call off_0_408050
.text:00401021 pop ecx
.text:00401022 pop ecx
.text:00401023 retn

Операнд | Пояснения
ea | Любой линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | 0 | Вызов завершился не успешно. Функция | не была создана
i j | 1 | Вызов завершился успешно
success SetFunctionEnd (long ea, long end)
Позволяет изменить линейный адрес конца функции. Для этого достаточно
передать любой адрес, принадлежащий функции, и новое значение конца.
Например:
копия экрана IDA:
segOOO:22CO start proc near
segOOO:22CO call sub_0_22DD
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8
segOOO:22D2 jnz loc_0_22DA
segOOO:22D4 пор
segOOO:22D5 пор
segOOO:22D6 пор
segOOO:22D7 call sub_0_2412
segOOO:22DA
segOOO:22DA loc_0_22DA:
segOOO:22DA call sub_0_2305
segOOO:22DA start endp
> ввод пользователя:
SetFunctionEnd(Oxl22C3,0x122CF);
копия экрана IDA:
segOOO:22CO start proc near
segOOO:22CO call sub_0_22DD
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374


IDA - 0183.htm

_____________ Функции
183
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8 ; -> источник
segOOO:22CF start endp
segOOO:22D2 jnz loc_0_22DA ; <- приемник
segOOO:22D4 пор
segOOO:22D5 пор
segOOO:22D6 пор
segOOO:22D7 call sub_0_2412
segOOO:22DA
segOOO:22DA loc_0_22DA:
segOOO:22DA call sub_0_2305
Однако при этом не удаляется перекрестная ссылка на следующую команду, что может иметь неприятные последствия, например, при попытке пометить функцию как undefined, что и видно на следующем примере:
> ввод пользователя:
MakeUnkn(Oxl22CO,l);
G копия экрана IDA:
segOOO:22CO start db OE8h ш
segOOO:22C1 db 1Ah
segOOO:22C2 db 0
segOOO:22C3 db OE8h ш
segOOO:22C4 db 5Fh
segOOO:22C5 db 0
segOOO:22C6 db OE8h ш
segOOO:22C7 db 92h Т
segOOO:22C8 db 0
segOOO:22C9 db OE8h ш
segOOO:22CA db OA8h и
segOOO:22CB db 0
segOOO:22CC db OE8h ш
segOOO:22CD db OE7h ч
segOOO:22CE db 0
segOOO:22CF db OE8h ш
segOOO:22DO db 26h &
segOOO:22D1 db 1
segOOO:22D2 db 75h u
segOOO:22D3 db 6
segOOO:22D4 db 90h P
segOOO:22D5 db 90h P
segOOO:22D6 db 90h P
Кроме того, если в качестве нового конца указать адрес, уже принадлежащий какой-нибудь функции (разумеется, кроме текущей), то вызов провалится.
G копия экрана IDA:
segOOO:2305 sub_0_2305 proc near
segOOO:2305 sti
segOOO:2306 call sub_0_1CA
segOOO:2309 mov ah, 4Ch
segOOO:230B int 21h
segOOO:230B sub_0_2305 endp


IDA - 0184.htm

184 Функции
segOOO:230B
segOOO:230D
segOOO:230D ; _________ S U B R 0 U Т I N E
segOOO:230D
segOOO:230D
segOOO:230D sub_0_230D proc near
segOOO:230D mov si, 2C51h
segOOO:2310 call sub_O_DD
segOOO:2313 mov si, 2C4Dh
segOOO:2316 call sub_0_2E2
segOOO:2319 jnb loc_0_2321
segOOO:231B nop
segOOO:231C nop
segOOO:231D nop
segOOO:231E mov si, 2A2Dh
segOOO:2321
segOOO:2321 loc_0_2321:
segOOO:2321 call sub_O_DD
segOOO:2324 retn
segOOO:2324 sub_0_230D endp
> ввод пользователя:
Message("Ox%X \n",
SetFunctionEnd(Oxl2305,Oxl2310)
);
< ответ IDA:
1
Если функция возвращает отличное от нуля число, то это признак неуспешности завершения операции. Следовательно, адрес конца не был изменен (повторный дамп из экономии не приводится).
Напротив, если за концом функции расположены данные (даже массив), то новый адрес конца будет успешно установлен:
G копия экрана IDA:
segOOO:292F sub_0_292F proc near
segOOO:292F inc bx
segOOO:2930 loop loc_0_292F
segOOO:2932 nop
segOOO:2933 retn
segOOO:2933 sub_0_292F endp
segOOO:2933
segOOO:2933 ; ----------------------------------
segOOO:2934*word_0_2934 dw 0
segOOO:2934*
segOOO:2936*byte_0_2936 db 0
> ввод пользователя:
SetFunctionEnd(Oxl2930,Oxl2934);
копия экрана IDA:
segOOO:292F sub_0_292F proc near
segOOO:292F inc bx


IDA - 0185.htm

Функции 185
segOOO:2930 loop loc_0_292F
segOOO:2932 пор
segOOO:2933 retn
segOOO:2933
segOOO:2933 ; ----------------------------------
segOOO:2934*word_0_2934 dw 0
segOOO:2934*
segOOO:2934 sub_0_292F endp
segOOO:2936*byte_0_2936 db 0
Можно даже указать на середину массива или ячейки. Функция завершится успешно, адрес будет изменен, но он перестанет отображаться на экране, поскольку IDA забыла его округлить или проверить на корректность!
Это подтверждает следующий пример, взята часть кода, приведенного выше:
> ввод пользователя:
Message("Ox%X \n",
SetFunctionEnd(Oxl2930, 0x12935)
);
< ответ IDA:
0
G копия экрана IDA:
segOOO:292F sub_0_292F proc near
segOOO:292F inc bx
segOOO:2930 loop loc_0_292F
segOOO:2932 пор
segOOO:2933 retn
segOOO:2933
segOOO:2933 ; ----------------------------------
segOOO:2934*word_0_2934 dw 0
segOOO:2934*
segOOO:2936*byte_0_2936 db 0
Однако то, что конец функции не отображается на экране, ничего не значит. Попробуем убедиться, что IDA действительно не выполнила округления, и адрес 0x12936 не принадлежит функции.
> ввод пользователя:
Message("Ox%X \n",
SetFunctionEnd(Oxl2936,0x12933)
);
< ответ IDA:
1
Вызов SetFunctionEnd возвратил ошибку, следовательно, адрес 0x12936 действительно не принадлежит функции. Попробуем уменьшить его на единицу:
> ввод пользователя:
Message("Ox%X \n",
SetFunctionEnd(Oxl2935,Oxl2933)


IDA - 0186.htm

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

Операнд | Пояснения
еа | Любой линейный адрес, принадлежащий функции
End | Новый линейный адрес конца функции
Return | Завершение | Пояснения
 | 0 | Вызов завершился неуспешно. Функция не была создана
 | 1 | Вызов завершился успешно
long NextFunction (long еа)
Вызов возвращает линейный адрес начала функции, следующий за 'ea'. Чтобы получить адрес первой функции, необходимо вызвать NextFunction(0).
Если больше ни одной функции возвратить невозможно, то функция возвращает ошибку BADADDR.
Пример использования:
копия экрана IDA:
segOOO:0000 sub_0_0 proc near
segOOO:0000 push ax
segOOO:0001 push bx
segOOO:0027 pop bx
segOOO:0028 pop ax
segOOO:0029 retn


IDA - 0187.htm

Функции 187
segOOO:0029 sub_0_0 endp
segOOO:0029
segOOO:002A
segOOO:002A ; _______ S U B R 0 U Т I N E ____________________
segOOO:002A
segOOO:002A
segOOO:002A sub_0_2A proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 sub_0_2A endp
segOOO:0036
segOOO:0037
segOOO:0037 ; _________ S U B R 0 U T I N E ________________
segOOO:0037
segOOO:0037
segOOO:0037 sub_0_37 proc near
segOOO:0037
segOOO:0037
segOOO:0037 push ax
segOOO:0038 push bx
> ввод пользователя:
auto a;
a=0;
while ((a=NextFunction(a))!=-l)
Message("%x \n",a);
< ответ IDA:
10000 1002a 10037

Операнд | Пояснения
ea | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Линейный адрес начала следующей | функции
 | BADADDR | Ошибка
long PrevFunction (long ea)
Вызов возвращает линейный адрес предыдущий функции. Чтобы получить
адрес последней функции, необходимо вызвать PrevFunction(BADADDR).
копия экрана IDA:
segOOO:0000 sub_0_0 proc near
segOOO:0000 push ax
segOOO:0001 push bx
segOOO:0027 pop bx


IDA - 0188.htm

188 Функции
segOOO:0028 pop ax
segOOO:0029 retn
segOOO:0029 sub_0_0 endp
segOOO:0029
segOOO:002A
segOOO:002A ; _______ S U B R 0 U T I N E ______________
segOOO:002A
segOOO:002A
segOOO:002A sub_0_2A proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 sub_0_2A endp
segOOO:0036
segOOO:0037
segOOO:0037 ; _________ S U B R 0 U T I N E ______
segOOO:0037
segOOO:0037
segOOO:0037 sub_0_37 proc near
segOOO:0037
segOOO:0037
segOOO:0037 push ax
segOOO:0038 push bx
> ввод пользователя:
auto a;
a=0xl0038;
while ((a=PrevFunction(a))!=-l)
Message("%x \n",a);
< ответ IDA:
10037 1002a 10000

Операнд | Пояснения
ea | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Линейный адрес начала предыдущей | функции
 | BADADDR | Ошибка
long GetFunctionFlags (long еа)
Вызов GetFunctionFlags позволяет узнать атрибуты функции. Назначение
отдельных битов в возвращаемом значении показано ниже.

Определение | Значение | Пояснения
FUNC_NORET | 0x0000000 1L | Функция не возвращает управления
FUNC_FAR | Ox00000002L | FAR (далекая) функция


IDA - 0189.htm

Функции 189

Определение | Значение | Пояснения
FUNC_LIB | Ox00000004L | Библиотечная функция
FUNC_STATIC | Ox00000008L | Статическая функция
FUNC_FRAME | 0x00000010L | Функция использует для указателя кадра стека регистр | BP
FUNC_USERFAR | Ox00000020L | Функция определена пользователем как далекая (FAR)
FUNC_HIDDEN | Ox00000040L | Скрытая функция
Подробнее о каждом атрибуте рассказано ниже.
FUNC_NORET
Этот атрибут устанавливается тем функциям, которые не возвращают управления командой ret. Однако в большинстве случаев IDA автоматически не присваивает его.
Так, например, в следующей функции он не установлен:
копия экрана IDA:
segOOO:2305 sub_0_2305 proc near
segOOO:2305 sti
segOOO:2306 call sub_0_1CA
segOOO:2309 mov ah, 4Ch
segOOO:230B int 21h
segOOO:230B sub_0_2305 endp
> ввод пользователя:
Message("%b \n",
GetFunctionFlags(0x12305)
);
< ответ IDA:
0
Если в вашей ситуации это обстоятельство окажется критичным, то можно написать собственный скрипт, выполняющий такие проверки и устанавливающий атрибуты через вызов SetFunctionFlags.
FUNC_FAR
"Далекая" функция. IDA считает далекими все функции, оканчивающиеся командой далекого возврата — retf.
Этот механизм несовершенен и может давать сбои. Так, например, в следующем фрагменте эмуляцию вызова CALL FAR\RET IDA интерпретировала как далекий возврат из функции.
Это не упрек в сторону IDA, поскольку ради частных случаев бессмысленно вносить дополнительные усовершенствования в дизассемблер, но учитывать этот факт всегда стоит, вручную корректируя адрес конца и атрибуты функции.
G копия экрана IDA:
segOOO:048B sub_0_48B proc far
segOOO:048B
segOOO:048B pushf


IDA - 0190.htm

190 Функции
segOOO:048C push cs
segOOO:048D push offset locret_0_499
segOOO:0490 push word ptr ds:74Dh
segOOO:0494 push word ptr ds:74Bh
segOOO:0498 retf
segOOO:0498 sub_0_48B endp ; sp = - OAh
segOOO:0498
segOOO:0499 ; ------------------------------------------
segOOO:0499
segOOO:0499 loc ret_0_499:
segOOO:0499 retn
> ввод пользователя:
Message("%b \n",
GetFunctionFlags(0x1048B)
) ;
< ответ IDA:
10
FUNC_LIB
Таким флагом отмечены стандартные "библиотечные" функции, — чьи сигна-
туры известны FLIRT.
G копия экрана IDA:
.text:004010FF ; Attributes: library function
.text:004010FF
.text:004010FF _amsg_exit proc near
.text:004010FF
.text:004010FF
.text:004010FF arg_0 = dword ptr 4
.text:004010FF
.text:004010FF cmp dword_0_408758, 2
.text:00401106 jz short loc_0_40110D
.text:00401108 call __FF_MSGBANNER
.text:0040110D
.text:0040110D loc_0_40110D:
.text:0040110D push [esp+arg_0]
.text:00401111 call __NMSG_WRITE
.text:00401116 push OFFh
.text:0040111B call off_0_408050
.text:00401121 pop ecx
.text:00401122 pop ecx
.text:00401123 retn
.text:00401123 __amsg_exit endp
> ввод пользователя:
Message("%b \n",
GetFunctionFlags(0x4010FF)
) ;
< ответ IDA:
100


IDA - 0191.htm

Функции 191
FUNC_FRAME
Функция использует в качестве указателя на кадр стека регистр BP (EBP). IDA определяет это, отслеживая последовательность
PUSH ВР
MOV ВР, SP
Большинство современных оптимизирующих компиляторов отказались от такого подхода и адресуют локальные переменные и аргументы непосредственно по регистру ESP. IDA распознает такую ситуацию и отслеживает значение ESP по ходу исполнения процедуры, освобождая пользователя от львиной доли работы.
G копия экрана IDA:
segOOO:20B8 ; Attributes: bp-based framesegOOO:20B8
segOOO:20B8 sub_0_20B8 proc near
segOOO:20B8
segOOO:20B8
segOOO:20B8 var_80 = byte ptr -80h
segOOO:20B8 var_6B = byte ptr -6Bh
segOOO:20B8 var_62 = byte ptr -62h
segOOO:20B8
segOOO:20B8 push bp
segOOO:20B9 mov ah, 2Fh
segOOO:20BB int 21h
segOOO:20BB
segOOO:20BD push bx
segOOO:20BE mov bp, sp
segOOO:20CO sub sp, 80h
segOOO:20C4 mov ah, 1Ah
segOOO:20C6 lea dx, [bp+var_80]
segOOO:20C9 int 21h
> ввод пользователя:
Message("%b \n",
GetFunctionFlags(0x4010FF)
) ;
< ответ IDA:
10000
Обратите внимание, что IDA распознала эту комбинацию, даже когда команды push bp и mov bp,sp оказались достаточно разнесены.
FUNC_USERFAR
Этот атрибут IDA устанавливает, когда пользователь вручную меняет тип функции с NEAR на FAR, вызывая диалог 'Modify Function' c помощью команды меню * Edit \ Function \ Edit Function.
Обратите внимание, что это не относится к вызовам SetFunctionFlags! Последняя устанавливает именно тот набор атрибутов, который ей передается.


IDA - 0192.htm

192 Функции
IDA - 0192-1.jpg
IDA - 0192-2.jpg
FUNC_HIDDEN
Этот атрибут устанавливается у "свернутых" функций. Свернуть любую функцию можно однократным нажатием Gray '-', переместив курсор в ее границы. Кроме этого, в зависимости от настроек IDA может автоматически сворачивать все библиотечные функции для экономии места.
G копия экрана IDA:
dseg:027B ; [00000009 BYTES: COLLAPSED FUNCTION sub_0_27B. PRESS KEYPAD "+" TO EXPAND]
> ввод пользователя:
Message("*b \n",
GetFunctionFlags(0x4010FF)
) ;
< ответ IDA:
100000
Заметим, что в результате упущения этот тип не определен в IDC.IDC, и чтобы им пользоваться, необходимо это сделать самостоятельно, внеся новую строку:
#define FUNC_HIDDEN Ox00000040L // a hidden function


IDA - 0193.htm

Функции 193
Все атрибуты функция IDA отображает в виде комментариев, расположенных
в начале функции.
dseg:0271 ; Attributes: library function
dseg:0271
dseg:0271 _checknull proc near
dseg:0271 retn
dseg:0271 __checknull 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.

Операнд | Пояснения
еа | Линейный адрес, принадлежащий функции
Flag | Атрибуты функции (см. таблицу в описании GetFunctionFlags) — стр. |  | 188
Return | Завершение | Пояснения
 | !=BADADDR | Набор атрибутов функции (см. таблицу выше)
 | BADADDR | Ошибка
Как уже отмечалось в описании функции SetFunctionFlags, часто IDA автоматически не распознает функции, не возвращающие управления (нет команды retf в завершении функции).
Если это критично, то нужный атрибут можно установить вручную. Покажем это на следующем примере:
копия экрана IDA:
dseg:0272 ; Attributes: library function bp-based frame
dseg:0272
dseg:0272 „terminate proc near ; COD
dseg:0272
dseg:0272 arg_0 = byte ptr 2
dseg:0272
dseg:0272 mov bp, sp
dseg:0274 mov ah, 4Ch ; 'L'
dseg:0276 mov al, [bp+arg_0]
dseg:0279 int 21h ; DOS
dseg:0279 _terminate endp ; AL


IDA - 0194.htm

194 Функции
> ввод пользователя:
SetFunctionFlags
(
0x10272,
GetFunctionFlags(Oxl0272) + 1
)
G копия экрана IDA:
dseg:0272 ; Attributes: library function noreturn bp-based frame.
dseg:0272
dseg:0272 „terminate proc near ; CODE XREF: sub_0_3C7+44p
dseg:0272
dseg:0272 arg_0 = byte ptr 2
dseg:0272
dseg:0272 mov bp, sp
dseg:0274 mov ah, 4Ch ; 'L'
dseg:0276 mov al, [bp+arg_0]
dseg:0279 int 21h ; DOS - 2+ - QUIT WITH EXIT
dseg:0279 „terminate endp ; AL = exit code
B большинстве случаев атрибуты никакого влияния на функции не оказывают. Так, например, если в приведенном примере сбросить флаг FUNC_FRAME, то это не повлечет за собой автоматического удаления всех локальных переменных, адресуемых через BP.
> ввод пользователя:
SetFunctionFlags
(
0x10272,
GetFunctionFlags(Oxl0272) - 0x10;
)
G копия экрана IDA:
dseg:0272 ; Attributes: library function
dseg:0272
dseg:0272 „terminate proc near ; CODE XREF: sub_0_3C7+44p
dseg:0272
dseg:0272 arg_0 = byte ptr 2
dseg:0272
dseg:0272 mov bp, sp
dseg:0274 mov ah, 4Ch ; 'L'
dseg:0276 mov al, [bp+arg_0]
dseg:0279 int 21h ; DOS - 2+ - OUIT WITH EXIT
dseg:0279 _terminate endp ; AL = exit code
Установка флага FUNC_HIDDEN приведет к незамедлительному сворачива-нию функции (сброс — соответственно наоборот).
> ввод пользователя:
SetFunctionFlags
(
0x10272,
GetFunctionFlags(Oxl0272) + 0x40;


IDA - 0195.htm

Функции 195
копия экрана IDA:
dseg:0272 ; [00000009 BYTES: COLLAPSED FUNCTION __terminate. PRESS KEYPAD "+" TO EX-
PAND]
char GetFunctionName (long ea)
Возвращает имя функции. Если указанный адрес не принадлежит ни одной из функций, то возвращается пустая строка.
Поскольку функции без имени не бывает, то неоднозначной ситуации не возникает.

Операнд | Пояснения
еа | Любой линейный адрес, принадлежащий функции
Return | Завершение | Пояснения J
 | ! = "" | Имя функции
 | .... | Ошибка
Пример использования:
G копия экрана IDA:
dseg:025E _cleanup proc near
dseg:025E mov es, cs:DGROUP@
dseg:0263 push si
dseg:0264 push di
> ввод пользователя:
Message("%s \n",
GetFunctionName(0x10263)
) ;
< ответ IDA:
_cleanup
void SetFunctionCmt (long ea, char cmtt long repeatable)
Задает комментарий, который размещается впереди функции. IDA поддерживает символ переноса, поэтому комментарий может располагаться на нескольких строках.
Существует возможность повторять тот же комментарий в точке вызова функции (так называемый repeatable comment). Для этого необходимо установить флаг 'repeatable* равным единице.
Например:
> ввод пользователя:
SetFunctionCmt(Oxl0271,"Hello, IDA 4.0",1);
G копия экрана IDA:
dseg:0271 ; Hello, IDA 4.0
dseg:0271 ; Attributes: static
dseg:0271


IDA - 0196.htm

196 Функции
dseg:0271 __checknull proc near ; CODE XREF: sub_0_3C7+2Cp
dseg:0271 retn
dseg:0271 __checknull endp
Если перейти по перекрестной ссылке к месту вызова функции, то там можно обнаружить следующее:
dseg:03FO call _restorezero
dseg:03F3 call __checknull ; Hello IDA 4.0
dseg:03F6 cmp [bp+arg_2], 0
dseg:03FA jnz loc_0_40F
Если в строке комментария будет присутствовать символ переноса, то экран будет выглядеть так:
> ввод пользователя:
SetFunctionCmt(Oxl0271,"Hello \nIDA 4.0",1);
G копия экрана IDA:
dseg:0271 ; Hello
dseg:0271 ; IDA 4.0
dseg:0271 ; Attributes: static
dseg:03F3 call __checknull ; Hello
dseg:03F3 ; IDA 4.0
dseg:03F6 cmp [bp+arg_2], 0
He рекомендуется перегружать листинг повторяемыми комментариями, ведь всегда можно обратиться за разъяснениями к самой функции. Наиболее полезны они на начальной стадии дизассемблирования, когда назначение большинства функций плохо понятно, и дать им осмысленное имя не удается. Тогда в повторяемом комментарии отражают все, что на данный момент известно о каждой кз функций, и по мере исследования текста, уточняют. На финалъной стадии дизассемблирования повторяемые комментарии обычно убирают.
Каждая функция может обладать комментариями сразу двух типов, но в заголовке будет отображаться только один из них — 'regular'.
Например:
> ввод пользователя:
SetFunctionCmt(Oxl0271,"Hello, IDA 4.0",1);
SetFunctionCmt(Oxl0271,"Hello, World",0);
копия экрана IDA:
dseg:0271 ; Hello, World
dseg:0271 ; Attributes: static
dseg:03F3 call __checknull ; Hello IDA 4.0
dseg:03F6 cmp [bp+arg_2], 0

Операнд | Пояснения
ea | Любой | линейный адрес, | принадлежащий функции
Cmp | Строка | комментария, включая символ переноса


IDA - 0197.htm

Функции 197

Операнд | Пояснения
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
char GetFunctionCmt (long ea, long repeatable)
Позволяет получить как повторяемый, так и постоянный комментарии. Для этого необходимо задать любой линейный адрес, принадлежащий функции.
Подробнее о повторяемых комментариях можно прочитать в описании функции SetFunctionCmt.
Например:
копия экрана IDA:
dseg:0271 ; Hello, IDA 4.0
dseg:0271 ; Attributes: static
dseg:0271
dseg:0271 _checknull proc near
dseg:0271 retn
dseg:0271 _checknull endp
> ввод пользователя:
Message("%s \n",
GetFunctionCmt(Ox010271,l)
);
< ответ IDA:
Hello, IDA 4.0
> ввод пользователя:
Message ("%s \n",
GetFunctionCmt(Ox010271,0)
);
Необходимо правильно указывать тип комментария (повторяемый или нет), иначе функция вернет совсем не то, что от нее ожидается.

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


IDA - 0198.htm

198 Функции
long ChooseFunction (char title)
Создает диалоговое окно, содержащее список всех существующих функций с краткой сводной информацией о каждой из них.
Возвращает линейный адрес начала выбранной функции или BADADDR, если ни одна функция не была выбрана.
Пример использования:
> ввод пользователя:
Message("Ox%X \n",
ChooseFunction("L i s t")
) ;
< ответ IDA:
0x401020
IDA - 0198-1.jpg
Поле |
Function Name | Имя функции
Segment | Сегмент, владеющий функцией
Start | Линейный адрес начала
Length | Длина функции в байтах
 | Атрибут | Определение |
 | R | !FUNC_NORET | Функция, возвращающая управление
RFLSBMICDV | F | FUNC_FAR | FAR (Далекая) функция
 | L | FUNC_LIB | Библиотечная функция
 | S | FUNC_STATIC | Static-функция
 | В | FUNC_FRAME | BP используется как указатель на кадр стека


IDA - 0199.htm

Функции 199

Поле | Атрибут |  | Определение |
 | * | м | FUNC_MEMBER | member function
 | * | I | FUNC_VIRTUAL | Виртуальная функция
RFLSBMICDV | * | с | FUNC_CTR | Конструктор
 | * | D | FUNC_DTR | Деструктор
 | * | V | FUNC_VARARG | Функция с переменным числом аргументов
* Не поддерживается в текущих версиях. Зарезервировано для будущего использования.
Подробнее об атрибутах функции можно узнать в описании SetFunctionFlags.

Операнд | Пояснения
title | Заголовок диалогового окна
Return | Завершение | Пояснения |
 | !=BADADDR | Линейный адрес начала выбранной функции
 | BADADDR | Ошибка
По умолчанию подсвечивается функция, в границах которой находится курсор. В противном случае подсвечивается ближайшая функция, лежащая "выше" курсора (т.е. в более младших адресах).
Для поиска подстроки в именах функции предусмотрена специальная клавишная комбинация <Atl-T>. Регистр символов при этом будет игнорироваться. Для продолжения поиска необходимо нажать <Ctrl-T>.
В контекстной помощи сообщается, что с помощью клавиш <Ins> и <Delete> можно соответственно добавлять или удалять функции. Но на самом деле в данном случае эти возможности недоступны.
<Enter> или двойной щелчок мышью выбирают функцию и возвращают управление, закрывая диалоговое окно.
char GetFuncOffset (long еа)
Преобразует линейный адрес в строковое представление в следующем формате: ИмяФункции+СмещениеОтносительноНачалаФункции. Смещение представлено в шестнадцатиричном виде без префиксов и постфиксов.
Например:
копия экрана IDA:
.text:004010FF _amsg_exit proc near
.text:004010FF
.text:004010FF
.text:004010FF arg_0 = dword ptr 4
.text:004010FF
.text:004010FF cmp dword_0_408758, 2
.text:00401106 jz short loc_0_40110D
.text:00401108 call __FF_MSGBANNER
.text:0040110D
.text:0040110D loc_0_40110D:
.text:0040110D push [esp+arg_0]


IDA - 0200.htm

200 Функции
.text:0040l111 call __NMSG_WRITE
.text:00401116 push OFFh
.text:0040111B call off_0_408050
.text:00401121 pop ecx
.text:00401121 __amsg_exit endp
.text:00401121
.text:00401122 pop ecx
.text:00401123 retn
> ввод пользователя:
Message("%s \n",
GetFuncOffset(0x4 0110 8)
) ;
< ответ IDA:
_amsg_exit+9

Операнд | Пояснения
ea | Линейный адрес, принадлежащий хотя бы одной функции
Return | Завершение | Пояснения
 | !="" | Смещение относительно начала функции (строка)
 | .... | Ошибка
Если смещение относительно функции равно нулю, то вызов GetFuncOffset возвратит только одно имя.
long FindFuncEnd (long еа)
Описание этой функции, приведенное в контекстной помощи, немного запутанное, и с первого взгляда ее назначение не ясно. На самом деле она находит широкое применение в автономных скриптах. Основное ее назначение — определение линейного адреса при создании функции. Необходимо, чтобы адрес конца не превышал линейного адреса следующей за ней функции, поскольку функции перекрываться не могут.
Например:
G копия экрана IDA:
segOOO:22CO start:
segOOO:22CO call sub_0_22DD
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8
segOOO:22D2 jnz loc_0_22DA
segOOO:22D4 пор
segOOO:22D5 пор
segOOO:22D6 пор
segOOO:22D7 call sub_0_2412
segOOO:22DA
segOOO:22DA loc_0_22DA: ;


IDA - 0201.htm

______ Функции 201
segOOO:22DA call halt
segOOO:22DD
segOOO:22DD ; _________ S U B R 0 U T I N E _
segOOO:22DD
segOOO:22DD
segOOO:22DD sub_0_22DD proc near ;
segOOO:22DD call sub_0_28CC
Функция start не - завершается командой возврата ret. Вместо этого она прерывает выполнение программы процедурой Halt.
Если пытаться создать функцию, определяя линейный адрес ее конца поиском ret, то полученный адрес будет принадлежать функции sub_0_22DD! Следовательно, адрес конца не может превышать линейный адрес следующей функции.
Еще одна проблема заключается в отождествлении инструкции возврата. Это может быть и RETN, и RETF. Определение конца функции "вручную" оказывается слишком утомительно, и тогда стоит прибегнуть к вызову FindFuncEnd. Функция возвращает линейный адрес, на единицу больший линейного адреса конца функции, которая может быть успешно создана.
Таким образом, задача определения адреса конца функции для ее создания упрощается, тем более, что FindFuncEnd ищет не первую встретившуюся ей инструкцию возврата, а последнюю в цепочке перекрестных ссылок на следующую команду (подробнее об этом рассказано в главе "Перекрестные ссылки").
Следовательно, она поддерживает функции со множественными возвратами (а таких, как правило, большинство).
Например:
G копия экрана IDA:
segOOO:0100 start:
segOOO:0100 mov ax, 3D01h
segOOO:0103 mov dx, 10Fh
segOOO:0106 int 21h
segOOO:0106
segOOO:0106
segOOO:0106
segOOO:0108 jb loc_O_1OB
segOOO:010A retn
segOOO:010B ; ———————————————— — ————
segOOO:010B
segOOO:010B loc_0_10B:
segOOO:010B mov ax, OFFFFh
segOOO:010E retn
segOOO:010E ; -------------------------------------
segOOO:010F aMyfile db 'MyFile',0
> ввод пользователя:
Message("Ox%X \n",
FindFuncEnd(Oxl0103)
);
< ответ IDA:
0x1010F


IDA - 0202.htm

202___________________Функции_____________________
Обратите внимание, что IDA эмулировала выполнение команды условного перехода и правильно определила точку выхода из функции.
Однако, если быть "буквоедом", то можно заметить, что строка aMyFile, вероятнее всего, принадлежала функции, но IDA автоматически не смогла этого распознать. Поэтому иногда результат работы функции все же приходится корректировать.
Очень важно, чтобы — линейный адрес указывал на начало команды, иначе вызов провалится.
Например:
> ввод пользователя:
Message("Ox%X \n",
FindFuncEnd(0 x1010 2)
);
< ответ IDA:
0xFFFFFFFF
То же самое происходит, если по указанному адресу расположены данные, поэтому функцию создать невозможно.
Если функция уже существует, то вызов FindFuncEnd возвратит адрес ее конца:
копия экрана IDA:
segOOO:0100 start proc near
segOOO:0100 mov ax, 3D01h
segOOO:0103 mov dx, 10Fh
segOOO:0106 int 21h
segOOO:0106
segOOO:0106
segOOO:0106
segOOO:0108 jb loc_0_10B
segOOO:010A retn
segOOO:010B ; ---------------------------------—
segOOO:010B
segOOO:010B loc_0_10B:
segOOO:010B mov ax, OFFFFh
segOOO:010E retn
segOOO:010E ; ------------------------------------
segOOO:010F aMyFile db 'MyFile',0
segOOO:010F start endp
> ввод пользователя:
Message("Ox%X \n",
FindFuncEnd(0 x1010 3)
);
< ответ IDA:
0x10116
То, что последний отображаемый адрес равен 0x10F, — это небольшой баг IDA. На самом деле это адрес начала строки, но не ее конца. Как нетрудно


IDA - 0203.htm

Функции 203
вычислить, адрес конца строки равен 0x115, следовательно, функция FindFuncEnd
работает правильно.
В описании этой функции в IDC.IDC утверждается, что требуется передать линейный адрес начала функции, но это не так. С таким же успехом функция принимает любой принадлежащий ей адрес, если он приходится на начало инструкции.

Операнд | Пояснения
еа | Линейный адрес конца функции
Return | Завершение | Пояснения
 | !=BADADDR | ID структуры, обеспечивающий доступ к локальным пе-
 |  | ременным и аргументам
 | BADADDR | Ошибка
long GetFrame (long еа)
Возвращает ID фрейма функции (если он есть) или BADADDR (в противном случае). Это значение может интерпретироваться только IDA и с точки зрения пользователя лишено всякого смысла (как и всякий дескриптор).
Все локальные переменные и аргументы объединены в одну структуру, с которой можно работать с помощью функций, описанных в разделе "Структуры".
Если функция не содержит ни одной локальной переменной и не имеет ни одного аргумента, то вызов GetFrame возвратит ошибку BADADDR.
Пример использования:
копия экрана IDA:
.text:004010FF __amsg_exit proc near
.text:004010FF
.text:004010FF arg_0 = dword ptr 4
.text:004010FF
.text:004010FF cmp dword_0_408758, 2
.text:00401106 jz short loc_0_40110D
.text:00401108 call __FF_MSGBANNER
.text:0040110D
.text:0040110D loc_0_40110D:
.text:0040110D push [esp+arg_0]
.text:00401111 call __NMSG_WRITE
.text:00401116 push OFFh
.text:0040111B call off_0_408050
.text:00401121 pop ecx
.text:00401122 pop ecx
.text:00401123 retn
.text:00401123 __amsg_exit endp
> ввод пользователя:
Message("%x \n",
GetFrame(Ox40110D)
);
< ответ IDA:
ff000162


IDA - 0204.htm

204 Функции

Операнд | Пояснения
еа | Линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | !=BADADDR | ID структуры, обеспечивающий доступ | к локальным пере-
 |  | менным и аргументам |
 | BADADDR | Ошибка
long GetFrameLvarSize (long ea)
Возвращает размер локальных переменных функции (в байтах). Если функция не имеет локальных переменных, то возвращается ноль. Если указанный адрес не принадлежит ни одной функции, возвращается ошибка BADADDR.
Например:
копия экрана IDA:
.text:00401806 __ioinit proc near
.text:00401806 var_44 = byte ptr -44h
.text:00401806 var_12 = word ptr -12h
.text:00401806 var_10 = dword ptr -10h
.text:00401806
.text:00401806 sub esp, 44h
.text:00401809 push ebx
.text:0040180A push ebp
> ввод пользователя:
Message("Ox%X \n",
GetFrameLvarSize(0x4018 0 9)
) ;
< ответ IDA:
0x44

Операнд | Пояснения
еа | Линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | !=0 | Размер локальных переменных функции в | байтах
 | !=BADADDR |  |
 | 0 | Функция не имеет локальных переменных
 | BADADDR | Ошибка
long GetFrameRegsSize (long еа)
Возвращает размер сохраненных в стековом фрейме регистров. Для 32-разрядных программ он равен четырем (четыре байта на регистр), для 16-разрядных — двум (два байта на регистр).
Если функция не имеет кадра стека, то возвращается ноль, если указанный адрес не принадлежит ни одной функции — возвращается BADADDR.
Пример использования:


IDA - 0205.htm

Функции 205
копия экрана IDA:
.text:0040124A __XcptFilter proc near
.text:0040124A
.text:0040124A arg_0 = dword ptr 8
.text:0040124A arg_4 = dword ptr OCh
.text:0040124A
.text:0040124A push ebp
.text:0040124B mov ebp, esp
.text:0040124D push ebx
.text:0040124E push [ebp+arg_0]
> ввод пользователя:
Message("Ox%X \n",
GetFrameRegsSize(Ox40124A)
);
< ответ IDA:
4
копия экрана IDA:
segOOO:2092 sub_0_2092 proc far
segOOO:2092
segOOO:2092 var_40 = byte ptr -40h
segOOO:2092
segOOO:2092 push bp
segOOO:2093 mov bp, sp
> ввод пользователя:
Message("Ox%X \n",
GetFrameRegsSize(0x12 093)
);
< ответ IDA:
2

Операнд | Пояснения
ea | Линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | !=0 | Размер сохраненных регистров | в стековом фрейме
 | !=BADADDR |  |
 | 0 | Функция не имеет кадра стека
 | BADADDR | Ошибка
long GetFrameArgsSize (long еа)
Возвращает размер (в байтах) аргументов, передаваемых функции. IDA определяет эту величину на основании анализа команд, очищающих стек от локальных переменных. Обычно компиляторы используют для этого два принципиально различных соглашения.
В Паскаль соглашение предписывает функции самой очищать стек от локальных переменных перед возвратом из функции. Независимо от способа реализации


IDA - 0206.htm

206 Функции
после возврата из функции стек должен быть сбалансирован. На платформе Intel
практически всегда для этого используют команду RET N, где N — число байт,
которые нужно вытолкнуть с верхушки стека после возврата. В этом случае IDA
возвращает аргумент, стоящий после RET.
Например:
Pascal_func:
Push bp
Mov bp,sp
Mov ax,[BP+4]
RET2
Endp
PUSH 10
CALL Pascal_func
В Си соглашение предписывает очищать локальные переменные вызываемому коду. При выходе из функции стек остается несбалансированным, поэтому необходимо скорректировать его верхушку.
Долгое время неоптимизирующие компиляторы использовали для этого команду ADD SP, N, где N — размер аргументов в байтах. Очевидно, что IDA также без проблем может распознать такую ситуацию.
Например:
C_func:
Push bp
Mov bp,sp
Mov ax,[BP+4]
RET
Endp
PUSH 10
CALL C_func
ADD SP,2
Но с появлением оптимизирующих компиляторов все изменилось. Они могут выталкивать аргументы командой POP в неиспользуемые регистры или вовсе оставлять стек несбалансированным на то время, пока к нему нет обращений, поэтому в определении размера аргументов возможны ошибки.
C_optimize_func:
Push bp
Mov bp,sp
Mov ax,[BP+4]
RET
Endp
PUSH 10
CALL C_optimize_func
OR AX,AX
JZ xxx
MOV AX,[BX]
Xxx:


IDA - 0207.htm

Функции 207
POP AX
RET
Даже для человека с первого взгляда неочевидно назначение команды 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 offset arb
PUSH offset aMyFile
CALL open
PUSH AX
PUSH [10]
PUSH buff
CALL read
ADD SP,4
PUSH 1
PUSH 20
CALL seek
ADD SP,10


IDA - 0208.htm

208 Функции
Разобраться в том, сколько аргументов принимает каждая функция, прибегая только к анализу балансировки стека, абсолютно невозможно! После выхода из первой функции стек остается несбалансированным. Вторая функция очищает только два аргумента, оставляя в стеке дескриптор файла для последующих функций. "Отдуваться" за все это приходится третьей функции, которая выталкивает из стека пять слов! Но на самом деле размер аргументов гораздо скромнее.
Можно, конечно, попытаться отслеживать аргументы, к которым функция обращается и, выбрав из них тот, что лежит "внизу", вычислить на основе этого размер аргументов. Однако такой подход предполагает, что функции известно число переданных ей аргументов, что в случае с Си-компиляторами неверно. Перенос заботы об очистке стека с самой функции на вызывающий ее код объясняется тем, что в Си-функции точно не знают, сколько параметров было передано. Можно только удивляться, что даже на оптимизированном коде IDA сравнительно редко ошибается.

Операнд | Пояснения
еа | Линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | !=0 | Размер аргументов в байтах
 | !=BADADDR |
 | 0 | Функция не имеет кадра стека
 | BADADDR | Ошибка
long GetFrameSize (long ea)
Возвращает полный размер стекового фрейма в байтах. Он вычисляется по
следующей формуле:
FrameSize == FrameLvarSize + FrameArgsSize + FrameRegsSize +
+ ReturnAddresSize
Таким образом, размер файла равен сумме размеров локальных переменных, аргументов, сохраненных в стеке регистров и адреса возврата.
Подробнее о каждой из них можно прочитать в описании функций GetFrameLvarSize, GetFrameArgsSize, GetFrameRegsSize.
Специальной функции, возвращающей значение размера адреса возврата, не существует, однако этот размер может быть вычислен по следующей формуле:
ReturnAddresSize == FrameSize — FrameLvarSize + FrameArgsSize +
__________________+ FrameRegsSize__________________
Поскольку в стековый фрейм входит и адрес возврата, то независимо от того,
имеет ли функция локальные переменные или нет, он не может быть равен нулю.
Примеры использования:
копия экрана IDA:
segOOO:0000 start proc near
segOOO:0000 call sub_O_A


IDA - 0209.htm

Функции 209
segOOO:0003 call sub_0_10
segOOO:0006 call sub_0_16
segOOO:0009 retn
segOOO:0009 start endp
> ввод пользователя:
Message("Ox%X \n",
GetFrameSize(OxlOOOO)
);
< ответ IDA:
2
копия экрана IDA:
segOOO:0010 sub_0_10 proc near
segOOO:0010 push bp
segOOO:0011 push ax
segOOO:0012 mov bp, sp
segOOO:0014 pop bp
segOOO:0015 retn
segOOO:0015 sub_0_10 endp ; sp = -2
> ввод пользователя:
Message("Ox%X \n",
GetFrameSize(Oxl0010)
);
< ответ IDA:
6
> ввод пользователя:
Message("Ox%X \n",
GetFrameRegsSize(0x10 010)
);
< ответ IDA:
4
Как видно, в последнем случае стековый фрейм состоит из адреса возврата и сохраненных в стеке регистров. Однако, если команды расположить по-другому, то результат изменится:
копия экрана IDA:
segOOO:OOOA sub_O_A proc near
segOOO:OOOA push bp
segOOO:OOOB mov bp, sp
segOOO:OOOD push ax
segOOO:OOOE pop bp
segOOO:OOOF retn
segOOO:OOOF sub_O_A endp ; sp = -2
> ввод пользователя:
Message("Ox%X \n",
GetFrameSize(OxlOOOA)
);


IDA - 0210.htm

210 Функции
<ответ IDА:
4
> ввод пользователя:
Message("Ox%X \n",
GetFrameRegsSize(OxlOOOA)
);
< ответ IDA:
2
Все команды, лежащие "ниже" (в более старших адресах) относительно команды mov bp, sp, которая и определяет стековый фрейм, в него не попадают, и можно безболезненно заносить (выталкивать) команды из стека, не боясь разрушить стековый фрейм.

Операнд | Пояснения
еа | Линейный адрес, принадлежащий функции
Return | Завершение | Пояснения
 | !-BADADDR | Размер стекового фрейма | в байтах
 | BADADDR | Ошибка
long MakeFrame (long ea, long lvsize, long frregs, long argsize)
Создает фрейм стека функции или модифицирует уже существующий. Для этого достаточно указать любой адрес, принадлежащий функции, и размеры области для локальных переменных, сохраненных регистров и аргументов.
Они расположены во фрейме в следующей последовательности:
Стековый фрейм
Локальные переменные
Сохраненные регистры
Аргументы, переданные функции
Адрес возврата из функции
При успешном завершении функция возвращает ID структуры, обеспечивающей доступ ко всем вышеперечисленным элементам. В противном случае функция вернет ошибку BADADDR.

Операнд | Пояснения
еа | Любой линейный адрес, принадлежащий функции
lvsize | Размер локальных переменных в стековом фрейме
frrgs | Размер сохраненных регистров в стековом фрейме
argsize | Размер аргументов, передаваемых функции
Return | Завершение | Пояснения
 | !=BADADDR | ID структуры, обеспечивающей доступ ко всем элемен-
 |  | там стекового фрейма
 | BADADDR | Ошибка


IDA - 0211.htm

__ Функции 211
Модифицирование уже существующего фрейма стека может повлечь за собой
удаление локальных переменных.
Например:
копия экрана IDA:
.text:00401487 _setargv proc near
.text:00401487
.text:00401487 var_8 = dword ptr -8
.text:00401487 var_4 = dword ptr -4
.text:00401487
.text:00401487 push ebp
.text:00401488 mov ebp, esp
.text:0040148A push ecx
.text:0040148B push ecx
> ввод пользователя:
MakeFrame(Ox401487,0,0,0);
копия экрана IDA:
.text:00401487 _setargv proc near
.text:00401487 push ebp
.text:00401488 mov ebp, esp
.text:0040148A push ecx
.text:0040148B push ecx
.text:0040148C push ebx
Аргументы функции никогда не удаляются из стекового фрейма, даже при уменьшении размера выделенного для них региона до нуля. Однако это нарушает целостность всего фрейма и локальных переменных, лежащих "выше":
копия экрана IDA:
.text:00401520 sub_0_401520 proc near
.text:00401520
.text:00401520
.text:00401520 arg_0 = dword ptr 8
.text:00401520 arg_4 = dword ptr OCh
.text:00401520 arg_8 = dword ptr 10h
.text:00401520 arg_C = dword ptr 14h
.text:00401520 arg_10 = dword ptr 18h
.text:00401520
.text:00401520 push ebp
.text:00401521 mov ebp, esp
.text:00401523 mov ecx, [ebp+arg_10]
.text:00401526 mov eax, [ebp+arg_C]
.text:00401529 push ebx
> ввод пользователя:
MakeFrame(Ox401520,0,0,0);
копия экрана IDA:
.text:00401520 sub_0_401520 proc near
.text:00401520
.text:00401520
.text:00401520 arg_0 = dword ptr 8


IDA - 0212.htm

212 Функции

.text: 00401520 arg_4 | = dword ptr OCh
.text: 00401520 arg_8 | = dword ptr 10h
.text: 00401520 arg_C | = dword ptr 14h
.text: 00401520 arg JO | = dword ptr 18h
.text: 00401520 |
.text: 00401520 | push ebp
.text: 00401521 | mov ebp, esp
.text: 00401523 | mov ecx, [ebp+arg_10]
.text: 00401526 | mov eax, [ebp+arg_C]
.text: 00401529 | push ebx
long GetSpd (long ea)
Возвращает значение регистра SP (ESP) в произвольной точке функции относительно его оригинального значения.
IDA использует достаточно простой алгоритм, отслеживающий только основные команды, модифицирующие стековый регистр.
Для специфичных случаев предусмотрена ручная коррекция (см. описание функции SetSpDiff), но в большинстве случаев IDA и сама справляется с этой задачей.
Пример использования:
копия экрана IDA:
.text:004010FF _amsg_exit proc near
.text:004010FF
.text:004010FF
.text:004010FF arg_0 = dword ptr 4
.text:004010FF
.text:004010FF cmp dword_0_408758, 2
.text:00401106 jz short loc_0_40110D
.text:00401108 call __FF_MSGBANNER
.text:0040110D
.text:0040110D loc__0_40110D:
.text:0040110D push [esp+arg_0]
.text:00401111 call __NMSG_WRITE
.text:00401116 push OFFh
.text:0040111B call off_0_408050
.text:00401121 pop ecx
.text:00401122 pop ecx
.text:00401123 retn
.text:00401123 __amsg_exit endp
.text:00401123
> ввод пользователя:
Message("%d \n", GetSpd(Ox4010FF) )/
< ответ IDA:
0


IDA - 0213.htm

Функции 213
> ввод пользователя:
Message("%d \n", GetSpd(Ox401111) ) ;
< ответ IDA:
-4
> ввод пользователя:
Message("%d \n", GetSpd(Ox401116) );
< ответ IDA:
-8
> ввод пользователя:
Message("%d \n", GetSpd(Ox401122) );
< ответ IDA:
-4
> ввод пользователя:
Message("%d \n", GetSpd(Ox401123) );
< ответ IDA:
0
В точке входа в функцию значение SP (ESP) всегда равно нулю. Затем, в нашем примере, оно изменяется командой push, заносящей в стек двойное слово.
Обратите внимание, что значение ESP изменяется только после завершения команды, т.е. с адреса начала следующей.

Относительное значение ESP | Адрес | Инструкция
0 | .text: 00401 10D | push [esp+arg_0]
-4 | .text: 00401 111 | call __NMSG_WRITE
В точке выхода из функции значение SP (ESP) также должно равняться нулю, иначе стек будет несбалансированным, и команда возврата вытолкнет из стека не адрес возврата, а что-то совсем иное.
В таком случае вероятнее всего, что IDA не смогла отследить все инструкции, модифицирующие значения стекового регистра (или сделала это неправильно). Рекомендуется обнаружить это место и скорректировать его вызовом SetSpDiff.

Операнд | Пояснения
еа | Линейный адрес в теле функции
Return | Относительное значение стекового регистра SP (ESP)


IDA - 0214.htm

214 Функции
long GetSpDiff (long ea)
Возвращает относительное изменение стекового регистра SP (ESP) командой,
расположенной по линейному адресу 'ea'. Например:
G копия экрана IDA:
.text:004010FF _amsg_exit proc near
.text:004010FF
.text:004010FF
.text:004010FF arg_0 = dword ptr 4
.text:004010FF
.text:004010FF cmp dword_0_408758, 2
.text:00401106 jz short loc_0_40110D
.text:00401108 call __FF_MSGBANNER
.text:0040110D
.text:0040110D loc_0_40110D:
.text:0040110D push [esp+arg_0]
.text:00401111 call __NMSG_WRITE
.text:00401116 push OFFh
.text:0040111B call off_0_408050
.text:00401121 pop ecx
.text:00401122 pop ecx
.text:00401123 retn
.text:00401123 __amsg_exit endp
> ввод пользователя:
Message("%d \n",
GetSpDiff(Ox4010FF)
) ;
< ответ IDA:
0
> ввод пользователя:
Message("%d \n",
GetSpDiff(0x401111)
);
< ответ IDA:
-4
> ввод пользователя:
Message("%d \n",
GetSpDiff(Ox401116)
);
< ответ IDA:
-8
> ввод пользователя:
Message("%d \n",
GetSpDiff(0x401122)
) ;
< ответ IDA:
-4


IDA - 0215.htm

Функции 215
> ввод пользователя:
Message("%d \n",
GetSpDiff(Ox401123)
);
< ответ IDA:
0

Относительное значение ESP | Адрес | Инструкция
0 | .text:0040110D | push [esp+arg_0]
-4 | .text: 004011 11 | call __NMSG_WRITE
Как и в случае с GetSpd, необходимо задавать адрес начала следующей
команды или, точнее, конца текущей.

Операнд | Пояснения
еа | Линейный адрес конца команды в теле функции
Return | Относительное изменение стекового регистра SP (ESP)
success SetSpDiff (long ea, long delta)
Задает изменение стекового регистра SP (ESP) командой, лежащей по указанному линейному адресу. IDA использует достаточно простой алгоритм отслеживания SP (ESP), который не учитывает ряда особенностей некоторых экзотических команд. В настоящее время этот механизм настолько усовершенствован, что практически невозможно придумать, в каком случае команда SetSpDiff могла бы оказаться полезной. Возьмем следующий, достаточно надуманный пример:
копия экрана IDA:
segOOO:0000 000 public start
segOOO:0000 start proc near
segOOO:0000 push ax
segOOO:0001 002 push ax
segOOO:0002 004 push bp
segOOO:0003 006 mov bp, sp
segOOO:0005 006 mov word ptr [bp+2], 2
segOOO:OOOA 006 pop bp
segOOO:OOOB 004 mov bp, sp
segOOO:OOOD 004 mov cx, [bp+0]
segOOO:0010 004 pop ax
segOOO:0011 002 add sp, cx
segOOO:0013 002 push ax
segOOO:0014 004 add sp, cx
segOOO:0016 004 retn
segOOO:0016 start endp ; sp = -4
> ввод пользователя:
Message("%d \n",
GetSpDiff(Oxl0013) );
< ответ IDA:
0


IDA - 0216.htm

216 Функции
> Ввод пользователя:
Message("%d \n",
GetSpDiff(Oxl0016)
);
< ответ IDA:
0
Чтобы узнать значение SP после завершения команды add sp, cx, IDA, очевидно, должна знать, чему равен регистр CX. Чтобы его отследить, пришлось бы включать в дизассемблер полноценный эмулятор процессора 0x86. Пока это не реализовано, и IDA предполагает, что значение CX равно нулю и, таким образом, уже неправильно определяет значение SP во всех нижележащих точках функции.
Исправить положение можно ручной коррекцией значения SP. Функция SetSpDiff задает изменение регистра SP после выполнения команды. Для этого необходимо передать линейный адрес конца, а не начала команды.
В нашем случае необходимо скорректировать величину изменения SP командами ADD SP, CX, расположенными по адресам segOOO:0011 и segOOO:0014. Линейные адреса команд соответственно равны segOOO:OOl3 и segOOO:0016. Их и необходимо передать функции вместе с действительной величиной изменения SP.
> ввод пользователя:
SetSpDiff(Oxl0013,2);
SetSpDiff(Oxl0016,2);
G копия экрана IDA:
segOOO:0000 000 public start
segOOO:0000 start proc near
segOOO:0000 push ax
segOOO:0001 002 push ax
segOOO:0002 004 push bp
segOOO:0003 006 mov bp, sp
segOOO:0005 006 mov word ptr [bp+2], 2
segOOO:OOOA 006 pop bp
segOOO:OOOB 004 mov bp, sp
segOOO:OOOD 004 mov cx, [bp+0]
segOOO:0010 004 pop ax
segOOO:0011 002 add sp, cx
segOOO:0013 000 push ax
segOOO:0014 002 add sp, cx
segOOO:0016 000 retn
segOOO:0016 start endp

Операнд | Пояснения
еа | Линейный адрес конца команды
Delta | Величина изменения SP указанной командой
Return | Завершение | Пояснения
 | 1 | Успешно
 | 0 | Ошибка


IDA - 0217.htm

Перекрестные ссылки 217
Перекрестные ссылки
Что такое перекрестные ссылки?
Долгое время SOURCER лидировал среди других дизассемблеров в умении находить и восстанавливать перекрестные ссылки. На этом, правда, его основные достоинства заканчивались, но все равно он активно использовался для исследования программного обеспечения.
Что же такое перекрестные ссылки и почему они так важны для популярности дизассемблера?
Рассмотрим простейший случай. Исходный файл выглядел так:
.MODEL TINY
.CODE
ORG 100h
Start:
MOV AH,9
LEA DX,sO
INT 21h
RET
sO DB "Hello, Sailor!",ODh,OAh,'S'
END Start
После дизассемблирования он будет выглядеть следующим образом:
segOOO:0100 start proc near
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHelloSailor ; "Hello, Sailor!\r\n$"
segOOO:0105 int 21h
segOOO:0105
segOOO:0107 retn
segOOO:0107 start endp
segOOO:0107
segOOO:0107 ; ------------------------------------------------------- —— ____________
segOOO:0108 aHelloSailor db 'Hello, Sailor!',ODh,OAh,'S'
segOOO:0108 segOOO ends
Допустим, мы хотим узнать, какой код выводит эту строку на экран. Когда-то для этого приходилось кропотливо изучать весь листинг, и то не было шансов, что с первого раза выводящий строку код удастся рассмотреть. Поэтому эту задачу возложили на дизассемблер, чтобы машина сама анализировала выполнение программы и восстанавливала все связи. Это невероятно упростило анализ программ, как впрочем, и взлом защит. Стало достаточно найти строку, которую защита выводит в случае неудачного завершения проверки (ключевой дискеты или пароля — неважно), как дизассемблер поможет мгновенно найти выводящий ее код, а значит и локализовать местонахождение защиты, какой бы длинной


IDA - 0218.htm

218 Перекрестные ссылки
программа не оказалась. Как правило, неподалеку расположен условный переход, передающий управление этой ветви. Стоит изменить его на противоположный — защиту можно считать взломанной.
Но ведь не для хакерства были придуманы перекрестные ссылки! Помощь хакерам — это побочный эффект (хотя и очень приятный для них). Значительно важнее поддержка перекрестных ссылок, чтобы правильно дизассемблировать код!
Покажем это на следующем примере:
.MODEL TINY
.CODE
ORG 100h
Start:
LEA AX,sO
PUSH AX
CALL Print
RET
sO DB 'Hello, Sailor!',ODh,OAh,'S'
Print:
POP AX
POP DX
PUSH AX
MOV AH,9
INT 21h
RET
END Start
Первые четыре строки любой дизассемблер разберет без труда. Но вот дальше начнутся сложности. Как узнать, что следом идет текстовая строка, а не исполняемый код? Если дизассемблировать ее как код, ничем не руководствуясь при этом, то полученный результат будет похож на бред, и вся программа окажется дизассемблированной неправильно. Поэтому приходится эмулировать ее исполнение и отслеживать все косвенные и непосредственные переходы. Если ассемблер сумеет распознать вызов подпрограммы, то ссылку на строку он восстановит с куда большей легкостью.
Однако тут скрывается один подводный камень. Эмуляция (даже частичная) требует больших накладных расходов, и если каждый раз ее выполнять "на лету", то никаких вычислительных ресурсов не хватит для нормальной работы!
Поэтому приходится прибегать к компиляции. Да, именно к компиляции. Ведь компиляция — это перевод с одного языка в другой, необязательно в машинные коды. В данном случае запутанный язык ассемблера (а точнее, одних лишь ссылок) преобразуется к максимально производительной и компактной форме записи. Другими словами, перекрестные ссылки — это сохраненный результат работы эмулятора.
Кроме того, если выполнение программы однонаправленно, т.е. часто невозможно сказать, выполнение какой инструкции предшествовало текущей, то перекрестные ссылки предоставляют такую возможность! Можно начать изучение программы с любой точки, свободно двигаясь по ходу ее исполнения как


IDA - 0219.htm

Перекрестные ссылки 219
назад, так и вперед. Это в самом деле очень удобно — ведь в большинстве случаев не требуется изучать всю программу целиком, — достаточно изучить один из ее компонентов. Например, тот, что отвечает за взаимодействие с интересующим вас форматом файла.
Предположим, что в заголовке находится некая сигнатура, которая проверяется исследуемой программой и находится в дизассемблируемом листинге в "прямом виде". Тогда можно по перекрестным ссылкам перейти на процедуру загрузки файла и начать ее изучение. И это не единственный пример. Перекрестные ссылки активно используются при дизассемблировании программ любой сложности и относятся к незаменимым компонентам дизассемблера.
Однако, как нетрудно догадаться, гарантированно можно отслеживать только непосредственные ссылки, такие как CALL 0x666, а вот MOV DX,Ox777 может быть как смещением, так и константой. Про инструкции типа CALL BX говорить и вовсе не приходится — для вычисления значения регистра BX потребуется мощный эмулятор процессора.
Удивительно, что большинство ассемблеров отслеживало перекрестные ссылки из рук вон плохо. Даже первые версии IDA не были совершенны в этом плане: поддержка перекрестных ссылок имелась, но их созданием приходилось заниматься человеку (ведь IDA изначально планировалась как интерактивная среда!), а не машине. С течением времени ситуация изменялась, и интеллектуальные механизмы IDA улучшались. С версии 3.7 она уже значительно превосходила в этом отношении все остальные существующие в мире дизассемблеры, включая SOURCER, и продолжала совершенствоваться.
ALMA MATER
Из предыдущей главы можно сделать вывод, что реально поддержка того, что подразумевается под термином "перекрестные ссылки", состоит как минимум из механизма отслеживания перекрестных ссылок и механизма работы с перекрестными ссылками, сохраненными в некотором внутреннем формате дизассемблера.
Устройство и способности интеллектуального анализатора IDA — тема для отдельного разговора. Остановимся исключительно на работе с уже созданными перекрестными ссылками.
Если перекрестная ссылка уже создана, то какие могут быть проблемы? На самом деле все не так просто. Перекрестная ссылка может быть создана (и при этом не в единственном числе), а может быть и нет. Как узнать это наверняка? И как получить адрес, на который перекрестная ссылка ссылается? Для этого и предусмотрено почти два десятка функций, поддерживающих работоспособность IDA. Все они ниже будут подробно рассмотрены, но сначала поговорим о механизмах взаимодействия с перекрестными ссылками, не углубляясь в детали.
Итак, любая перекрестная ссылка состоит из источника и приемника. В контекстной помощи IDA источник обозначается как 'from', а приемник — как 'to'.
Источником называется операнд, ссылающийся на приемник, но не наоборот! Разберем в свете этого приведенный выше пример:


IDA - 0220.htm

220 Перекрестные ссылки
3. .MODEL TINY
4. .CODE
5. ORG 100h
6. Start:
7. MOV AH,9 ;
8. LEA DX,sO ; =>
9. INT 21h ;
10. RET ;
11. sO DB "Hello, Sailor!",ODh,OAh,'S' ; <=
12. END Start
Так, в строке 8 должен быть создан источник перекрестной ссылки, а в строке 11 — приемник.
Часто вызывает путаницу, что IDA создает комментарий к перекрестной ссылке только возле приемника. Это, разумеется, правильно, потому что источник в комментариях не нуждается — в большинстве случаев очевидно, на что ссылается операнд (хотя в случае инструкций подобных CALL BX, этого сказать нельзя), а вот по виду приемника источник установить невозможно.
Вот IDA и отображает его в виде комментария:
segOOO:0100 public start
segOOO:0100 start proc near
segOOO:0100 mov ah, 9
segOOO:0102 mov dx, offset aHelloSailor
segOOO:0105 int 21h
segOOO:0105
segOOO:0107 retn
segOOO:0107 start endp
segOOO:0107
segOOO:0107 ; ------------------------------------------------------
segOOO:0108aHelloSailor db 'Hello, Sailor!',ODh,OAh,'S' ; DATAXREF: start+2o
segOOO:0108 segOOO ends
Обратите внимание, что IDA ничем не выделила строку 0x102 — создается иллюзия, что здесь ничего нет. Но на самом деле именно с этим адресом и связана перекрестная ссылка, а точнее, ее источник.
Практически для всех манипуляций с перекрестными ссылками нужно знать пару значений — линейные адреса источника и приемника. Впрочем, есть и такие функции, что возвращают источник (источники) для указанного адреса приемника. Но о них поговорим позднее, а пока остановимся на том факте, что одновременно по одному и тому же адресу может располагаться несколько как приемников, так и источников.
Начнем с приемника, как более простого для понимания.
segOOO:0100 org 100h
segOOO:0100 assume es:nothing, ss:nothing, ds:segOOO, fs:nothing, gs:nothing
segOOO:0100
segOOO:0100 public start
segOOO:0100 start: ; "Hello, World!\r\n$"
segOOO:0100 push offset aHelloWorld
segOOO:0103 call Print


IDA - 0221.htm

________________Перекрестные ссылки______________221
segOOO:0106 push offset aHelloSailor ; "Hello, Sailor!\r\n$"
segOOO:0109 call Print
segOOO:010C retn
segOOO:010D
segOOO:010D; ________ S U B R 0 U T I N E ______________________________________
segOOO:010D
segOOO:010D
segOOO:010DPrint proc near ; CODE XREF: segOOO:0103p
segOOO:010D ; segOOO:0109p
segOOO:010D pop ax
segOOO:010E pop dx
segOOO:010F push ax
segOOO:0110 mov ah, 9
segOOO:0112 int 21h ; DOS - PRINT STRING
segOOO:0112 ; DS:DX -> string terminated by "$"
segOOO:0114 retn
segOOO:0114 Print endp ; sp = 2
segOOO:0114
segOOO:0114 ; — — — — — — — — ————————————— ----- ———— —— — _ ————
segOOO:0115aHelloWorld db 'Hello, World!',ODh,OAh,'S' ; DATA XREF: segOOO:0100o
segOOO:0125aHelloSailor db 'Hello, Sailor!',ODh,OAh,'S' ; DATA XREF: segOOO:0106o
segOOO:0125 segOOO ends
-0001010D: sub_0_10D
B этом примере процедура Print вызывалась из двух точек кода, о чем свидетельствуют две перекрестные ссылки, проставленные IDA как комментарии. Следовательно, один приемник может иметь и более одного источника.
Чтобы изучить вызывающий эту процедуру код, достаточно подвести курсор в границы адреса, указанного в перекрестной ссылке, и нажать <Enter>, а при желании возвратиться назад по <Esc>.
Можно переместить курсор в любое место строки 0x10D и выбрать пункт меню ~ View \ Cross references. Появится окно следующего вида:
IDA - 0221-1.jpg
IDA - 0221-2.jpg


IDA - 0222.htm

222 Перекрестные ссылки
Поскольку довольно часто на один приемник ссылаются десятки (а то и больше!) различных источников, то IDA считает нерациональным отображать их в виде комментариев и показывает по умолчанию лишь две первые ссылки (перекрестные ссылки отсортированы по линейным адресам источников — от младших адресов к старшим). Просматривать остальные ссылки приходится именно в таком окне.
Как программно получить линейный адрес источника — описано ниже (поскольку это делается по-разному в зависимости от типов перекрестных ссылок).
Рассмотрим ситуацию, когда один источник имеет более одного приемника. С первого взгляда это абсурдно, но если выйти за рамки непосредственных операндов, то можно вообразить себе следующую ситуацию:
segOOO:0002 mov ds, ax
segOOO:0004 assume ds:segOOO
segOOO:0004 mov ah, 6
segOOO:0006 mov di, offset off_0_25
segOOO:0009 jmp short Print
segOOO:0009 ; --------- ———— — —— — — — — — — — — — — —
segOOO:OOOC
segOOO:OOOC Def_1: ; CODE XREF: start+1Bu
segOOO:OOOC ; DATA XREF: segOOO:0025o
segOOO:OOOC mov dl, 31h ; '1'
segOOO:OOOE retn
segOOO:OOOF ; --------------- ——— --__--_-__---______-__-___-_...
segOOO:OOOF
segOOO:OOOF Def_2: ; CODE XREF: start+1Bu
segOOO:OOOF ; DATA XREF: segOOO:0027o
segOOO:OOOF mov dl, 32h ; '2'
segOOO:0011 retn
segOOO:0012 ; ----------------------- — ------------------------ —
segOOO:0012
segOOO:0012 Print: ; CODE XREF: start+9j
segOOO:0012 ; start+1Fj
segOOO:0012 mov bx, [di]
segOOO:0014 add di, 2
segOOO:0017 or bx, bx
segOOO:0019 jz loc_0_21
segOOO:001B call bx
segOOO:001D int 21h
segOOO:001F jmp short Print
segOOO:0021 ; ------------------------------------------------------
segOOO:0021
segOOO:0021 loc_0_21: ; CODE XREF: start+19j
segOOO:0021 mov ah, 4Ch
segOOO:0023 int 21h
segOOO:0023 start endp ; AL = exit code
segOOO:0023
segOOO:0023 ; ----------------------------------------------------
segOOO:0025 off_0_25 dw offset Def_1 ; DATA XREF: start+6o
segOOO:0027 dw offset Def_2


IDA - 0223.htm

Перекрестные ссылки 223
segOOO:0029 dw offset def_3
segOOO:002B dw offset def_4
segOOO:002D dw 0
segOOO:002F ; -----------------------------------------------------
segOOO:002F
segOOO:002F def_3: ; CODE XREF: start+1Bu
segOOO:002F ; DATA XREF: segOOO:0029o
segOOO:002F mov dl, '3'
segOOO:0031 retn
segOOO:0032 def_4:
segOOO:0032
segOOO:0032 mov dl, '4'
segOOO:0034 retn
Подобные примеры не редкость.
Обратим внимание на следующую строку:
segOOO:OOlB call bx
Она осуществляет последовательную передачу управления функциям, читаемым в цикле из списка, а, следовательно, ссылается более чем на один адрес.
К сожалению, пока IDA не умеет автоматически вычислять значение регистра BX и, следовательно, не может создать перекрестных ссылок или дизассемблировать вызываемые этой строкой функции. Скорее всего они будут помечены как 'unexplored'.
Поэтому эта часть работы ложится на плечи пользователя. Часто при этом дизассемблируют код, но забывают создать перекрестные ссылки. Вернувшись к дизассемблируемому файлу спустя некоторое время, вы уже не будете помнить, какой код вызывает эти функции, и анализ придется начинать сначала.
Архитектура перекрестных ссылок
В предыдущей главе говорилось о том, что, независимо от типа, перекрестная ссылка состоит из двух разных частей — источника и приемника. Каждый из них связан с определенным линейным адресом. Причем с любым адресом может быть связано одновременно как несколько приемников, так и несколько источников.
Другими словами, с каждым линейным адресом может быть ассоциирован список источников (приемников) — следовательно, нужно быть готовыми для работы со списками. Но для начала разберемся с тем, какие типы перекрестных ссылок существуют, ибо для работы с ними используются различные функции. В первом приближении их всего два: это ссылки на код и ссылки на данные. Ссылки на код встречаются всякий раз, когда какая-то инструкция нарушает нормальное выполнение кода программы и изменяет (возможно, лишь при некоторых обстоятельствах) значение регистра — указателя команд. Говоря проще, такие ссылки образуют все команды условного и безусловного перехода и вызова подпрограмм, такие как JMP, CALL, JZ и тому подобные.


IDA - 0224.htm

224 Перекрестные ссылки
С перекрестными ссылками на данные мы сталкиваемся всякий раз, когда какая-то инструкция обращается к данным по их смещению. Например, LEA, MOV xx, offset и так далее, в том числе и DW offset MyData.
Есть еще и третий тип ссылок, который кардинально отличается от первых двух уже тем, что является внутренним типом перекрестных ссылок IDA, и пользователям знать о его существовании, а уж тем более вникать в технические детали реализации совсем необязательно. Однако он поможет лучше понять работу многих команд, поэтому ниже мы его рассмотрим.
Речь идет о "ссылке на следующую команду" (Ordinary flow в терминологии IDA). Именно с помощью этой ссылки IDA и отслеживает выполнение программы. Эта перекрестная ссылка указывает на следующую команду при нормальном исполнении программы.
Покажем это на следующем примере:
segOOO:0012 mov bx, [di] =>
segOOO:0014 add di, 2 <=->
segOOO:0017 , ог bx, bx <- =>
segOOO:0019 jz loc_0_21 s=->
segOOO:001B call bx <-=>
segOOO:001D int 21h <=->
segOOO:001F jmp short Print <--
segOOO:0021 ; ------------------------------------
segOOO:0021
segOOO:0021 loc_0_21:
segOOO:0021 mov ah, 4Ch ; -->
segOOO:0023 int 21h ; <-
segOOO:0023 start endp
Различные стрелки (-> и =») используются для облегчения восприятия, — чтобы отличить пары приемник-источник друг от друга. Как видно, цепочка проходит сквозь все команды, пока не доходит до команды безусловного перехода. Тут она и обрывается.
Адрес перехода можно узнать по перекрестной ссылке типа "код", которая автоматически образуется здесь. Что это дает? Возможность трассировки программы, например, для определения адреса конца функции, который заканчивается, как правило, RET, т.е. также инструкцией безусловной передачи управления без возврата в текущую последовательность команд, — в отличие от CALL, которая после выполнения подпрограммы передает управление следующей за ней команде.
Словом, если все упростить, то нет никакого смысла выделять ссылку на следующую команду в отдельный тип. Она хорошо описывается одним типом ссылки на код, поскольку текущая инструкция как бы вызывает следующую (во всяком случае фактически происходит именно так, или, другими словами, текущая инструкция передает, или может передавать управление следующей). В этих случаях и создается ссылка Ordinary flow.
"Скрытой" она объявлена по двум причинам. Первая из них очевидна — какой смысл захламлять текст лишней, никому не нужной информацией? Впрочем, IDA все же выделяет перекрестные ссылки на следующую команду. Точнее, выделяет


IDA - 0225.htm

Перекрестные ссылки 225
их отсутствие. Сплошная черта (в нашем примере в строке 0x21) говорит о том, что ссылка на следующую команду в данном месте отсутствует.
Вторая причина кроется в оптимизации. Если все остальные ссылки хранятся в bTree, которое обеспечивает не самый быстрый доступ к данным, то ссылки на следующую команду содержатся во флагах (см. описание виртуальной памяти), что значительно ускоряет работу с ними. А поскольку IDA интенсивно использует их, то выигрыш в скорости весьма существенен.
Таким образом, Ordinary flow можно рассматривать как отдельный, самостоятельный тип ссылок, а с другой стороны — как разновидность ссылок на код.
Предоставленные в распоряжение пользователя функции большей частью скрывают эти различия, но часть команд приходится выполнять с оговорками, о чем и сказано в их описании.
Как будет показано ниже, IDA поддерживает еще и "уточняющий" тип — скажем, Jump, call или offset. Ho на самом деле это всего лишь флаг или атрибут перекрестной ссылки, который играет только информационную роль и никакого другого влияния не оказывает.
Два типа перекрестных ссылок — на код и на данные — можно рассматривать вместе, поскольку операции с ними производятся аналогично, независимо от типа, но разными наборами функций. Ссылки на следующую инструкцию при этом лучше не модифицировать без особой нужды, обращаясь к ним только на чтение, хотя и запись тоже доступна.
Итак, рассмотрим работу со списком. Как было сказано ранее, вся архитектура IDA базируется на линейных адресах и связанных с ними объектах и элементах. Но если с каждым линейным адресом может быть связан только один комментарий каждого типа и только одно имя, то источников и приемников у каждого адреса может быть сколько угодно. Причем один и тот же адрес может быть одновременно источником одной перекрестной ссылки и приемником другой.
Например:
segOOO:OOOC jnb loc_0_17
segOOO:OOOE mov ah, 3Ch ; '<'
segOOO:0010 xor cx, cx
segOOO:0012 mov dx, 206h
segOOO:0015 int 21h
segOOO:0015
segOOO:0015
segOOO:0017
segOOO:0017loc_OJ7 ; CODE XREF: segOO
segOOO:0017 mov ds:word_0_1DA, ах
segOOO:01DA*word_0_1DA dw 0 ; DATA XREF: segOOO:0017w
Поэтому необходимо говорить о двух независимых списках — приемников и источников, да еще отдельных для каждого типа ссылок — для кода и для данных.
Возникает вопрос: а куда входят ссылки на следующую инструкцию? Ответ: они вообще не входят в упомянутый выше список, так как физически хранятся отдельно, но некоторые функции IDA эмулируют их присутствие в списке ссылок типа "код". Однако это часто приводит к путанице, поэтому будем все же считать,


IDA - 0226.htm

226 Перекрестные ссылки
что ссылки на следующую команду существуют сами по себе. Это значительно упрощает понимание.
Перейдем к рассмотрению организации этого списка и работе с ним поскольку независимо от хранимых данных работа со всеми списками идентична. Но при ближайшем рассмотрении никакого списка нет, а есть только двоичное дерево, в узлах которого и хранятся перекрестные ссылки в виде (from, to). При необходимости IDA просматривает дерево и извлекает все элементы, адреса которых совпадают с запрошенным.
Доступа к Btree IDA не предоставляет, но дает функции, работающие с перекрестными ссылками исключительно как с односвязным списком, — это Rfirst (получение первого элемента) и Rnext (получение всех последующих элементов). При этом работа идет не с индексами (которых попросту нет), а исключительно со значениями элементов списка. Rnext принимает линейный адрес и, просматривая двоичное дерево, выдает следующую за ним перекрестную ссылку указанного типа или -1, когда список исчерпан.
Таким образом, Rnext(0) с первого взгляда равносильна Rfirst, которая становится попросту не нужна. На самом деле все немного запутаннее, и понять это автор смог только после того, как обратился за разъяснениями к разработчику IDA.
На самом деле Rnext никогда не возвращает ссылок на следующую инструкцию. Они хранятся отдельно и поэтому выпадают из поля зрения Rnext. Но вот Rfirst действует иначе. Она проверяет, существует ли ссылка на следующую инструкцию, и если да, то возвращает в первую очередь ее. В противном случае — первый элемент списка.
А теперь вообразим себе следующую ситуацию. Пусть у нас имеется следующий код:
segOOO:0000 push ax ; CODE XREF:
segOOO:2864p
segOOO:0000 ; <— приемник
segOOO:2864 call bx ; -> источник
segOOO:2869 loc_0_286 ; CODE XREF:
segOOO:2864p
segOOO:2869 inc si ; <— приемник
segOOO:2892 loc_0_2892: ; CODE XREF:
segOOO:2864p
segOOO:2892 ; <- приемник
segOOO:2892 cmp byte ptr [si], 22h ; ""
Что будет, если просмотреть список приемников в строке 0x2864? По логике Rfirst должна возвратить адрес ссылки на следующую команду, т.е. 0x2869. Если теперь передать его Rnext, то она должна будет возвратить следующий на ним приемник, т.е. 0x2892, а приемник по адресу 0x0 окажется "вне поля зрения". Так ли это? На самом деле нет! Rnext сначала проверяет, является ли переданный ей адрес ссылкой на следующую команду, и если да, то начинает просмотр с начала списка!


IDA - 0227.htm

Перекрестные ссылки 227
Однако функции xfirstO ведут себя иначе и не выполняют этой дополнительной проверки. В остальном они ничем не отличаются от своих собратьев.
Зачем знать все эти подробности? Дело в том, что работать программно с перекрестными ссылками придется намного чаще, чем этого хотелось бы: IDA может отображать список приемников как в виде комментариев, так и в окне списка (подробности см. в главе "ALMA MATER" — стр. 219), но никак не отображает источники, считая, что они и так очевидны.
Но на самом деле это не так в случае с командами типа "CALL BX", и возникает естественная потребность посмотреть, куда передается управление. Конечно, IDA не отслеживает значение регистра BX автоматически и не создает в этом месте перекрестные ссылки, но вот человек это сделать может.
А вот перейти ли по требуемому адресу (или просмотреть список адресов) интерактивно решить, видимо, невозможно, поэтому приходится прибегать к языку скриптов и самостоятельно просматривать список значений.
Для облегчения навигации по файлу можно добавить в комментарий к инструкции. Это, по-видимому, будет наилучшим решением.
Подробнее об архитектуре перекрестных ссылок рассказано в описании функций, которые приведены ниже.
Хранение перекрестных ссылок
Как хранятся перекрестные ссылки внутри IDA — от пользователя скрыто. Достоверно известно лишь то, что хранятся они достаточно эффективно. Детали реализации недокументированы и могут меняться от версии к версии. Знание того, как физически хранятся перекрестные ссылки, помогает лучше понять их структуру и работу с ними. Перекрестная ссылка представляет собой два объекта. Первый из них — источник, который "одним концом" ассоциирован с линейным адресом, по которому он расположен, а другим указывает на адрес приемника. Аналогично и с приемником. Он также состоит из двух концов. Таким образом, перекрестная ссылка — это "двуполый" объект. Этим и объясняется идентичный набор функций для работы с ее источником и приемником. Скорость доступа к источнику и приемнику одинакова. Это было бы не так, если бы в узле дерева хранилась структура (from, to). Тогда для поиска каждого приемника пришлось бы просматривать все дерево.
Это не относится к ссылкам на следующую команду, которые хранятся во флагах и организованы по-другому. На каждую ссылку расходуется всего один бит(!). Если он установлен, то, следовательно, ссылка на следующую команду присутствует, и наоборот.
Адрес ссылки определяется длиной инструкции, которая численно совпадает с длиной объекта. Подробнее об этом можно прочитать в главе "Объекты в IDA".
В какой-то конкретной версии IDA хранение ссылок может быть организовано по-другому. Кроме того, с течением времени алгоритмы могут быть пересмотрены и изменены на другие, более эффективные. Возможно, изменения затронут и встроенный язык IDA, что происходит, прямо скажем, регулярно. Поэтому рекомендуется пользоваться своими функциями — обертками: переписать библи-


IDA - 0228.htm

228 Перекрестные ссылки
отеку своих функций намного проще, чем изменить все скрипты. К тому же библиотеку легко разместить в любом включаемом файле в IDA, и тогда скрипты окажутся переносимыми. В противном случае их придется редактировать каждый раз заново.
Если бы язык скриптов был на порядок популярнее, то автор не стал бы
менять прототипы встроенных функций без сохранения обратной совместимости.
Методы

Функция | Назначение
void AddCodeXref | Создает перекрестную ссылку типа 'code'
(long From, long То, long flowtype) |
long DelCodeXref | Удаляет перекрестную ссылку типа 'code'
(long From, long То, int undef) |
long Rfirst (long From) | Возвращает линейный адрес приемника первой пере-
 | крестной ссылки указанного источника
long Rnext (long From, long current) | Возвращает линейный адрес приемника очередной пе-
 | рекрестной ссылки в списке
long RfirstB (long To) | Возвращает адрес следующего источника в списке
 | перекрестных ссылок, расположенного по указанному
 | приемнику
long RnextB (long To, long current) | Возвращает адрес следующего источника в списке
 | перекрестных ссылок, расположенного по указанному
 | приемнику
long RfirstO (long From) | Возвращает линейный адрес приемника перекрестной
 | ссылки для заданного источника
long RnextO | Эта функция, следуя из сказанного в файле idc. idc,
(long From, long current) | должна отличаться от Rnext только отсутствием досту-
 | па к перекрестным ссылкам на следующую инструк-
 | цию. Однако из-за ошибок реализации функции Rnext
 | она "не видит" такой тип ссылок, и это делает обе
 | функции полностью идентичными
long RfirstBO (long To) | Возвращает линейный адрес источника перекрестной
 | ссылки для заданного приемника
long RnextBO (long To, long current) | Эта функция, следуя из сказанного в файле idc. idc,
 | должна отличаться от RnextB только отсутствием до-
 | ступа к перекрестным ссылкам на следующую инст-
 | рукцию
void add_dref | Добавляет перекрестную ссылку типа 'data'
(long From, long To, long drefType) |
void del_dref (long From, long To) | Удаляет перекрестную ссылку типа 'data'
long Dfirst (long From) | Возвращает линейный адрес приемника первой пере-
 | крестной ссылки указанного источника
long Dnext (long From, long current) | Возвращает линейный адрес приемника очередной пе-
 | рекрестной ссылки в списке
long DfirstB (long To) | Возвращает линейный адрес первого источника для
 | указанного списка приемников


IDA - 0229.htm

Перекрестные ссылки 229

Функция | Назначение
long DnextB (long To, long current) | Возвращает адрес следующего источника в списке
 | перекрестных ссылок, расположенного по указанному
 | приемнику
long XrefType (void) | Возвращает тип перекрестной ссылки, которая была
 | возвращена последним вызовом функций Rfirst, Rnext,
 | RfirstB, RnextB, Dfirst, Dnext, DfirstB, DnextB
voldAddCodeXref(long From, long To, long flowtype)
Функция создает кодовую перекрестную ссылку. IDA содержит мощный механизм, эмулирующий выполнение инструкций процессора и отслеживающий не только прямые, но и косвенные ссылки. Начиная с версии 3.74 IDA значительно превосходит в этом даже SOURCER, до этого лидировавший среди других дизассемблеров в умении восстанавливать перекрестные ссылки.
Перекрестные ссылки действительно очень облегчают анализ исследуемой программы. Допустим, встречается в тексте строка
SegOOO:0123 DB 'Hello, World!',ODh,OAh,0
Как узнать — какой код ее выводит? Для этого, очевидно, нужно найти ссылку на смещение 0x123. Это IDA делает автоматически. Благодаря перекрестным ссылкам можно трассировать исполнение программы, что позволяет лучше понять ее структуру.
IDA поддерживает два типа ссылок — на код и на данные. AddCodeXref, как нетрудно догадаться добавляет новую перекрестную ссылку на код, т.е. инструкцию, изменяющую линейное исполнение программы.
Структура ссылок следующая:
| From (Source) -> То (Target)
Источник — это 32-битный линейный адрес начала инструкции, вызывающей изменение линейного исполнения кода, а приемник — это линейный адрес начала инструкции, на которую выполняется такой переход.
IDA отображает перекрестные ссылки в виде комментариев исключительно возле инструкции приемника.
segOOO:0475 jnz loc_0_47A
segOOO:0477 mov cx, 4
segOOO:047A
segOOO:047A loc_0_47A: ; CODE XREF: segOOO:0475j
segOOO:047A sub bx, 10h
В приведенном выше примере показана перекрестная ссылка From == 0x475, То == 0x47A. Особым образом отмечать источник нет необходимости, поскольку он предполагается очевидным (в данном случае адрес указан в непосредственном операнде).


IDA - 0230.htm

230 Перекрестные ссылки
Подведя курсор к метке 'loc__0_47A' и нажав на Enter, можно перейти к приемнику, а чтобы вновь вернуться к источнику, — достаточно кликнуть по перекрестной ссылке.
Разумеется, что на один и тот же приемник может ссылаться не один источник.
Например:
segOOO:OC4A cmp ah, 4Dh ; 'M'
segOOO:OC4D jnz loc_0_C5A
segOOO:OC4F cmp byte_0_F76, 9
segOOO:OC54 ja loc_0_C5A
segOOO:OC56 inc byte_0_F76
segOOO:OC5A loc_0_C5A: ; CODE XREF: segOOO:OC4Dj
segOOO:OC5A ; segOOO:OC54j
IDA поддерживает мульти-источники. Возможно условное ветвление: в зависимости от операнда приемник может варьироваться — например, широко распространенная команда JMP BX, используемая многими компиляторами объектно-ориентированных языков, да и в моделях Маркова.
Тип ссылки указывается в постфиксе. В данном случае это 'j', что обозначает близкий (NEAR) условный или безусловный переход. IDA поддерживает четыре основных типа ссылок, которые перечислены в следующей таблице:

Определение |  | Пояснения | Уточнение | Легенда
fLCF | 1 6 | Межсегментный вызов процедуры | Call Far | P
fL_CN | 1 7 | Внутрисегментный вызов процедуры | Call Near | P
fL_JF | 1 8 | Межсегментный переход | Jump Far | J
fL_JN | 1 9 | Внутрисегментный переход | Jump Near | j
fl_US | 2 0 | Определяется пользователем | User specified | u
fL_F | 2 1 | Следующая инструкция | Ordinary flow | ^
Последний тип необходимо отметить особо. В контекстной помощи об этом нет никакого упоминания. Правда, заглянув в SDK, можно узнать, что такие перекрестные ссылки предназначены для указаний на следующую инструкцию и стоят особняком от всех остальных перекрестных ссылок. Вообще перекрестные ссылки хранятся в базе Btree, но значение Ordinary flow извлекается из флагов ячеек виртуальной памяти.
Для чего это может понадобиться? Дело в том, что некоторые процессоры имеют такую запутанную архитектуру, что вычисление адреса следующей команды подчас представляется весьма нетривиальной задачей. Поэтому, нелишне возложить эту работу на плечи IDA, создав перекрестные ссылки соответствующего типа.


IDA - 0231.htm

Перекрестные ссылки 231
Однако заметим, что тип перекрестных ссылок — понятие чисто условное и субъективное. Он был введен лишь затем, чтобы в удобочитаемой форме предоставить пользователю дополнительную информацию о ссылке, облегчая ему работу по изучению программ.
IDA не следит за корректностью типов ссылок, — об этом заботится код, создающий ссылки. Ничто не помешает нам создать и вовсе бессмысленную ссылку, например:
segOOO:OC29 mov dx, word ptr byte_0_F76
segOOO:OC2D call sub_0_EF8
segOOO:OC30 call sub_0_F45
segOOO:OC33 jb loc_0_C69 ; CODE XREF: segOOO:OC29J
Очевидно, что инструкция mov не может изменять порядок выполнения кода, однако IDA безболезненно позволяет создавать перекрестную ссылку с таким источником под типом "межсегментный переход".
В зависимости от некоторых настроек при создании перекрестных ссылок типа "вызов процедуры" IDA может автоматически создавать процедуру на месте приемника, даже если сама ссылка ошибочная. Поэтому для пользовательских скриптов рекомендуется использовать специально определенный тип, который гарантированно не влечет за собой никаких последствий.
IDA позволяет создавать перекрестные ссылки, указывающие на середину инструкции. Они выделяются красным цветом и располагаются перед указанной инструкцией. Аналогично адрес округляется и при всех попытках перехода.

Операнд | Пояснения
From | Адрес источника перекрестной ссылки
То | Адрес приемника перекрестной ссылки
flowtype | Тип перекрестной ссылки (см. таблицу на стр. | 230)
Функция не возвращает никакого значения, по которому можно было бы судить об успешности завершения операции. Вместо этого функция может при необходимости выводить пояснения в окно сообщений, но это не поможет в определении ошибки автономным скриптам.
long DelCodeXref(long From, long To, int undef)
Функция удаляет перекрестную ссылку типа 'code'. Для этого необходимо знать ее источник (From) и приемник (То). Подробнее об этом можно прочитать в описании функции AddCodeXref (стр. 229).
Имеется возможность автоматически помечать приемник (и все последующие инструкции) как 'undefined', если на них не указывает больше ни одна ссылка. Для этого флаг 'undef' необходимо установить равным единице.
Например:
копия экрана IDA:
segOOO:002B 11 02 dw 211h
segOOO:002D ; ———————————————- ——— -- —————


IDA - 0232.htm

232 Перекрестные ссылки
segOOO:002D E8 AD 00 call sub_O_DD ; CODE XREF: segOOO:0395p
segOOO:002D ;
segOOO:22F5p
segOOO:0030 BE BA 02 mov si, 2BAh
segOOO:0033 E8 A7 00 call sub_O_DD
segOOO:0036 C3 retn
> ввод пользователя:
DelCodeXref(Oxl0395, 0x1002D, 1) ;
DelCodeXref(Oxl22F5,Oxl002D,l) ;
копия экрана IDA:
segOOO:002B 11 02 dw 211h
segOOO:002D E8 db OE8h
segOOO:002E AD db OADh
segOOO:002F 00 db 0
segOOO:0030 BE db OBEh
segOOO:0031 BA db OBAh
segOOO:0032 02 db 2
segOOO:0033 E8 db OE8h
segOOO:0034 A7 db OA7h
segOOO:0035 00 db 0
Однако, если то же проделать со следующим кодом, то даже после удаления всех перекрестных ссылок он не будет помечен как undefined.
копия экрана IDA:
segOOO:014C stosb
segOOO:014D loop loc_0_143
segOOO:014F
segOOO:014F loc_0_14F: ; CODE XREF: segOOO:013Bj
segOOO:014F ;
segOOO:0146j
segOOO:014F pop es
segOOO:0150 pop ds
> ввод пользователя:
DelCodeXref(Oxl013B, 0x1014F, 0) ;
DelCodeXref(Oxl0146,Oxl014F,l) ;
копия экрана IDA:
segOOO:014C stosb
segOOO:014D loop loc_0_143 ; =>
segOOO:014F
segOOO:014F loc_0_14F: ; <=
segOOO:014F pop es
segOOO:0150 pop ds
На самом деле мы удалили не все перекрестные ссылки. IDA поддерживает и при необходимости автоматически создает так называемую ссылку на следующую команду, которая не отображается явно на экране, но, тем не менее, скрыто присутствует, увеличивая счетчик ссылок на единицу. DelCodeXref проверяет значение счетчика, убеждается, что он больше нуля, и не преобразует код в undefined.


IDA - 0233.htm

Перекрестные ссылки 233
Как и любую другую, эту ссылку можно удалить, но прежде нужно выяснить ее источник и приемник. Приемником, очевидно, будет линейный адрес начала текущей инструкции: segOOO:Oxl4F или 0x1014F, а источником — линейный адрес начала предыдущей инструкции. В нашем случае это 0x1014D.
Теперь можно вызвать функцию DelCodeXref и удалить эту перекрестную ссылку:
DelCodeXref(Oxl014F,0x1014D,1);
Это сработало! Счетчик перекрестных ссылок стал равен нулю, и IDA пометила приемник и нижележащий код, как undefined.
Выше, при описании функции MakeUndef, говорилось, что она удаляет все связанные инструкции. Теперь же, познакомившись с архитектурой перекрестных ссылок, можно уточнить это определение. IDA просто спускается по цепочке перекрестных ссылок и помечает как undefined все инструкции на пути своего следования.
копия экрана IDA:
segOOO:014C stosb
segOOO:014D loop loc_0_143
segOOO:014D ; ———______________________________
segOOO:014F unk_0_14F db 7
segOOO:0150 db 1Fh
segOOO:0151 db OC7h |
segOOO:0152 db 5
segOOO:0153 db 29h )
segOOO:0154 db 0
segOOO:0155 db OC7h |
segOOO:0156 db 45h E
segOOO:0157 db 1
При этом функция возвратит единицу. Это сигнал того, что код успешно преобразован в undefined.

Операнд | Пояснения
From | Адрес источника перекрестной ссылки
То | Адрес приемника перекрестной ссылки
undef | ==1 Преобразовывать код в undefined, когда на | него не останется ссылок
 | ==0 Не преобразовывать код в undefined, когда | на него не останется ссылок
Return | Пояснения
 | ==1 если код успешно преобразован в undefined
long Rfirst (long From)
Функция возвращает линейный адрес приемника первой перекрестной ссылки указанного источника. Подробнее о перекрестных ссылках было рассказано в описании функции AddCodeXref и DelCodeXref.
Хотя это не очевидно, источник может иметь несколько перекрестных ссылок. Например, когда используется инструкция наподобие JMP BX. Практически все


IDA - 0234.htm

234______________Перекрестные ссылки________________
инструкции снабжены перекрестными ссылками на линейный адрес начала следующей инструкции. Если по указанному линейному адресу существует перекрестная ссылка на следующую инструкцию, то функция возвратит именно ее. Несмотря на то, что в idc.idc утверждается, что этот тип ссылок доступен Rnext (см. описание ниже), на самом деле Rnext, проходя список приемников перекрестных ссылок, игнорирует этот тип.
Если указан неверный источник (линейный адрес, не содержащий перекрестных ссылок) или источник перекрестной ссылки данных, то функция возвратит ошибку BADADDR.
Примеры использования:
копия экрана IDA:
segOOO:28C6 pop di ; -> источник
segOOO:28C7 pop si ; <- приемник
> ввод пользователя:
Message("Ox%X \n",
Rfirst(Oxl28C6)
);
< ответ IDA:
0x28C7
G копия экрана IDA:
segOOO:28CB retn
segOOO:28CB sub_0_2847 endp
> ввод пользователя:
Message("Ox%X \n",
Rfirst(Oxl28CB)
);
< ответ IDA:
0xFFFFFFFF
G копия экрана IDA:
segOOO:2870 jmp loc_0_2892 ; -> источник
segOOO:2870 ; ---------------------------------------
segOOO:2872 db 90h ; P
segOOO:2873 db 90h ; P
segOOO:2892 loc_0_2892: ; CODE XREF: segOOO:2870j
segOOO:2892 ; <— приемник
segOOO:2892 cmp byte ptr [si], 22h ; ""
> ввод пользователя:
Message("Ox%X \n",
Rfirst(Oxl2870)
) ;
< ответ IDA:
0x12892


IDA - 0235.htm

Перекрестные ссылки 235

Операнд | Пояснения
From | Линейный адрес | источника перекрестной ссылки |
Return | Пояснения |  |
 | Линейный адрес | приемника первой перекрестной | ссылки
long Rnext (long From, long current)
Эта функция возвращает линейный адрес приемника очередной перекрестной сСылки в списке. При этом тип перекрестных ссылок, указывающих на следующую инструкцию (ordinary flows), игнорируется и никогда не может быть возвращен.
Для понимания того, как работает данная функция, рекомендуется прочесть описания функций AddCodeXref, DelCodeXref, Rfirst.
IDA хранит список перекрестных ссылок для каждого источника, отсортированный по адресам приемника. Первыми в нем идут ссылки, у которых линейный адрес приемника наименьший.
Функция игнорирует указатель на следующую инструкцию. Если она завершилась успешно, то возвратит линейный адрес приемника перекрестной ссылки, следующей за current. Current не обязательно точно равен адресу приемника текущей перекрестной ссылки в списке. Он может быть меньше его, но, разумеется, обязательно должен превосходить адрес приемника предыдущей ссылки.
Поясним это на примере:
segOOO:0000 push ax ; CODE XREF: segOOO:2864p
segOOO:0000 ; <— приемник
segOOO:2864 call bx ; -> источник
segOOO:2869 loc_0_286 ; CODE XREF: segOOO:2864p
segOOO:2869 inc si ; <— приемник
segOOO:2892 loc_0_2892: ; CODE XREF: segOOO:2864p
segOOO:2892 ; <- приемник
segOOO:2892 cmp byte ptr [si], 22h ; ""
Пусть при изучении программы было определено, что BX может принимать следующие значения: 0x0, 0x2869, 0x2892. В этом случае по линейному адресу segOOO:2864 будут расположены три перекрестных ссылки на соответствующие приемники. Точнее, их будет четыре — с учетом ссылки на следующую инструкцию, но, поскольку Rnext никогда не возвращает ее, то достаточно рассмотреть только упомянутые три.
IDA сформирует по линейному адресу 0x12864 следующий список приемников: {OxlOOOO, 0x12869, 0xl2892}. Эти адреса и будут возвращаться при прохождении списка функцией Rnext.
Необязательно начинать первый вызов с Rfirst (см. описание выше). Как уже упоминалось, Rnext не хранит указатель на текущую ссылку во внутренней скрытой переменной, а принимает его как параметр. Это дает нам возможность легко манипулировать значением указателя, управляя поведением функции.


IDA - 0236.htm

236 Перекрестные ссылки
Зачем понадобилось вводить Rfirst? Ведь первую перекрестную ссылку можно найти с помощью Rnext — и это будет следующая ссылка за нулем. Очевидно, что Rnext(Oxl2864,0) вернет 0x10000 — первую перекрестную ссылку в списке. Следовательно, Rnext(X, 0) идентична Rfirst. Ha самом деле нас поджидает небольшой сюрприз. Функция Rnext, проходя список, не обнаруживает в нем ссылок на следующую команду. Это не ошибка, а документированная особенность IDA.
Например:
auto а;
a=0;
for (;;)
{
a=Rnext(ScreenEA() ,a);
if (a==-l) break;
Message("Ox%X \n",a);
}

Операнд | Пояснения
From | Линейный адрес источника списка перекрестных | ссылок
Current | Текущий адрес
Return | Пояснения |
 | Следующий адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long RfirstB (long То)
Функция возвращает линейный адрес первого источника для указанного списка приемников. Для понимания этого рекомендуется прочесть описания функций AddCodeXref, DelCodeXref, Rfirst, Rnext. Очевидно что по одному и тому же линейному адресу может существовать несколько приемников перекрестных ссылок. Например:
копия экрана IDA:
segOOO:013B jz loc_0_14F ; -> источник
segOOO:013D mov ds, ax
segOOO:013F mov ax, cs
segOOO:0141 mov es, ax
segOOO:0143 lodsb
segOOO:0144 cmp al, 21h ; '!'
segOOO:0146 jb loc_0_14F ; -> источник
segOOO:0148 cmp al, 7Ah ; 'z'
segOOO:014A ja loc_0_14F ; -> источник
segOOO:014C stosb
segOOO:014D loop loc_0_143 ; -> источник
segOOO:014F
segOOO:014F loc_0_14F: ; CODE XREF: segOOO:013Bj
segOOO:014F ; segOOO:0146j ...


IDA - 0237.htm

Перекрестные ссылки 237
segOOO:014F ; <— приемник
segOOO:014F pop es
Источник по адресу segOOO:Oxl4D помечен неслучайно. Он, разумеется, не имеет отношения к операнду loc__0_143, а представляет собой перекрестную ссылку на следующую команду.
Реализация этой функции повторяет особенность реализации Rfirst. Рассмотрим список источников перекрестных ссылок, который IDA сформировала по адресу segOOO:Oxl4F — {Oxl013B, 0x10146, 0x1014A, Oxl014C}. Вполне естественно ожидать, что вызов RfirstB должен был бы вернуть первый — самый наименьший из них, но вместо него возвращается источник ссылки на следующую инструкцию, т.е. 0x1014A. Покажем это ниже:
> ввод пользователя:
Message("Ox%X \n",
RfirstB(Oxl014F)
) ;
< ответ IDA:
0x1014A
Как получить действительно первый элемент источника списка, можно прочитать в описании функции RnextB.

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных | ссылок
Return | Пояснения |
 | Источник ссылки на следующую инструкцию или | , если
 | ее нет, то первый адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long RnextB (long To, long current)
Функция возвращает адрес следующего источника в списке перекрестных ссылок, расположенного по указанному приемнику. Для более глубокого понимания принципов работы рекомендуется ознакомиться с описанием функций AddCodeXref, DelCodeXref, Rnext, RfirstB.
Очевидно, что по одному и тому же линейному адресу может существовать более одного приемника перекрестных ссылок.
Например:
копия экрана IDA:
segOOO:013B jz loc_0_14F ; -> источник
segOOO:013D mov ds, ax
segOOO:013F mov ax, cs
segOOO:0141 mov es, ax
segOOO:0143 lodsb
segOOO:0144 cmp al, 21h ; '!'
segOOO:0146 jb loc_0_14F ; -> источник
segOOO:0148 cmp al, 7Ah ; 'z'


IDA - 0238.htm

238 Перекрестные ссылки
segOOO:014A ja loc_0_14F ; -> источник
segOOO:014C stosb
segOOO:014D loop loc_0_143 ; -> источник
segOOO:014F
segOOO:014F loc_0_14F: ; CODE XREF: segOOO:013Bj
segOOO:014F ; segOOO:0146j ...
segOOO:014F ; <- приемник
segOOO:014F pop es
Реализация этой функции повторяет особенность реализации Rnext. Несмотря на то, что в idc.idc утверждается, будто эта функция "видит" тип перекрестных ссылок, указывающих на следующую команду, в действительности этого не происходит. И выделенный жирным адрес источника функция не вернет никогда: {Oxl013B, 0x10146, 0x1014A, Oxl014C}.
Поскольку RnextB хранит текущий адрес не во внутренней переменной, а в передаваемом параметре, то существует возможность, модифицируя его, управлять работой функции.
Так, например, RnextB(ScreenEA(), 0) гарантированно вернет следующий за ним адрес, т.е. 0x1013B, а пройти весь список (за исключением ссылок на следующую инструкцию) можно с помощью следующего кода:
> ввод пользователя:
auto а;
a=0;
for (;;)
{
a=RnextB(ScreenEA(),a);
if (a==-l) break;
Message("Ox%X \n",a);
}
< ответ IDA:
0x1013B 0x10146 0x1014C
Немного модернизировав код, можно добиться того, чтобы на экран выдавался действительно весь список источников, включая и ссылки на следующую команду.
> ввод пользователя:
auto а;
a=0;
for (;;)
{
a=RnextB(ScreenEA(),a);
if (a==-l) break;
if (a>RfirstB(ScreenEA())
Message("Ox%X \n",
RfirstB(ScreenEA()
);
Message("Ox%X \n",a);
}


IDA - 0239.htm

Перекрестные ссылки 239
< ответ IDA:
0x1013B 0x10146 0x1014A 0x1014C
Функция возвращает ошибку BADADDR, если список исчерпан, (т.е. текущий
адрес наибольший в списке) или его не существует.

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных | ссылок
Current | Текущий адрес
Return | Пояснения |
 | Следующий адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long RfirstO (long From)
Функция возвращает линейный адрес приемника перекрестной ссылки для заданного источника.
Практически идентична Rfirst, за тем исключением, что не имеет доступа к ссылкам на следующую инструкцию, поэтому возвращает действительно первый элемент списка линейных адресов приемников.
Функцию рекомендуется использовать в паре с функцией Rnext, впрочем, Rnext(xxx, 0) возвращает идентичный результат, и хотя работает ничуть не быстрее, но немного экономит на компактности кода.
Для понимания этого рекомендуется ознакомиться с описанием функций AddCodeXref, DelCodeXref, Rfirst, Rnext.
Если указан неверный источник (т.е. линейный адрес, не содержащий перекрестных ссылок) или источник перекрестной ссылки данных, то функция возвратит ошибку BADADDR.
Примеры использования:
копия экрана IDA:
segOOO:28C6 pop di ; —> источник
segOOO:28C7 pop si ; <— приемник
> ввод пользователя:
Message("Ox%X \n",
RfirstO(0x128C6)
);
< ответ IDA:
0xFFFFFFFF
копия экрана IDA:
segOOO:28CB • retn
segOOO:28CB sub_0_2847 endp


IDA - 0240.htm

240 Перекрестные ссылки
> ввод пользователя:
Message("Ox%X \n",
RfirstO(Oxl28CB)
);
< ответ IDA:
0xFFFFFFFF
G копия экрана IDA:
segOOO:2870 jmp loc__0_2892 ; ~> источник
segOOO:2870 пор
segOOO:2892 loc_0_2892: ; CODE XREF: segOOO:2870j
segOOO:2892 ; <- приемник
segOOO:2892 cmp byte ptr [si], 22h ; ""
> ввод пользователя:
Message("Ox%X \n",
RfirstO(Oxl2870)
);
< ответ IDA:
0x12892

Операнд | Пояснения
From | Линейный адрес | источника перекрестной ссылки |
Return | Пояснения |  |
 | Линейный адрес | приемника первой перекрестной | ссылки
long RnextO (long From, long current)
Эта функция по идее (а точнее, следуя из сказанного в файле idc.idc) должна отличаться от Rnext только отсутствием доступа к перекрестным ссылкам на следующую инструкцию, но из-за особенностей реализации функции Rnext она "не видит" такой тип ссылок, и это делает обе функции полностью идентичными. Вот почему мы не приводим здесь описания, поскольку пришлось бы полностью повторить все сказанное об Rnext.
Чаще всего ссылка на следующую команду не требуется. В этих случаях следует применять вызов RnextO. B противном случае придется воспользоваться листингом, приведенным ниже.
auto а;
a=0;
for (;;)
{
a=RnextB(ScreenEA(),а);
if (a==-l) break;
if (a>RfirstB(ScreenEA())
Message("Ox%X \n",
RfirstB(ScreenEA() );
Message("Ox%X \n",a);
}


IDA - 0241.htm

Перекрестные ссылки 241

Операнд | Пояснения
From | Линейный адрес источника списка перекрестных |  | ссылок
Current | Текущий адрес
Return | Пояснения |  |
 | Следующий адрес | в списке |
 | -1 если список исчерпан или отсутствует источник
long RfirstBO (long То)
Функция возвращает линейный адрес источника перекрестной ссылки для заданного приемника.
Практически идентична RfirstB, за тем исключением, что не имеет доступа к ссылкам на следующую инструкцию, поэтому возвращает действительно первый элемент списка линейных адресов источников. Ее рекомендуется использовать в паре с функцией RnextB, впрочем, RnextB(xxx, 0) возвращает идентичный результат, и хотя работает ничуть не быстрее, но немного экономит на компактности кода.
Для понимания этого рекомендуется ознакомиться с описанием функций AddCodeXref, DelCodeXref, RfirstB, RnextB.
Если указан неверный источник (т.е. линейный адрес, не содержащий перекрестных ссылок) или источник перекрестной ссылки данных, то функция возвратит ошибку BADADDR.

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных | ссылок
Return | Пояснения |
 | Источник ссылки на следующую инструкцию или, | если
 | ее нет, то первый адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long RnextBO (long To, long current)
Эта функция по идее (а точнее, следуя из сказанного в файле idc.idc) должна отличаться от RnextB только отсутствием доступа к перекрестным ссылкам на следующую инструкцию, однако из-за особенностей реализации функции Rnext она "не видит" такой тип ссылок, и это делает обе функции полностью идентичными.
Описание здесь не приводится, поскольку пришлось бы полностью повторить все сказанное об Rnext.
Чаще всего ссылка на следующую команду не требуется. В этих случаях и следует применять вызов RnextO. B противном случае придется воспользоваться листингом приведенным выше на стр. 240.


IDA - 0242.htm

242 Перекрестные ссылки

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных | ссылок
Current | Текущий адрес
Return | Пояснения |
 | Следующий адрес в списке |
 | -1 если список исчерпан или отсутствует источник
void add_dref (long From, long To, long drefType)
Подробнее об архитектуре перекрестных ссылок было сказано в описании функции AddCodeXref. Для удобства IDA поддерживает две группы перекрестных ссылок — на данные и на код, каждая группа со своим набором функций и возможностей.
Типы поддерживаемых перекрестных ссылок на данные следующие:

Определение |  | Пояснения | Легенда
dr_0 | 1 | Смещение (Offset) | 0
dr_W | 2 | Запись (Write) | W
dr_R | 3 | Чтение (Read) | г
dr_T | 4 | Пользовательский тип | t
С первого взгляда кажется, что можно создать перекрестную ссылку на данные с помощью вызова AddCodeXref, указав лишь соответствующий тип ссылки.
Например:
> ввод пользователя:
AddCodeXref(Oxl0148,Oxl014C,2);
G копия экрана IDA:
segOOO:014C loc_0_14C: ; CODE XREF: segOOO:0148w
На самом деле постфикс (в данном случае 'w') играет только информационную роль и не влияет на тип ссылки, которая так и осталась кодовой, что видно по предваряющему ее ключевому слову.
Сравните это со следующим примером:
> ввод пользователя:
add_dref(Oxl0148, Oxl014C,2) ;
G копия экрана IDA:
segOOO:014A ja loc_0_14F
segOOO:014C ; DATA XREF: segOOO:0148w
segOOO:014C stosb
IDA не контролирует корректность ссылки, полностью перекладывая эту работу на код, вызывающий эту функцию. Тип ссылки играет чисто информационную роль и служит для ускорения анализа дизассемблируемой программы.


IDA - 0243.htm

Перекрестные ссылки 243
Никаких других влияний на работу IDA он не оказывает. Однако стоит придерживаться единой схемы наименования перекрестных ссылок, чтобы не приводить пользователя в замешательство.
dr_O — Смещение(Offset)
Под смещением понимается любое (прямое или косвенное) обращение к
адресу ячейки данных.
Например:
копия экрана IDA:
SegOOO:0301 push offset loc_0_30A ; -> источник
segOOO:030A loc_0_30A: ; DATA XREF: segOOO:0301o
segOOO:030A ; <— приемник
segOOO:3000 DW offset byte_0_293A ; -> источник
SegOOO:293A byte_0_293A DB ? ; DATA XREF: segOOO:3000o
SegOOO:293A ; <- приемник
Например, LEA.
Dr_W — Запись (Write)
Любая инструкция, производящая прямую запись в ячейку.
segOOO:2928 mov cs:byte_0_2939,1 ; ->источник
segOOO:2939* DB ? ; DATA XREF segOOO:2928w
segOOO:2939 ; <— приемник
segOOO:OE5F dec word_O_F72 ; -> источник
segOOO:OF72* DW ? ; DATA XREF segOOO:OE5Fw
segOOO:OF72 ; <— приемник
Для таких инструкций, как, например, MOVS, IDA автоматически не создает перекрестных ссылок. Но это могут делать продвинутые пользовательские скрипты.
DrR — Чтение (Read)
Любая инструкция, производящая прямое чтение ячейки. Например:
segOOO:OD08 mov ax, word_0_F72 ; —> источник
segOOO:OF72*word_0_F72 dw 0 ; DATA XREF: segOOO:OD08r
segOOO:OF72 ; f- приемник
Инструкции, выполняющие цикл операций чтение — вычисление — запись, IDA всегда относит к типу dr_w, а не dr_r.
Обратите внимание, что функция add_dref не возвращает результата успешности операции, поэтому для того, чтобы определить, действительно ли была создана перекрестная ссылка или нет, приходится прибегать к полному прохождению списка в попытках найти в нем "свой" адрес.

Операнд | Пояснения
From | Адрес источника перекрестной ссылки
То | Адрес приемника перекрестной ссылки
Dreftype | Тип перекрестной ссылки (см. таблицу на стр. | 242)


IDA - 0245.htm

Перекрестные ссылки 245
> ввод пользователя:
Message("Ox%X \n",
Dfirst(Oxl2331)
) ;
< ответ IDA:
0x12934

Операнд | Пояснения
From | Линейный адрес источника перекрестной ссылки
Return | Пояснения
 | Линейный адрес приемника первой перекрестной ссылки
long Dnext (long From, long current)
Эта функция возвращает линейный адрес приемника очередной перекрестной ссылки в списке. Для понимания того, как работает данная функция, рекомендуется прочесть описания функций AddCodeXref, add_dref, Dfirst.
IDA хранит список перекрестных ссылок для каждого источника, отсортированный по адресам приемника. Первыми в нем идут ссылки, у которых линейный адрес приемника наименьший. Если функция завершится успешно, то возвратит линейный адрес приемника перекрестной ссылки, следующей за current. Current необязательно точно равен адресу приемника текущей перекрестной ссылки в списке. Он может быть меньше его, но, разумеется, обязательно должен превосходить адрес приемника предыдущей ссылки.
Поясним это на примере:
segOOO:2331 mov word_0_2934, ах ;-> источник
segOOO:26C1 cmp ax, word_0_2934 ;-> источник
segOOO:277B cmp dx, word_0_2934 ;-> источник
segOOO:2934* word_0_2934 dw 0 ; DATA XREF: segOOO:2331w
segOOO:2934* segOOO:26C1r
segOOO:2934* segOOO:277Br
segOOO:2934 <— приемник
IDA сформирует по линейному адресу 0x12934 следующий список приемников: {0xl2331, 0x126C1, Oxl277B}. Эти адреса и будут возвращаться при прохождении списка функцией Dnext.
Необязательно начинать первый вызов с Dfirst (см. описание выше). Как уже упоминалось, Dnext не хранит указатель на текущую ссылку во внутренней скрытой переменной, а принимает его как параметр. Это дает возможность легко манипулировать ее значением, управляя поведением функции.
Зачем понадобилось вводить Dfirst? Ведь первую перекрестную ссылку можно найти с помощью Dnext, и это будет следующая ссылка за нулем. Очевидно, что Dnext(Oxl2934,0) вернет 0x12331 — первую перекрестную ссылку в списке. Следовательно, Dnext(X, 0) идентична Dfirst.


IDA - 0246.htm

246 Перекрестные ссылки
Вывести на зкран адреса всех источников перекрестных ссылок поможет
следующий код:
auto а;
a=0;
for (;;)
{
a=Dnext(ScreenEA(),a);
if (a==-l) break;
Message("Ox%X \n",a);

Операнд | Пояснения
From | Линейный адрес источника списка перекрестных | ссылок
Current | Текущий адрес
Return | Пояснения |
 | Следующий адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long DfirstB (long То)
Функция возвращает линейный адрес первого источника для указанного списка приемников. Для понимания этого рекомендуется прочесть описания функций AddCodeXref, add_dref, Dfirst. Очевидно, что по одному и тому же линейному адресу может существовать более одного приемника перекрестных ссылок.
Например:
segOOO:2331 mov word_0_2934, ах ; -> источник
segOOO:26C1 cmp ax, word_0_2934 ;-> источник
segOOO:277B cmp dx, word_0_2934 ; -> источник
segOOO:2934* word_0_2934 dw 0 ; DATA XREF: segOOO:2331w
segOOO:2934* ; segOOO:26C1r
segOOO:2934* ; segOOO:277Br
Рассмотрим список источников перекрестных ссылок, который IDA сформировала по адресу segOOO:Ox2934 — {0xl2331, 0x126C1, Oxl277B}.
Вызов DfirstB возвратит первый из них — с наименьшим линейным адресом.
> ввод пользователя:
Message("Ox%X \n",
DfirstB(Oxl2934)
);
< ответ IDA:
0x12331


IDA - 0247.htm

Перекрестные ссылки 247

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных ссылок
Return | Пояснения |
 | Источник ссылки на следующую инструкцию или, если | ее
 | нет, то первый адрес в списке |
 | -1 если список исчерпан или отсутствует источник |
long DnextB (long Tо, long current)
Функция возвращает адрес следующего источника в списке перекрестных ссылок, расположенного по указанному приемнику. Для более глубокого понимания принципов работы рекомендуется ознакомиться с описанием функций AddCodeXref, add_dref, Dnext.
Очевидно, что по одному и тому же линейному адресу может существовать несколько приемников перекрестных ссылок.
Например:
segOOO:2331 mov word_0_2934, ах ;-> источник
segOOO:26C1 cmp ax, word_0_2934 ;-> источник
segOOO:277B cmp dx, word_0_2934 ; —> источник
segOOO:2934* word_0_2934 dw 0 ; DATA XREF: segOOO:2331w
segOOO:2934* ; segOOO:26C1r
segOOO:2934* ; segOOO:277Br
Поскольку DnextB хранит текущий адрес не во внутренней переменной, а в передаваемом параметре, то существует возможность, модифицируя его, управлять работой функции.
Так, например, DnextB(ScreenEA(), 0) гарантированно вернет следующий за ним адрес, а пройти весь список (за исключением ссылок на следующую инструкцию) можно с помощью следующего кода:
> ввод пользователя:
auto а;
a=0;
for (;;)
{
a=DnextB(ScreenEA(),a);
if (a==-l) break;
Message("Ox%X \n",a);
}
< ответ IDA:
0x12331 0x126C1 0x1277B
Функция возвращает ошибку BADADDR, если список исчерпан (т.е. текущий адрес наибольший в списке) или не существует.


IDA - 0248.htm

248 Перекрестные ссылки

Операнд | Пояснения
То | Линейный адрес приемника списка перекрестных | ссылок
Current | Текущий адрес
Return | Пояснения |
 | Следующий адрес в списке |
 | -1 если список исчерпан или отсутствует источник
long XrefType (void)
Эта функция возвращает тип перекрестной ссылки, которая была возвращена последним вызовом функций Rfirst, Rnext, RfirstB, RnextB, Dfirst, Dnext, DfirstB, DnextB. Функция не принимает никаких параметров, а взаимодействует исключительно с внутренними переменными IDA. При этом она имеет одну грубую ошибку (точнее, недостаток, который вряд ли будет скоро исправлен) реализации.
Возращаемое значение принадлежит множеству определений fl_x или dr_x. Однако, как отмечалось выше, типы перекрестных ссылок — понятие условное. Функция AddCodeXref принимает в качестве параметра определения из множества dr_x и даже успешно создает такие перекрестные ссылки, все равно являющиеся перекрестными ссылками на код.
Поэтому невозможно гарантированно определить тип перекрестной ссылки по возвращаемому функцией XrefType значению.
Например:
копия экрана IDA:
segOOO:014C loc_0_14C: ; CODE XREF: segOOO:0148w
Rfirst(Oxl0148);
Message("Ox%X \n",
XrefType()
);
< ответ IDA:
0x15
Функция вернула тип dr_W, но это еще не дает возможности утверждать, что эта перекрестная ссылка указывает на данные.

Return | Пояснения |
 | Тип перекрестной ссылки, | возвращенной последним вы-
 | зовом, манипулирующей с | ней функции


IDA - 0249.htm

Точка входа 249
Точки входа
Архитектура точек входа
Поддержка точек входа (Entry Point) — самый малопроработанный элемент архитектуры IDA. Причина тому — абсолютная ненужность их для пользователя. В большинстве случаев даже не требуется знать, что такое понятие есть и поддерживается IDA.
С одной стороны, вполне логично, что каждый файл имеет некоторую точку, с которой начинается его выполнение, причем эта точка может не совпадать с началом файла. Например, ехе-файл может начинать выполнение с любой своей точки (между прочим, возможно, даже выходящей за границы файла, — но это относится к недокументированным особенностям MSDOS-совместимых операционных систем поэтому не будем заострять на этом внимание).
Адрес регистра (регистров) — указателя команд в момент передачи управления загруженному файлу называется точкой входа. Таким образом, любой дизассемблер как минимум должен быть осведомлен, как вычислить этот адрес. Чаще всего адрес присутствует в заголовке файла (значит, дизассемблер должен понимать его формат), реже предполагается по умолчанию — так, для сот-файлов он всегда расположен по адресу 0x100, но для бинарных файлов (дампов RAM, например) точка входа может и вовсе не иметь смысла, поскольку управление может быть передано на множество мест, в зависимости от обстоятельств.
Поэтому, с первого взгляда, говорить о поддержке точек входа можно только на уровне ядра дизассемблера, скрытом от пользователя, — собственно, говорить не о чем — манипулировать точками входа лучше предоставить ядру. В общих чертах так оно и есть — набор функций, взаимодействующих с точками входа, очень ограничен и, откровенно говоря, не полон. Так, например, не предусмотрены функции удаления точек входа, в том числе и созданных пользователем.
Создавать свои точки входа в большинстве случаев нет нужды, а вот получить адреса существующих требуется очень часто — должны же скрипты знать, с какого адреса начинается выполнение программы? Для этого предусмотрена функция long GetEntryPoint (long ordinal), чем потребности рядового пользователя с лихвой удовлетворяются.
Но при анализе ПЗУ можно столкнуться с тем, что код начинает выполняться с десятков разных мест (например, обработчиков прерываний), и хорошим решением будет создать собственные точки входа (IDA, разумеется, бессильна их определить) и потом взаимодействовать с ними как интерактивно, так и программно (из скриптов).
Многие просто создают в нужных местах функции, а точками входа пренебрегают. В чем-то такая позиция верна, поскольку точки входа не дают никаких


IDA - 0250.htm

250 Точка входа
преимуществ, за исключением того, что явно указывают, с каких адресов может начинаться выполнение программы.
Еще IDA предваряет их имена директивой public, делая их общедоступными. Но то же можно сделать и вручную.
Подытоживая сказанное: вникать в технические подробности организации точек входа необязательно даже опытным пользователям, а тем более самостоятельно манипулировать ими.
Методы

Функция | Описание
Long GetEntryPointQty (void) | Возвращает число точек входа
success AddEntryPoint (long ordinal, | Добавляет новую точку входа
long еа, char name, long makecode)
long GetEntryOrdinal (long index) | Возвращает ординал точки входа | по ее индексу
long GetEntryPoint (long ordinal) | Возвращает адрес точки входа по | ординалу
success RenameEntryPoint | Переименовывает точку входа
(long ordinal, char name)
long GetEntryPointQty (void)
Функция возвращает число точек входа (Entry Points). Обычно IDA создает только одну точку входа, адрес которой извлекается из заголовков исполняемого файла. Но иногда возникает потребность в создании более одной точки входа.
Например, РЕ-файл, имеющий DOS-заглушку: если мы захотим дизассемблировать последнюю, то необходимо добавить новую точку входа "вручную", поскольку IDA предпочитает в большинстве случаев обходиться одной Entry Point.
IDA - 0250-1.jpg
IDA - 0250-2.jpg
Пример использования:
> ввод пользователя:
Message("Ox%X \n", GetEntryPointQty());


IDA - 0251.htm

Точка входа 251
< ответ IDA:
0x1

Операнд | Пояснения
Return | Число точек входа
success AddEntryPoint (long ordinal, long ea, char name, long makecode)
Добавляет новую точку входа. Будьте внимательны при вызове этой функции, ведь удалить созданную точку входа уже не удастся!
Для доступа к точке входа необходимо знать ее ординал, который задается пользователем при вызове функции. Если он равен нулю, то IDA установит его равным линейному адресу точки входа (строго говоря, при этом ординал не создается). С одним и тем же ординалом может существовать только одна точка входа.
При генерации точки входа IDA вставляет директиву ассемблера PUBLIC.
G копия экрана IDA:
segOOO:0000 public start
segOOO:0000 start proc near
segOOO:0000 push ax
segOOO:0001 mov cx, 1
segOOO:0004 shl cx, 1
segOOO:0006 add sp, cx
segOOO:0008 push ax
segOOO:0009 add sp, cx
segOOO:OOOB retn
segOOO:OOOB start endp ; sp = -4
segOOO:OOOB
segOOO:OOOB segOOO ends
> ввод пользователя:
AddEnrtyPoint(l,0x10006,"NewEntryPoint",0);
копия экрана IDA:
segOOO:0000 public start
segOOO:0000 start proc near
segOOO:0000 push ax
segOOO:0001 mov cx, 1
segOOO:0004 shl cx, 1
segOOO:0006
segOOO:0006 public NewEntryPoint
segOOO:0006 NewEntryPoint:
segOOO:0006 add sp, cx
segOOO:0008 push ax
segOOO:0009 add sp, cx
segOOO:OOOB retn
segOOO:OOOB start endp ; sp = -4
segOOO:OOOB
segOOO:OOOB segOOO ends


IDA - 0252.htm

252 Точка входа
Если попытаться создать более одной точки с идентичными именами, то IDA
добавит к последнему знак прочерка и номер имени, начиная с нуля.
> ввод пользователя:
AddEnrtyPoint(2,0x10009,"NewEntryPoint",0);
G копия экрана IDA:
segOOO:0000 public start
segOOO:0000 start proc near
segOOO:0000 push ax
segOOO:0001 mov cx, 1
segOOO:0004 shl cx, 1
segOOO:0006
segOOO:0006 public NewEntryPoint
segOOO:0006 NewEntryPoint:
segOOO:0006 add sp, cx
segOOO:0008 push ax
segOOO:0009
segOOO:0009 public NewEntryPoint_0
segOOO:0009 NewEntryPoint_0:
segOOO:0009 add sp, cx
segOOO:OOOB retn
segOOO:OOOB start endp ; sp = -4
segOOO:OOOB
segOOO:OOOB segOOO ends
Если попытаться создать точку входа с уже существующим ординалом, то она
не будет создана, а функция вернет ошибку.
> ввод пользователя:
Message("Ox%X \n",
AddEnrtyPoint(2,Oxl0009,"MyEntryPoint",0)
);
< ответ IDA:
0x0
Допускается создание точки входа без имени. При этом она не будет отобра-
жена на экране, но появится в списке точек входа.
> ввод пользователя:
AddEntryPoint(l,Oxl22C6,"",1);
G копия экрана IDA:
segOOO:22CO public start
segOOO:22CO start proc near
segOOO:22CO call sub_0_22DD
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8
Если по указанному адресу уже существует метка (или функция), то она будет
переименована.


IDA - 0253.htm

Точка входа 253
IDA - 0253-1.jpg
G копия экрана IDA:
segOOO:002A sub_0_2A proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 sub_0_2A endp
> ввод пользователя:
AddEntryPoint(8,0x1002A,"EntryPoint",0);
G копия экрана IDA:
segOOO:002A public EntryPoint
segOOO:002A EntryPoint proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 EntryPoint endp
Если же по указанному адресу уже существует точка входа, то она не будет затерта новой, и по одному адресу будут расположены две точки входа. При этом имя предыдущей точки входа переместится в комментарий.
> ввод пользователя:
AddEntryPoint(9,Oxl002A,"NewEntryPoint",0);
IDA - 0253-2.jpg
Q копия экрана IDA:
segOOO:002A
segOOO:002A public NewEntryPoint
segOOO:002A NewEntryPoint proc near ; EntryPoint
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 NewEntryPoint endp
Если флаг makecode будет установлен на единицу, то IDA при необходимости
формирует функцию и дизассемблирует инструкции.


IDA - 0254.htm

254 Точка входа
G копия экрана IDA:
segOOO:002A db OBEh
segOOO:002B db 11h
segOOO:002C db 2
segOOO:002D db OE8h
segOOO:002E db OADh
segOOO:002F db 0
segOOO:0030 db OBEh
segOOO:0031 db OBAh
segOOO:0032 db 2
segOOO:0033 db OE8h
segOOO:0034 db OA7h
segOOO:0035 db 0
segOOO:0036 db OC3h
> ввод пользователя:
AddEntryPoint(l,0x1002A,"MyEntryPoint",1);
копия экрана IDA:
segOOO:002A
segOOO:002A public MyEntryPoint
segOOO:002A MyEntryPoint proc near
segOOO:002A mov si, 211h
segOOO:002D call sub_O_DD
segOOO:0030 mov si, 2BAh
segOOO:0033 call sub_O_DD
segOOO:0036 retn
segOOO:0036 MyEntryPoint endp

Операнд | Пояснения
Ordinal | Ординал функции
ea | Линейный адрес конца команды
Name | Имя точки входа
Makecode | ==makecode | Пояснения
 | ==1 | Преобразовывать undefine | в инструкции
 | ==0 | Не преобразовывать undefine в инструкции
Return | Завершение | Пояснения
 | 1 | Успешно
 | 0 | Ошибка
long GetEntryOrdinal (long index)
Возвращает ординал точки входа по порядковому номеру из списка. Index может принимать значения от нуля до GetEntryPointQty()-l. Все точки входа (если их больше одной) хранятся в несортированном списке, расположенные в порядке их создания.
Если запросить несуществующий индекс, то функция вернет ноль, а не ошибку BADADDR, что само по себе достаточно странно: что по нулевому линейному адресу теоретически возможно создать точку входа, хотя это случает-


IDA - 0255.htm

Точка входа 255
ся крайне редко, поскольку для большинства файлов адрес загрузки по умолчанию
лежит значительно выше и равен 0x10000.
Ординал точки входа будет необходим в дальнейшем для функций GetEntry-
Point и RenameEntryPoint.
IDA - 0255-1.jpg
Следующий пример выдаст на экран ординалы всех существующих точек
входа.
> ввод пользователя:
auto a,i;
i=0;
while((a=GetEntryOrdinal(i++)))
Message("Ox%X \n",a);
< ответ IDA:
0x122C0
0x1
0x2
0x3

Операнд | Пояснения
index | Индекс точки входа в списке (от нуля до GetEntryPointQty()-l)
Return | Завершение | Пояснения
 | !=0 | Ординал точки входа
 | 0 | Ошибка
long GetEntryPoint (long ordinal)
Возвращает адрес точки входа по ординалу. Если указанный ординал не
существует, возвращается ошибка BADADDR.
IDA - 0255-2.jpg
Следующий пример выдаст на экран адреса всех существующих точек входа.
>ввод пользователя:
auto a,i;
i = 0;
while((a=GetEntryOrdinal(i++)))
Message("Ox%X %x \n",a, GetEntryPoint(a));


IDA - 0256.htm

256 Точка входа
< ответ IDA:
0x122C0 122cO
0x1 122dd
0x2 122e5
0x3 1002a

Операнд | Пояснения
Ordinal | Ординал точки входа
Return | Завершение | Пояснения
 | !=BADADDR | Адрес точки входа
 | ==BADADDR | Ошибка
success RenameEntryPoint (long ordinal, char name)
Позволяет изменить имя точки входа по ординалу. При этом предыдущее имя переносится в комментарий. Если операция завершится неуспешно, то функция вернет неравное нулю число. Такое может случиться, например, при попытке передать в качестве нового имени пустую строку.
Пример использования:
Q копия экрана IDA:
segOOO:22CO start proc near
segOOO:22CO call My_1
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8
segOOO:22CF start endp
> ввод пользователя:
RenameEntryPoint(Oxl22CO,"main");
Q копия экрана IDA:
segOOO:22CO main proc near; start
segOOO:22CO call My_1
segOOO:22C3 call sub_0_2325
segOOO:22C6 call sub_0_235B
segOOO:22C9 call sub_0_2374
segOOO:22CC call sub_0_23B6
segOOO:22CF call sub_0_23F8
segOOO:22CF main endp

Операнд | Пояснения
Ordinal | Ординал точки входа
Name | Новое имя функции
Return | Завершение | Пояснения
 | !=0 | Успешно
 | ==0 | Ошибка


IDA - 0257.htm

Структуры 257
Структуры
ALMA MATER
Строго говоря, в языке процессора — машинном коде — нет ни типов данных, ни структур, — все это привилегии языков высокого уровня. Процессор оперирует с регистрами и ячейками памяти. Это самый низкий уровень в абстракции данных, и приходится вручную разбираться с такими техническими деталями, как интерпретация знаковых битов или разрядностей ячеек.
Но ассемблер — это не машинный код. Это первый высокоуровневый язык, придуманный человеком, и огромный шаг вперед в абстракции данных. Современные ассемблеры уже трудно назвать языками низкого уровня, ибо они поддерживают макросы, средства автоматизации проектирования, сложные конструкции, типизацию данных и даже элементы объективно ориентированного программирования!
Поддержка структур на этом фоне уже не выглядит чем-то удивительным и активно используется многими программистами. Это позволяет забыть о смещениях и оперировать одними удобочитаемыми метками.
Рассмотрим простой пример — фрагмент кода, который проходит по цепочке MCB блоков памяти MSDOS.
CALL .FirstMCB Найти первый MCB
gmm_while: Сканируем цепочку MCB
MOV ES, AX ES = first MCB
CMP Byte ptr ES:[fi],'M' Это продолжение цепочки?
JNZ gmm__z --> Конец/Обрыв цепочки
gmm__next: // Следущий элемент цепочки
ADD AX, ES:[3] Размер блока
INC АХ заголовок MC
JMP Short gmm_while --> Цикл
Подчеркнуты константы, которые без знания структуры МСВ-блока делают этот код бессмысленным. Но разве возможно удержать в голове архитектуру всех компонентов современных операционных систем с точностью до смещений?
Разумеется, нет. Да этого и не требуется — достаточно лишь заменить их соответствующими символьными именами, которые несложно запомнить.
В нашем примере можно поступить так:
MCB struc ; (sizeof=Ox10)
IsLastBlock db ?
ParentPSPaddr dw ?
Size dw ?
Name db 11 dup(?)
MCB ends


IDA - 0258.htm

258 Структуры
Мы определили новую структуру "MCB", и теперь для доступа к ее членам необязательно знать их смещения от начала структуры. Это сделает за нас ассемблер!
Тогда исходный текст программы будет выглядеть так:
CALL .FirstMCB Найти первый MCB
gmm_while: Сканируем цепочку MCB
MOV ES, AX ES = first MCB
CMP Byte ptr ES:[MCB.IsLastBlock],'M';
Это продолжение цепочки?
JNZ gmm_z --> Конец/Обрыв цепочки
gmm_next: // Следущий элемент цепочки
ADD AX, ES:[MCB.Size] Размер блока
INC АХ заголовок MC
JMP Short gmm_while --> Цикл
Не правда ли, он стал понятнее? Разумеется, то же можно сказать и о дизассемблированием листинге, — чтобы не держать все смещения в памяти и ежесекундно не заглядывать в справочник, лучше определить их как члены структуры, дав им понятные символьные имена.
После работы с IDA может показаться странным, что далеко не все дизассемблеры поддерживают структуры, а уж тем более собственноручно определенные пользователем. В этом отношении IDA неоспоримый лидер. Она обладает развитой поддержкой структур, которые использует для множества целей. Именно так, например, происходит обращение к локальным переменным и аргументам функций.
Да, это все члены скрытой от пользователя структуры, но программно (т.е. на уровне скриптов) ничем не отличающиеся от остальных.
.text:00403A80 _memset proc near
.text:00403A80
.text:00403A80
.text:00403A80 arg_0 = dword ptr 4
.text:00403A80 arg_4 = byte ptr 8
.text:00403A80 arg_8 = dword ptr OCh
Действительно, если немного изменить синтаксис объявления локальных переменных, то он ничем не будет отличаться от структуры:
Memset_arg struc
Save_reg DD ?
Arg_0 DD ?
Arg_4 DD ?
Arg_8 DD ?
Memset_arg ends
Таким образом, структуры перестают быть всего лишь синтаксической конструкцией целевого ассемблера, а становятся ключевым элементом архитектуры IDA и используются ее ядром для облегчения доступа ко многим сгруппированным по какому-то признаку данных.


IDA - 0259.htm

Структуры 259
Внешне (интерактивно) для работы со структурами достаточно пары команд меню, поэтому создается ложное впечатление, что в поддержке структур ничего сложного нет. На самом деле требуется около двух десятков высокоуровневых функций, чтобы обеспечить реализацию всех необходимых операций. Но прежде чем углубляться в описание каждой из них, полезно получить представление об архитектуре структур в целом.
Архитектура структур в IDA
Итак, что есть структура с точки зрения IDA? Это прежде всего элемент bTree, как сегмент или функция, но в отличие от них структура не связана ни с каким линейным адресом. Это самостоятельный объект, существующий вне адресного пространства дизассемблируемого файла.
В таком случае возникает вопрос: как к ней может осуществляться доступ? Приходится выбирать другую уникальную характеристику, которая отличала бы одну структуру от другой. Можно было бы использовать имя или любую производную от него величину, но разработчик IDA выбрал другой путь. Он связал каждую структуру с 32-разрядным целым числом — идентификатором (сокращенно ID), который возвращался при создании структуры. Грубо говоря, можно считать идентификатор аналогом дескриптора файла, с которым приходится сталкиваться в современных операционных системах. Различия между ними и в самом деле несущественны, хотя все же имеются — так, например, после закрытия файла его дескриптор освобождается и может быть повторно присвоен вновь открытому файлу, а идентификаторы уникальны и никогда не присваиваются дважды, — даже если связанный с ними объект был разрушен.
Идентификаторы неудобны тем, что их приходится не только хранить, но и распределять между несколькими процессами. Ведь чаще всего один скрипт (IDA, пользователь) создает структуру, с которой приходится работать совсем другому скрипту.
Такая проблема стояла и перед разработчиками операционной системы Zerro Way (более известной широким кругам как Windows NT). И в этом случае выход был один — использовать помимо идентификаторов, поименный доступ к объектам.
Символьные имена в самом деле гораздо удобнее малоосмысленных 32-битных числовых значений, однако поддерживать два набора функций для имен и для идентификаторов — по меньшей мере неразумно. Поэтому в IDA была введена функция, которая позволяла по имени структуры установить ее идентификатор (GetStrucIdByName), и обратная ей, GetStrucName, которая по идентификатору возвращала имя.
Это позволило писать понятный код наподобие следующего:
DelStruc(
GetStrucIdByName("struc_10")
) ;
Небольшое замедление выполнения кода с лихвой окупалось его удобочитаемостью, и поэтому он стал очень популярным (именно так построены все примеры


IDA - 0260.htm

260 Структуры
скриптов, приведенные ниже). Однако одно лишь это не решало всех проблем: имя структуры требовалось как-то передавать скрипту, что было не всегда осуществимо. Поэтому необходим механизм, обеспечивающий доступ ко всем существующим структурам. Теоретически это можно осуществить с помощью идентификаторов. Так, если просканировать все числа от нуля до 0xFFFFFFFE, то можно обнаружить все структуры, которые присутствуют в базе, и получить к ним доступ.
Как же это медленно! Но не стоит отказываться от умных идей: можно загнать все структуры в один список, проиндексированный числами от нуля до номера последней созданной структуры, — тогда все операции с ним не потребуют никаких накладных расходов.
И в самом деле, IDA поддерживает именно такой список. Так, например, чтобы узнать идентификаторы всех существующих структур, достаточно выполнить следующий бесхитростный код:
auto а;
a=0;
while(1)
{
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
);
a=GetNextStrucIdx(а);
if (a==-l) break/
}
Ключевой его фигурой является функция GetStrucid, которая возвращает идентификатор по индексу структуры.
Однако индексы нежестко связаны с идентификаторами, и использовать их для доступа к структурам можно только сразу же после получения, а точнее, только на протяжении того времени, в течение которого гарантированно ни одна структура не была добавлена или удалена. Фактически индексы были введены, чтобы быстро получить список структур, и ни для чего большего их использовать не рекомендуется — разве что на свой страх и риск.
При этом будьте внимательны, иначе можно совершить ошибку наподобие следующей:
auto а;
for(a=0;a<GetStrucQty();a++) DelStruc(GetStrucId(a));
С первого взгляда в этих двух строчках нет никакой ошибки, и скрипт будет работать как часы, но попробуйте его запустить — и произойдет нечто невразумительное.
В чем же дело? Причина в том, что индексы обновляются при каждом удалении структуры. Удалив структуру с индексом ноль, мы не можем переходить к индексу один, так как индексы были реорганизованы, и теперь нулевому индексу соответствует другая структура, а список был сокращен на единицу.
Правильный код, как это ни парадоксально на первый взгляд, должен выглядеть так:


IDA - 0261.htm

auto a;
for(a=0;a<GetStrucQty();a++)
DelStruc(GetStrucId(0));
Если вы не хотите искать приключений, не используйте индексы ни для чего другого, кроме как просмотра существующих структур!
Как осуществляется доступ к членам структуры? Рассмотрим все характеристики члена структуры. Как известно из руководств к языкам высокого уровня, это имя, тип и смещение относительно начала структуры.
В отличие от языков высокого уровня ассемблер MASM использует глобальное пространство имен, а это означает, что имя каждого члена структуры уникально и не может быть дважды повторено в системе. Это огромный недостаток, который сводит на нет все преимущества структур. Так, например, если структура MCB (см. выше) имеет член с именем size, то невозможно дать то же имя никакому члену другой структуры.
Впрочем, в TASM это ограничение устранено. Но, к сожалению, IDA не поддерживает такого режима работы. Имя члена могло бы служить идеальным средством доступа к нему, однако в IDA использован другой подход, который при ближайшем рассмотрении оказывается не только более удобным, но и универсальным.
Доступ к элементам структуры осуществляется по их смещениям, а точнее — заданием любого принадлежащего им смещения. Это позволяет рассматривать структуру как непрерывный "лоскут" адресного пространства с "объектами" — членами. Именно так, например, организован доступ к локальным переменным функций.
С точки зрения IDA каждый член структуры характеризуется не только его типом (грубо говоря, размером ячейки), но может иметь связанные объекты, такие как имя или комментарий.
Более того, член структуры может являться не только ячейкой памяти, но и вложенной структурой!
Методы

Функция | Описание
Long GetStrucQty (void) | Возвращает количество структур, созданных вызовом
 | AddStrucEx
Long GetFirstStrucIdx (void) | Возвращает индекс первой структуры в списке
long GetLastStrucIdx (void) | Возвращает индекс последней структуры в списке
long GetNextStrucIdx (long index) | Возвращает индекс следующей в списке структуры
long GetPrevStrucIdx (long index) | Возвращает индекс предыдущей в списке структуры
long GetStrucId (long index) | Возвращает ID структуры по индексу
long GetStrucIdByName | Возвращает идентификатор структуры по ее имени
(char name) |
char GetStrucName (long id) | Возвращает имя структуры по ее идентификатору


IDA - 0262.htm

262 Структуры

Функция | Описание
char GetStrucComment | Возвращает комментарии к структуре
(long id, long repeatable) |
long GetStrucSize (long id) | Возвращает размер структуры в байтах, который равен
 | сумме размеров всех ее членов
long GetMemberQty (long id) | Возвращает число членов структуры
long GetFirstMember (long id) | Возвращает смещение начала первого члена структуры
long GetLastMember (long id) | Возвращает смещение начала (не конца!) последнего
 | члена структуры
long GetStrucNextOff | Возвращает смещение начала следующего члена в
(long id, long offset) | структуре
long GetStrucPrevOff | Возвращает смещение начала предыдущего члена
(long id, long offset) | структуры
char GetMemberName | Возвращает имя члена структуры
(long id, long member_offset) |
char GetMemberComment (long id, | Возвращает комментарий, связанный с членом струк-
long member_offset, long repeatable) | туры
long GetMemberSize | Возвращает размер члена структуры в байтах
(long id, long member_offset) |
long AddStrucEx | Создает новую структуру
(long index, char name, long is_union) |
long IsUnion (long id) | Возвращает единицу, если тип структуры — объедине-
 | ние
success DelStruc (long id) | Удаляет существующую структуру по ее идентифика-
 | тору
long SetStrucIdx (long id, long index) | Изменяет индекс структуры
long SetStrucName | Изменяет имя структуры
(long id, char name) |
long SetStrucComment (long id, | Задает комментарий к структуре
char comment, long repeatable) |
long DelStrucMember | Удаляет член структуры
(long id, long member_offset) |
long SetMemberName (long id, | Изменяет имя члена структуры
long member_offset, char name) |
long SetMemberType (long id, | Изменяет тип члена структуры
long member_offset, long flag, |
long typeid, long nitems) |
long SetMemberComment (long id, | Задает комментарий члена структуры
long member_offset, char comment, |
long repeatable) |
long GetStrucQty (void)
Функция возвращает количество структур, созданных вызовом AddStrucEx. Все они отображаются IDA в списке структур, который доступен из меню ~ View \ Structures.


IDA - 0263.htm

Структуры 263
Структуры, обеспечивающие доступ к элементам стековых фреймов, в это
число не входят.
Если не создано ни одной структуры, то функция возвращает ноль.
Пример использования:
Q копия экрана IDA:
0000 struc_1 struc
0000 field_0 db ?
0001 field_1 db ?
0002 struc_1 ends
0002
0000 ; -------------------
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; -------------------
0000
0000 struc_3 struc
0000 field_0 db ?
0001 struc_3 ends
> ввод пользователя:
Message("Ox%X \n",
GetStucQty()
);
< ответ IDA:
3

Return | ==return | Пояснения
 | !=0 | Число структур, созданных вызовами AddStrucEx
 | ==0 | Нет ни одной структуры
long GetFirstStrucIdx (void)
Функция возвращает индекс первой структуры в списке. Если существует
хотя бы одна структура, то функция всегда возвращает ноль.
Например:
Q копия экрана IDA:
0000 struc_1 struc
0000 field_0 db ?
0001 field_1 db ?
0002 struc_1 ends
. 0002
0000 ; -------------------
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends


IDA - 0264.htm

264 Структуры
0002
0000 ; -------------------
0000
0000 struc_3 struc
0000 field_0 db ?
0001 struc_3 ends
> ввод пользователя:
Message("Ox%X \n",
GetFirstStrucIdx()
)/
< ответ IDA:
0x0
Список автоматически перестраивается при операциях удаления или добавления структур, поэтому индексы не остаются постоянными. Использовать их для доступа к структуре не рекомендуется.
Например, если удалить struc_l, а потом повторить вызов GetFirstStrucIdx, то она вновь вернет ноль, однако теперь это индекс struc_2, а не srtuc_l.
Q копия экрана IDA:
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; -------------------
0000
0000 struc_3 struc
0000 field_0 db ?
0001 struc_3 ends
> ввод пользователя:
Message("Ox%X \n",
GetFirstStrucIdx()
);
< ответ IDA:
0x0

Return | ==return | Пояснения
 | ==0 | Индекс первой структуры в списке (всегда | ноль)
 | ==BADADDR | Нет ни одной структуры
long GetLastStrucIdx (void)
Функция возвращает индекс последней структуры в списке. Он всегда равен GetStrucQty() - 1. В том случае, если не определено ни одной структуры, функция возвратит ошибку BADADDR.
Q копия экрана IDA:
0000 struc_1 struc
0000 field 0 db ?


IDA - 0265.htm

Структуры 265
0001 field_1 db ?
0002 struc_1 ends
0002
0000 ; -------------------
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; -------------------
0000
0000 struc_3 struc *
0000 field_0 db ?
0001 struc_3 ends
> ввод пользователя:
Message("Ox%X \n",
GetLastStrucIdx()
) ;
< ответ IDA:
0x2

Return | ==return | Пояснения
 | !=BADADDR | Индекс последней структуры в списке
 | ==BADADDR | Нет ни одной структуры
long GetNextStrucIdx (long index)
Функция возвращает следующий индекс в списке структур. Индекс выражается целым числом от нуля до GetStrucQty() - 1. Индексы следуют последовательно вплотную друг за другом без "пустот", поэтому псевдокод этой функции очень прост:
Q копия экрана IDA:
CODE:1001D3EO push ebx
CODE:1001D3E1 mov ebx, eax
CODE:1001D3E3 inc ebx
CODE:1001D3E4 call @get_struc_qty$qqrv
CODE:1001D3E9 cmp ebx, eax
CODE:1001D3EB jb short loc_0_1001D3F2
CODE:1001D3ED ог eax, OFFFFFFFFh
CODE:1001D3FO pop ebx
CODE:1001D3F1 retn
Или то же на языке Си:
long GetNextStructIdx(long index)
{
if (GetStrucQty()<a) return -1;
return ++index;
}


IDA - 0266.htm

266 Структуры
По этой причине два следующих скрипта абсолютно идентичны:
Q копия экрана IDA:
0000 struc_1 struc
0000 field_0 db ?
0001 field_1 db ?
0002 field_2 db ?
0003 struc_1 ends
0003
0000 ; -------------------
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; ---------------
0000
0000 struc_3 struc
0000 field_0 dd ?
0004 struc_3 ends
0004
> ввод пользователя:
auto а;
for(a=0;a<GetStrucQty();a++)
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
);
< ответ IDA:
0x0 0xFF0000F0 0x1 0xFF0000FE 0x2 0xFF000100
> ввод пользователя:
auto а;
a=0;
while(l)
{
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
);
a=GetNextStrucIdx(a);
if (a==-l) break;
}
< ответ IDA:
0x0 0xFF0000F0 0x1 0xFF0000FE 0x2 0xFF000100
Какой из этих двух способов использовать — дело вкуса каждого, однако читабельность первого примера значительно лучше, и вероятность допустить ошибку — меньше.


IDA - 0267.htm

Структуры 267

Операнд | Пояснения
index | Индекс структуры в списке
 | (от нуля до GetStrucQty()-l)
Return | ==return | Пояснения
 | !=BADADDR | Индекс следующей структуры в списке
 | ==BADADDR | Ошибка
long GetPrevStrucIdx (long index)
Функция возвращает предыдущий индекс в списке структур. Индекс выражается целым числом от нуля до GetStrucQty() - 1. Индексы следуют последовательно вплотную друг за другом без "пустот", поэтому псевдокод этой функции очень прост:
Q копия экрана IDA:
long GetPrevStrucIdx(long index)
{
if (index<-1) return;
return -index-;
}
По этой причине два следующих скрипта абсолютно идентичны:
Q копия экрана IDA:
0000 struc_1 struc
0000 field_0 db ?
0001 field_1 db ?
0002 field_2 db ?
0003 struc_1 ends
0003
0000 ; -------------------
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; -------------------
0000
0000 struc_3 struc
0000 field_0 dd ?
0004 struc_3 ends
0004
> ввод пользователя:
auto а;
for(a=GetStrucQty();a>0;—а)
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
);


IDA - 0268.htm

268 Структуры
< ответ IDA:
0x2 0xFF000100 0x1 0xFF0000FE 0x0 0xFF0000F0
> ввод пользователя:
auto а;
a=GetStrucQty()-1;
while (1)
{
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
) ;
a=GetPrevStrucIdx(а);
if (a==-l) break;
}
< ответ IDA:
0x2 0xFF000100 0x1 0xFF0000FE 0x0 0xFF0000F0
Какой из этих двух способов использовать — дело вкуса. Читабельность
первого примера значительно лучше, и вероятность допустить ошибку — меньше.

Операнд | Пояснения
index | Индекс структуры в списке (от нуля до GetStrucQty()- 1)
Return | ==return | Пояснения
 | !=BADADDR | Индекс предыдущей структуры в списке
 | ==BADADDR | Ошибка
long GetStrucId (long index)
Функция возвращает ID структуры по индексу. Как уже отмечалось, индекс не может точно идентифицировать связанную с ним структуру, поскольку при любых операциях, связанных с дополнением или удалением структур, список перестраивается, и тот же индекс может указывать совсем на другую структуру.
В отличие от индекса идентификатор (ID) структуры представляет собой уникальное 32-битное значение, всегда указывающее на одну и ту же структуру. Более того, даже если структура, связанная с конкретным идентификатором, была удалена, тот же идентификатор не будет выдан ни одной созданной после этого структуре. Это гарантирует непротиворечивость ситуации и позволяет различным скриптам совместно использовать один и тот же идентификатор.
Пример использования:
Q копия экрана IDA:
0000 struc_1 struc
0000 field_0 db ?
0001 field_1 db ?
0002 field_2 db ?


IDA - 0269.htm

Структуры 269
0003 struc_1 ends
0003
0000 ; ------ — — --- —
0000
0000 struc_2 struc
0000 field_0 dw ?
0002 struc_2 ends
0002
0000 ; — — — — — — —
0000
0000 struc_3 struc
0000 field_0 dd ?
0004 struc_3 ends
0004
> ввод пользователя:
auto a;
for(a=0;a<GetStrucQty();a++)
Message("Ox%X Ox%X \n",
a,GetStrucId(a)
);
< ответ IDA:
0x0 0xFF0000F0 0x1 0xFF0000FE 0x2 0xFF000100
Идентификатор, как и дескриптор, с точки зрения пользователя — абстрактное "магическое" число, интерпретировать которое допускается только операционной системе (в качестве которой выступает в данном случае IDA).

Операнд | Пояснения
index | Индекс структуры в списке (от нуля до GetStrucQty()- 1)
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор (ID) структуры
 | ==BADADDR | Ошибка
long GetStrucldx (long id)
Функция позволяет получить индекс структуры в списке по ее идентификатору (ID). Обычно такой операции не требуется, поскольку практически все функции принимают на входе именно идентификатор, а не индекс.

Операнд | Пояснения
id | Идентификатор структуры
Return | ==return | Пояснения
 | !=BADADDR | Индекс
 | ==BADADDR | Ошибка


IDA - 0270.htm

270 Структуры
long GetStrucIdByName (char name)
Функция возвращает идентификатор структуры по ее имени. Имя структуры уникально (двух и более структур с одним и тем же именем существовать не может), поэтому неоднозначности не возникает.
Пример использования:
> ввод пользователя:
auto a,b;
a=AddStrucEx(-l,"MyNewStrucl", 0) ;
b=GetStrucIdByName("MyNewStrucl");
Message("Ox%X Ox%X \n",a,b);
Q копия экрана IDA:
0000 MyNewStruc struc ; (sizeof=0)
0000 MyNewStruc ends
< ответ IDA:
0xFF00020A 0xFF00020A
Обратите внимание, что функция чувствительна к регистру (большинство ассемблеров его игнорируют), поэтому имена "MyStruc" и "mystruc" не считаются идентичными, что и доказывает следующий пример:
> ввод пользователя:
auto a,b;
a=AddStrucEx(-1,"MyNewStruc", 0) ;
b=GetStrucIdByName("mynewstruc");
Message("Ox%X Ox%X \n",a,b);
Q копия экрана IDA:
0000 MyNewStruc struc ; (sizeof=0)
0000 MyNewStruc ends
< ответ IDA:
0xFF00020A 0xFFFFFFFF

Операнд | Пояснения
Name | Имя структуры
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор
 | ==BADADDR | Ошибка
char GetStrucName (long id)
Функция возвращает имя структуры по ее идентификатору. Очень часто
используется совместно с GetStrucId.
Например:
Q копия экрана IDA:
0000 MyGoodStuc struc ; (sizeof=Ox2)
0000 field_0 dw ?
0002 MyGoodStuc ends


IDA - 0271.htm

Структуры 271
0002
0000 ; — — — — — — — — — — — —
0000
0000 MyStruc struc ; (sizeof=Ox5)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 db ?
0005 MyStruc ends
0005
0000 ; ----------------------------------
0000
0000 My struc ; (sizeof=0)
0000 My ends
0000
0000 ; -----------------------------
0000
0000 MyNewStruc struc ; (sizeof=0)
0000 MyNewStruc ends
0000
> ввод пользователя:
auto a;
for (a=0;a<GetStrucQty();a++)
Message("%s \n",
GetStrucName(GetStrucId(a))
) ;
< ответ IDA:
MyGoodStruc
MyStruc
My
MyNewStruc

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | ==return | Пояснения
 | I="" | Имя структуры
 | =="" | Ошибка
char GetStrucComment (long id, long repeatable)
Функция возвращает комментарии к структуре. В текущих версиях, включая IDA 4.0, комментарии к структурам поддерживаются лишь частично. Так, например, отсутствует возможность интерактивного комментирования функций (приходится пользоваться вызовом SetStructComment), повторяемые комментарии поддерживаются лишь частично, что подтверждается следующим примером:
> ввод пользователя:
SetStrucComment(
GetStrucIdByName("_msExcInfo") ,
" MyComment", 1) ;


IDA - 0272.htm

272 Структуры
Q копия экрана IDA:
0000 ; MyComment
OOOO_msExcInfo struc; (sizeof=Ox8) XREF: .rdata:004077E6
0000 .rdata:00407780r ...
0000 Id dd ? sss
0004 Proc dd ? offset (FFFFFFFF)
0008 _msExcInfo ends
.rdata:004077E6 dd 1879048192 ; Id
.rdata:004077E6 dd 0 ; Pro
> ввод пользователя:
Message(."%s \n",
GetStrucComment(
GetStrucIdByName("_msExcInfo"),
1) ;
MyComment
Обратите внимание, что IDA не отобразила повторяемый комментарий в
строке rdata:004077E6, хотя это следовало бы сделать.

Операнд | Пояснения
id | Идентификатор (ID) структуры
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | Завершение | Пояснения
 | !="" | Комментарий
 | "" | Ошибка
long GetStrucSize (long id)
Функция возвращает размер структуры в байтах, который равен сумме размеров всех ее членов. Он отображается в качестве комментария в окне просмотра структур.
Допускается существование структур без единого элемента, размер которых равен нулю.
Q копия экрана IDA:
0000 _msExcInfo struc ; (sizeof=Ox8)
0000
0000 Id dd ?
0004 Proc dd ?
0008 _msExcInfo ends
> ввод пользователя:
Message("Ox%X \n",
GetStrucSize(GetStrucIdByName("_msExcInfo"))
);


IDA - 0273.htm

Структуры 273
< ответ IDA:
0x8
G копия экрана IDA:
0000 struc_3 struc ; (sizeof=0)
0000 struc_3 ends
> ввод пользователя:
Message("Ox%X \n",
GetStrucSize(GetStrucIdByName("struc_3"))
) ;
< ответ IDA:
0x0

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | ==return | Пояснения
 | !=BADADDR | Размер структуры
 | ==BADADDR | Ошибка
long GetMemberQty (long id)
Функция возвращает число членов структуры. Допускается существование
структур без единого элемента, число членов которых равно нулю.
G копия экрана IDA:
0000 _msExcept struc ;
0000 Magic dd ?
0004Count dd ?
0008 InfoPtr dd ?
OOOC CountDtr dd ?
0010 DtrPtr dd ?
0014 _unk dd 3 dup(?)
0020 Info _msExcInfo 0 dup(?)
0020 _msExcept ends
> ввод пользователя:
Message("Ox%X \n",
GetMemberQty(GetStrucIdByName("_msExcept"))
);
< ответ IDA:
0x7

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | !=BADADDR | Число членов структуры
 | ==BADADDR | Ошибка


IDA - 0274.htm

274 Структуры
long GetStrucNextOff(long id, long offset)
Функция возвращает смещение начала очередного элемента в структуре. Первый элемент всегда имеет нулевое смещение (что очевидно), а последний — смещение, численно равное размеру структуры минус единица. Это происходит потому, что каждую структуру замыкает "виртуальный" элемент, который невидим для всех остальных функций (в том числе и для GetMemberQty). Он был введен из соображений удобства программирования и во всех остальных случаях может не браться в расчет.
Если неверно задан идентификатор или структура не содержит ни одного члена, то в обоих случаях возвращается ошибка BADADDR.
Например:
Q копия экрана IDA:
0000 _msExcept struc ; (sizeof=Ox22)
0000
0000 Magic dd ?
0004 Count dd ?
0008 InfoPtr dd ?
OOOC CountDtr dd ?
0010 DtrPtr dd ?
0014 _unk dd 3 dup(?)
0020 Info dw ?
0022 _msExcept ends
> ввод пользователя:
auto a;
a=0;
for (;;)
{
Message("Ox%X \n",a);
a=GetStrucNextOff
(GetStrucIdByName("_msExcept"),a);
if (a==-l) break;
}
< ответ IDA:
0x0
0x4
0x8
0xC
0x10
0x14
0x20
0x22
G копия экрана IDA:
0000 struc_9 struc ; (sizeof=0)
0000 struc_9 ends
> ввод пользователя:
Message("Ox%X \n",
GetStrucNextOff(


IDA - 0275.htm

Структуры 275
GetStrucIdByName("struc_9"))
) ;
< ответ IDA:
0xFFFFFFFFF
Числа, отображаемые IDA слева от элементов структуры, и есть искомые смещения элементов. При этом необязательно, чтобы каждому смещению соответствовал именованный элемент. Поскольку для доступа к членам структуры используются не имена, а смещения элементов, то IDA поддерживает и безымянные поля, которые могут оказаться полезными в ряде случаев.
0000 struc_3 struc ; (sizeof=Oxd)
0000 field_0 dw ?
0002 db ? ; undefined
0003 field_3 dd ?
0007 field_7 db ?
0008 db ? undefined
0009 db ? undefined
OOOA db ? undefined
OOOB field_B db ?
OOOC field_C db ?
OOOD struc__3 ends
Членом структуры могут быть, в том числе, и массивы однотипных (гомогенных) элементов.
0000 struc_7 struc ; (sizeof=Ox15)
0000 field_0 dd ?
0004 field_4 db 16 dup(?)
0014 field_14 db ?
0015 struc_7 ends
В этом случае очередным смещением будет смещение следующего элемента структуры.

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | !=BADADDR | Смещение начала очередного | члена структуры
 | ==BADADDR | Ошибка
long GetStrucPrevOff (long id, long offset)
Функция возвращает смещение начала предыдущего члена структуры. В остальном полностью идентична GetStrucNextOff.
Смещение конца (не начала!) последнего элемента можно получить вызовом GetStrucPrevOff(id,-0;
Если неверно задан идентификатор или структура не содержит ни одного члена, то в обоих случаях возвращается ошибка BADADDR.


IDA - 0276.htm

276 Структуры
Например:
Q копия экрана IDA:
0000 _msExcept struc ; (sizeof=Ox22)
0000 Magic dd ?
0004 Count dd ?
0008 InfoPtr dd ?
OOOC CountDtr dd ?
0010 DtrPtr dd ?
0014 _unk dd 3 dup(?)
0020 Info dw ?
0022 _msExcept ends
> ввод пользователя:
auto a;
a=-l;
for (;;)
{
a=GetStrucPrevOff
(GetStrucIdByName("_msExcept"),a);
if (a==-l) break;
Message("Ox%X \n",a);
}
< ответ IDA:
0x22
0x20
0x14
0x10
0xC
0x8
0x4
0x0
Q копия экрана IDA:
0000 struc_9 struc ; (sizeof=0)
0000 struc_9 ends
> ввод пользователя:
Message("Ox%X \n",
GetStrucPrevOff(
GetStrucIdByName("struc_9"))
);
< ответ IDA:
0xFFFFFFFFF

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | !=BADADDR | Смещение начала предыдущего члена | структуры
 | ==BADADDR | Ошибка


IDA - 0277.htm

Структуры 277
long GetFirstMember (long id)
Функция возвращает смещение начала первого члена структуры. Это значение всегда равно нулю, за тем исключением, когда неверно задан идентификатор или структура не содержит ни одного члена. В обоих случаях возвращается ошибка BADADDR.
Например:
Q копия экрана IDA:
0000 _msExcInfo struc ; (sizeof=Ox8)
0000 Id dd ?
0004 Proc dd ?
0008 _msExcInfo ends
> ввод пользователя:
GetFirstMember(
GetStrucIdByName("_msExcept"))
);
< ответ IDA:
0x0
G копия экрана IDA:
0000 struc_9 struc ; (sizeof=0)
0000 struc_9 ends
> ввод пользователя:
Message("Ox%X \n",
GetFirstMember(
GetStrucIdByName ("struc__9") )
);
< ответ IDA:
0xFFFFFFFFF

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | !=BADADDR | Смещение начала первого | члена структуры
 | ==BADADDR | Ошибка
long GetLastMember (long id)
Функция возвращает смещение начала (не конца!) последнего члена структуры. Обратите внимание, что этот результат не совпадает со значением, возвращаемым GetStrucNextOff для последнего элемента!
Например:
Q копия экрана IDA:
0000 _msExcept struc ; (sizeof=Ox22)
0000
0000 Magic dd ?


IDA - 0278.htm

278 Структуры
0004 Count dd ?
0008 InfoPtr dd ?
OOOC CountDtr dd ?
0010 DtrPtr dd ?
0014 _unk dd 3 dup(?)
0020 Info dw ?
0022 _msExcept ends
> ввод пользователя:
auto a;
a=0;
for (;;)
{
Message("Ox%X \n",a);
a=GetStrucNextOff
(GetStrucIdByName("_msExcept"),a);
if (a==-l) break;
}
< ответ IDA:
0x0
0x4
0x8
0xC
0x10
0x14
0x20
0x22
> ввод пользователя:
Message("Ox%X \n",
GetLastMember(
GetStrucIdByName("_msExcept"))
);
< ответ IDA:
0x20
Если неверно задан идентификатор или структура не содержит ни одного
члена, то в обоих случаях возвращается ошибка BADADDR.

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | !=BADADDR | Смещение начала последнего члена | структуры
 | ==BADADDR | Ошибка
char GetMemberName (long id, long member_offset)
Функция возвращает имя члена структуры. Для этого необходимо задать
идентификатор (ID) структуры и смещение интересующего нас члена от ее начала


IDA - 0279.htm

Структуры 279
(member_offset). Подробнее об этом можно прочитать в описании функции
GetStrucNextOff.
Пример использования:
Q копия экрана IDA:
0000 MyStruc struc ; (sizeof=Ox7)
0000 field_0 db ?
0001 field_1 dw ?
0003 field_3 dd ?
0007 MyStruc ends
> ввод пользователя:
auto а;
for (a=0;;)
{
Message("Ox%X %s \n",
а,
GetMemberName (
GetStrucIdByName("MyStruc") , a)
) ;
a=GetStrucNextOff(
GetStrucIdByName("MyStruc"),a
) ;
if (a==-l) break;
}
< ответ IDA:
0x0 field_0 0x1 field_l 0x3 field_3 0x7
Очевидно, что код работает неправильно и пытается возвратить на одно имя члена больше, чем содержит структура. Причина такого поведения заключается в том, что функция GetStrucNextOff возвращает смещение "виртуального" элемента, замыкающего структуру. И хотя IDA отображает его имя, как показано ниже, на самом деле виртуальный элемент не имеет никакого имени и невидим для всех остальных функций, кроме GetStrucNextOff, GetPrevNextOff:
0007 MyStruc ends
Поэтому необходимо использовать другую проверку, например, очередное смещение, возвращенное GetStrucNextOff co смещением последнего элемента, которое можно узнать вызовом GetLastMember.
8 результате код должен выглядеть так:
> ввод пользователя:
auto а;
for (a=0;;)
{
Message("Ox%X %s \n",
а'
GetMemberName(


IDA - 0280.htm

280 Структуры
GetStrucIdByName("MyStruc") ,a)
);
a=GetStrucNextOff(GetStrucIdByName("MyStruc"),a);
if (a>GetLastMember(GetStrucIdByName("MyStruc"))) break;
}
< ответ IDA:
0x0 field_0 0x1 field_l 0x3 field_3
Необязательно указывать точное смещение начала члена. Необходимо лишь, чтобы указанная величина лежала в границах интересующего нас члена структуры. IDA автоматически округлит ее до смещения начала члена.
Это может продемонстрировать следующий скрипт:
Q копия экрана IDA:
0000 MyStruc struc
0000 field_0 dw ?
0002 field_1 dw ?
0004 field_2 dw ?
0006 MyStruc ends
> ввод пользователя:
auto а;
for (a=0;;)
{
Message("Ox%X %s \n",
а,
GetMemberName (
GetStrucIdByName("MyStruc"),
a+1)
) ;
a=GetStrucNextOff(
GetStrucIdByName("MyStruc"),a
) ;
if (a==-l) break;
}
< ответ IDA:
0x0 field_0 0x1 field_l 0x3 field 2

Операнд | Пояснения
id | Идентификатор (ID) структуры
member_offset | Смещение, лежащее в границах интересующего нас элемента
Return | Завершение | Пояснения
 | !="" | Имя члена структуры
 | =="" | Ошибка


IDA - 0281.htm

Структуры 281
char GetMemberComment (long id, long member_offset, long repeatable)
Функция возвращает комментарий, связанный с членом структуры. IDA поддерживает два типа комментариев — 'regular' и 'repeatable'. Последний отличается тем, что дублируется по месту обращения к элементу структуры. Однако в случае со структурами и их членами IDA игнорирует это требование, в чем убеждает следующий пример:
0000 MyStruc struc ; (sizeof=Ox4) ; XREF: segOOO:OF72r
0000 field_0 dw ? ; My Repeatable comment
0002 field_1 dw ? ;
0004 MyStruc ends
segOOO:OF72*stru_0_F72 dw 0 ; field_0 ; DATA XREF: sub_0_F56r
segOOO:OF72* ; sub_0_2456+1Cw ...
segOOO:OF72* dw 0 ; field_1
segOOO:OF56 mov es, stru_0_F72.field_0
segOOO:OF5A assume es:nothing
segOOO:OF5A xor bx, bx
Остается только надеяться, что рано или поздно такая поддержка появится.

Операнд | Пояснения
id | Идентификатор (ID) структуры
member_offset | Смещение, лежащее в границах интересующего нас элемента
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | Завершение | Пояснения
 | !="" | Комментарий
 | "" | Ошибка
long GetMemberSize (long Id, long member_offset)
Функция возвращает размер члена структуры в байтах. Для этого необходимо задать любое принадлежащее ему смещение. Это дает возможность самостоятельно проходить список элементов, без использования функции GetStrucNextOff, которая отличается необычным поведением (подробности можно узнать в описании этой функции).
Например:
Q копия экрана IDA:
0000 MyStruc struc ; (sizeof=Ox19)
0000 field_0 db ?
0001 field_1 dw ?
0003 field_3 dd ?
0007 field_7 dq ?
OOOF field_F dt ?
0019 MyStruc ends


IDA - 0282.htm

282 Структуры
> ввод пользователя:
auto а;
for (a=0;;)
{
Message ("Ox%X Ox%X\n",
а,
GetMemberSize(
GetStrucIdByName("MyStruc")/
а) ) ;
a=a+GetMemberSize(
GetStrucIdByName("MyStruc"),
a) ;
if (a>GetLastMember(
GetStrucIdByName("MyStruc"))
) break;
}
< ответ IDA:
0x0 0x1 0x1 0x2 0x3 0x4 0x7 0x8 0xF 0xA

Операнд | Пояснения
id | Идентификатор (ID) структуры
member_offset | Смещение, лежащее в границах интересующего |  | нас элемента
Return | Завершение | Пояснения
 | !=BADADDR | Размер члена структуры в | байтах
 | ==BADADDR | Ошибка
long GetMemberStrld (long id, long member__offset)
Функция возвращает ID члена структуры, если он является структурой, или BADADDR в противном случае. IDA поддерживает вложенные структуры и дает возможность получить идентификатор для нисходящего разбора. Вложение при этом может быть как угодно глубоким, и вложенные структуры могут содержать ссылки друг на друга.
Например:
Q копия экрана IDA:
0000 struc_1 struc ; (sizeof=Ox11)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 field_8 struc_2 0 dup(?)
0011 struc_1 ends
0011


IDA - 0283.htm

Структуры 283
0000 ; — — — — — — — — — — — — —
0000
0000 struc_2 struc ; (sizeof=Ox11)
0000 field_0 struc_1 ?
0011 struc_2 ends
0011
Можно создавать и вовсе бессмысленные комбинации, за исключением, пожалуй, структур, ссылающихся на самих себя.
Максимальная глубина вложенности равна единице: IDA всего-навсего поддерживает членов типа "структура" и умеет возвращать их ID. Bce остальное ложится на плечи программиста, пишущего скрипт.
Q копия экрана IDA:
0000 MyStruc struc ; (sizeof=Ox12)
0000 field_0 db ?
0001 field_1 dw ?
0003 field_3 dd ?
0007 field_7 dq ?
OOOF field_F ChldStruc ?
0012 MyStruc ends
0012
0000
0000 ChldStruc struc ; (sizeof=Ox3)
0000 field_0 db ?
0001 field_1 dw ?
0003 ChldStruc ends
> ввод пользователя:
auto a;
for (a=0;;)
{
Message ("Ox%X Ox%X\n",
a,
GetMemberStrld(
GetStrucIdByName("MyStruc") ,
a)) /
a=a+GetMemberSize(
GetStrucIdByName("MyStruc") ,
a);
if (a>GetLastMember(
GetStrucIdByName("MyStruc") )
) break;
}
< ответ IDA:
0x0 0xFFFFFFFF
0x1 0xFFFFFFFF
0x3 0xFFFFFFFF
0x7 0xFFFFFFFF
0xF 0xFF0000FB


IDA - 0284.htm

284 Структуры
Вольности, допускаемые IDA при обращении со структурами, приводят к огромным трудностям в написании действительно корректно работающего скрипта. Так, в приведенном выше примере при попытке вывести полный перечень членов структуры, включая вложенные, получится бесконечный рекурсивный спуск, и скрипт "зависнет". Однако не стоит усложнять код, — будьте внимательнее в отношении вложенных структур и не допускайте подобных ситуаций.
Пример использования:
Q копия экрана IDA:
0000 struc_2 struc ; (sizeof=Ox11)
0000 field_0 struc_1 ?
0011 struc_2 ends
> ввод пользователя:
Message("%x \n",
GetMemberStrld(
GetStrucIdByName("struc_2"), 0x0)
);
< ответ IDA:
FFOOOOF8

Операнд | Пояснения
id | Идентификатор (ID) структуры
member_offset | Смещение, лежащее в границах интересующего нас элемента
Return | Завершение | Пояснения
 | !=BADADDR | Идентификатор структуры
 | ==BADADDR | Элемент не является структурой
long AddStrucEx (long index, char name, long is_union)
Функция позволяет создавать новую структуру. Для этого необходимо указать ее имя, которое впоследствии может быть изменено, и тип, который в дальнейшем уже не может быть изменен.
Поддерживаются следующие типы структур:

Флаг is_union | Значение
1 | Структура типа "Объединение" UNION
0 | Структура по умолчанию
С точки зрения IDA оба типа идентичны, и работа с одним из них ничем не отличается от работы с другим. Различия проявляются при ассемблировании листинга. При необходимости ассемблер может располагать члены структуры по удобным для него адресам, вставляя незначимые байты, например, для выравнивания (что ускоряет работу кода). Во многих случаях это недопустимо и просто развалит всю программу. Для предотвращения этого и была введена поддержка типа "объединение", которая отличается от обычной структуры тем, что ассемб-


IDA - 0285.htm

____________________Структуры__________________285
лер всегда оставляет ее в неприкосновенности и гарантирует, что смещения
членов относительно друг друга останутся неизменными.
0000 struc_3 ' struc
0000 field_0 dw ?
0002 field_2 db ?
0003 field_3 dw ?
0005 struc_3 ends
0000 struc_4 union
0000 field_0 dw ?
0002 field_2 db ?
0003 field_3 dw ?
0005 struc_3 ends
Какой тип выбрать при создании структуры — должен решать сам пользователь. Чаще всего взаимное расположение членов в структуре некритично, поскольку ассемблер автоматически вычисляет смещение каждого члена. Но если речь идет о структуре, выражающей, скажем, заголовок файла, то любые отклонения от жестко заданных смещений приведут к неправильной работе программы, а следовательно, для работы с ними необходимо использовать тип "объединение".
При этом структуры обоих типов разделяют общее пространство имен. Другими словами, невозможно создать структуру, совпадающую по имени с уже существующим объединением. В остальном на имена наложены такие же ограничения, как и на метки. IDA различает строчные и заглавные буквы, поэтому 'MyStruc' и "MYSTRUC" для нее — два разных имени, которые могут быть присвоены двум структурам.
0000 MyStruc struc ; (sizeof=OxO)
0000 MyStruc ends
0000
0000 ; -------------------------------------
0000
0000 MYSTRUC struc ; (sizeof=OxO)
0000 MYSTRUC ends
Но большинство ассемблеров не различают регистра и выдадут ошибку, поэтому необходимо не допускать таких ситуаций.
Индекс структуры обычно устанавливают равным BADADDR — при этом IDA добавляет новую структуру в конец списка, автоматически расширяя последний.
Например:
G копия экрана IDA:
0000 struc_10 struc ; (sizeof=OxO)
0000 struc_10 ends
0000
0000 ; --------------------------------------
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc 11 ends


IDA - 0286.htm

286 _____________Структуры____________________
> ввод пользователя:
AddStrucEx(-l,"MYSTRUC",0) ;
Q копия экрана IDA:
0000 struc_10 struc ;
0000 struc_10 ends
0000
0000 ; — — — — — — —
0000
0000 struc_1 struc ;
0000 struc_11 ends
0000
0000 ; ---------------------
0000
0000 MYSTRUC struc ;
0000 MYSTRUC ends
0000
Имеется возможность задать произвольный индекс из уже существующих. При этом старая структура будет уничтожена, поэтому обычно к этому способу не прибегают.
Например:
Q копия экрана IDA:
0000 struc_10 struc ; (sizeof=OxO)
0000 struc_10 ends
0000
0000 ; ---------------------------------
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
> ввод пользователя:
AddStrucEx(0,"MY_STRUC",0);
Q копия экрана IDA:
0000 MY_STRUC struc ;
0000 MY_STRUC ends
0000
0000 ; ---------------------
0000
0000 struc_10 struc ;
0000 struc_10 ends
0000
Невозможно создать структуру с индексом, более чем на единицу превышаю-
щим последний существующий.
Например:
Q копия экрана IDA:
0000 struc_10 struc ; (sizeof=OxO)
0000 struc_10 ends
0000
0000 ; --------------------------------------


IDA - 0287.htm

Структуры 287
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
> ввод пользователя:
Message("Ox%X \n",
AddStrucEx(Ox22,"MY_STRUC",0)
);
< ответ IDA:
0xFFFFFFFF

Операнд | Пояснения
index | ==index | Действие
 | [0,MaxIdx] | Индекс структуры (старая структура при | этом будет затерта)
 | Maxldx+l | Индекс новой структуры
 | BADADDR | Индекс новой структуры
name | Имя струкуры
Is_union | ==is union | Значение
 | 1 | Структура типа "Объединение" UNION
 | 0 | Структура по умолчанию
Return | Завершение | Пояснения
 | !=BADADDR | Идентификатор структуры
 | ==BADADDR | Элемент не является структурой
Интерактивно создать структуру можно, вызвав командой меню ~ View \
Structures список всех структур и нажав <INS>.
Появится следующее диалоговое окно:
IDA - 0287-1.jpg
IDA - 0287-2.jpg


IDA - 0288.htm

288_________________Структуры______________
long IsUnion (long id)
Функция определяет тип структуры. Если она представляет собой объедине-
ние, то возвращается единица, — и ноль в противном случае.
Пример использования:
Q копия экрана IDA:
0000 union_13 union ; (sizeof=OxO)
0000 union_13 ends
0000
> ввод пользователя:
Message("%x \n",
IsUnion(
GetStrucIdByName("union_13")
)
);
< ответ IDA:
1
Q копия экрана IDA:
0000 MY_STRUC struc
0000 MY_STRUC ends
> ввод пользователя:
Message("%x \n",
IsUnion(
GetStrucIdByName("MY^STRUC")
)
);
< ответ IDA:
0
Обратите внимание, что функция в результате ошибки возвращает не BADADDR, а ноль\ Например:
Q копия экрана IDA:
0000 MY_STRUC struc
0000 MY_STRUC ends
> ввод пользователя:
Message("%x \n",
IsUnion(
GetStrucIdByName("MYSTRUCT")
)
);
< ответ IDA:
0
Неясно, действительно ли структура MYSTRUCT — не объединение, или ее вообще не существует. Поэтому достоверным значением, возвращенным функцией, следует считать только единицу.


IDA - 0289.htm

Структуры 289

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | Завершение | Пояснения
 | ==1 | Структура типа UNION
 | ==0 | Структура не типа UNION | или ошибка
success DelStruc (long id)
Функция удаляет существующую структуру по ее идентификатору. В большинстве случаев используется совместно с GetStrucIdxByName, поскольку ID структуры скрыт от пользователя.
Например:
Q копия экрана IDA:
0000 MY_STRUC struc
0000 MY_STRUC ends
0000
0000 ; --------------------
0000
0000 struc_10 struc
0000 struc_10 ends
0000
0000 ; --------------------
0000
0000 union_13 union
0000 union_13 ends
> ввод пользователя:
DelStruc(
GetStrucIdByName("struc_10")
);
Q копия экрана IDA:
0000 MY_STRUC struc ;
0000 MY_STRUC ends
0000
0000 ; -----------------
0000
0000 union_13 union ;
0000 union_13 ends
0000
При этом заново перестраиваются таблицы индексов структур, поэтому
полученные ранее значения уже не действительны. Их необходимо обновить.
Так, например:
G копия экрана IDA:
0000 MY_STRUC struc
0000 MY_STRUC ends
0000
0000 ; --——————
0000


IDA - 0290.htm

290 Структуры
0000 struc_10 struc
0000 struc_10 ends
0000
0000 ; --------------------
0000
0000 union_13 union
0000 union_13 ends
> ввод пользователя:
auto idl,id2;
idl= GetStrucId(0);
id2 = GetStrucId(l);
DelStruc(idl);
DelStruc(id2);
Q копия экрана IDA:
0000 struc_10 struc
0000 struc_10 ends
Вместо того, чтобы удалить первые две структуры, IDA удалила первую и третью. Но это ошибка не IDA, а разработчика скрипта! Действительно, когда была удалена первая структура, то под индексом 1 стала расположена не вторая, а третья структура!
Попытаемся сделать так:
auto idl,id2;
idl= GetStrucId(0);
DelStruc(idl);
id2= GetStrucId(l);
DelStruc(id2);
Получилось то же самое! Индексы были обновлены, однако этого оказалось мало! В действительности код должен выглядеть так:
auto idl,id2;
idl= GetStrucId(0);
DelStruc(idl);
id2= GetStrucId(0);
DelStruc(id2);
Из этого примера следует вывод: чтобы не усложнять себе жизнь, не стоит пользоваться индексами структур, особенно при операциях удаления. Вместо этого лучше получить идентификаторы структур по их имени, вызвав функцию GetStrucIdByName.

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0291.htm

Структуры 291
Для того, чтобы интерактивно удалить функцию, достаточно вызвать список функцией командой меню ~ View \ Structures, a затем, "встав" на любой элемент структуры, предназначенной для удаления, нажать DEL.
long SetStrucIdx (long id, long index)
Эта функция позволяет изменить индекс структуры, заданной ее идентификатором. Может использоваться для упорядочения структур в списке. Индекс может принимать значения от нуля до максимального индекса структуры, при этом две структуры обмениваются местами, и затирания не происходит. Например:
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=OxO)
0000 MY_STRUC ends
0000
0000 ; --------------------------------------
0000
0000 union_13 union ; (sizeof=OxO)
0000 union_13 ends
0000
0000 ; --- - - - ----------- - ------ - -
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
0000
0000 ; --------------------------------------
0000
0000 MYSTRUC struc ; (sizeof=OxO)
0000 MYSTRUC ends
> ввод пользователя:
SetStrucIdx(
GetStrucIdByName("MY_STRUC"), 2);
Q копия экрана IDA:
0000 union_13 union ; (sizeof=OxO)
0000 union_13 ends
0000
0000 ; -----------------------------------------
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
0000
0000 ; -----------------------------------------
0000
0000 MY_STRUC struc ; (sizeof=OxO)
0000 MY_STRUC ends
0000
0000 ; -----------------------------------------
0000
0000 MYSTRUC struc ; (sizeof=OxO)
0000 MYSTRUC ends
10*


IDA - 0292.htm

292__________________Структуры____________________
Если заданный индекс больше максимально допустимого, то считается, что
был указан последний существующий индекс. Например:
Q копия экрана IDA:
0000 union_13 union ; (sizeof=OxO)
0000 union_13 ends
0000
0000 ; - — -—------- — - — — - — — ---
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
0000
0000 ; -----------------------------------------
0000
0000 MY_STRUC struc ; (sizeof=OxO)
0000 MY_STRUC ends
0000
0000 ; --------------------------------------
0000
0000 MYSTRUC struc ; (sizeof=OxO)
0000 MYSTRUC ends
0000
> ввод пользователя:
SetStrucIdx(
GetStrucIdByName("MY_STRUC") ,
44);
G копия экрана IDA:
0000 union_13 union ; (sizeof=OxO)
0000 union_13 ends
0000
0000 ; ---------------------------------------
0000
0000 struc_11 struc ; (sizeof=OxO)
0000 struc_11 ends
0000
0000 ; ----------------------------------------
0000
0000 MYSTRUC struc ; (sizeof=OxO)
0000 MYSTRUC ends
0000
0000 ; - ——— — ------- —— —— ——— ———
0000
0000 MY_STRUC struc ; (sizeof=OxO)
0000 MY_STRUC ends

Операнд | Пояснения
id | Идентификатор (ID) структуры
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0293.htm

Структуры
293
long SetStrucName (long id, char name)
Функция позволяет изменить имя структуры, заданной по ее идентификатору.
Например:
G копия экрана IDA:
0000 union_13 union ;
0000 union_13 ends
> ввод пользователя:
SetStrucName(
GetStrucIdByName ("union__13") ,
"MyGoodRenamedStruc");
G копия экрана IDA:
0000 MyGoodRenamedStruc union ; (sizeof=OxO)
0000 MyGoodRenamedStruc ends

Операнд | Пояснения
id | Идентификатор (ID) структуры
name | Имя структуры
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Интерактивно переименовать функцию можно, вызвав список всех структур командой меню ^ View \ Structures, затем переместить курсор в начало выбранной структуры и нажать <N>.
IDA - 0293-1.jpg
IDA - 0293-2.jpg


IDA - 0294.htm

294 Структуры
long SetStrucComment (long id, char comment, long repeatable)
Функция задает комментарии к структуре. В текущих версиях, включая IDA 4.0, комментарии к структурам поддерживаются лишь частично. Так, например, отсутствует возможность интерактивного комментирования функций (приходится пользоваться вызовом SetStructComment), повторяемые комментарии поддерживаются лишь частично, что подтверждается следующим примером:
> ввод пользователя:
SetStrucComment(
GetStrucIdByName ("__msExcInfo") ,
" MyComment", 1) ;
Q копия экрана IDA:
0000 ; MyComment
OOOO_msExcInfo struc ; (sizeof=Ox8) ; XREF: .rdata:004077E6
0000 ; .rdata:00407780r ...
0000 Id dd ? ; sss
0004 Proc dd ? ; offset (FFFFFFFF)
0008 _msExcInfo ends
.rdata:004077E6 dd 1879048192 ; Id
.rdata:004077E6 dd 0 ; Pro
Обратите внимание, что IDA не отобразила повторяемый комментарий в строке rdata:004077E6, хотя следовало бы это сделать.

Операнд | Пояснения
id | Идентификатор (ID) структуры
Comment | Комментарий
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
long AddStrucMember (long id, char name, long offset, long flag,
long typeid, long nbytes)
Функция добавляет новый член к ранее созданной структуре, заданной ее идентификатором ID. Членом структуры может быть ASCII-строка, ячейка или другая структура, заданная ее идентификатором.
Тип добавляемого члена структуры определяется флагом flag следующим образом:

Определение | Значение | Пояснения
FF_BYTE | OxOOOOOOOOL | Байт
FF_WORD | OxlOOOOOOOL | Слово


IDA - 0295.htm

Структуры 295

Определение | Значение | Пояснения
FF_DWRD | Ox20000000L | Двойное слово
FF_QWRD | Ox30000000L | Четверное слово
FF_TBYT | Ox40000000L | Восьмерное слово
FF_ASCI | Ox50000000L | ASCII-строка
FF_STRU | Ox60000000L | Структура
FF_RESERVED | Ox70000000L | Зарезервировано
FF_FLOAT | Ox80000000L | Float
FF_DOUBLE | Ox90000000L | Double
FF_PACKREAL | OxAOOOOOOOL | Packed decimal real
FF_ALIGN | OxBOOOOOOOL | Директива выравнивания
B зависимости от состояния флага значение аргумента typeid может тракто-
ваться по-разному.

Состояние flag | Значение typeid
FF_STRU | ID структуры
FF_ASCII | Тип ASCII-строки (см. таблицу ниже)
Другое | BADADDR
Обратите внимание, что если новый член структуры не представляет собой ни вложенную структуру, ни ASCII-строку, то аргумент typeid должен быть равен BADADDR.

Определение | Значение | Пояснения
ASCSTR_C | ASCSTRJTERMCHR | C-style ASCII string
ASCSTR__TERMCHR | 0 | Character-terminated ASCII string
ASCSTR_PASCAL | 1 | Pascal-style ASCII string (length byte)
ASCSTR_LEN2 | 2 | Pascal-style, length has 2 bytes
ASCSTR_UNICODE | 3 | Unicode string
ASCSTR_LEN4 | 4 | Pascal-style, length has 4 bytes
Таким образом, тип нового члена структуры определяется сразу двумя аргументами.
Аргумент offset указывает смещение члена в структуре (как уже было сказано в описании предыдущих функций, доступ к членам структуры осуществляется по их смещению). Чтобы добавить новый член к структуре, достаточно в качестве смещения указать BADADDR, и тогда IDA вычислит его автоматически.
Например:
G копия экрана IDA:
0000 MYSTRUC struc
0000 field_0 db ?
0001 MYSTRUC ends


IDA - 0296.htm

296__________________Структуры____________________
> ввод пользователя:
AddStrucMember(
GetStrucIdByName("MYSTRUC"),
"MyMember",
-1,
FF_WORD,
-1,
2);
Q копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox3)
0000 field_0 db ?
0001 MyMember dw ?
0003 MYSTRUC ends
Однако то же значение можно вычислить и самостоятельно:
Q копия экрана IDA:
0000 MYSTRUC struc
0000 field_0 db ?
0001 MYSTRUC ends
> ввод пользователя:
AddStrucMember(
GetStrucIdByName("MYSTRUC"),
"MyMember",
GetLastMember(
GetStrucIdByName("MYSTRUC")
),
FF_WORD,
-1,
2);
Q копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox3)
0000 field_0 db ?
0001 MyMember dw ?
0003 MYSTRUC ends
Но так или иначе, при попытке указать смещение, принадлежащее уже
существующему члену, функция вернет ошибку.
Q копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox3)
0000 field_0 db ?
0001 MyMember dw ?
0003 MYSTRUC ends
> ввод пользователя:
Message("Ox%X \n",
AddStrucMember(
GetStrucIdByName("MYSTRUC"),
"MyMember2",
з,
FF WORD,


IDA - 0297.htm

Структуры ____ 297
-l,
2)
);
G копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox3)
0000 field_0 db ?
0001 MyMember dw ?
0003 MYSTRUC ends
< ответ IDA:
0xFFFFFFFE
Возникает вопрос: для чего был введен аргумент смещение? На самом деле он может быть равен не только смещению последнего элемента. Дело в том, что при удалении членов структуры IDA не изменяет смещения остальных, а преобразует удаляемые члены в неопределенные байты, на месте которых могут быть созданы новые.
Например:
G копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox6)
0000 field_0 db ?
0001 MyMember4 db ?
0002 db ? undefined
0003 db ? undefined
0004 db ? undefined
0005 field_5 db ?
0006 MYSTRUC ends
> ввод пользователя:
AddStrucMember(
GetStrucIdByName("MYSTRUC"),
"MyMember4",
3,
FF_WORD,
-1,
2)
G копия экрана IDA:
0000 MYSTRUC struc ; (sizeof=Ox6)
0000 field_0 db ?
0001 MyMember4 db ?
0002 db ? ; undefined
0003 MyMember2 dw ?
0005 field_5 db ?
0006 MYSTRUC ends
0006
Последний аргумент nbytes задает размер нового члена структуры в байтах. С первого взгляда это бессмысленно, поскольку это и так ясно из типа члена (см. определения flag) — за исключением, может быть, ASCII-строки, но и та в большинстве случаев определяется завершающим символом или типом, указанным в typeid.


IDA - 0298.htm

298 Структуры
На самом деле этот аргумент необходимо указывать всегда. Он должен быть кратен размеру члена структуры, и если это отношение больше единицы, то IDA автоматически создает массив однотипных элементов.
Например:
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox8)
0008 MY_STRUC ends
> ввод пользователя:
AddStrucMember(
GetStrucIdByName("MY_STRUC"),
"MyMember_l",
-1,
FF_WORD,
-1/
8);
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox8)
0000 MyMember_1 dw 4 dup(?)
0008 MY_STRUC ends
Если указать не кратный размер, то вызов функции завершится ошибкой:
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox8)
0000 MyMember_1 dw 4 dup(?)
0008 MY_STRUC ends
> ввод пользователя:
Message("Ox%X \n",
AddStrucMember(
GetStrucIdByName("MY_STRUC"),
"MyMember_2",
-1,
FF_WORD,
-1,
9)
);
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox8)
0000 MyMember_1 dw 4 dup(?)
0008 MY_STRUC ends
< ответ IDA:
0xFFFFFFFD
Эта функция практически единственная, способная возвращать расширенный код ошибки, который не только указывает на неуспешное завершение вызова, но и определяет его источник.
Все возможные коды возврата приведены ниже:


IDA - 0299.htm

Структуры 299

Определение |  | Значение
STRUC_ERROR_MEMBER_NAME | -1 | Заданное имя уже существует
STRUC_ERROR_MEMBER_OFFSET | -2 | Смещение принадлежит другому члену
STRUC_ERROR_MEMBER_SIZE | -3 | Неверный аргумент nbyte
STRUC_ERROR_MEMBER_TINFO | -4 | Неверный аргумент typeid
STRUC_ERROR_MEMBER_STRUCT | -5 | Неверный id структуры
STRUC_ERROR_MEMBER_UNIVAR | -6 | Объединение не может иметь членов перемен-
 |  | ного размера
STRUC_ERROR_MEMBER_VARLAST | -7 | Члены переменного размера должны находить-
 |  | ся в конце

Операнд | Пояснения
id | Идентификатор (ID) структуры
name | Имя структуры
Offset | ==offset | Значение
 | !=BADADDR | Смещение нового члена структуры
 | ==BADADDR | Добавить новый член в конец
flag | Тип нового члена
typeid | Идентификатор структуры или тип ASCII-строки
nbytes | Размер нового члена в байтах
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка (см. коды завершения | выше)
long DelStrucMember (long id, long member_offset)
Функция удаляет член структуры, заданной идентификатором. Доступ к члену обеспечивается указанием любого принадлежащего ему смещения.
На самом деле IDA не удаляет член структуры, а только преобразует его в последовательность неопределенных байтов. Поэтому, строго говоря, удалить никакой член структуры (кроме последнего) нельзя, — во всяком случае, так, чтобы изменить смещения всех остальных (а это требуется, и довольно часто!).
Например:
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Oxb)
0000 MyMember_1 dw 4 dup(?)
0008 field_8 db ?
0009 field_9 dw ?
000В MY_STRUC ends


IDA - 0300.htm

300 Структуры
> ввод пользователя:
DelStrucMember(
GetStrucIdByName("MY_STRUC"),
0);
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Oxb)
0000 db ? undefined
0001 db ? undefined
0002 db ? undefined
0003 db ? undefined
0004 db ? undefined
0005 db ? undefined
0006 db ? undefined
0007 db ? undefined
0008 field_8 db ?
0009 field_9 dw ?
OOOB MY_STRUC ends
При попытке удалить последний член структуры он действительно удаляется
без остатка.
Пример:
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Oxb)
0000 MyMember_1 dw 4 dup(?)
0008 field_8 db ?
0009 field_9 dw ?
OOOB MY_STRUC ends
> ввод пользователя:
DelStrucMember(
GetStrucIdByName("MY_STRUC"),
9);
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Oxb)
0000 MyMember_1 dw 4 dup(?)
0008 field_8 db ?
OOOB MY_STRUC ends

Операнд | Пояснения
id | Идентификатор (ID) структуры
offset | Смещение удаляемого члена структуры
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Интерактивно то же самое можно сделать, если перевести курсор на нужный
элемент структуры и нажать клавишу <U>.


IDA - 0301.htm

Структуры 301
long SetMemberName (long Id, long member_offset, char name)
Функция позволяет изменять имя члена структуры. Структура задается
идентификатором, а член — любым принадлежащим ему смещением.
Например:
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox9)
0000 db ? undefined
0001 db ? undefined
0002 db ? undefined
0003 db ? undefined
0004 db ? undefined
0005 db ? undefined
0006 db ? undefined
0007 db ? undefined
0008 field_8 db ?
0009 MY_STRUC ends
> ввод пользователя:
SetMemberName (
GetStrucIdByName("MY_STRUC"),
8,"MyGoodMember");
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox9)
0000 db ? undefined
0001 db ? undefined
0002 db ? undefined
0003 db ? undefined
0004 db ? undefined
0005 db ? undefined
0006 db ? undefined
0007 db ? undefined
0008 MyGoodMember db ?
0009 MY_STRUC ends

Операнд | Пояснения
id | Идентификатор (ID) структуры
name | Новое имя члена структуры
offset | Смещение удаляемого члена структуры
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
Интерактивно можно изменить имя, переместив на него курсор и нажав
клавишу <N>.


IDA - 0302.htm

302 Структуры
long SetMemberType (long Id, long member_offset, long flag,
long typeid, long nitems)
Функция позволяет изменять тип члена структуры. Он определяется флагом
flag следующим образом:

Определение | Значение | Пояснения
FF_BYTE | OxOOOOOOOOL | Байт
FF_WORD | OxlOOOOOOOL | Слово
FF_DWRD | Ox20000000L | Двойное слово
FF_QWRD | Ox30000000L | Четверное слово
FF_TBYT | Ox40000000L | Восьмерное слово
FF_ASCI | Ox50000000L | ASCII-строка
FF_STRU | Ox60000000L | Структура
FF_RESERVED | Ox70000000L | Зарезервировано
FF_FLOAT | Ox80000000L | Float
FF_DOUBLE | Ox90000000L | Double
FF_PACKREAL | OxAOOOOOOOL | Packed decimal real
FF_ALIGN | OxBOOOOOOOL | Директива выравнивания
B зависимости от состояния флага значение аргумента typeid может тракто-
ваться по-разному.

Состояние flag | Значение typeid
FF_STRU | ID структуры
FF_ASCII | Тип ASCII-строки (см. таблицу ниже)
Другое | BADADDR
Обратите внимание, что если новый член структуры не представляет собой ни вложенную структуру, ни ASCII-строку, то аргумент typeid должен быть равен BADADDR.

Определение | Значение | Пояснения
ASCSTR_C | ASCSTR_TERMCHR | C-style ASCII string
ASCSTR_TERMCHR | 0 | Character-terminated ASCII string
ASCSTR_PASCAL | 1 | Pascal-style ASCII string (length byte)
ASCSTR_LEN2 | 2 | Pascal-style, length has 2 bytes
ASCSTR_UNICODE | 3 | Unicode string
ASCSTR_LEN4 | 4 | Pascal-style, length has 4 bytes
Необходимо, чтобы для "нового" члена хватало места. Если его размер превосходит размер "старого" (в соответствии с типом), а следующие за ним смещения принадлежат остальным членам, то тип члена не будет изменен.


IDA - 0303.htm

Структуры 303
Пример:
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox9)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
0009 MY_STRUC ends
0009
> ввод пользователя:
Message("Ox%X \n",
SetMemberType(
GetStrucIdByName("MY_STRUC"),
2,
FF_DWRD,
-1, 4));
Q копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox9)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
0009 MY_STRUC ends
0009
< ответ IDA:
0x0
"Новый" член занимает места больше, чем "старый", и действия привели к ошибке. Напротив, если "новый" член занимает меньше места, чем старый, то преобразование происходит без проблем, а "лишние" байты помечаются как неопределенные.
Например:
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof-Ox9)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
0009 MY_STRUC ends
0009
> ввод пользователя:
Message("Ox%X \n",
SetMemberType(
GetStrucIdByName("MY_STRUC") ,
2,


IDA - 0304.htm

304 Структуры
FF_BYTE,
-1,
D)
G копия экрана IDA:
0000 MY_STRUC struc ; (sizeof=Ox9)
0000 field_0 dw ?
0002 field_2 db ?
0003 db ? ; undefined
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
0009 MY_STRUC ends

Операнд | Пояснения
id | Идентификатор (ID) структуры
name | Имя структуры
Offset | ==offset | Значение
 | !=BADADDR | Смещение нового члена структуры
 | ==BADADDR | Добавить новый член в конец
flag | Новый тип члена
typeid | Идентификатор структуры или тип ASCII-строки
Nbytes | Размер нового члена в байтах
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка (см. коды завершения | выше)
long SetMemberComment (long Id, long member_offset,
char comment, long repeatable)
Функция устанавливает комментарий, связанный с членом структуры. IDA поддерживает два типа комментариев — 'regular' и 'repeatable'. Последний отличается тем, что дублируется по месту обращения к элементу структуры. Однако в случае со структурами и их членами IDA игнорирует это требование, в чем убеждает следующий пример:
0000 MyStruc struc ; (sizeof=Ox4) XREF: segOOO:OF72r
0000 field_0 dw ? My Repeatable comment
0002 field_1 dw ?
0004 MyStruc ends
segOOO:OF72*stru_0_F72 dw 0 ; field_0 ; DATA XREF: sub_0_F56r
segOOO:OF72* ; sub_0_2456+1Cw ...
segOOO:OF72* dw 0 ; field_1
segOOO:OF56 mov es, stru_0_F72.field_0
segOOO:OF5A assume es:nothing
segOOO:OF5A xor bx, bx


IDA - 0305.htm

Структуры 305
Остается только надеяться, что в будущем такая поддержка появится.

Операнд | Пояснения
id | Идентификатор (ID) структуры
comment | Комментарий члена
member_offset | Смещение, лежащее в границах интересующего нас |  | элемента
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка (см. коды завершения | выше)


IDA - 0306.htm

306 Перечисления
Перечисления
ALMA MATER
Организация перечислений очень близка к организации структур, поэтому рекомендуется ознакомиться с главой "структуры". Многие моменты при описании перечислений будут опущены, если они ничем не отличаются от описанных выше.
Прежде всего, что такое перечисления? Это константы, т.е. предопределенные символьные значения, которые в ходе ассемблирования заменяются действительными значениями.
Использование непосредственных значений — дурной тон программирования. Как, например, насчет следующего кода:
PUSH 10
PUSH 02
CALL GotoXY
PUSH offset ProgramName
CALL WriteLn
Числа 10 и 2 представляют собой экранные координаты, в которых будет выведено имя программы. Впрочем, если вы не автор этого фрагмента кода, то догадаться об этом не просто, да и, кроме того, что делать, если придется переписывать программу для работы с другим экранным разрешением? Просматривать весь код на предмет поиска всех относящихся к экранным координатам констант?
В ассемблерах появилась директива EQU, которая давала возможность определить "говорящие" константы. Это не только повышало информативность листинга и заменяло комментарии, но и позволяло легко модифицировать их, — ведь теперь непосредственное значение указывалось только в одной точке.
Разумеется, IDA поддерживает константы. Все ассемблеры поддерживают исключительно глобальные списки констант, что часто вызывает путаницу, а IDA умеет "разбивать" их на отдельные группы — каждая под своей "крышей".
Внешне список констант напоминает структуру:
; enum enum_1
enum_1_0 = 3
enum_1_4 = 5
; enum enum_3
enum_3_14E = 1
enum_3_0 = 2Ch
enum_3_2D = 14Dh


IDA - 0307.htm

Перечисления 307
Однако, в отличие от структуры, члены перечисления не имеют ни типа, ни размера. Точнее, тип определяется только на стадии ассемблирования.
Так, например, если enum_l_0 равен трем, это еще не означает, что он относится к типу байт. Вполне вероятно, что он окажется словом или даже и словом и байтом одновременно, например:
MOVAL, enum_1_0
CMPAX, enum_1_0
Этот код успешно ассемблирован!
Но если нет типов и невозможно вычислить размер членов, то как же осуществить доступ к ним? Теоретически можно было условиться, что каждый член занимает 32 байта (двойное слово), и организовать к ним доступ так же, как и в структурах. И это бы неплохо работало!
Но разработчик IDA пошел по другому пути — он связал каждый член с идентификатором! Разумеется, существует функция, возвращающая идентификатор по имени члена и наоборот. В свете этого становится еще более непонятным, какой смысл имеет "группировка" перечислений. Имена членов — глобальные, идентификаторы — тем более. Что же дает принадлежность члена к той или иной группе?
IDA - 0307-1.jpg
В каждой группе может существовать не более одной константы с одним и тем же значением. С первого взгляда непонятно, как можно "жить" с этим и какие мотивы побудили разработчика принять такое нелепое ограничение. На самом деле это следствие выбранной архитектуры. И весьма удачной — стоит только взглянуть на нее изнутри.
Архитектура перечислений
Прежде чем углубляться в технические дебри реализации и архитектуры перечислений, зададимся простым вопросом, — что же по сути представляют собой члены перечислений?
Разумеется, это операнды, или, точнее, иная форма представления непосредственных операндов. В главе, посвященной объективной модели IDA, уже отмечалось, что один и тот же операнд может быть по-разному отображен на экране


IDA - 0308.htm

308_________________Перечисления___________________
дизассемблера. Он может быть не только непосредственным значением, но и смещением, например.
Однако перечисления — это не просто иная форма отображения операнда на экране — с точки зрения IDA это элемент bTree, который может ссылаться на линейный адрес объекта... впрочем, не стоит повторяться.
Но если каждый сегмент (имя, комментарий, функция) связан только с одним линейным адресом, то одно и то же перечисление может повторяться в десятке разных мест! Вот почему старые методы для него не подходят. Поэтому был использован тот же механизм, который был создан для поддержки структур. Каждый объект ссылался на тег структуры, а операнд указывал на требуемый член внутри нее.
То же происходит и с перечислениями. Есть список перечислений, на который ссылается объект. Элементы списка просматриваются до тех пор, пока не найдется элемент, совпадающий по значению с операндом объекта.
И структура, и перечисление связываются не с операндом, а с обладающим им объектом, а точнее — с линейным адресом его начала. Представление операнда в виде члена структуры или перечисления происходит на втором этапе, — и жесткой связи тут нет, простой поиск на совпадение значений. Но если в структуре смещение каждого члена уникально, т.е. никакие два члена не могут быть расположены по одному и тому же смещению, то в перечислениях два разных члена могут иметь одно и то же смещение.
Вот, собственно, ответ на вопрос о необходимости поддержки более чем одного списка перечислений, а заодно и тактика группировки элементов: главный критерий — не родственность каких-то признаков, а гарантия несовпадающих значений.
При этом разумно стремиться к уменьшению числа списков, поскольку, как уже говорилось выше, для представления операнда в виде перечисления достаточно сослаться на список — и IDA самостоятельно подберет нужный элемент!
В идеале, если у нас всего один список (что бывает достаточно часто), то необходимо перевести курсор на нужную строку и нажать <T>, — IDA все сделает автоматически.
Программная работа, в отличие от интерактивной, несколько сложнее. Кроме того, теги списков (это не теги, но иного названия нет — поэтому будем считать, что это как бы теги) вообще практически отсутствуют. Действительно, все члены связаны с уникальными глобальными идентификаторами, да и имена каждого из них не менее уникальны.
Методы

Функция | Назначение
long GetEnumQty (void) | Возвращает число перечислений
long GetnEnum (long idx) | Возвращает идентификатор перечисления по его индексу
long GetEnumldx (long enum_id) | Возвращает индекс перечисления по его идентификатору
long GetEnum (char name) | Возвращает идентификатор перечисления по его имени
char GetEnumName (long enum_id) | Возвращает имя перечисления по его идентификатору


IDA - 0309.htm

Перечисления 309

Функция | Назначение
char GetEnumCmt | Возвращает комментарий перечисления
(long enum_id, long repeatable) |
long GetEnumSize (long enum_id) | Возвращает число членов перечисления
long GetEnumFlag (long enum_id) | Возвращает флаги, определяющие представление членов
 | перечисления
long GetConstByName (char name) | Возвращает идентификатор константы по ее имени
long GetConstValue (long const_id) | Возвращает значение константы по ее идентификатору
char GetConstName (long const_id) | Возвращает имя константы по ее идентификатору
char GetConstName | Возвращает комментарий константы по ее идентифика-
(long const_id) | тору
long AddEnum (long idx, | Добавляет новое перечисление
char name, long flag) |
void DelEnum (long enum_id) | Удаляет перечисление
success SetEnumldx | Задает индекс перечисления в списке
(long enum_id, long idx) |
long GetEnumQty (void)
Функция возвращает число перечислений.
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; ----------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
FFFFFFFF
> ввод пользователя:
Message("Ox%X \n",
GetEnumQty() ) ;
< ответ IDA:
0x2

Return | ==return | Пояснения
 | !=0 | Число перечислений
 | ==0 | Нет ни одного перечисления
Интерактивно перечисления могут быть отображены вызовом списка коман-
дой меню ~View \ Enumeration's.
long GetnEnum (long idx)
Функция возвращает ID перечисления по индексу. Как уже отмечалось выше,
индекс не может точно идентифицировать связанное с ним перечисление, по-


IDA - 0310.htm

310 Перечисления
скольку при любых операциях, связанных с дополнением или удалением перечислений, список перестраивается, и тот же индекс может указывать совсем на другое перечисление.
В отличие от индекса, идентификатор (ID) перечисления представляет собой уникальное 32-битное значение, всегда указывающие на одно и то же перечисление. Более того, если перечисление, связанное с конкретным идентификатором, было удалено, тот же идентификатор не будет выдан ни одному созданному после этого перечислению.
Это гарантирует непротиворечивость ситуации и позволяет совместно использовать один и тот же идентификатор различным скриптам.
Пример использования:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; -----------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
> ввод пользователя:
auto a;
for(a=0;a<GetEnumQty();a+ + )
Message("Ox%X Ox%X \n",
a,GetEnumId(a)
) ;
< ответ IDA:
0x0 0xFF0000F0
0x1 0xFF0000FE
Идентификатор, как и дескриптор, с точки зрения пользователя — абстрактное "магическое" число, интерпретировать которое допускается только операционной системе (в качестве которой выступает в данном случае IDA).

Операнд | Пояснения
index | Индекс перечисления в списке (от нуля до GetEnumQty()-l)
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор (ID) перечисления
 | ==BADADDR | Ошибка
long GetEnumldx (long enum_id)
Функция возвращает индекс перечисления по его идентификатору. Индексы не связаны жестко с перечислениями, и при каждой операции удаления или добавления новых перечислений тем же индексам могут соответствовать новые перечисления.


IDA - 0311.htm

Перечисления 311
Пример использования:
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; -----------------———
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
> ввод пользователя:
Message("Ox%X \n",
GetEnumldx(
GetEnum("enum_l")
)
)/
< ответ IDA:
0x0
> ввод пользователя:
Message("Ox%X \n",
GetEnumldx(
GetEnum("enum_2")
)
);
< ответ IDA:
0x1

Операнд | Пояснения
ID | Идентификатор перечисления
Return | ==return | Пояснения
 | !=BADADDR | Индекс перечисления
 | ==BADADDR | Ошибка
long GetEnum (char name)
Функция возвращает идентификатор перечисления по его имени. Если перечисление с указанным именем не существует, то функция возвращает ошибку — BADADDR.
Пример использования:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; -----------------————
FFFFFFFF


IDA - 0312.htm

312 Перечисления
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
> ввод пользователя:
Message("Ox%X \n",
GetEnum("enum__l") ) ;
< ответ IDA:
0xFF000131
> ввод пользователя:
Message("Ox%X \n",
GetEnum("enum_2")
);
< ответ IDA:
0xFF000132
> ввод пользователя:
Message("Ox%X \n",
GetEnum (" enum_3 ")
);
< ответ IDA:
0xFFFFFFFF

Операнд | Пояснения
name | Имя перечисления
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор перечисления
 | ==BADADDR | Ошибка
char GetEnumName (long enum_id)
Функция возвращает имя перечисления по его идентификатору. Если указанному идентификатору не соответствует ни одно перечисление, функция возвращает пустую строку.
Например:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; ----------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
> ввод пользователя:
Message("%s \n",
GetEnumName(


IDA - 0313.htm

Перечисления 313
GetnEnum(1)
)
);
< ответ IDA:
enum_2

Операнд | Пояснения
Enum_id | ID перечисления
Return | ==return | Пояснения
 | !__"" | Имя перечисления
 | =="" | Ошибка
char GetEnumCmt (long enum_id, long repeatable)
Функция возвращает комментарий перечисления, заданного идентификатором. Комментарии бывают двух типов — постоянные и повторяемые. Постоянные отображаются только впереди перечисления, а повторяемые при обращении к каждому из его членов.
Q копия экрана IDA:
FFFFFFFF ; My Enum regular comment
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
segOOO:0046 rol bx, enum_1_0
> ввод пользователя:
Message("%s \n",
GetEnumCmt (
GetEnum("enum_l") ,
0);
< ответ IDA:
My Enum regular comment
Q копия экрана IDA:
FFFFFFFF ; My Enum repeatable comment
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
segOOO:0046 rol bx, enum_1_0 ; My Enum Repeatable comment
> ввод пользователя:
Message("%s \n",
GetEnumCmt (
GetEnum("enum_l") ,
l);
< ответ IDA:
My Enum Repeatable comment


IDA - 0314.htm

314 Перечисления

Операнд | Пояснения
id | Идентификатор (ID) перечисления
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | Завершение | Пояснения
 | 1__"" | Комментарий
 | "" | Ошибка
long GetEnumSize (long enum_id)
Функция возвращает число членов перечисления, заданного идентификатором. Обратите внимание: именно число элементов, а не занимаемый ими размер, который вычислить невозможно, поскольку члены перечисления не имеют типа.
Пример:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
> ввод пользователя:
Message("Ox%X \n",
GetEnumSize(
GetEnum("enum_l")
)
);
< ответ IDA:
0x2
Если перечисление пусто, то функция возвращает ноль, но то же значение возвращается, если указать неверный идентификатор, поэтому возникает неоднозначная ситуация — либо перечисление отсутствует (было удалено?), либо попросту пусто.
Пример:
G копия экрана IDA:
FFFFFFFF ; enum enum_2
> ввод пользователя:
Message("Ox%X \n",
GetEnumSize(
GetEnum ("enum__2 " )
)
);
< ответ IDA:
0x0


IDA - 0315.htm

Перечисления 315
> ввод пользователя:
Message("Ox%X \n",
GetEnumSize(BADADDR)
);
< ответ IDA:
0x0

Операнд | Пояснения
Enum__id | Идентификатор перечисления
Return | ==return | Пояснения
 | !=0 | Число членов перечисления
 | ==0 | Пустое перечисление
 |  | Ошибка
long GetEnumFlag (long enum_id)
Функция возвращает флаги, определяющие представление членов перечисле-
ния, заданного идентификатором.
Возможные значения перечислены ниже.

FF_ONUMH | 0x001 00000 | Шестнадцатиричное представление первого операнда
FF_ONUMD | 0x00200000 | Десятичное представление первого операнда
FF_OCHAR | 0x00300000 | Символьное представление первого операнда
FF_OSEG | 0x00400000 | Первый операнд — сегмент
FF_OOFF | 0x00500000 | Первый операнд — смещение
FF_ONUMB | 0x00600000 | Представление первого операнда в бинарном виде
FF_ONUMO | 0x00700000 | Представление первого операнда в восьмиричном виде
FF_OENUM | 0x00800000 | Представление первого операнда в виде перечисления
FF_OFOP | 0x00900000 | Принудительный первый операнд
FF__OSTRO | 0x00A00000 | Представление первого операнда как смещения в структуре
FF_OSTK | 0x00B00000 | Первый операнд — стековая переменная
FF_1VOID | 0x00000000 | Тип второго операнда Void
FF_1NUMH | 0x00100000 | Шестнадцатиричное представление второго операнда
FF_1NUMD | 0x00200000 | Десятичное представление второго операнда
FF_1CHAR | 0x00300000 | Символьное представление второго операнда
FF_ISEG | 0x00400000 | Второй операнд — сегмент
FF_1OFF | 0x00500000 | Второй операнд — смещение
FF_1NUMB | 0x00600000 | Представление второго операнда в бинарном виде
FF_1NUMO | 0x00700000 | Представление второго операнда в восьмиричном виде
FF_1ENUM | 0x00800000 | Представление второго операнда в виде перечисления
FF_1FOP | 0x00900000 | Принудительный второй операнд
FF_1STRO | 0x00A00000 | Представление второго операнда как смещения в структуре
FF_1STK | 0x00B00000 | Второй операнд — стековая переменная


IDA - 0316.htm

376_________________Перечисления______________
Пример:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
> ввод пользователя:
Message("Ox%X \n",
GetEnumFlag(
GetEnum("enum_l")
);
< ответ IDA:
0x1100000

Операнд | Пояснения
Enum_id | Идентификатор перечисления
Return | ==return | Пояснения
 | !=0 | Флаг отображения членов | перечисления
 | ==0 | Ошибка
long GetConstByName (char name)
Функция возвращает идентификатор константы по ее имени. Все перечисления разделяют общее пространство имен. Другими словами, одно и то же имя не может быть повторено дважды, поэтому является уникальным.
Например:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; --------- — ___-____-___-__
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF MyEnum = 16h
> ввод пользователя:
Message("Ox%X \n",
GetConstByName("MyEnum")
) ;
< ответ IDA:
0xFF000136
Идентификатор обеспечивает доступ к константе. Чтобы, например, получить ее значение, необходимо воспользоваться функцией long GetConstValue (long const__id), которая описана ниже.


IDA - 0317.htm

Перечисления 317

Операнд | Пояснения
name | Имя константы
Return | ==return | Пояснения
 | !=0 | Идентификатор константы
 | ==0 | Ошибка
long GetConstValue (long const_id)
Функция возвращает значение константы по ее идентификатору или ноль в результате ошибки. Часто возникает неопределенность — то ли действительно произошла ошибка (например, был указан несуществующий идентификатор), то ли константа действительно имеет нулевое значение.
Пример использования:
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; ------- —— ________________
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF MyEnum = 16h
> ввод пользователя:
Message("Ox%X \n",
GetConstValue(
GetConstByName("MyEnum")
) );
<OTBeт lDA:
0x16

Операнд | Пояснения
Const_id | Идентификатор константы
Return | ==return | Пояснения
 | !=0 | Значение константы
 | ==0 | Ошибка
 |  | Значение константы
char GetConstName (long const-id)
Функция возвращает имя константы, заданной идентификатором. Если иден-
тификатор указан неправильно, то возвращается пустая строка.
Например:
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1


IDA - 0318.htm

318 Перечисления
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; -----------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF MyEnum = 16h
FFFFFFFF
> ввод пользователя:
Message("%s \n",
GetConstName(
GetConstByName("MyEnum")
)
);
< ответ IDA:
MyEnum

Операнд | Пояснения
Const_id | ID константы
Return | ==return | Пояснения
 | !="" | Имя константы
 | =="" | Ошибка
char GetConstCmt (long const_id, long repeatable)
Функция возвращает комментарий константы, заданной идентификатором. Комментарии бывают двух типов — постоянные и повторяемые. Постоянные отображаются только справа от константы, а повторяемые — при каждом обращении к ней.
Q копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1 ; My regular comment
FFFFFFFF enum_1_2 = 2
segOOO:0046 rol bx, enum_1_0
> ввод пользователя:
Message("%s \n",
GetConstCmt(
GetConstByName("enum_l_0"),
0);
< ответ IDA:
My regular comment
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1 ; My Enum repeatable comment
FFFFFFFF enum_1_2 = 2
segOOO:0046 rol bx, enum_1_0 ; My Repeatable comment


IDA - 0319.htm

Перечисления 319
> ввод пользователя:
Message("%s \n",
GetConstCmt(
GetConstByName("enum_l_0") ,
1);
< ответ IDA:
My Repeatable comment

Операнд | Пояснения
id | Идентификатор (ID) константы
Repeatable | Флаг | Пояснения
 | 0 | Неповторяемый комментарий
 | 1 | Повторяемый комментарий
Return | Завершение | Пояснения
 | !__"" | Комментарий
 | "" | Ошибка
long AddEnum (long idx, char name, long flag)
Функция добавляет новое перечисление. Для этого необходимо указать его имя (которое впоследствии может быть изменено) и тип представления членов в перечислении.
Индекс задает положение перечисления в списке. Если он равен BADADDR, то новое перечисление будет добавлено в конец списка, иначе старое перечисление будет затерто! Подробнее об этом рассказано в описании функции AddStrucEx.
Флаг определяет представление членов в перечислении. Может принимать значения, перечисленные в таблице:

FF_0NUMH | 0x00100000 | Шестнадцатиричное представление первого операнда
FF_ONUMD | 0x00200000 | Десятичное представление первого операнда
FF_OCHAR | 0x00300000 | Символьное представление первого операнда
FF_OSEG | 0x00400000 | Первый операнд — сегмент
FF_OOFF | 0x00500000 | Первый операнд — смещение
FF_ONUMB | 0x00600000 | Представление первого операнда в бинарном виде
FF_ONUMO | 0x00700000 | Представление первого операнда в восьмиричном виде
FF_OENUM | 0x00800000 | Представление первого операнда в виде перечисления
FF_OFOP | 0x00900000 | Принудительный первый операнд
FF_OSTRO | 0x00A00000 | Представление первого операнда как смещения в структуре
FF_OSTK | 0x00B00000 | Первый операнд стековая переменная
FF_1VOID | 0x00000000 | Тип второго операнда Void
FF_1NUMH | 0x00100000 | Шестнадцатиричное представление второго операнда
FF_1NUMD | 0x00200000 | Десятичное представление второго операнда
FF_1CHAR | 0x00300000 | Символьное представление второго операнда


IDA - 0320.htm

320 Перечисления

FF_1SEG | 0x00400000 | Второй операнд — сегмент
FF_1OFF | 0x00500000 | Второй операнд — смещение
FF_1NUMB | 0x00600000 | Представление второго операнда в бинарном виде
FF_1NUMO | 0x00700000 | Представление второго операнда в восьмиричном виде
FF_1ENUM | 0x00800000 | Представление второго операнда в виде перечисления
FF_1FOP | 0x00900000 | Принудительный второй операнд
FF_1STRO | 0x00A00000 | Представление второго операнда как смещения в структуре
FF_1STK | 0x00B00000 | Второй операнд — стековая переменная
Пример использования:
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
> ввод пользователя:
AddEnum(-1,"MyNewEnum",0);
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF ; -----------------------
FFFFFFFF ; enum MyNewEnum

Операнд | Пояснения
index | ==index | Действие
 | [0,MaxIdx] | Индекс перечисления (старое перечисление | при этом будет затерто)
 | Maxldx+l | Индекс нового перечисления
 | BADADDR | Индекс нового перечисления
name | Имя перечисления
Return | Завершение | Пояснения
 | !=BADADDR | Идентификатор перечисления
 | ==BADADDR | Ошибка
Интерактивно можно добавить структуру, вызвав список командой меню
View \ Structures и нажав клавишу <INS>.
IDA - 0320-1.jpg


IDA - 0321.htm

Перечисления 321
IDA - 0321-1.jpg
void DelEnum (long enum_id)
Функция удаляет перечисление, заданное идентификатором, вместе со всеми
его членами.
Пример:
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; -------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF MyEnum = 16h
FFFFFFFF
> ввод пользователя:
DelEnum(
GetEnum("enum_2")
) ;
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF

Операнд | Пояснения
Enum_id | Идентификатор перечисления
Интерактивно перечисление можно удалить, установив курсор на любой его
элемент и нажав клавишу <DEL>.
success SetEnumldx (long enum_id, long idx)
Функция позволяет изменять индекс перечисления в списке. При этом
перечисления меняются местами, и затирания не происходит.


IDA - 0322.htm

322 Перечисления
Например:
G копия экрана IDA:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
FFFFFFFF ; ---------------------------
FFFFFFFF
FFFFFFFF ; enum MyNewEnum
FFFFFFFF MyNewEnum_0 = 0
FFFFFFFF
FFFFFFFF ; ---------------------------
FFFFFFFF
FFFFFFFF ; enum enum_9
FFFFFFFF enum_9_0 = 0
> ввод пользователя:
SetEnumldx(
GetEnum("enum_l")/l
) ;
G копия экрана IDA:
FFFFFFFF ; enum MyNewEnum
FFFFFFFF MyNewEnum_0 = 0
FFFFFFFF
FFFFFFFF ; ---------------------------------
FFFFFFFF
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
fFFFFFFF
FFFFFFFF ; ---------------------------------
FFFFFFFF
FFFFFFFF ; enum enum_9
FFFFFFFF enum_9_0 = 0
FFFFFFFF
I

Операнд | Пояснения
id | Идентификатор (ID) перечисления
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0323.htm

_______________________FIXUP_____________________323
FIXUP
ALMA MATER
Более привычным синонимом слова fixup, вероятно, окажется термин "перемещаемые элементы".
Что это такое? Как можно судить по названию, это относительные адреса, обеспечивающие портабельность кода, т.е. независимость от базового адреса загрузки. Поскольку IDA эмулирует загрузку файла в собственное адресное пространство и даже трассирует его выполнение, то она должна поддерживать и перемещаемые элементы.
Покажем это на небольшом примере MS-DOS, ехе-файла.
Просмотр с помощью HIEW:
00000200: B80100 mov ax,00001
00000203: 8ED8 mov ds,ax
00000205: B409 mov ah,009 ;"
00000207: BAOOOO mov dx,00000
0000020A: CD21 int 021
Просмотр с помощью IDA:
segOOO:0000 start proc near
segOOO:0000 mov ax, 1001h
segOOO:0003 mov ds, ax
segOOO:0005 assume ds:dseg
segOOO:0005 mov ah, 9
segOOO:0007 mov dx, 0
segOOO:OOOA int 21h
Что грузится в регистр АХ? С виду — непосредственное значение, однако это не так. Если приглядеться, то можно увидеть, что дальше оно помещается в регистр DS и, следовательно, скорее всего указывает на сегмент данных программы: 0x1 — это адрес сегмента данных, выраженный в параграфах. Знакомые с архитектурой IBM PC спросят, почему он оказался расположенным глубоко в таблице векторов прерываний. Но сегмент данных расположен вовсе не там, где можно подумать, ведь это относительный адрес. Относительно чего? B exe-файлах он отсчитывается от адреса загрузки первого байта, расположенного за заголовком файла. По умолчанию IDA загружает ехе-файлы по адресу 0x10000. Следовательно, считая в параграфах, OxlOOO+Oxl == 0x1001. Как видим, результат совпадает с тем, что отобразила IDA.
Это и понимается под поддержкой перемещаемых элементов. Остается только ответить на вопрос, каким образом IDA узнала, как следует трактовать этот непосредственный операнд? Используя эвристический анализатор? Нет, конечно.


IDA - 0324.htm

324_____________________FIXUP_______________________
Она поступила точно так же, как и операционная система на ее месте, — просто
прочитала заголовок файла.
00000000: 4D 5A 2C 00-02 00 02 00-20 00 00 OO-FF FF 00 00 MZ,
00000010: 00 00 00 00-00 00 00 00-ЗЕ 00 00 00-01 00 FB 71 > _q
00000020: 6A 72 00 00-00 00 00 00-00 00 00 00-00 00 00 00 jr
00000030: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 01 00
00000040: 00 00 0D 00-00 00 00 00-00 00 00 00-00 00 00 00
Файлы типа OLD EXE имеют очень простую структуру, реализующую поддержку перемещаемых элементов. В таблице, состоящей из двойных слов, перечисляются линейные адреса, указывающие на перемещаемые элементы, — относительные адреса, каждый из которых представляет собой слово, ссылающееся на сегмент (адрес в параграфах).
Таким образом, IDA выполняет простое арифметическое сложение. Впрочем, существуют и более запутанные форматы. Так, например, все файлы win32, поддерживающие динамическое связывание, или, говоря проще, возможность вызова функций чужих DLL, вынуждены иметь похожие структуры, — ведь адреса вызываемых функций неизвестны на этапе линковки программы и могут быть вычислены только после загрузки файла в память.
Если обратиться к другим платформам, а не замыкаться на серии Intel 80x86, то мы столкнемся с фейерверком самых разных технических решений.
Каким же образом IDA может поддерживать все это? Ведь перемещаемыми элементами управляют меньше десяти функций. Действительно, если абстрагироваться от вариаций технических реализаций и сосредоточиться на природе перемещаемых элементов, то можно с удивлением обнаружить, что ее основы могут быть сформулированы всего одной фразой. Вот этот операнд ссылается туда. И все! Этого достаточно, чтобы обеспечить нормальную функциональность и работоспособность!
Какой конкретно элемент и куда ссылается, вычисляют специальные модули, отвечающие за загрузку файла определенного формата. Ядро IDA такие проблемы не волнуют. Поэтому, фактически, поддержка перемещаемых элементов обеспечивается не IDA, а внешними модулями, к которым у пользователя нет программного доступа из языка скриптов. Другими словами, пользователь не управляет перемещаемыми элементами. Конечно, он может просмотреть все существующие перемещаемые элементы и даже их скорректировать, но нужно ли это? IDA и сама неплохо справляется с поставленной задачей. Поддержка перемещаемых элементов не требует внимания со стороны пользователя.
Впрочем, из этого правила есть исключения. Например, если некая хитрая программа имеет свой загрузчик (или чаще — интерпретируемый код), который работает нестандартно и, стало быть, IDA его не поддерживает. Сюда же можно отнести и некоторые случаи самомодифицирующегося кода: поддержка перемещаемых элементов лежит на самом низком уровне иерархии IDA. Другими словами, "исправляется" виртуальная память, поэтому это работает не только с операндами, но и с кодом, т.е. влияет на дизассемблирование инструкций.
Основная трудность описания перемещаемых элементов — в объяснении их типов: большая часть существует только на других платформах и чужда пользо-


IDA - 0325.htm

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

Функция | Назначение
long GetNextFixupEA (long еа) | Возвращает линейный адрес следующего переме-
 | щаемого элемента
long GetPrevFixupEA (long еа) | Возвращает линейный адрес предыдущего пере-
 | мещаемого элемента
long GetFixupTgtType (long еа) | Возвращает тип перемещаемого элемента
long GetFixupTgtSel (long еа) | Возвращает селектор перемещаемого элемента
long GetFixupTgtOff (long еа) | Возвращает смещение перемещаемого элемента
void SetFixup (long ea, long type, | Добавляет новый перемещаемый элемент
long targetsel, long targetoff, long displ) |
void DelFixup (long ea) | Удаляет перемещаемый элемент
long GetNextFixupEA (long еа)
Функция возвращает линейный адрес следующего перемещаемого элемента (а
не адрес начала содержащей его инструкции).
Например:
Q копия экрана IDA:
dseg:0000 public start
dseg:0000 start proc near
dseg:0000 B8 00 10 mov ax, seg dseg
dseg:0003 8E D8 mov ds, ax
> ввод пользователя:
Message("Ox%X \n",
GetNextFixupEA(0)
) ;
< ответ IDA:
0x1001
Эмулятор загрузки инициализировал перемещаемый элемент необходимым значением, указывающим на адрес сегмента в виртуальной памяти. В нашем случае он равен 0x10.

Операнд | Пояснения
еа | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Успешно
 | ==BADADDR | Ошибка


IDA - 0326.htm

326______________________FIXUP________________________
long GetPrevFixupEA (long ea)
Функция возвращает адрес предыдущего перемещаемого элемента. (Но не
адрес начала содержащей его инструкции!)
Например:
Q копия экрана IDA:
dseg:0000 public start
dseg:0000 start proc near
dseg:0000 B8 00 10 mov ax, seg dseg
dseg:0003 8E D8 mov ds, ax
> ввод пользователя:
Message("Ox%X \n",
GetNextFixupEA(-1)
) ;
< ответ IDA:
0x1001
Эмулятор загрузки инициализировал перемещаемый элемент необходимым значением, указывающим на адрес сегмента в виртуальной памяти. В нашем случае он равен 0x10.

Операнд | Пояснения
еа | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Успешно
 | ==BADADDR | Ошибка
long GetFixupTgtType (long еа)
Функция возвращает тип перемещаемого элемента по его линейному адресу. Возможные значения перечислены ниже. Большинство типов перемещаемых элементов на платформе Intel не используются, поэтому подробное описание их назначения планируется привести в факультативном приложении к книге "Использование IDA на не-1п1е1-платформах".

FIXUP_BYTE | FIXUP_OFF8 | Восьмибитное смещение
FIXUP_OFF8 | 0
FIXUP_OFF16 | 1 | 16-битное смещение
FIXUP_SEG16 | 2 | 16-битный сегмент (селектор)
FIXUP_PTR32 | 3 | 32-битный длинный указатель (16-бит база;
 |  | 16-бит селектор) |
FIXUP_OFF32 | 4 | 32-битное смещение
FIXUP_PTR48 | 5 | 48-битный указатель (16-бит база; 32-бит | смещение)
FIXUP_HI8 | 6 | Старшие 8 бит 16-битного смещения
FIXUP_HI16 | 7 | Старшие 16 бит 32-битного смещения


IDA - 0327.htm

FIXUP 327

FIXUP_LOW8 | 8 | Младшие 8 бит 16-битного смещения
FIXUP_LOW16 | 9 | Младшие 16 бит 32-битного смещения
FIXUP_REL | 0x10 | Fixup is relative to the linear address specified in the 3d
 |  | parameter to set_fixup()
FIXUP_SELFREL | 0x0 | Self-relative? — disallows the kernel to convert operands
 |  | in the first pass — this fixup is used during output/ This
 |  | type of fixups is not used anymore. Anyway you can use it
 |  | for commenting purpose in the loader modules
FIXUP_EXTDEF | 0x20 | Target is a location (otherwise — segment)
FIXUP_UNUSED | 0x40 | Fixup is ignored by IDA disallows the kernel to convert
 |  | operands — this fixup is not used during output
FIXUP_CREATED | 0x80 | Fixup was not present in the input file
Пример:
G копия экрана IDA:
segOOO:032D cmp word ptr [bp+8], seg segOOO
segOOO:0332 jnz loc_0_33A
> ввод пользователя:
Message("Ox%X \n",
GetFixupTgtType(
GetNextFixupEA(0)
)
);
< ответ IDA:
0x2

Операнд | Пояснения
ea | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Тип перемещаемого элемента
 | ==BADADDR | Ошибка
long GetFixupTgtSel (long ea)
Функция возвращает селектор перемещаемого элемента, заданного линейным
адресом.
Пример использования:
Q копия экрана IDA:
C0000000 ; Segment type: Риге code
C0000000 LCOD segment para public 'CODE' use32
C0000000 assume cs:LCOD
C0000000 ;org OCOOOOOOOh
C0000000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
C0000000 Service_Table_0 dd offset unkserv_0 ; DATA XREF: LCOD:C0000040


IDA - 0328.htm

328_____________________FIXUP____________
> ввод пользователя:
Message("Ox%X, Ox%X \n",
GetNextFixupEA(0),
GetFixupTgtSel(GetNextFixupEA(0)
)
)/
< ответ IDA:
0xC0000000, 0x2

Операнд | Пояснения
еа | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Селектор перемещаемого элемента
 | ==BADADDR | Ошибка
longGetFixupTgtOff(long ea)
Функция возвращает смещение перемещаемого элемента, заданного линей-
ным адресом.
Пример использования:
Q копия экрана IDA:
C0000000 ; Segment type: Pure code
C0000000 LCOD segment para public 'CODE' use32
C0000000 assume cs:LCOD
C0000000 ;org OCOOOOOOOh
C0000000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing, gs:nothing
C0000000 Service_Table_0 dd offset unkserv_0 ; DATA XREF: LCOD:C0000040
> ввод пользователя:
Message("Ox%X, Ox%X \n",
GetNextFixupEA(0),
GetFixupTgtOff(GetNextFixupEA(0)
)
);
< ответ IDA:
0xC0000000, 0xC0003514

Операнд | Пояснения
ea | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Смещение перемещаемого элемента
 | ==BADADDR | Ошибка
long GetFixupTgtDispl (long еа)
Функция возвращает displacement перемещаемого элемента, заданного линей-
ным адресом. В настоящее время она на платформе Intel практически не исполь-


IDA - 0329.htm

_______________________FIXUP_____________________329
зуется и подробнее описана в факультативном приложении к книге "Использова-
ние IDA на не-intе1-платформах".

Операнд | Пояснения
еа | Линейный адрес
Return | Завершение | Пояснения
 | !=BADADDR | Displacement перемещаемого | элемента
 | ==BADADDR | Ошибка
void SetFixup (long ea, long type, long targetsel, long targetoff, long displ)
Функция позволяет добавить новый перемещаемый элемент. Она практически не используется, так как IDA сама разбирается в большинстве существующих форматов файлов и правильно задает значения перемещаемых элементов.
Функция может быть полезна при написании своего загрузчика файла. Тогда необходимо для каждого перемещаемого элемента вызвать эту функцию, указав его линейный адрес (не адрес начала манипулирующей с ним инструкции).
Тип перемещаемого элемента может принимать одно из следующих значений:

FIXUP_MASK | 0xF |
FIXUP BYTE | 0 | 8-битное смещение
FIXUP_OFF8 | 0
FIXUP_OFF16 | 1 | 16-битное смещение
FIXUP_SEG16 | 2 | 16-битный сегмент (селектор)
FIXUP_PTR32 | 3 | 32-битный длинный указатель (16-бит база; 16-бит селек-
 |  | тор) |
FIXUP__OFF32 | 4 | 32-битное смещение
FIXUP_PTR48 | 5 | 48-битный указатель (16-бит база; 32-бит смещение)
FIXUP_HI8 | 6 | Старшие 8 бит 16-битного смещения
FIXUP__HI16 | 7 | Старшие 16 бит 32-битного смещения
FIXUP__LOW8 | 8 | Младшие 8 бит 16-битного смещения
FIXUP__LOW16 | 9 | Младшие 16 бит 32-битного смещения
FIXUP_REL | 0x10 | Fixup is relative to the linear address specified in the 3d |
 |  | parameter to set_fixup() |
FIXUP_SELFREL | 0x0 | Self-relative? — disallows the kernel to convert operands | in
 |  | the first pass — this fixup is used during output. This type
 |  | of fixups is not used anymore. Anyway you can use it | for
 |  | commenting purpose in the loader modules |
FIXUP_EXTDEF | 0x20 | Target is a location (otherwise — segment)
FIXUP_UNUSED | 0x40 | Fixup is ignored by IDA disallows the kernel to convert
 |  | operands — this fixup is not used during output |
FIXUP_CREATED | 0x80 | Fixup was not present in the input file
Следующие два аргумента задают селектор (сегмент) и смещение объекта, на
который ссылается перемещаемый элемент.


IDA - 0330.htm

330_____________________FIXUP______________________
Например:
Q копия экрана IDA:
segOOO:0000 start proc near
segOOO:0000 mov ax, seg dseg
segOOO:0003 mov ds, ax
segOOO:0005 assume ds:dseg
segOOO:0005 mov ah, 9
segOOO:0007 mov dx, OCh
segOOO:OOOA int 21h ; DOS - PRINT STRING
segOOO:OOOA ; DS:DX -> string terminated by "$'
segOOO:OOOC mov ax, seg dseg
segOOO:OOOF mov ds, ax
segOOO:0011 mov ah, 9
segOOO:0013 mov dx, OCh
segOOO:0016 int 21h ; DOS - PRINT STRING
segOOO:0016 ; DS:DX -> string terminated by "$"
segOOO:0018 mov ah, 4Ch
segOOO:001A int 21h ; DOS - 2+ - OUIT WITH EXIT CODE (EXIT)
segOOO:001A start endp ; AL = exit code
segOOO:001A
segOOO:001A segOOO ends
segOOO:001A
dseg:OOOC ; -- —————— —— —— ________— __. — ____________ ——— __ ——— ——— —— _
dseg:OOOC
dseg:OOOC ; Segment type: Pure data
dseg:OOOC dseg segment para public 'DATA' use16
dseg:OOOC assume cs:dseg
dseg:OOOC ;org OCh
dseg:OOOCaHelloSailor db 'Hello,Sailor!',ODh,OAh,T
dseg:OOOC dseg ends
B приведенном выше коде перемещаемый элемент был создан вызовом:
SetFixup(Oxl0001, FIXUP_SEG16, 0, 0x1001, 0);

Операнд | Пояснения
еа | Линейный адрес
Туре | Тип перемещаемого | элемента
sel | Селектор
off | Смещение
Displ | Displacement
void DelFixup (long еа)
Функция удаляет перемещаемый элемент, заданный линейным адресом.

Операнд | Пояснения
еа | Линейный адрес


IDA - 0331.htm

Анализ 331
Анализ

long FindText (long еа, long flag, long у, long х, | Ищет фрагмент дизассемблируемого
char str) | текста
Прочие функции
Void DeleteAll | Удаляет все элементы и связанные с
 | ними элементы
Long AnalyseArea (long sEA, long eEA) | Дизассемблирует выбранную область
void AutoMark (long еа, long queuetype) | Управляет автоанализом
void AutoMark2 (long start, long end, long queuetype) | Управляет автоанализом
void DeleteAll ()
Невероятно "полезная" функция, удаляющая всю информацию о дизассемблируемой программе — сегменты, метки, комментарии, словом все флаги и связанные с ними объекты.
Как вариант частичного отката результата использования этой функции — немедленный аварийный выход из IDA без сохранения последних изменений.
Пример использования:
G копия экрана IDA:
segOOO:0000 ;
segOOO:0000 ; File Name : F:\IDAF\IDA\test.exe
segOOO:0000 ; Format : MS-DOS executable (EXE)
segOOO:0000 ; Base Address: 1000h Range: 10000h-132EAh Loaded length: 32EAh
segOOO:0000 ; Entry Point : 1000:22CO
segOOO:0000
segOOO:0000
segOOO:0000 ; ——— —————— ——— - ——— ---------- —— ______ ——— ______
segOOO:0000
se,gOOO:0000 ; Segment type: Pure code
segOOO:0000 segOOO segment byte public 'CODE' use16
segOOO:0000 assume cs:segOOO
segOOO:0000 assume es:nothing, ss:nothing, ds:nothing, fs:nothing,
gs:nothing
segOOO:0000
segOOO:0000; ____ S U B R 0 U T I N E ____________________
segOOO:0000
segOOO:0000
segOOO:0000 MyFunct proc near ; CODE XREF: sub_0_22DD+1Ep
segOOO:0000 push ax ; My Comment
segOOO:0001 push bx
segOOO:0002 push cx
segOOO:0003 push dx
segOOO:0004
segOOO:0004 MyLabel:


IDA - 0332.htm

332___________________Анализ______________ ____
segOOO:0004 mov ax, 3D02h
segOOO:0007 mov dx, 206h
segOOO:OOOA int 21h
> ввод пользователя:
DeleteAll ();
Q копия экрана IDA:
0:00010000 db 50h P
0:00010001 db 53h S
0:00010002 db 51h Q
0:00010003 db 52h R
0:00010004 db OB8h +
0:00010005 db 2
0:00010006 db 3Dh
0:00010007 db OBAh ш
0:00010008 db 6
0:00010009 db 2
0:0001000A db OCDh
long AnalyseArea (long sEA, long eEA)
Полный анализ выбранной области. Весь код будет дизассемблирован незави-
симо от того, были обнаружены на него ссылки или нет.
Сравните:
G копия экрана IDA:
segOOO:0100 start db OBBh +
segOOO:0101 db 5
segOOO:0102 db 1
segOOO:0103 db OFFh
segOOO:0104 db OE3h у
segOOO:0105 db OB4h ш
segOOO:0106 db 6
segOOO:0107 db OB2h
segOOO:0108 db 7
segOOO:0109 db OCDh
segOOO:010A db 21h !
segOOO:010B db OC3h ; +
segOOO:010B segOOO ends
> ввод пользователя:
MakeCode(Oxl0100);
Q копия экрана IDA:
segOOO:0100 start:
segOOO:0100 mov bx, 105h
segOOO:0103 jmp bx
segOOO:0103 ; ------- — --------------- —— ___--__
segOOO:0105 db OB4h ; ш
segOOO:0106 db 6 ;
segOOO:0107 db OB2h ;
segOOO:0108 db 7 ;
segOOO:0109 db OCDh ; -


IDA - 0333.htm

Анализ 333
segOOO:010A db 21h ; !
segOOO:010B db OC3h ; +
segOOO:010B segOOO ends
> ввод пользователя:
AnalyseArea(Oxl0100,0x10B);
Q копия экрана IDA:
segOOO:0100 start:
segOOO:0100 mov bx, 105h
segOOO:0103 jmp bx
segOOO:0105 ; ————--- ——— - ——————— ---- ———
segOOO:0105 mov ah, 6
segOOO:0107 mov dl, 7
segOOO:0109 int 21h
segOOO:010B retn
segOOO:010B segOOO ends
Аналогично автоанализу, функция может выполняться в фоновом режиме. Поэтому, в скриптах эта функция используется совместно с Wait(). К сожалению никаких других, более развитых средств синхронизации не предусмотрено.
Задаваемые границы анализируемой области не обязательно должны существовать в природе. В крайнем случае, будет проанализирована область от наименьшего до наибольшего существующих адресов.
AnalyseArea(0,Ox30000) успешно выполнится, даже если наименьшим из существующих окажется адрес 'OxlOOOO'.
'AnalyseArea(0,BADADDR-l);' выполнится успешно, и для удобства может быть определено, как макрос 'AnalyseAll', и размещено в файле 'ida.idc'.
При нормальном завершении функция возвращает 'Г, и '0' — если анализ был прерван пользователем по нажатию 'Ctrl-Break'.

Операнд | Пояснения
sEA | Линейный адрес начала анализируемой области
еЕА | Линейный адрес конца анализируемой области
Return | ==return | Пояснения
 | ==1 | Анализ был успешно завершен
 | ==0 | Анализ был прерван нажатием Ctrl-Break
voidAutoMark (long ea, long queuetype)
void AutoMark2 (long start, long end, long queuetype)
Это функции, позволяющие непосредственно управлять автоанализом. Пользователь может явно указать, как будет трактоваться та или иная область. Допустим, нам известно, что в приведенном ниже примере по адресу 0xE расположены данные, а не код.
Однако IDA не может "догадаться" до этого и дизассемблирует код не так, как мы этого ожидаем.
Разумеется, можно прибегнуть к ручному анализу, но это будет слишком утомительно. Гораздо проще использовать следующие команды:


IDA - 0334.htm

334 Анализ
G копия экрана IDA:
segOOO:0004 unk_0_4 db OB8h
segOOO:0005 db 2
segOOO:0006 db 3Dh
segOOO:0007 db OBAh
segOOO:0008 db 0
segOOO:0009 db 32h
segOOO:OOOA db OCDh
segOOO:OOOB db 21h
segOOO:OOOC db 73h
segOOO:OOOD db 9
segOOO:OOOE db OB4h
segOOO:OOOF db 3Ch
> ввод пользователя:
AutoMark(OxlOOOE,AU_UNK);
MakeCode(0x10004);
G копия экрана IDA:
segOOO:0004 loc_0_4:
segOOO:0004 mov ax, 3D02h
segOOO:0007 mov dx, 3200h
segOOO:OOOA int 21h
segOOO:OOOC jnb near ptr unk_0_1
segOOO:OOOC ; ————_______________________________
segOOO:OOOE db OB4h ; ш
segOOO:OOOF db 3Ch ; <
Как видно, "MakeCode" дошла до адреса 0xE и остановилась. Это очень удобный способ ограничить диапазон ее действия (конечного адреса MakeCode не имеет).
Кроме этого с помощью AutoMark можно создавать функции, применять библиотеки сигнатур и так далее. Действие определяется выбором параметра queuetype. Для него определены следующие константы:

Определение | Действие
AU_UNK | НЕ исследовать указанную область
AU_CODE | Преобразовать указанную область в код
AU_PROC | Создать функцию по указанному адресу
AL_USED | Реанализ
AU_LIBF | Применить сигнатуру FLIRT
AU_FINAL | Свернуть все неисследованные области
Для одного и того же адреса допустимо задавать более одного указания. IDA помещает все запросы в очередь, поэтому "затирания" не происходит. После анализа запросы удаляются из очереди, поэтому при реанализе следует их задать повторно.
AutoMark2 отличается от AutoMark тем, что позволяет явно указать область действия запроса, тогда как вторая определяет ее автоматически.


IDA - 0335.htm

Анализ 335
Обращаться с этой функцией следует осторожно. Если тип запроса не будет соответствовать определенным константам, то IDA выдаст сообщение об ошибке и аварийно выйдет в операционную систему без сохранения результата работы.

Операнд | Пояснения
'еа' | Линейный адрес
queuetype | Тип запроса (смотри таблицу выше)
long FindText (long ea, long flag, long у, long x, char str)
Это очень полезная и мощная функция для глобального поиска подстроки во всем дизассемблируемом тексте. Это касается не только строковых выражений, но и символьных представлений инструкций, имен, комментариев, меток и перекрестных ссылок. Словом, равносильно тому, как если бы мы вывели результат работы дизассемблера в LST-файл отчета и искали бы в нем требуемую подстроку.
Заметим, что чаще все же так и поступают, потому что FindText работает достаточно медленно и не поддерживает символы-джокеры, как, например hiew. Очень часто критерии поиска настолько сложны, что не могут быть реализованы через FindText. Поэтому приходится прибегать к созданию хитрых скриптов для весьма изощренного поиска.
Однако, в ряде случаев FindText все же хватает для простых задач и легче использовать несколько вызовов этой функции с разными параметрами, чем прибегать к нештатным средствам.
Младший бит второго атрибута — флага задает направление поиска. Если он установлен, то поиск будет идти от младших адресов к старшим или наоборот.
Первый, считая от нуля, бит флага указывает на чувствительность функции к регистру. Если он установлен, то заглавные и строчные символы будут различаться.
Координаты 'x' и 'y' применимы только к многострочным комментариям (ExtLinA\ExtLinB), в остальных же случаях они игнорируются и могут быть равны нулю.
'srt' задает подстроку поиска.
Q копия экрана IDA:
segOOO:005A 88 04 . mov [si], al
segOOO:005C 46 inc si
segOOO:005D E2 E7 loop loc_0_46
segOOO:005F BE ЕС 01 mov si, 1ECh
segOOO:0062 E8 78 00 call sub_O_DD
> ввод пользователя:
Message("%x \n",
FindText(Oxl005A,l,0,0,"loop")
) ;
< ответ IDA:
1005D
В случае ошибки поиска функция возвращает константу BADADDR.


IDA - 0336.htm

336 Анализ

Операнд | Пояснения
еа | Линейный адрес
flag | ==flag | Направление поиска
 | 1 | Поиск "вперед"
 | 0 | Поиск "назад"
Return | ==return | Пояснения
 | !=BADADDR | Линейный адрес найденного текстового вхождения
 | ==BADADDR | Ошибка
char Demangle (char name, long disable_mask)
Функция "размангляет" переданное ей имя name в соответствии с заданными настройками disable_mask (см. табл. ниже).
Если функция не может разманглить имя, она возвращает пустую строку. IDA Рго поддерживает спецификации Watcom, Microsoft и Borlad. Перечни символов, используемых для замангления имен содержатся в поле "MangleChars" конфигурационного файла <ida.cfg>.
MangleChars = "$:?([.)]" // watcom
"@$%?" // microsoft
"@$%"; // borland
Пример использования:
Message(">%s\n",Demangle("??1streambuf@@UAE@XZ", MNG_DEFNONE));
a) вызов функции Demangle для "размангления" имени "??1streambuf@@UAE@XZ"
>public: virtual _thiscall streambuf::~streambuf(void)
b) результат

Флаг | # | Пояснения
MNG_NOPTRTYPE | 0x00000001 | Не показывать ни far, ни near, ни huge модифи-
 |  | каторы
MNG_DEFNEAR | 0x00000000 | Не показывать near модификатор
MNG_DEFFAR | 0x00000002 | Не показывать far модификатор
MNG_DEFHUGE | 0x00000004 | Не показывать huge модификатор
MNG_DEFNONE | 0x00000006 | Показывать модификаторы near, far, huge (если
 |  | установлен MNG_NOPTRTYPE, модификаторы не
 |  | будут отображаться)
MNG_NODEFINIT | 0x00000008 | Не показывать ничего, кроме главного имени
MNG_NOUNDERSCORE | 0x00000010 | Не показывать символы прочерка
MNG_NOTYPE | 0x00000020 | Не выполнять преобразование типов передаваемых
 |  | параметров и базового класса
MNG_NORETTYPE | 0x00000040 | Не показывать тип значения, возвращаемого
 |  | функцией
MNG_NOBASEDT | 0x00000080 | Не показывать базовый тип
MNG_NOCALLC | 0x00000100 | Нигде не преобразовывать типы


IDA - 0338.htm

338 Взаимодействие с пользователем
Взаимодействие с пользователем
ALMA MATER
Изначально IDA проектировалась как интерактивная среда, тесно взаимодействующая с пользователем. Однако для скриптов большинство интерфейсных функций недоступно. Нельзя, например, сконструировать диалог или создать свой пункт меню. В распоряжении пользователя оказывается набор функций, обеспечивающий примитивный ввод-вывод, т.е. простейшие диалоговые окна запроса параметров и вывода результатов деятельности на экран. В большинстве случаев этого оказывается достаточно: скрипты работают и в автономном режиме.
Но иногда все же требуется узнать позицию курсора на экране, или наоборот, перевести курсор на определенное место, чтобы привлечь внимание пользователя, сообщить о результатах своей работы и так далее.
Вот для этого и существует набор специальных функций, ютящихся под одной крышей только потому, что они попали под критерий "взаимодействие с пользователем". В отличие от всех описанных выше, они не относятся к какому-то определенному объекту, и непонятно, какой частью архитектуры IDA они являются.
Но... они есть и, следовательно, будут тщательно рассмотрены и описаны. Для облегчения понимания введем дополнительную условную классификацию. .
Итак, одна группа функций взаимодействует с курсором на экране. Что такое курсор с точки зрения IDA? Это указатель текущей строки, которая связана с некоторым объектом, точнее — с линейным адресом его начала. При работе с курсором IDA рассматривает не его экранные координаты, а только линейный адрес памяти, на который этот курсор указывает.
Часто несколько строк расположены по одному и тому же линейному адресу. Например:
G копия экрана IDA:
.text:00401020
.text:00401020 ; __________ S U B R 0 U Т I N E _________________________________
.text:00401020
.text:00401020 ; Attributes: library function bp-based frame
.text:00401020
.text:00401020 public start
.text:00401020 start proc near
.text:00401020
.text:00401020 var_20 = dword ptr -20h
.text:00401020 var_1C = dword ptr -1Ch
.text:00401020 var_18 = dword ptr -18h
.text:00401020 var_14 = dword ptr -14h
.text:00401020 var_4 = dword ptr -4
.text:00401020
.text:00401020 push ebp


IDA - 0339.htm

Взаимодействие с пользователем 339
Все эти строки идентичны с точки зрения IDA, поэтому в которой бы из них ни находился курсор, при попытке определить его положение всегда вернется адрес 0x401020, что неудивительно. Но далеко не однозначно, на какую строку переместится курсор при попытке изменить его положение. Оказывается, что на первую, в которой присутствует инструкция или команда. Это относится к тонкостям реализации, сохранность которых в последующих версиях не гарантируется.
Другая группа функций работает с выделенным, т.е. отмеченным блоком на экране. Это наиболее популярный способ передачи скрипту входных данных для работы, а точнее — диапазона адресов. Этим заведуют две функции — SelStart и SelEnd. K сожалению, выделение программно доступно "Только на чтение", и выделить регион самостоятельно скрипт не может.
Теперь перейдем к функциям, управляющим экранным вводом — выводом. Ввод данных обеспечивают едва ли не десяток специализированных функций, создающих диалоговые окна и проверяющие корректность ввода пользователя. Однако, в силу больших условностей и множества оговорок, лучше пользоваться только низкоуровневыми функциями, обеспечивающими ввод строкового или длинного целого значения, и проверять их корректность самостоятельно.
Вывод данных в основном направляется в окно сообщений и, за редким исключением, — во всплывающие диалоговые окна. Все необходимые функции подробнее будут описаны ниже.
Методы

Функция | Назначение
char AskStr | Запрашивает у пользователя строку
(char defval, char prompt) |
char AskFile (long forsave, | Создает диалоговое окно для выбора имени файла
char mask, char prompt) |
long AskAddr | Запрашивает у пользователя адрес
(long defval, char prompt) |
long AskLong | Запрашивает у пользователя число типа long
(long defval, char prompt) |
long AskSeg | Запрашивает у пользователя сегмент
(long defval, char prompt) |
char Askldent | Запрашивает у пользователя ввод имени идентификатора
(char defval, char prompt) |
long AskYN | Создает модальный диалог Yes \ No \ Cancel
(long defval, char prompt) |
void Message (char format,...) | Выводит строку в окно сообщений
void Warning (char format,...) | Выводит предупреждающий диалог
void Fatal (char format,...) | Выводит фатальный диалог
long ScreenEA () | Возвращает линейный адрес строки, на которой стоит курсор


IDA - 0340.htm

340 Взаимодействие с пользователем

Функция | Назначение
long SelStart () | Возвращает линейный адрес начала выделенной области
long SelEnd () | Возвращает линейный адрес конца выделенной области
success Jump (long ea) | Изменяет позицию курсора в окне дизассемблера
void Wait () | Ожидает конца автоанализа
long AddHotkey | Добавляет новую горячую клавишу
(char hotkey, char idcfunc) |
success DelHotkey (char hotkey) | Удаляет горячую клавишу
char AskStr (char defval, char prompt)
Функция создает и выводит модальный диалог ввода строки. Используется в
скриптах, тесно взаимодействующих с пользователем.
В консольной версии окно будет выглядеть следующим образом:
AskStr("MyDefaultString","MyPrompt");-

А в графической версии приглашение для ввода выглядит так:
IDA - 0340-1.jpg
Операнд | Пояснения
defval | Значение по умолчанию
prompt | Пояснение, которое будет выведено в диалоговом окне
Return | Завершение | Пояснения
 | !="" | Строка
 | "" | Ошибка
Если пользователь откажется от ввода и нажмет <Esc> или <Cancel>, то
функция возвратит пустую строку, а не значение по умолчанию.


IDA - 0341.htm

Взаимодействие с пользователем 341
Пример использования:
auto s;
s=" ";
while(s!="-")
{
s=AskStr(s,"Bame имя?");
Message
("Добрый день (утро, вечер, ночь!) %s! \n",s);
}
charAskFile (long forsave, charmask, charprompt)
Функция создает диалоговое окно, предназначенное для выбора файла, оснащенное минимальными средствами навигации.
Внешний вид окна для консольной и GUI-версий, различен. И в последнем случае у пользователя значительно больше возможностей и свободы действий.
IDA - 0341-1.jpg
IDA - 0341-2.jpg
Два приведенных выше окна были созданы с помощью вызова
AskFile(0,"*.*","MyPrompt");


IDA - 0342.htm

342 Взаимодействие с пользователем
Флаг 'forsave', вероятно, должен был уточнять тип окна — на открытие файла или на запись. За кажущейся идентичностью (оба окна с точки зрения пользователя выглядит одинаково) скрыта большая разница. Окно выбора файла для записи должно само запрашивать подтверждение, если запрошенный файл уже существует. IDA, однако, этого не делает независимо от значения флага forsave и не выдает подтверждений, если указанный файл уже существует.
Если пользователь откажется от выбора и нажмет <Esc> или CANCEL, функция вернет пустую строку, в противном случае — имя файла.
Эта функция поддерживает длинные имена Windows 95\Windows NT 4.0, и это следует учитывать при операциях с именами файлов (например, при синтаксическом разборе).

Операнд | Пояснения
forsave | Флаг выбора типа диалога. Не поддерживается в настоящих версиях
mask | Маска для выбора отображаемых в окне файлов
promt | Заголовок окна
Return | Завершение | Пояснения
 | |="" | Имя файла
 | "" | Ошибка
long AskAddr (long defval, char prompt)
Функция выводит модальный диалог, запрашивающий у пользователя ввод адреса в формате segment : offset. Если сегмент указан, то функция вернет значение, вычисленное по следующей формуле:
|| Value == segment « 4 + offset
При этом функция позволяет указывать не только адреса, но и имена сегментов, вычисляя при этом адреса автоматически (обратите внимание, что при этом необходимо соблюдать регистр).
Например:
> ввод пользователя:
Message ("%x \n", AskAddr(0,"MyPrompt"));
Q копия экрана IDA:
[segOOO:0]
< ответ IDA:
10000
Но вот уже [SegOOO:0] приведет к выводу диалога, предупреждающего о неверно введенном адресе, а функция возвратит ошибку BADADDR.


IDA - 0343.htm

Взаимодействие с пользователем 343
IDA - 0343-1.jpg
Если не указывать сегмент (а только одно смещение), то функция возьмет по
умолчанию базовый сегмент файла.
Например:
> ввод пользователя:
Message ("Ox%x \n",AskAddr(0, "MyAddr") ) ; <Ctrl-Enter>
123h <Enter>
< ответ IDA:
0x10123
Если необходимо, чтобы функция воспринимала ввод "как он есть1', то следует
вместо сегмента указать '0', как показано ниже:
IDA - 0343-2.jpg
IDA - 0343-3.jpg
Очень полезна поддержка относительного адреса. Если перед вводимым числом указать знак, то возвращенное функцией значение будет вычислено по следующей формуле:
RetVal == ScreenEA() + EntVal
Адрес вычисляется относительно текущей позиции курсора. При этом полученное значение может выходить за рамки доступных адресов сегмента, — никакой проверки функция не обеспечивает. Эта задача ложится на плечи программиста, разрабатывающего скрипт.
Например:


IDA - 0344.htm

344 Взаимодействие с пользователем
G копия экрана IDA:
segOOO:32E8> db 21h ; segOOO:32E9 db 0 ; segOOO:32E9 segOOO ends :
> ввод пользователя:
Message("Ox%X \n",AskAddr(0,"")); <Ctrl-Enter>
+4 <Enter>
< ответ IDA:
0x132ED
Если указать '+0:4', то IDA будет трактовать такое выражение совершенно иначе! А именно — как абсолютный адрес. При этом отрицательные значения преобразуются в беззнаковые с учетом разрядности сегмента (16 или 32 бит), а переполнение "вверх" никак не отслеживается. Это дает возможность адресовать память как в пределах сегмента, так и за ними.

Операнд | Пояснения
defval | Значение по умолчанию (long)
promt | Заголовок окна
Return | Завершение | Пояснения
 | !=BADADRR | Адрес
 | "" | Ошибка
Обратите внимание, что 'defval' имеет значение long, а не char, и, следовательно, представляет собой линейный адрес. Преобразование линейного адреса в сегментный делает IDA. Логично, что для этого необходимо воспользоваться следующей формулой:
| seg = EntVal / 10 ; off = EntVal - seg
Однако до версии 4.0 IDA не выполняет операций над адресом, используя нулевой сегмент, если только адрес невозможно представить комбинацией уже существующего сегмента и смещения. Следовательно, '0xl0002' будет автоматически преобразовано IDA в 'segOOO:2'. При этом всегда проверяется принадлежность образовавшегося смещения к доступному диапазону адресов выбранного сегмента, и в случае нарушения границ преобразования не происходит.
long AskLong (long defval, charprompt)
Функция запрашивает у пользователя ввод длинного целого числа. По умолчанию используется шестнадцатиричная система исчисления. Префикс 'x' можно ставить, а можно не ставить — все равно число будет трактоваться как шестнадцатиричное.
Отмена ввода, или некорректный ввод, приводит к возвращению ошибки BADADDR и, возможно, появлению предупреждающего диалогового окна, поясняющего источник ошибки.


IDA - 0345.htm

Взаимодействие с пользователем 345
IDA - 0345-1.jpg
IDA - 0345-2.jpg
Пример использования:
AskLong(86562,"MyLong");

Операнд | Пояснения
defval | Значение по умолчанию
promt | Заголовок окна
Return | Завершение | Пояснения
 | !=BADADRR | Введенное пользователем число
 | .... | Ошибка
long AskSeg (long defval, charprompt)
Функция выводит диалог, запрашивающий ввод сегмента (селектора). Допустимо вводить имена сегментов с учетом регистра. Введенные селекторы автоматически не преобразуется в адреса сегментов, и эту операцию приходится выполнять вручную.
В любом случае функция возвращает значение типа word, а не long. О факте выхода за допустимые границы IDA не сообщает, но отбрасывает старшее слово введенного значения.

Операнд | Пояснения
defval | Значение по умолчанию (long)
promt | Заголовок окна
Return | Завершение | Пояснения
 | !=BADADRR | Сегмент
 | "". | Ошибка
Обратите внимание, что 'defval' имеет тип long, а не char, поэтому непосредственная передача имени сегмента по умолчанию невозможна. Однако IDA автоматически подставляет его, если сегмент с заданным адресом уже существует.


IDA - 0346.htm

346 Взаимодействие с пользователем
К сожалению, в IDA (включая версию 4.0) присутствует ошибка, в результате которой вместо ожидаемого символьного имени сегмента выводится нечто нечитабельное и непечатаемое. Пример:
AskSeg(1,"");
IDA - 0346-1.jpg
IDA - 0346-2.jpg
IDA успешно распознала переданный ей селектор и определила, какому сегменту он принадлежит. Обратная операция, к сожалению, не поддерживается.
В случае ошибки (или отмены) ввода возвращается ошибка BADSEL (не BADADDR!). Это происходит потому, что функция маскирует старшее слово, в результате чего (0xFFFFFFFF & 0xFFFF) == 0xFFFF, т.е. BADSEL, a не BADADDR и не -1.
char Askldent (char defval, char prompt)
Эта функция предназначена для ввода идентификатора (имени). От AskStr ee отличает лишь дополнительная проверка корректности (максимальная длина имени, первый символ строки не цифра и так далее).
В отличие от остальных функций, возвращающих в случае неверного ввода ошибку, Askldent возвращает управление, дождавшись корректного ввода или его явной отмены.
IDA - 0346-3.jpg
IDA - 0346-4.jpg


IDA - 0347.htm

Взаимодействие с пользователем 347
Если строка начинается с символа '@', то функция всегда возвращает ""; двоеточие не считается недопустимым символом, даже если оно находится в середине строки. Поэтому в некоторых ответственных случаях не помешает воспользоваться функцией AskStr и все необходимые проверки выполнить самостоятельно.

Операнд | Пояснения
defval | Значение по умолчанию
promt | Заголовок окна
Return | Завершение | Пояснения
 | !="" | Строка
 | мм | Ошибка
long AskYN (long defval, charprompt)
Функция создает модальный диалог "Yes \ No \ Cancel".
AskYN(l,"Hello!");
IDA - 0347-1.jpg
IDA - 0347-2.jpg
Операнд | Пояснения
Defval | Значение по умолчанию
 | ==defval | Кнопка по умолчанию
 | 0 | <NO>
 | 1 | <YES>
 | -1 | <CANCEL> ^
return | 0 | Пользователь нажал <NO>
 | 1 | Пользователь нажал <YES>
 | -1 | Пользователь нажал <CANCEL> | или Escape


IDA - 0348.htm

348 Взаимодействие с пользователем
void Message (char format,...)
Функция выводит строку в окно сообщений (Messages windows) IDA. Это наиболее популярный способ вывода результатов работы скриптов, а также отладочных и диагностических сообщений.
IDA - 0348-1.jpg
IDA - 0348-2.jpg


IDA - 0349.htm

Взаимодействие с пользователем 349
Перед выполнением примера убедитесь, что окно сообщений не закрыто остальными окнами.
Функция Message понимает стандартные спецификаторы формата вывода Си и ближе всего к функции printf (см. также описание form).

Сф |
%d | Десятичное длинное знаковое целое. Пример: Message ("%d", 0xF) ; 15
%х | Шестнадцатиричное длинное целое строчными символами. Пример: Message ("%x", 10) ; а
%Х | Шестнадцатиричное длинное целое заглавными символами. Пример: Message ("%X", 10) ; Л
%о | Восьмиричное длинное знаковое целое. Пример: Message ( "%о" , 11) ; 13
%u | Десятичное длинное беззнаковое целое. Пример: Message ("%u", -1) ; 4294967295
%f | Десятичное с плавающей точкой. Пример: Message ("%f", 1000000); I.e6
%с | Символьное значение. Пример: Message ("%c", 33) ; i
%s | Строковое значение. Пример: Message ("%s", "Hello, Word! \n") ; Hello, Word!
%е | Вывод чисел в экспоненциальной форме. Пример: Message ("%e", 1000000); 1.еб
%g | Вывод чисел в экспоненциальной форме. Замечание. В оригинале спецификатор '%g' заставляет функцию саму решать, в какой форме выводить число — с десятичной точкой или в экспоненциальной форме, из соображений здравомыслия и удобочитаемости. IDA всегда при задании этого спецификатора представляет числа в экспоненциальной форме


IDA - 0350.htm

350 Взаимодействие с пользователем

Сф |
%а | Вывод указателя (не поддерживается). Замечание. Вместо спецификатора '%p' IDA использует '%a', преобразующий линейный адрес в строковый сегментный, и автоматически подставляет имя сег- мента. Так, например, 'Message ("%a \n", 0x10002) ' выдаст ' segOOO: 2'. Обрати- те внимание, что таким способом нельзя узнать адрес переменной. Пример: auto а; a="Hello!\n"; Message ("%a \n",a); 0 Возвращается ноль, а не указатель на переменную
%р | Вывод десятичного целого (всегда со знаком, не опуская плюс)
%+d | В оригинале — вывод шестнадцатиричного целого всегда со знаком, но IDA воспринимает эту конструкцию точно так же, как и 'x'
%+х | 'n' — длина выводимого десятичного числа, при необходимости дополняемого слева пробелами. Например: Message ("Число-%3с1 \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3с1 \п", 10000) ; Число-10000
%nd | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополняемого слева пробелами. Например: Message ("Число-%3х \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3х \n", 0x1234); Число-1234
%nd | 'n' — длина выводимого десятичного числа, при необходимости дополняемого слева незначащими нулями. Пример: Message ("Число-%ОЗсГ', 1) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%ОЗсГ , 1000) Число-1000
%0nx | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополняемого слева незначащими нулями. Пример: Message ("Чис-г/ -%03x" , 0x1) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%03х", 0x1234) ; Число-1234


IDA - 0351.htm

Взаимодействие с пользователем 351

Сф |
%#х | Вывод префикса '0x' перед Пример: Message ("%#x", 123) ; 0x123 | шестнадцатиричными числами.
%#0 | Вывод префикса '0' перед восьмиричными числами. Пример: Message ("%#o", 1) ; 01
%п | Количество выведенных символов (не поддерживается)
void Warning (char format,...)
Функция выводит диалоговое окно, предупреждающие об аварийной ситуации. Обратите внимание на тип возращаемого значения void: функция не предоставляет информации о том, какая клавиша была нажата.
<OK> или <ESC> просто возвращают управление скрипту; <Abort> приводит к аварийному выходу из IDA (правда, перед этим у пользователя будет запрошено подтверждение). A <SILENT> включает "тихий" режим, в котором подобные окна не отображаются. Пример:
Warning ("Hello!");
IDA - 0351-1.jpg
IDA - 0351-2.jpg
Ситуаций, в которых бы требовался аварийный выход из IDA, очень немного. Между тем эта функция — вторая по популярности после Message. Очень часто она используется как простой информирующий диалог, не ожидающий от пользователя никакого выбора (например, так поступает демонстрационный плагин Strings).
Сравните приведенный выше пример с результатом демонстрации работы AskYN. Не правда ли, "HELLO! — OK" вполне очевидно, тогда как "HELLO! YES? NO? CANCEL?" может вызвать легкое недоумение и растерянность. К тому же Warning, в отличие от AskYN, поддерживает стандартные спецификаторы форматированного вывода Си. (Подробнее см. описание функции Message). Ho все же использование Warning по поводу и без повода относится к "дурным" приемам программирования, которых следует по возможности избегать.


IDA - 0352.htm

352 Взаимодействие с пользователем
void Fatal (char format,...)
Эта функция создает модальный диалог, выводящий указанное сообщение, и производит немедленный аварийный выход из IDA без подтверждений.
Существует очень немного случаев, требующих применения столь "варварских" средств. Пример:
Fatal ("Hello");
IDA - 0352-1.jpg
IDA - 0352-2.jpg
Функция поддерживает стандартные спецификаторы Си, которые подробнее были описаны в функции Message.
long ScreenEA()
Функция возвращает линейный адрес строки с текущей позицией курсора. Очень широко используется в скриптах, в том числе и приведенных в этой книге.
Позволяет организовать взаимодействие между скриптом и пользователем, а также облегчает вычисление линейного адреса в произвольной точке. Вместо того, чтобы искать адрес начала сегмента по имени и суммировать его с необходимым смещением, можно просто ткнуть курсором в нужное место и вызвать эту функцию.
Возвращаемый адрес всегда округляется до начала строки. Невозможно выбрать элемент массива, отличный от первого. Особенно много неудобств это доставляет при просмотре дизассемблируемого файла в шестнадцатиричном виде, когда независимо от положения курсора в строке функция всегда возвращает адрес ее начала.
Пример использования:
G копия экрана IDA:
.text:004010F4 sub_0_4010F4 proc near ; DATA XREF:
.text:004010F4 mov esp, [ebp-18h]
.text:004010F7 push dword ptr [ebp-20h]
.text:004010FA call __exit
.text:004010FA sub_0_4010F4 endp


IDA - 0353.htm

Взаимодействие с пользователем 353
IDA - 0353-1.jpg
> ввод пользователя:
Message ("%x \n",
ScreenEA ()
);
< ответ IDA:
4010f7

Return | ==return | Пояснения
 | !=BADADDR | Линейный адрес | начала элемента в текущей позиции курсора
 | ==BADADDR | Ошибка
long SelStart()
Функция возвращает линейный адрес начала выделенной области.
Широко используется для работы с блоками и позволяет организовать взаимодействие между скриптом и пользователем.
Выделять можно только строки целиком. Как и при использовании функции ScreenEA(), можно узнать только адрес начала строки. Если выделение отсутствует, то возвращается ошибка (BADADDR).
Пример использования:


IDA - 0354.htm

354 Взаимодействие с пользователем
G копия экрана IDA:
segOOO:OB50 pusha
segOOO:OB51 push ds
segOOO:OB52 push es
segOOO:OB53 call sub_0_E89
> ввод пользователя:
Message ("%x \n",
SelStart () );
< ответ IDA:
10B52

Return | ==return | Пояснения
 | !=BADADDR | Линейный адрес | начала выделенной области
 | ==BADADDR | Ошибка
Long SelEnd ()
Функция возвращает линейный адрес первого байта за концом выделенной
области. Если выделение отсутствует, то функция вернет ошибку BADADDR.
Пример использования:
Q копия экрана IDA:
segOOO:OB50 pusha
segOOO:OB51 push ds
segOOO:OB52 push es
segOOO:OB53 call sub_0_E89
> ввод пользователя:
Message ("%x \n",
SelEnd () );
< ответ IDA:
10B53

Return | ==return | Пояснения
 | !=BADADDR | Линейный адрес следующего байта за ной области | концом выделен-
 | ==BADADDR | Ошибка
success Jump (long ea)
Функция перемещает позицию курсора в окне дизассемблера IDA по требуе-
мому адресу.

Операнд |
еа | 32-разрядный линейный адрес
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0355.htm

Взаимодействие с пользователем 355
Функция активно используется в пользовательских скриптах, однако имеется ряд тонкостей.
Прежде всего, экран обновляется не в момент выполнения функции, а только после завершения работы скрипта. Поэтому следующий код не будет выполняться желаемым образом:
while(1)
Jump(AskAddr(OxlOOOO,"Введите адрес для перехода"));
Эта особенность не позволяет динамически иллюстрировать работу скрипта и заставляет искать особые решения, когда требуется интерактивное взаимодействие и одновременно показ нужного кадра окна. Приходится предусматривать временный выход из скрипта и последующий вход, благо это возможно, достаточно лишь сохранять значения всех переменных в массиве или виртуальной памяти (для любителей экзотики или перестраховщиков — в файле). Но это все же концепция, непривычная для рядового программиста.
Другим минусом является округление адреса перехода до целой строки.
IDA - 0355-1.jpg
При задании несуществующего адреса курсор не изменяет своей позиции, а функция возвращает ноль.
Особенно это неудобно при переключении экрана в шестнадцатиричный режим. Jump не позволяет указывать на конкретный байт — только на строку целиком.
void Wait ()
Функция ожидает конца автоанализа, после чего возвращает управление. Большинство скриптов не могут работать параллельно с фоновым дизассемблером по тем очевидным причинам, что ожидают полностью готовый текст, а не динамически и непредсказуемо изменяющийся.
Автоанализ происходит при загрузке нового файла, а также при выполнении некоторых операций с исследуемым текстом. В интерактивном режиме можно дождаться окончания автоанализа визуально, но в пакетном так не получится.
Для этого и служит функция Wait. Хороший пример ее использования можно найти в файле 'analys.idc', поставляемом вместе с IDA.
long AddHotkey (char hotkey, char idcfunc)
Функция задает новую комбинацию клавиш для вызова функции IDA, определенной пользователем. Это очень удобное средство для интеграции своих скриптов в интерфейсную оболочку IDA.


IDA - 0356.htm

356 Взаимодействие с пользователем

Операнд | Назначение
Hotkey | Требуемая комбинация клавиш. Записывается в виде символьной строки. Например, "Alt-A". Могут так же использоваться "Ctrl", "Shift", "Enter", а также их комбинации
idcfunc | Символьное имя функции. Например, 'MyFunc'
В файле idc.idc содержатся следующие определения констант, связанных с
возвращением значений этой функцией:

Определение | Константа | Назначение
IDCHK_OK | 0 | Успешное завершение
IDCHK_ARG | -1 | Неверные аргументы
IDCHK_KEY | -2 | Ошибка в синтаксисе горячей клавиши
IDCHK_MAX | -3 | Задано слишком много горячих клавиш
Создадим и откомпилируем для примера следующий файл:
static MyFunc()
{
Message("Hello, IDA! \n");
}
Введем с консоли 'AddHotkey("ALT-A","MyFunc'');'. Если теперь нажать 'Alt-A', то на экране появится приветствие 'Hello, IDA!'.
Заметим, что перекрывать существующие клавиатурные комбинации можно совершенно безболезненно, за исключением того, что они автоматически не удаляются и 'кушают' при этом немного ресурсов, да и число "горячих клавиш" ограничено. Поэтому ненужные в данный момент комбинации рекомендуется предварительно удалять функцией 'DelHotkey'.
success DelHotkey (char hotkey)
Функция "удаляет" заданные пользователем "горячие клавиши". При попытке
удаления системной или несуществующей комбинации функция возвратит ошибку.
Операнд 'hotkey' был рассмотрен в описании функции AddHotKey.
Пример использования:
DelHotkey "Alt-A" ) ;

Операнд | Назначение
Hotkey | Требуемая комбинация клавиш. Записывается в Например, "Alt-A". Могут также использоваться также их комбинации |  | виде символьной строки. "Ctrl", "Shift", "Enter", a
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0357.htm

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

Функция | Назначение
void MarkPosition (long ea, long lnnum, long x, long у, long slot, char comment) | Добавляет элемент в список быстрых переходов
long GetMarkedPos (long slot) | Возвращает линейный адрес закладки
char GetMarkComment (long slot) | Возвращает комментарий к закладке
IDA поддерживает возможность быстрого перемещения между отдельными фрагментами дизассемблируемого текста с сохранением позиции курсора и относительного положения текста в окне.
Для запоминания текущей позиции необходимо нажать <Alt-N>, а для вызова списка всех запомненных ранее позиций — <Ctrl-N>. При этом возникнет следующего вида диалоговое окно:
IDA - 0357-1.jpg
IDA - 0357-2.jpg
IDA позволяет формировать содержание этого списка не только интерактивно, но и с помощью функций встроенного языка. Это может быть удобно в тех случаях, когда скрипт в результате анализа возвращает требующие внимания со стороны пользователя линейные адреса. Чаще всего их просто выводят в окно сообщений, но это плохое решение. Гораздо удобнее вывести их в список быстрых переходов.


IDA - 0358.htm

358 Маркировка позиций быстрого перемещения
void MarkPosition (long ea, long lnnum, long x, long у,
long slot, char comment)
Функция добавляет новый элемент в список быстрых переходов. Каждый элемент характеризуется набором атрибутов. Прежде всего, это линейный адрес строки, в которой расположен курсор. Поскольку часто по одному и тому же адресу расположено несколько строк, то атрибут 'lnnum' указывает на требуемую строку считая от нуля.
Позиция курсора по горизонтали, начиная от левого края окна, задается атрибутом 'x', a 'y' задает позицию по вертикали, начиная от верхней границы окна. Поскольку курсор жестко связан с выбранной строкой, то IDA прокручивает текст в окне дизассемблера на требуемую величину.
Положение элемента в списке определяется атрибутом Slot. Он может принимать любые значения в интервале от 1 до 20. Элементы необязательно должны следовать друг за другом, однако IDA не уничтожает пустые элементы в списке, поэтому задача их упорядочивания ложится на плечи разработчиков скрипта. Ситуация осложняется тем, что существует только один глобальный список, разделяемый одновременно как пользователем, так и всеми скриптами. Прежде чем заносить новый элемент, рекомендуется проверить, что требуемый слот свободен. Если указать слот, выходящий за допустимые границы, то IDA выведет интерактивный диалог для его выбора.
Рассмотрим это подробнее на следующем примере:
Q копия экрана IDA:
========================== IDA view-A ==========================
segOOO:0122 pop dx
segOOO:0123 pop cx
segOOO:0124 pop bx
segOOO:0125 pop ax
segOOO:0126 retn
segOOO:0126 sub_O_DD endp
segOOO:0126
segOOO:0127
segOOO:0127 ; _________ S U B R 0 U Т I N E _______________
segOOO:0127
——————————————— Lnnum
segOOO:0127 sub_0_127 i proc near ; CODE XREF:
segOOO:0127 call sub_O_DD
segOOO:012A retn
segOOO:012A sub_0_127 endp
segOOO:012A
segOOO:012B
segOOO:012B ; _________ S U B R 0 U Т I N E _______________
segOOO:012B
segOOO:012B
segOOO:012B sub_0_12B proc near ; CODE XREF:
segOOO:012B push ax
segOOO:012C push si
segOOO:012D push di
== 0001012C: sub 0 12B+1 ========================================


IDA - 0359.htm

Маркировка позиций быстрого перемещения 359
> ввод пользователя:
MarkPosition(Oxl0127, 4, 26, 12,1, "MyMark");
IDA - 0359-1.jpg
Допустимо существование двух и более объектов по одному и тому же
линейному адресу, поскольку IDA идентифицирует их по номеру слота.

Операнд | Пояснения
еа | Линейный адрес начала строки
lnnum | Номер линии, располагающейся по тому же адресу начиная с нуля
X | Горизонтальное положение курсора относительно левой |  | границы окна
Y | Вертикальное положение курсора относительно верхней |  | границы окна
 | ==slot | Пояснения
slot | ==1...20 | Номер слота
 | 0 | >20 | Интерактивный выбор номера слота
Comment | Комментарий к закладке
long GetMarkedPos (long slot)
Функция возвращает линейный адрес закладки по указанному slot. Подробнее
о закладках можно прочитать в файле <idc.idc>.
Например:
IDA - 0359-2.jpg
IDA - 0359-3.jpg


IDA - 0360.htm

360_______Маркировка позиций быстрого перемещения_________
> ввод пользователя:
auto а;
for (a=l;a<21;a++)
if (GetMarkedPos(a)!=-l) Message("Ox%X \n", GetMarkedPos(a));
< ответ IDA:
0x1005A 0x10037 0x100A7 0x10392
Обратите внимание, что следующий код не является корректным:
auto a,x;
a=0;
while ((x=GetMarkedPos(a++)!=-l)
Message("Ox%X \n", x);
Закладки необязательно следуют одна за другой и могут разделяться пустыми слотами. В этом случае приведенный выше код дойдет лишь до первой такой "дырки".

Операнд | Пояснения
slot | ==slot | Пояснения
 | ==1...20 | Номер слота
 | 0 | >20 | Интерактивный выбор номера слота
Return | ==return | Пояснения
 | !=BADADDR | Линейный адрес закладки
 | ==BADADDR | Ошибка
char GetMarkComment (long slot)
Функция возвращает комментарий к закладке, расположенной по указанному
slot. Подробнее о закладках можно прочитать в файле <idc.idc>.
Например:

IDA - 0360-1.jpg
> ввод пользователя:
auto а;
for (a=l;a<21;a++)
if (GetMarkComment(a)!=-l)
Message("%s \n", GetMarkComment (a));


IDA - 0361.htm

Маркировка позиций быстрого перемещения 361
< ответ IDA:
_ZwFilter
MyMarkl
WinALK
aOpenFile
Обратите внимание, что следующий код не является корректным:
auto a,x;
a=0;
while ((x=GetMarkComment (a++)!=-l)
Message("%s \n", x);
Закладки необязательно следуют одна за другой и могут разделяться пустыми слотами. В этом случае приведенный выше код дойдет лишь до первой такой "дырки".

Операнд | Пояснения
Slot | ==slot | Пояснения
 | ==l...20 | Номер слота
 | 0 | >20 | Интерактивный выбор номера слота
Return | ==return | Пояснения
 | !="" | Комментарий к закладке
 | =="" | Ошибка


IDA - 0362.htm

362 Генерация выходных файлов
Генерация выходных файлов
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 | База IDA в виде IDC-файла
OFILE_LST | Полный файл отчета
OFILE_ASM | Готовый к ассемблированию файл
OFILE_DIF | Файл различий (более известный как crk)
МАР-файл записывается в стандарте Borland и выглядит приблизительно следующим образом:
Start Stop Length Name Class
OOOOOH 032E9H 032EAH segOOO CODE
Address Publics by Value
0000:0002 MyLabelName
0000:0206 aScreen_log
0000:03EA aDeifxcblst
0000:22CO start
0000:2970 aOtkrivaemFail
0000:297F aMyfile
0000:2980 aYfile
0000:298F aCalc
Program entry point at 0000:22CO
МАР-файл используется для облегчения отладки программ. В коде становится легче ориентироваться по символьным меткам, переменным и функциям. Например, вместо абсолютного адреса для точки останова можно указывать ее имя. "Говорящие" названия улучшают восприятие кода, не дают запутаться и избавляют от необходимости возвращаться к уже проанализированным фрагментам.
Указанный формат поддерживают Borland Turbo Debuger, Periscope, a также другие отладчики. Популярный Soft-Ice имеет в стандартной поставке конвертор, преобразующий такие файлы к своему собственному формату.


IDA - 0363.htm

Генерация выходных файлов 363
Некоторые отладчики не поддерживают сегментацию, а другие имеют ограничение на количество имен, поэтому генерацией файла можно управлять. Для этого определены следующие значения флага 'flag':

Определение | Пояснения
GENFLG_MAPSEGS | Включать | в файл карту сегментов
GENFLG_MAPNAME | Включать | "dummy''-имена
"Dummy''-имена представляют собой автоматически генерируемые IDA имена, используемые для определения меток, процедур и данных. Они выглядят в виде sub_, loc__, ofL, seg_ и так далее. Чтобы не захламлять листинг, они не включаются в файл.
ЕХЕ-файл генерируется после того, как программа была изменена функциями PatchByte или PatchWord. Эти функции изменяют не оригинальный файл, а только содержимое базы IDA, и чтобы изменения возымели действие необходимо сгенерировать новый файл. Поддерживается очень ограниченный список форматов. Вот все, что можно получить на выходе:
• MS DOS .exe;
• MS DOS .com;
• MS DOS .drv;
• MS DOS .sys;
• general binary;
• Intel Hex Object Format;
• MOS Technology Hex Object Format.
При этом ЕХЕ-файл генерируется заново. Он содержит ту же таблицу перемещаемых элементов (ее невозможно изменить), а все неиспользуемые структуры заполняются нулями. Некоторые программы чувствительны к таким изменениям и откажут в работе.
К сожалению, не поддерживаются РЕ- и другие win32 файлы. В этом случае (а также когда ЕХЕ-файл чувствителен к неиспользуемым полям, — например, в свободное пространство заголовка иногда может быть помещен оверлей) можно сохранить различия в DIF-файле, и затем любой из поддерживающих его многочисленных утилит модифицировать оригинальный файл.
IDA позволяет сохранять базу в виде текстового IDC-файла. Это обеспечивает ее переносимость между различными версиями. Дело в том, что основной рабочий формат IDB в любой момент может измениться, и база перестанет загружаться в новые версии. Для преодоления этой проблемы и был введен текстовой формат.
Заметим, что это далеко не вся база, и часть информации оказывается необратимо утерянной. Например, отсутствует виртуальная память, и для анализа потребуется исходный файл. Кроме того, загружаться IDC-файл будет гораздо медленнее IDB, поскольку потребуется вновь все дизассемблировать. Поэтому применять данный формат в качестве рабочего бессмысленно.
Но что же представляет собой IDC-файл? Как нетрудно догадаться по его расширению, это обыкновенный скрипт!


IDA - 0364.htm

364 Генерация выходных файлов
static Segments(void)
SegCreate(0x10000,Oxl32ea,OxlOOO, 0, 1,2) ;
SegRename(0x10000,"segOOO");
SegClass (0x10000,"CODE");
SetSegmentType(OxlOOOO,2);
И его можно безболезненно редактировать, — в отличие от бинарного IDB-формата. Например, если IDA что-то неправильно дизассемблирует, то положение будет нетрудно исправить, отредактировав нужным образом скрипт.
LST-файл представляет собой копию дизассемблированного файла в том виде, в каком он отображается на экране IDA. Выглядит он приблизительно так:
segOOO:0100 loc_0_100:
segOOO:0100 cmp byte ptr [bx+si], 0
segOOO:0103 jz loc_0_108
segOOO:0105 inc bx
segOOO:0106 jmp short loc_0_100
Разумеется, он не пригоден для последующего ассемблирования и может использоваться только в качестве "твердой копии экрана". Кстати, в демонстрационной версии генерация LST-файла не поддерживается.
ASM-файл — это дизассемблированный файл, полностью готовый к ассемблированию. Выглядит он следующим образом:
p586n
; Segment type: Pure code
segOOO segment byte public 'CODE' use16
assume cs:segOOO
ss:nothing, ds:nothing, fs:nothing,
; _________ S U B R 0 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
B демонстрационных версиях вывод дизассемблированного текста в ASM-файл не поддерживается.
DIF-файл хранит в себе результаты сравнения оригинального и модифицированного функциями PatchByte и PatchWord файлов. Для некоторых форматов IDA позволяет генерировать исполняемый (или бинарный) файл с учетом изменений.
В большинстве случаев этих возможностей оказывается недостаточно (например, не поддерживаются win32 форматы), и тогда приходится прибегать к сохранению всех изменений в отдельном файле. Формат его следующий:
This difference file is created by The Interactive Disassembler
xsafe-iv.exe
00002390: OC 11


IDA - 0365.htm

Генерация выходных файлов 365
В нем нетрудно распознать типичный crk-файл, который поддерживается многими утилитами (например, cra386). В некоторых случаях придется модифицировать исходный файл вручную. Несложно написать скрипт на IDA-си, который будет выполнять такую работу автоматически.
Для генерации любого из приведенных выше в таблице типа файлов отчетов требуется задать виртуальный адрес начала и конца области. Если требуется вывести файл целиком, то в качестве адреса начала можно задать 0, а в качестве конца — константу BADADDR или —1.
Функция GenerateFile не работает с именами файлов, она требует дескриптора уже открытого на запись файла. В упрощенном виде ее вызов может выглядеть так:
auto а;
a=fopen("myfile.ext","wt");
GenerateFile (OFILE_ASM, а, 0, -1,0);
fclose (а);
Поскольку модификация только что сгенерированного файла требуется исключительно редко, то полезно будет создать макрос или функцию, включающую в себя приведенный выше текст. Это упросит ее вызов и позволит программисту не отвлекаться на посторонние мелочи.

Операнд | Пояснения
type | Тип генерируемого файла
file_habdle | Дескриптор открытого файла
eal | Линейный адрес начала области для отображения | в файле
ea2 | Линейный адрес конца области для отображения в | файле
flags | Флаги, управляющие генерацией файла


IDA - 0366.htm

366 Файловый ввод-вывод
Файловый ввод-вывод
IDA обладает развитым файловым вводом-выводом, что открывает поистине неограниченные возможности.
Можно самостоятельно загружать файлы любых форматов, создавать отчеты и листинги любых видов.
Можно распаковывать или модифицировать исполняемые файлы, при желании — работать с принтером или, например, модемом!
Все это богатство возможностей реализуется относительно небольшим набором стандартных функций Си. Работа с файлами в IDA-Си ничем не отличается от "классического" Си. За тем, может быть, исключением, что ввиду отсутствия массивов в их общепринятом понимании используется посимвольный, а не блочный обмен.
long fopen (char file, char mode)
Функция открывает файл и возвращает его обработчик в случае успешного завершения этой операции.
Прототип функции полностью совпадает с аналогичной функцией в стандартной библиотеке Си. Действительно, реализация этой функции в IDA только передает управление библиотечной функции qfopen (char *, char *) без дополнительной обработки аргументов.
Необходимые атрибуты доступа задаются флагом mode в виде простой символьной строки. Их возможные значения показаны ниже.

Атрибут | Назначение
W | Открывает файл для записи. Если файл не существует, то он автоматически создается. Содержимое любого непустого файла будет уничтожено начиная с текущей позиции (по умолчанию с начала файла)
г | Открывает файл для чтения. Если указанный файл не существует, то функция возвратит ошибку (NULL)
а | Открывает файл для записи и перемещает его указатель в конец, (фактически открывает файл для дозаписи). Если указанный файл не существует, то он будет автоматически создан
г+ | Открывает файл на чтение и запись. Если файл не существует, то функция возвратит ошибку
w+ | Открывает файл на чтение и запись. Если файл не существует, то он будет автоматически создан. Содержимое уже существующего файла будет уничтожено
а+ | Открывает файл на чтение и дозапись в конец. Если файл не существует, то он автоматически будет создан


IDA - 0367.htm

Файловый ввод-вывод 367
Тип файла
Пояснения
Открывает файл как текстовый. В этом режиме символ CTRL-Z (ASCII 27) трактуется как конец файла. Также по-особому транслируется символ переноса '\n'. Компилятор превращает его в код 0xA. При записи его в текстовой файл функция на самом деле поместит комбинацию 0xD 0xA, интерпретируемую сервисами MSDOS и некоторыми элементами управления Windows как перенос на следующую строку.
Часто с текстовыми файлами удобнее работать, открывая их как бинарные (см. ниже)
Открывает файл как бинарный. В этом режиме все символы транслируются
AS IS без каких-либо изменений
Функцию необходимо вызывать обязательно с указанием атрибута доступа и типа файла, иначе она завершится с ошибкой.
Если файл по каким-то причинам открыть не удалось, то функция возвратит ноль, в противном случае — дескриптор открытого файла.
Примеры использования:
Del file.dem
> ввод пользователя:
Message("Ox%X \n",fopen("file.dem","wb");
< ответ IDA:
1
Dir file.dem
file.dem 11.11.99 13:33 file.dem
> ввод пользователя:
Message("Ox%X \n",fopen("Long File Name","wb");
< ответ IDA:
1
Dir longfi~l
LONGFI~1 0 11.11.99 15:06 Long File Name
> ввод пользователя:
Message("Ox%X \n",fopen("myfile","r+b");
< ответ IDA:
0
IDA возвращает один и тот же обработчик при открытии различных файлов, хотя прежние файлы не были закрыты. Это говорит о том, что они закрываются автоматически, после того как скрипт завершит свою работу.
Часто забывают, что в Windows сохранилась поддержка имен устройств, идущая еще со времен CP\M. Поэтому, чтобы вывести данные на печать, достаточно открыть на запись устройство "PRN" и направить в него необходимые данные.
Например:
writestr(fopen("PRN","wt"),"Hello,Printer!");


IDA - 0368.htm

368 Файловый ввод-вывод
Необходимо учитывать, что эта печать идет в обход менеджера печати и, кроме того, так нельзя получить доступ к сетевому принтеру. Но, в большинстве случаев, этих возможностей окажется достаточно.
Точно так же можно читать данные с консоли или выводить их на нее. При первой же перерисовке окна сообщений эти данные будут стерты, но это скорее достоинство, чем недостаток. Действительно, зачем загромождать окно сообщений?

Операнд | Пояснения
File | Имя файла (при необходимости с полным или частичным путем). Обе версии IDA (консольная и GUI) поддерживают длинные файлы
mode | Атрибуты доступа и типа файла
Return | Завершение | Пояснения
 | !=0 | Дескриптор открытого файла
 | ==0 | Ошибка
Закрытие всех открытых файлов гарантируется только при корректном выходе из IDA. Операционная система гарантированно закроет все файлы, порожденные любым процессом (в том числе и IDA) при его завершении, но может потеряться часть данных, которая в этот момент находилась во внутренних буферах IDA и еще не была записана на диск.
void fclose (long handle)
Функция закрывает файлы, открытые с помощью fopen. В момент закрытия файла в него записываются все данные, находящиеся в этот момент во внутренних буферах, а файловый объект (то, на что ссылается дескриптор) уничтожается, предотвращая утечку ресурсов.
Файлы автоматически закрываются в момент завершения работы породившего их скрипта (при условии, что обладающая дескриптором процедура не описана как static), а также при корректном завершении работы IDA. В противном случае операционная система все равно освободит все ресурсы, принадлежащие процессу, но при этом не будут записаны данные, оставшиеся во внутренних буферах.
Функция не возвращает результата успешности операции.

Операнд | Пояснения
handle | Дескриптор открытого файла
Пример использования:
Auto a;
A=fopen("PRN", "wt");
If (a!=-l)
writestr(a,"Hello,Printer!") ;
fclose(a);


IDA - 0369.htm

Файловый ввод-вывод 369
В приведенном примере fclose выполняется даже тогда, когда файл не был успешно открыт. Это не ошибка, поскольку fclose(0) не приводит ни к каким побочным последствиям.
long filelength (long handle)
Функция возвращает логическую длину открытого файла (с учетом незаписанных внутренних буферов). Эта длина может не совпадать с физическим размером файла на диске в данный момент.
Длина символьных устройств (таких, как PRN, например) всегда равна нулю. Например:
> ввод пользователя:
Message("Ox%X \n",filelength(fopen("PRN", "wt"));
< ответ IDA:
0x0

Операнд | Пояснения
handle | Дескриптор открытого файла
long fseek (long handle, long offset, long origin)
Функция позиционирует указатель в открытом файле. Флаг origin, задающий
необходимое позиционирование, может принимать следующие значения:

Origin | Значение
0 | Позиционировать | относительно начала файла
1 | Позиционировать | относительно текущей позиции
2 | Позиционировать | относительно конца файла
Правда, вплоть до версии 4.0 эта функция реализована с ошибкой, приводящей к тому, что флаг 'Г трактуется также, как и '0' — т.е. относительно начала файла. Это видно на следующем примере:
> ввод пользователя:
auto а;
a=Fopen("myfile","wt");
fseek(a,0x10, 0) ;
Message("Ox%X \n",ftell(a));
fseek(a,0x0, 1) ;
Message("Ox%X \n",ftell(a));
fclose (a);
< ответ IDA:
0x10
0x0
Не поддерживается отрицательная адресация относительно начала файла. Относительно конца файла можно свободно позиционироваться в двух направлениях.


IDA - 0370.htm

370 Файловый ввод-вывод
> ввод пользователя:
auto а;
a=Fopen("myfile","wt");
fseek(a,0x0,2) ;
Message("Ox%X \n",ftell(a));
fseek(a,0x5,2);
Message("Ox%X \n",ftell(a));
fseek(a,-0x5, 2) ;
Message("Ox%X \n",ftell(a));
fclose (a);
< ответ IDA:
0x100 0x105 0x100
Напомним, что ранние версии DOS содержали ошибку, приводящую к тому, что при наращивании размера файла с помощью функции позиционирования сверх определенного размера (зависящего от многих обстоятельств) нарушалась целостность FAT16. Та же ошибка повторена в первых реализациях FAT32 (Windows 95 OSPO, в народе прозванная "Лебединой редакцией").
При перемещении указателя за конец файла в него попадает информация, расположенная в выделяемых ему дисковой подсистемой кластерах. При этом файл не должен быть открыт на запись, иначе, игнорируя флаг, функция будет вычислять смещение относительно файла, и его содержание окажется утерянным! Оно окажется утерянным и в том случае, если указать неверное отрицательное смещение или origin > 2.

Операнд |
Handle | Обработчик открытого файла
Offset | Смещение (относительно конца файла — знаковое)
Origin | Указывает, относительно чего отсчитывается смещение (см. |  | таблицу выше)
Return | Завершение | Возвращаемое значение
 | Успешно | 0
 | Ошибка | !=0
long ftell (long handle)
Функция возвращает текущую позицию указателя в открытом файле относи-
тельно его начала.

Операнд | Пояснения
Handle | Дескриптор открытого файла
Return | Завершение | Возвращаемое значение
 | Успешно | Текущая позиция
 | Ошибка | -1


IDA - 0371.htm

-________________Файловый ввод-вывод______________371
success loadfile (long handle, long pos, long ea, long size)
Функция позволяет загружать бинарный файл (или его часть) в произвольный регион виртуальной памяти IDA. Это позволяет писать свои динамические загрузчики, дает возможность эмулировать работу оверлеев и многое другое.
Перед началом операции искомый файл необходимо открыть в бинарном режиме функцией fopen с правами только на чтение. Если открыть на запись, то его содержимое окажется необратимо уничтожено! Затем следует указать позицию в файле для чтения (аргумент pos). Позиция всегда считается относительно начала файла, независимо от текущего положения указателя. Далее укажите виртуальный линейный адрес, по которому будет скопирован фрагмент файла. Операция завершится независимо от того, производится загрузка в границах существующего сегмента или вне оных. При необходимости IDA выделяет дополнительную виртуальную память для загрузки.
Последний аргумент, передаваемый функции — это число загружаемых из файла байт. Если оно превосходит длину "хвоста" файла (т.е. от указанной позиции до конца), то IDA выдаст предупреждение:
Can't read input file (file structure error?), only part of file will be loaded...
и загрузит столько байт, сколько сможет.
Q копия экрана IDA:
segOOO:2C93 aWatchAvailable db 'Watch available DOS memory.........................'
> ввод пользователя:
auto а;
a=fopen("readme.txt","rb");
loadfile(a,0,Oxl2C93, 0x40);
Q копия экрана IDA:
segOOO:2C93 aWatchAvailable db 'This patch allows you to permanently access the bonus'
Загрузка не вызывает реассемблирования исследуемой программы. Перезаписывается только соответствующий регион виртуальной памяти, но не меняются связанные с ней флаги!
Это хорошо видно на следующем примере:
G копия экрана IDA:
segOOO:02E4 sub_0_2E4 proc near ; CODE XREF: segOOO:232Ep
segOOO:02E4 push ds
segOOO:02E5 xor ax, ax
segOOO:02E7 mov ds, ax ; DS == NULL
segOOO:02E9 assume ds:nothing
segOOO:02E9
segOOO:02E9 MyLabel:
segOOO:02E9 mov ax, ds:413h
segOOO:02EC shl ax, 6
segOOO:02EF cmp ax, OAOOOh
segOOO:02F2 pop ds
segOOO:02F3 assume ds:segOOO
segOOO:02F3 retn
segOOO:02F3 sub_0_2E4 endp


IDA - 0372.htm

372 Файловый ввод-вывод
> ввод пользователя:
auto а;
a=fopen("readme.txt","rb");
loadfile(a,0,Oxl02E4,Ox40);
G копия экрана IDA:
segOOO:02E4 sub_0_2E4 proc near ; CODE XREF: segOOO:232Ep
segOOO:02E4 push sp
segOOO:02E5 push 7369h
segOOO:02E7 jnb loc_0_309 ; DS == NULL
segOOO:02E9 assume ds:nothing
segOOO:02E9
segOOO:02E9 MyLabel:
segOOO:02E9 jo loc_0_34C
segOOO:.02EC arpl [bx+si+20h], bp
segOOO:02EF popa
segOOO:02F2 outsw
segOOO:02F3 assume ds:segOOO
segOOO:02F3 ja near ptr loc_0_367+1
segOOO:02F3 sub_0_2E4 endp
Сохранились прежние метки, комментарии и перекрестные ссылки, код оказался неверно дизассемблированным! Но это не ошибка IDA, а ее архитектурная особенность. Вместе с обычными перекрестными ссылками сохранились и так называемые ссылки на следующую инструкцию. Поэтому вновь загруженный код был дизассемблирован с учетом прежнего "каркаса" — линейных адресов начала инструкций.
Чтобы исправить ситуацию, необходимо пометить измененный фрагмент как undefined и потом ассемблировать его заново. В результате получится следующее:
segOOO:02E4 push sp
segOOO:02E5 push 7369h
segOOO:02E8 and [bx+si+61h], dh
segOOO:02EB jz loc_0_350
segOOO:02ED push 6120h
segOOO:02FO ins byte ptr es:[di], dx
segOOO:02F1 ins byte ptr es:[di], dx
segOOO:02F2 outsw
Чаще всего функцию loadfile используют для частичного дизассемблирования файла. Например, если внутри многомегабайтовой DLL необходимо исследовать лишь небольшой фрагмент, то нет нужды несколько часов ждать, пока IDA дизассемблирует ее целиком — достаточно лишь загрузить требуемый фрагмент.
Кроме того, многие приложения во время работы подгружают свои компоненты с диска. Если их необходимо исследовать, то для этого можно воспользоваться loadfile. Иногда даже не требуется создавать для этого дополнительный сегмент, загрузив данные за его границы.
Q копия экрана IDA:
segOOO:32AO db OE2h, 20h, OA4h, OAOh, 2 dup(OADh), OEBh, OA9h, 20h
segOOO:32AO db OACh, OA5h, OE5h, OAOh, OADh, OA8h, OA7h, OACh, 21h


IDA - 0373.htm

Файловый ввод-вывод 373
segOOO:32AO db 0
segOOO:32AO segOOO ends
segOOO:32AO
segOOO:32AO
segOOO:32AO end start
> ввод пользователя:
auto a;
a=fopen("readme.txt","rb");
loadfile(a,0,Oxl02E4,OxlO);
G копия экрана IDA:
segOOO:32AO db OE2h, 20h, OA4h, OAOh, 2 dup(OADh), OEBh, OA9h, 20h
segOOO:32AO db OACh, OA5h, OE5h, OAOh, OADh, OA8h, OA7h, OACh, 21h
segOOO:32AO db 0
segOOO:32AO segOOO ends
segOOO:32AO end start
0:000132EA db 54h T
0:000132EB db 68h h
0:000132EC db 69h i
0:000132ED db 73h s
0:000132EE db 20h
0:000132EF db 70h p
0:000132FO db 61h a
0:000132F1 db 74h t
0:000132F2 db 63h c
0:000132F3 db 68h h
Доступ к загруженным данным может быть осуществлен, например, вызовами Byte. Для интерактивной работы (например, чтобы преобразовать загруженные данные в строку) все же придется создать сегмент (как это сделать — рассказано в описании функции SegCreate):
MySeg:OOOA ; Segment type: Regular
MySeg:OOOA MySeg segment byte public '' use16
MySeg:OOOA assume cs:MySeg
MySeg:OOOA ;org OAh
MySeg:OOOA assume es:nothing,ss:nothing,ds:nothing,fs:nothing,gs:nothing
MySeg:OOOA aThisPatchAllow db 'This patch allows you to permanently access the bonus track'
MySeg:OOOA MySeg ends
MySeg:OOOA
Строго говоря, приведенный пример дизассемблирован неправильно. Если программа подгружала ресурсы из текстового файла динамически во время работы, то перемещение их в сегмент может нарушить ее работоспособность после ассемблирования и уж точно не изменит алгоритм, чтобы файловый обмен заменился обращением к памяти.
Но на этапе исследования дизассемблируемого кода это невероятно удобно. Можно даже вычислить, какие инструкции загружают определенные коды, и создать перекрестные ссылки для обеспечения быстрого перехода между различными фрагментами дизассемблируемого текста.


IDA - 0374.htm

374 Файловый ввод-вывод

Операнд | Пояснения
handle | Обработчик открытого только на чтение файла
pos | Позиция в файле относительно его начала
еа | Линейный адрес начала региона виртуальной памяти
Size | Число байт для чтения
Return | Завершение | Пояснения
 | 0 | Функция завершилась неуспешно
 | 1 | Функция завершилась успешно
success savefile (long handle, long pos, long ea, long size)
Функция, обратная loadfile (см. описание выше), позволяет сохранить фрагмент виртуальной памяти на диске в виде файла. Например:
Q копия экрана IDA:
segOOO:03D3 sub_0_3D3 proc near ; CODE XREF: segOOO:03C7p
segOOO:03D3 push ax
segOOO:03D4 push bx
segOOO:03D5 mov al, byte ptr es:loc_0_F+1
segOOO:03D9 mov bx, 3EAh
segOOO:03DC
segOOO:03DC loc_0_3DC: ; CODE XREF: segOOO:03E4j
segOOO:03DC cmp [bx], al
segOOO:03DE jz loc_0_3E7
segOOO:03EO inc bx
segOOO:03E1 cmp byte ptr [bx], 0
segOOO:03E4 jnz loc_0_3DC
segOOO:03E6 inc bx
segOOO:03E7
segOOO:03E7 loc_0_3E7: ; CODE XREF: segOOO:03DEj
segOOO:03E7 pop bx
segOOO:03E8 pop ax
segOOO:03E9 retn
segOOO:03E9 sub_0_3D3 endp
segOOO:03E9
segOOO:03EA aDeifxcblst db 'DEIFXCBLST',0
> ввод пользователя:
auto а;
a=fopen("fileme","wb");
savefile(а,0,0x103D9, 0x200) ;
Q копия экрана IDA:
___________________________ F:\IDAF\fileme --------------------- 23:28:03-
00000000: 50 53 26 АО 10 00 BB ЕА 03 38 07 74 07 43 80 3F | PS&a .+ъ 8tCA?
'' 00000010: 00 75 F6 43 5В 58 C3 44 45 49 46 58 43 42 4C 53 | .uC[X+DEIFXCBLS
Возможность сохранения отдельных фрагментов файла очень полезна и может стать основой для множества утилит (например, такой, которая извлекает все текстовые строки, встретившиеся в программе, в отдельный файл).


IDA - 0375.htm

Файловый ввод-вывод 375
Кроме того, она пригодится, если необходимо сохранить модифицированный вызовами PatchByte файл на диске. Дело в том, что IDA не поддерживает экспорт ни во что другое, кроме СОМ и MS-DOS EXE. И то, и другое давно устарело. Поддержку формата популярных сегодня РЕ-файлов придется реализовывать самостоятельно.
Перед началом операции необходимо открыть целевой файл на запись с помощью функции fopen и передать savefile его дескриптор.
Позиция в файле для записи может быть выбрана любая, как внутри, так и вне его. Однако, в последнем случае возможно разрушение FAT, поэтому необходимо соблюдать дополнительные меры предосторожности.
Размер записываемого фрагмента может превосходить длину дизассемблируемого файла. В этом случае "хвост" будет заполнен символами 0xFF (именно такое значение возвращает функция Byte при попытке чтения несуществующих адресов), но функция, несмотря на это, завершится без ошибки.

Операнд | Пояснения
handle | Обработчик открытого на запись файла
pos | Позиция в файле относительно его начала
еа | Линейный адрес начала региона виртуальной памяти
Size | Число байт для записи
Return | Завершение | Пояснения
 | 0 | Функция завершилась неуспешно
 | 1 | Функция завершилась успешно
long fgetc (long handle)
Функция читает один байт из файла. При этом файл должен быть предварительно открыт вызовом fopen с правами, разрешающими чтение. Эта функция относится к функциям стандартной библиотеки Си.
При неуспешном завершении возвращает ошибку BADADDR (иначе — очередной считанный символ). Если не достигнут конец файла, то указатель увеличивается на единицу.
Пример использования:
> ввод пользователя:
auto a,ch;
a=fopen("readme.txt","rt");
while((ch=fgetc(a))!=-l)
Message (ch)/
fclose (a);
< ответ IDA:
This patch allows you to permanently access the bonus track and bonus
car without winning the tournaments.


IDA - 0376.htm

376 Файловый ввод-вывод

Операнд | Пояснения
Handle | Дескриптор открытого с правами на чтение файла
Return | Завершение | Пояснения
 | Норма | Считанный символ
 | BADADDR | Ошибка
long fputc (long byte, long handle)
Функция записывает один байт в файл. Файл должен быть предварительно
открыт с правами на запись функцией fopen.
При неуспешной записи функция возвратит ошибку BADADDR, иначе ноль.

Операнд | Пояснения
byte | Записываемый символ
handle | Дескриптор открытого с правами |  | на запись файла
Return | Завершение | Пояснения
 | Норма | 0
 | BADADDR | Ошибка
long fprintf (long handle,char format,...)
Ближайший аналог известной функции sprintf, однако результат копируется в файл, а не в буфер. Очевидно, что файл должен быть предварительно открыт с правами на запись вызовом fopen.
Например:
> ввод пользователя:
auto a,sO;
sO=Oxl23;
a=fopen("CON","wt");
fprintf(a, "%x \n",sO);
< ответ IDA:
123
Управляющие символы стандартные, частично совместимы с 'printf' и полностью совместимы со спецификаторами функции Message встроенного языка IDA.

Сф |
%d | Десятичное длинное знаковое целое. Пример: Message ("%d", 0xF) ; 15
%х | Шестнадцатиричное длинное целое строчными символами. Пример: Message ("%x", 10) ; а


IDA - 0377.htm

Файловый ввод-вывод 377

Сф |
%х | Шестнадцатиричное длинное целое заглавными символами. Пример: Message ("%X", 10) ; А
%о | Восьмиричное длинное знаковое целое. Пример: Message ("%o",ll) ; 13
%u | Десятичное длинное беззнаковое целое Пример: Message ("%u", -1) ; 4294967295
%f | Десятичное с плавающей точной. Пример: Message ("%f", 1000000) ; I.e6
%с | Символьное значение. Пример: Message ("%c", 33) ; /
%s | Строковое значение. Пример: Message ("%s", "Hello, Word! \n") ; Hello, Word!
%е | Вывод чисел в экспоненциальной форме. Пример: Message ("%e", 1000000) ; I.e6
%g | Вывод чисел в экспоненциальной форме. Замечание. В оригинале спецификатор '%g' заставляет функцию саму решать, в какой форме выводить число — с десятичной точкой или в экспоненциальной форме, из соображений здравомыслия и удобочитаемости. IDA всегда при задании этого спецификатора представляет числа в экспоненциальной форме.
%а | Вывод указателя (не поддерживается). Замечание. Вместо спецификатора '%p' IDA использует '%a', преобразующее линейный адрес в строковой сегментный, и автоматически подставляет имя сегмента. Так, например, 'Message("%a \n", 0x10002)' выдаст 'segOOO:2'. Обратите внимание, что таким способом нельзя узнать адрес переменной. Пример: .auto а; a="Hello! \n"; Message ("%a \n",a); 0 Возвращается ноль, а не указатель на переменную
%Р | Вывод десятичного целого всегда со знаком, не опуская плюс
%+d | В оригинале — вывод шестнадцатиричного целого всегда со знаком, но IDA воспринимает эту конструкцию точно так же, как и 'x'


IDA - 0378.htm

378 Файловый ввод-вывод

Сф |
%+х | 'n' — длина выводимого десятичного числа, при необходимости дополняемого сле- ва пробелами. Например: Message ("Число-%3с1 \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3с1 \п", 10000) ; Число-10000
%nd | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополняемого слева пробелами. Например: Message ( "Число-%3х \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3х \n", 0x1234) ; Число-1234
%nd | 'n' — длина выводимого десятичного числа, при необходимости дополняемого слева незначащими нулями. Пример: Message ("xccei-%03d",l) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%03с1" , 1000) Число-1000
%0nx | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополняемого слева незначащими нулями. Пример: Message ("Число-%03х" , 0x1) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%03х", 0x1234) ; Число-1234
%#x | Вывод префикса '0x' перед шестнадцатиричными числами. Пример: Message ("%#x", 123) ; 0x123
%#o | Вывод префикса '0' перед восьмиричными числами. Пример: Message ("%#o", 1) ; 01
%n | Количество выведенных символов (не поддерживается)
long readshort (long handle, long mostfirst)
Функция считывает два байта из файла. До начала операции файл должен
быть открыт функцией fopen с правами на чтение. Примечательной особенностью


IDA - 0379.htm

Файловый ввод-вывод 379
данной функции является возможность трансляции знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит расположен "слева", т.е. идет самым старшим в слове. Если флаг mostfirst равен единице, то функция будет ожидать, что знаковый бит расположен "справа", т.е. идет самым младшим в слове.
В случае, если во время выполнения функции возникнут ошибки, будет возвращена константа BADADDR, иначе — 16-битное прочитанное значение.

Операнд | Пояснения
handle | Дескриптор открытого с правами на чтение файла
mostfirst | ==0 | Знаковый байт, самый старший в слове
 | ==1 | Знаковый байт, самый младший | в слове
Return | Завершение | Пояснения
 | Норма | Прочитанное 16-битное знаковое | слово
 | BADADDR | Ошибка
long readlong (long handle, long mostfirst)
Функция считывает четыре байта из файла. До начала операции файл должен быть открыт функцией fopen с правами на чтение. Примечательной особенностью данной функции является возможность трансляции знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит расположен "слева" т.е. идет самым старшим в двойном слове. Если флаг mostfirst равен единице, то функция будет ожидать, что знаковый бит расположен "справа" т.е. идет самым младшим в двойном слове.
Если во время выполнения функции возникнут ошибки, то будет возвращена константа BADADDR, иначе — 32-битное прочитанное значение. Формально функция не возвращает ошибку, потому что она неотличима от возможного 32-битного значения. Однако в результате ошибки BADADDR все же возвращается. Например:
> ввод пользователя:
Message("Ox%X \n",readlong(123));
< ответ IDA:
0xFFFFFFFF

Операнд | Пояснения
handle | Дескриптор открытого с правами на чтение файла
mostfirst | ==0 | Знаковый байт, самый старший | в слове
 | ==1 | Знаковый байт, самый младший | в слове
Return | Завершение | Пояснения
 | Норма | Прочитанное 16-битное знаковое слово
 | BADADDR | Ошибка


IDA - 0380.htm

380 Файловый ввод-вывод
long writeshort (long handle, long word, long mostfirst)
Функция записывает два байта в файл. До начала операции файл должен быть открыт функцией fopen с правами на запись. Позволяет транслировать знаковый бит во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит расположен "слева", т.е. идет самым старшим в слове. Если флаг mostfirst равен единице, то функция будет ожидать, что знаковый бит, расположен "справа", т.е. идет самым младшим в слове.
Если во время выполнения функции возникнут ошибки, то будет возвращено ненулевое значение.

Операнд | Пояснения
Handle | Дескриптор открытого с правами на запись файла
Mostfirst | ==0 | Знаковый байт, самый старший | в слове
 | ==1 | Знаковый байт, самый младший | в слове
Return | Завершение | Пояснения
 | 0 | Норма
 | !=0 | Ошибка
long writelong (long handle, long dword, long mostfirst)
Функция записывает четыре байта в файл. До начала операции файл должен быть открыт функцией fopen с правами на запись. Предоставляет возможность трансляции знакового бита во время чтения.
Если флаг mostfirst равен нулю, то функция будет полагать, что знаковый бит, расположен "слева", т.е. идет самым старшим в двойном слове. Если флаг mostfirst равен единице, то функция будет ожидать, что знаковый бит расположен "справа", т.е. идет самым младшим в двойном слове.
Если во время выполнения функции возникнут ошибки, то будет возвращено ненулевое значение.

Операнд | Пояснения
Handle | Дескриптор открытого с правами на запись файла
Mostfirst | ==0 | Знаковый байт, самый старший | в слове
 | ==1 | Знаковый байт, самый младший | в слове
Return | Завершение | Пояснения
 | 0 | Норма
 | !=0 | Ошибка
char readstr (long handle)
Функция читает строку из файла (с текущей позиции до символа EOL). До
начала операции файл должен быть открыт функцией fopen с правами на чтение.


IDA - 0381.htm

Файловый ввод-вывод 381
Независимо от заданного типа при открытии файла (текстовый или двоичный) readstr всегда правильно распознает конец строки, представленный как 0xD 0xA, так и 0xA. Однако если файл открыт как текстовый, то функция будет преобразовывать все символы 0xA в 0xD 0xA. Это можно наблюдать на следующем примере:
> ввод пользователя:
auto а;
a=fopen("readme.txt","rb");
Message(readstr(a));
< ответ IDA:
This patch allows you to permanently access the bonus track and bonus car
> ввод пользователя:
auto a;
a=fopen("readme.txt","rt");
Message(readstr(a));
< ответ IDA:
This patch allows you to permanently access the bonus track and bonus car

Операнд | Пояснения
Handle | Дескриптор открытого с правами на чтение файла
Return | Завершение | Пояснения
 | Норма | Считанная строка
 | "" | Ошибка
long writestr (long handle, char str)
Функция записывает строку в файл. До начала операции файл должен быть открыт функцией fopen с правами на запись.
Если файл открыт как текстовой, то функция будет преобразовывать все символы 0xA в 0xD 0xA.

Операнд | Пояснения
Handle | Дескриптор открытого с правами | на чтение файла
str | Записываемая строка
Return | Завершение Пояснения
 | Норма 0
 | !=0 Ошибка


IDA - 0382.htm

382 Виртуальные массивы
Виртуальные массивы
Организация массивов
IDA поддерживает два типа массивов, и это иногда порождает небольшую путаницу. Массив, как структура данных дизассемблируемого файла (см. ~ Edit \ Array) для повышения удобочитаемости, принципиально ничем не отличается от тех же данных, записанных построчно (первый тип).
segOOO:0006 db OAOh,OACh,OAEh,OA3h,OAEh, 20h,OADh,OAOh
segOOO:0006 db OAOh, 20h,OADh,OAOh,OA4h,OAEh, 20h,OAEh
Второй тип — массивы, как выделенные под нужды скриптовых программ области памяти. Рассмотрим второй тип. Концептуально такие массивы сильно отличаются от привычных для нас массивов языков С и Pascal.
Массив — это скорее объект, который в Microsoft непременно назвали бы CArray, предоставляющий соответствующие API, но скрывающий реализацию всех своих методов.
Уникальность массивов IDA в том, что они поддерживают смешанный тип данных. В одном и том же массиве можно хранить как числа, так и строки. Правда, обработку типов (или в принятой терминологии — тегов) IDA возлагает на наши плечи, и нам придется явно указывать, строковое это значение или нет.
Очень приятно, что IDA поддерживает разряженные массивы, индексированные произвольным образом. С первого взгляда они могут напоминать списки, но на самом деле это разряженные массивы. Это дает очень большую экономию в тех случаях, когда диапазон индексов значительно превосходит реально используемые данные.
Однако, как уже упоминалось выше, в ряде случаев выгоднее не пользоваться массивами, а создать для этих целей сегмент в виртуальной памяти. Это поможет, например, упростить ввод / вывод данных, так как эту задачу можно возложить на файловый загрузчик IDA. Вывод готовых данных можно осуществить штатными функциями, — например, всего одной командой сохранить данные в файле — в любом из многочисленных поддерживаемых IDA форматов.
Но есть задачи, в которых массивы несравненно удобнее. Например, это уже отмечавшаяся работа со списками или строковыми данными и, кроме того, массивы хорошо подходят в качестве долговременного хранилища данных.
Массивы сохраняются в базе IDA (а точнее, в Btree) до момента их принудительного удаления. Это же относится и к сегментам, но массивы в отличие от последних не загромождают дизассемблируемый текст.
Попробуем составить нехитрый скрипт, нечто вроде "мимоходных заметок" некоторых пришедших в голову программиста мыслей, которые забывать не хочется, но они не имеют отношения к собственно дизассемблируемому тексту. Для этого нам потребуется познакомиться с базовыми операциями над массивами.


IDA - 0383.htm

Виртуальные массивы 383
Начнем с создания. Что бы как-то различать массивы, каждому из них дается уникальное имя (до 120 символов, при этом может начинаться с цифры) и связанный с ним идентификатор, который возвращает функция создания сегмента в случае успешного завершения:
long CreateArray(char name);
Если массив с таким именем уже существует, то функция возвратит BA-DADRR. Иначе мы получим идентификатор массива, который необязательно сохранять, ибо при первой необходимости его можно узнать по имени массива. Но что-то одно сохранить все же придется.
Как узнать идентификатор ранее созданного массива при перезапуске скрипта? Его можно хранить как в самой базе, так и во внешнем файле, но удобнее получить его по имени массива, воспользовавшись следующей функцией:
long GetArrayId(char name);
Если указанного массива не существует, то функция возвратит BADADDR, в противном случае — идентификатор. С помощью идентификатора массив в любой момент можно переименовать функцией:
success RenameArray(long id,char newname);
С другой стороны, если вам не нравятся конструкции типа:
auto ID;
ID=GetArrayId("MyArray");
RenameArray(ID,"MyRenamedArray");
то можно непосредственно получить идентификатор "на лету" типа:
RenameArray(GetArrayId("MyArray"),"MyRenamedArray");
Это экономит одну переменную и ставит под вопрос удобочитаемость листинга (с одной стороны, видеть каждый раз перед глазами имя массива удобнее, а с другой — одноименная переменная ничуть не хуже). Кроме того, подобный подход может изрядно понизить скорость работы, особенно в цикле, но он имеет какую-то особую притягательность, и многие программисты часто используют его вопреки здравому смыслу.
Создавая массивы, необходимо помнить, что они располагаются не в оперативной памяти, а в базе, и перезапуск не разрушает их.
Увлекшись созданием массивов, особенно на этапе знакомства и экспериментирования с ними, можно не только "скушать" порядочно ресурсов, но и заблокировать многие имена, так что потом при попытке создания массива с идентичным именем возникнет непонятно с чем связанная ошибка. Поэтому сразу же, как необходимость в массиве отпадет, его следует удалить функцией:
void DeleteArray(long id);
Жалко, что не предусмотрено возможности создания массивов, автоматически удаляющихся при выходе из IDA. Однако, немного поразмыслив, при запуске выполним следующие действия (для этого достаточно включить эту строку в файл ida.idc):


IDA - 0384.htm

384 Виртуальные массивы
CreateArray("SysListTempArray") ;
Теперь определим функцию:
static CreateTempArray(Name)
{
auto a,temp;
temp=GetLastIndex('S',GetArrayId("SysListTempArray") ) ;
a=CreateArray(Name);
If (a>0) SetArrayString(GetArrayId("SysListTempArray",++temp,Name);
return a;
}
При выходе из IDA уже нетрудно будет удалить все временные массивы из базы автоматически. Однако в этом на первый взгляд логичном поступке есть одна ошибка: если сеанс работы будет завершен аварийно, то наш скрипт не получит управления, и временные массивы не будут удалены! Вот почему необходимо очищать их при входе (запуске) IDA. При этом массив "SysListTemp-Array" будет необходимо создавать только один раз для каждой новой базы.
Этот пример еще раз наглядно демонстрирует всю мощь интегрированного языка IDA. Любые ваши пожелания и фантазии могут быть воплощены в простом или сложном (чаще простом) скрипте, который выполнит за вас большую часть работы. При этом нет необходимости связываться с автором и ждать исполнения пожеланий в последующих версиях (или, попросту говоря, через неопределенное время).
Массивы IDA имеют и другую уникальность. Один и тот же элемент (а точнее, индекс) может одновременно содержать строковое и числовое значения, причем оба не перекрывают друг друга, — вместо одного массива мы как бы получаем целых два!
Для задания значений элементов используется пара функций:
success SetArrayLong (long id,long idx,long value);
success SetArrayString(long id,long idx,char str);
Обе функции могут принимать как символьный, так и числовой тип значения.
SetArrayString(id,idx,Ox21) занесет в ячейку знак Т, SetArrayLong (id,idx,'!*') -0x2A21.
Это бывает очень удобно для преобразования типов данных, которое IDA выполняет автоматически.
Нет необходимости предварительно задавать размер массива, — следует указать только требуемый индекс.
Всего доступно 0x100000000 индексов (32 бита), что позволяет расширять массивы не только "вперед", но и "назад". IDA прекрасно справляется с отрицательными указателями. Не стоит, однако, забывать, что отрицательные указатели на самом деле трактуются как беззнаковые, и расширение массива "назад" происходит по кольцу.
Чтение элементов массива выполняется несколько неожиданным способом. Вместо двух функций — GetArrayLong и GetArrayString — используется одна:
char or long GetArrayElement (long tag, long id, long idx);


IDA - 0385.htm

Виртуальные массивы 385
Уточнение типа требуемого элемента выполняется через тег. Если он равен '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);
Заметим, что список элементов не замкнут в кольцо, и при достижении обоих
его концов функции возвратят ошибку, а не "перескочат" на следующий конец.
Ну и, наконец, удалить любой элемент массива можно с помощью функции:
success DelArrayElement(long tag,long id,long idx);
Теперь можно попробовать реализовать наш проект "Мимоходные заметки". Начнем с создания массива. С первого взгляда стоило бы реализовать такую конструкцию:
if (GetArrayId("Notepad")==-l) CreateArray("Notepad");
однако можно ограничиться вызовом CreateArray("Notepad"), так как если массив уже существует, то функция вернет ошибку. И если обращаться к массиву по имени, то совершенно необязательно сохранять его ID.
Создадим функцию "NotepadAdd" для внесения новых записей:
static NotepadAdd(sO)
{
SetArrayString(GetArrayId("Notepad"),


IDA - 0386.htm

386 Виртуальные массивы
GetLastIndex(GetArrayId("Notepad"))+1,
sO) ;
}
И для просмотра оных:
static NotepadPrint()
{
auto а;
a=0;
Меssаgе("Блокнот: \n");
while((a=GetNextIndex('S',GetArrayId("Notepad"),а))>0)
Message("%s \n",GetArrayElement('S',GetArrayId("Notepad"),a));
}
Чуть позже мы добавим к "Блокноту" соответствующий интерфейс, а пока
будем пользоваться его функциями с консоли. Нажмем <Shift-F2> и введем
NotepadAdd("ЭTO только тест");
и нажмем <Ctrl-Enter>. Затем вызовем консоль еще раз и введем еще одну строку:
NotepadAdd("Пусик хочет кушать");
Попробуем посмотреть содержимое блокнота командой
NotepadPrint ();
< ответ IDA:
Блокнот:
Это только тест
Пусик хочет кушать
А теперь реализуем наш "универсальный расшифровщик" на массивах и
сравним с предыдущими результатами.
auto a,temp;
CreateArray("MyArray") ;
for (a=SegStart(0x10000);a<SegEnd(OxlOOOO);a++)
SetArrayLong(GetArrayId("MyArray"),
Byte(a),GetArrayElement('A',GetArrayId("MyArray"),
Byte(a))+l);
a=GetFirstIndex('A',GetArrayId("MyArray"));
temp=0;
while(l)
{
if
(GetArrayElement('A',GetArrayId("MyArray"),a)>GetArrayElement('A',
GetArrayId("MyArray"),a)) temp=a;
a=GetNextIndex('A',GetArrayId("MyArray"),a);
}
// процедура дешифровки
//
DeleteArray(GetArrayId("MyArray"));


IDA - 0387.htm

Виртуальные массивы 387
Как видно, использование массивов дает определенные преимущества по сравнению с использованием виртуальной памяти сегментов для своих нужд. Поскольку созданный массив заполнен едва ли не на треть, то переход по элементам списка функцией GetNextIndex() заметно быстрее перебора всего массива в цикле, как это было в предыдущем примере. Кроме того, нет никакого риска прочитать неинициализированные элементы массива, что позволяет избежать многих трудноуловимых ошибок.
Большой неожиданностью явилась поддержка IDA Perl-подобных ассоциативных массивов. Кардинальное отличие их от обычных заключается в возможности индексирования элементов строковыми значениями.
Например:
a["MocKBa"] = "Москва-Столица";
а["Кремль"] = "Старинное здание в Москве";
Конечно, IDA использует другой синтаксис, и данный пример приведен только для облегчения понимания сущности этой возможности. При этом внутреннее представление индексов таких массивов очень компактно и они быстродействующие.
Ассоциативные массивы можно считать полудокументированной особенностью IDA. В контекстной помощи им уделено всего несколько строк, а в прототипах функций отсутствуют комментарии (видимо, разработчик IDA считает, что этого вполне достаточно).
Впрочем, с какой-то стороны это и оправданно. Ассоциативные массивы реализованы "поверх" существующих и отличаются только тем, что используют строковые, а не числовые индексы. К ним применимы функции CreateArray, GetArrayID, RenameArray и остальные. Различие лишь в том, что все функции ассоциативных массивов не имеют тегов. Это означает, что один и тот же индекс не может одновременно ссылаться на строковое и числовое значение. Поэтому следующий пример вернет 'Oxl234', так как последнее присвоение затирает предыдущее.
SetHashString(GetArrayId("MyArray"),"1st","Это строка");
SetHashLong (GetArrayId("MyArray"),"1st",0x1234);
Message("%x \n",GetHashLong(GetArrayId("MyArray"),"1st"));
Можно безболезненно присваивать ячейке строковое значение, а считывать числовое (и наоборот). Это бывает иногда полезно для преобразования данных. Чтение значений осуществляется функциями:
long GetHashLong(long id,char idx);
char GetHashString(long id,char idx);
Несмотря на то, что эти функции работают непосредственно с массивом, созданным CreateArray, они не могут видеть или модифицировать элементы, заданные SetArrayLong\SetArrayString, поэтому можно безбоязненно использовать один массив для разных нужд.
Удалить любой элемент ассоциативного массива можно с помощью функции:
success DelHashElement(long id,char idx);
e.
13*


IDA - 0388.htm

388 Виртуальные массивы
При удалении элементов 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 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 GetHashString (long id, char idx) | Возвращает значение элементу ассоциативного массива типа "строка"


IDA - 0389.htm

Виртуальные массивы 389

Функция | Назначение
success DelHashElement (long id, char idx) | Удаляет элемент ассоциативного массива
char GetFirstHashKey (long id) | Возвращает индекс первого элемента ассоциативного массива
char GetLastHashKey (long id) | Возвращает индекс последнего элемента ассоциативного массива
char GetNextHashKey (long id, char idx) | Возвращает индекс следующего элемента ассоциативного массива
char GetPrevHashKey (long id, char idx) | Возвращает индекс предыдущего элемента ассоциативного массива
long CreateArray (char пате)
Функция создает новый разряженный массив, в котором можно будет хранить данные обоих типов — как строковые, так и длинные целые.
Массив сохраняется в базе IDA как элемент Btree до тех пор, пока не будет принудительно удален из нее. Никаких ограничений на размер массива не налагается.
Каждый массив должен иметь свое уникальное имя (два массива с одинаковыми именами существовать не могут). Массивы имеют собственное пространство имен (можно создать метку или сегмент, совпадающие с именем уже существующего массива, и наоборот).
На имена наложены следующие ограничения: длина до 120 символов, может начинаться с цифры, но не должно содержать пробелов.
При успешном завершении функция возвращает идентификатор массива, в противном случае (массив с таким именем уже существует?) — BADADDR.
Пример:
> ввод пользователя:
Message("Ox%X \n",
CreateArray("MyArray") );
< ответ IDA:
0xFF000C41

Операнд | Пояснения
Name | Имя массива
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор массива
 | ==0 | Ошибка
long GetArrayId (char пате)
Функция возвращает идентификатор массива по его имени. Это позволяет не сохранять идентификаторы массивов, полученные при их создании, а обращаться к массивам по имени.
Пример:


IDA - 0390.htm

390 Виртуальные массивы
> ввод пользователя:
Message("Ox%X \n",
CreateArray("MyArray")
) ;
Message("Ox%X \n"
GetArrayId("MyArray")
) ;
DeleteArray(
GetArrayId("MyArray")
) ;
Message("Ox%X \n"
GetArrayId("MyArray")
);
< ответ IDA:
0xFF000041 0xFF000041 0xFFFFFFFF

Операнд | Пояснения
Name | Имя массива
Return | ==return | Пояснения
 | !=BADADDR | Идентификатор массива
 | ==0 | Ошибка
success RenameArray (long id, char newname)
Функция позволяет изменить имя массива, заданного идентификатором.
Обычно используется редко.
Например:
> ввод пользователя:
Message("Ox%X \n",
CreateArray("MyArray")
) /
< ответ IDA:
0xFF000041
> ввод пользователя:
RemaneArray(
GetArrayId("MyArray") ,
"MyNewname"
) ;
Message("Ox%X \n"
GetArrayId("MyNewName")
) ;
< ответ IDA:
0xFF000041


IDA - 0391.htm

Виртуальные массивы 391

Операнд | Пояснения
id | Идентификатор массива
Newname | Новое имя массива
Return | ==return | Пояснения
 | ==l | Успешное завершение
 | ==0 | Ошибка
void DeleteArray (long id)
Функция удаляет массив, заданный идентификатором. Массивы как элемент
Btree хранятся в базе IDA до того момента, пока не будут удалены принудительно.
Это можно сделать, например, следующим образом:
DeleteArray(
GetArrayId("MyArray")
) ;

Операнд | Пояснения
id | Идентификатор массива
success SetArrayLong (long id, long idx, long value)
Функция присваивает значение типа "длинное целое" элементу массива, заданного индексом. Массив задается идентификатором. Индекс массива выражается 32-битным целым числом. Разряженные массивы позволяют эффективно хранить данные, не резервируя память под несуществующие элементы, поэтому индексы необязательно должны следовать один за другим. Так, например, массив может состоять всего из двух индексов, — скажем, 0x0 и 0x10000, — при этом будет потрачено всего две ячейки памяти.
Один и тот же индекс одного и того же массива может хранить одновременно данные двух типов — как строковые, так и длинных целых, и "затирания" при этом не происходит.
Пример использования:
SetArrayLong(
GetArrayId("MyArray"),
0x100,
Оx666);

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива
value | Присваиваемое значение типа "длинное целое"
Return | ==return | Пояснения
 | ==l | Успешное завершение
 | ==0 | Ошибка


IDA - 0392.htm

392 Виртуальные массивы
success SetArrayString (long id, long idx, char str)
Функция присваивает значение типа "строка" элементу массива, заданного индексом. Массив задается идентификатором. Индекс массива выражается 32-битным целым числом. Разряженные массивы позволяют эффективно хранить данные, не резервируя памяти под несуществующие элементы.
Индексы необязательно должны следовать один за другим. Так, например, массив может состоять всего из двух индексов, — скажем, 0x0 и 0x10000, — при этом будет потрачено всего две ячейки памяти.
Один и тот же индекс одного и того же массива может хранить одновременно данные двух типов — как строковые, так и длинных целых, и "затирания" при этом не происходит.
Пример использования:
SetArrayString(
GetArrayId("MyArray"),
0x100,
"MyString");

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива
str | Присваиваемое значение типа "строка"
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
char or long GetArrayElement (long tag, long id, long idx)
Функция служит для чтения обоих типов элементов разряженных массивов.
Выбор интересующего типа осуществляется тегом tag.
Он может принимать следующие значения:

Определение |  | Значение
AR_LONG | 'А' | Элемент типа "длинное целое"
AR_STR | 'S' | Элемент типа "строка"
Запрашиваемый индекс должен быть инициализирован ранее, иначе функция
вернет ошибку.
Пример использования:
> ввод пользователя:
SetArrayLong(
GetArrayId("MyArray") ,
0x100,
Ох666);
SetArrayString(
GetArrayId("MyArray"),


IDA - 0393.htm

Виртуальные массивы 393
0x100,
"MyString");
Message("%s \nOx%X\n",
GetArrayElement(AR_STR,
GetArrayId("MyArray") ,
0x100),
GetArrayElement(AR_LONG,
GetArrayId("MyArray") ,
0x100),
) ;
< ответ IDA:
MYString
0x666

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
Id | Идентификатор массива
idx | Индекс массива
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
success DelArrayElement (long tag, long id, long idx)
Функция удаляет указанный тип элемента разряженного массива. Тип задает-
ся тегом tag, который может принимать следующие значения:

Определение |  | Значение
AR_LONG | 'А' | Элемент типа "длинное целое"
AR_STR | 'S' | Элемент типа "строка"
Пример использования:
DelArrayElement(AR_LONG,
GetArrayId("MyArray") ,
0x100);

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
id | Идентификатор массива
idx | Индекс массива


IDA - 0394.htm

394 Виртуальные массивы

Операнд | Пояснения
Return | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
long GetFirstIndex (long tag, long id)
Функция возвращает индекс первого элемента разряженного массива. В отличие от "обычных" массивов, известных нам по языками С и Pascal, разряженные массивы могут начинаться с любого индекса — необязательно с нулевого.
"Первым" индексом разряженного массива будет индекс инициализированного элемента с наименьшим числовым значением.
Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray")) ;
a=CreateArray("MyArray") ;
SetArrayLong(a,OxlOO,Ox666);
SetArrayLong(a,0x77, 0x67) ;
SetArrayLong(a,0x210,0x777);
Message("Ox%X \n",
GetFirstIndex(AR_LONG,а)
) ;
DeleteArray(a);
< ответ IDA:
0x77

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
id | Идентификатор массива
Return | ==return | Пояснения
 | !=BADADDR | Индекс первого элемента разряженного массива
 | ==BADADDR | Ошибка
long GetLastIndex (long fag, long id)
Функция возвращает индекс последнего элемента разряженного массива. Число элементов разряженного массива обычно намного меньше, чем индекс последнего из них.
Так, например, массив может состоять всего из трех элементов с индексами, скажем, 0x5, 0x777, 0x666777 — и функция GetLastIndex возвратит 0x666777, тогда как массива содержит всего лишь три индекса.


IDA - 0395.htm

Виртуальные массивы 395
К сожалению, не предоставлено никаких функций, позволяющих узнать размер массива. Все что можно сделать — это пройтись по цепочке элементов функциями GetNextIndex (GetPrevIndex). Поэтому вызов GetLastIndex используется очень редко, так как в нем особой нужды не бывает.
Пример использования:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray")) ;
a=CreateArray("MyArray");
SetArrayLong(a,0x100,Охббб);
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
Message("Ox%X \n",
GetLastIndex(AR_LONG,а)
) ;
DeleteArray(a);
< ответ IDA:
0x210

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
id | Идентификатор массива
Return | ==return | Пояснения
 | !=BADADDR | Индекс последнего элемента разряженного массива
 | ==BADADDR | Ошибка
long GetNextIndex (long tagt long id, long idx)
Функция возвращает следующий индекс разряженного массива. Как уже было сказано выше, в разряженных массивах индексы необязательно следуют друг за другом, а могут быть разделены "дырами" произвольного размера, поэтому для "путешествия" по цепочке инициализированных элементов массива и была введена функция GetNextIndex.
Передаваемый текущий индекс (idx) необязательно существует, — функция возвращает следующий сразу за ним инициализированный элемент. Это дает возможность отказаться от использования функции GetLastIndex, поскольку функция GetNextIndex(,,0) идентична ей, что и показано на примере, приведенном ниже:
> ввод пользователя:
auto a,b;
b=0;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetArrayLong(a,0x100,Охббб);


IDA - 0396.htm

396 Виртуальные массивы
SetArrayLong(a,0x77,0x67);
SetArrayLong(a,0x210,0x777);
while(l)
{
b=GetNextIndex(AR_LONG,a,b);
if (b==-l) break;
Message("Ox%X \n",b);
}
DeleteArray(a);
< ответ IDA:
0x77
0x100
0x210

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
id | Идентификатор массива
idx | Индекс массива
Return | ==return | Пояснения
 | !=BADADDR | Следующий индекс
 | ==BADADDR | Ошибка
long GetPrevIndex (long tag, long id, long idx)
Функция возвращает предыдущий индекс разряженного массива. Как уже было сказано выше, в разряженных массивах индексы необязательно следуют друг за другом, а могут быть разделены "дырами" произвольного размера. Для "путешествия" по цепочке инициализированных элементов массива была введена функция GetPrevIndex.
Передаваемый текущий индекс (idx) необязательно существует, — функция возвращает первый предшествующий ему инициализированный элемент. Это дает возможность отказаться от использования функции GetLastIndex, поскольку функция GetPrevIndex(,,-l) идентична ей, что и показано на примере, приведенном ниже:
> ввод пользователя:
auto a,b;
b=0;
DeleteArray(GetArrayId("MyArray")) ;
a=CreateArray("MyArray") ;
SetArrayLong(a,0x100, 0x666) ;
SetArrayLong(a,0x77, 0x67) ;
SetArrayLong(a,0x210,0x777);
while(l)
{
b=GetPrevIndex(AR_LONG,a,b);


IDA - 0397.htm

Виртуальные массивы 397
if (b==-l) break;
Message("Ox%X \n",b);
}
DeleteArray (a);
< ответ IDA:
0x210 0x100 0x77

Операнд | Пояснения
tag | ==tag | Значение
 | AR_STR | Элемент типа "строка"
 | AR_LONG | Элемент типа "длинное целое"
id | Идентификатор массива
idx | Индекс массива
Return | ==return | Пояснения
 | !=BADADDR | Предыдущий индекс
 | ==BADADDR | Ошибка


IDA - 0398.htm

398 Ассоциативные массивы
Ассоциативные массивы
Об ассоциативных массивах
Ассоциативные массивы — это маленькое чудо IDA. Большинству программистов, возможно, ранее никогда не приходилось сталкиваться ни с чем подобным. К сожалению, о них очень мало (совсем ничего) не сказано в контекстной помощи, поэтому будет не лишним рассмотреть их подробнее.
Одним из распространенных языков, поддерживающих такие конструкции, можно назвать Perl, который ввиду свой кросс-платформенности стал очень популярен среди разработчиков Internet-приложений. Но многие тысячи поклонников С и Pascal могли о нем не слышать.
Когда-то авторитет программиста зависел от числа освоенных им языков. Сегодня разработчики предпочитают сосредоточиваться на одном языке, не интересуясь, что происходит у "соседей". К сожалению, ставшие популярными языки реализуют многие конструкции далеко не лучшим образом. Так случилось и с ассоциативными массивами. Чем они отличаются от своих собратьев и какие возможности предоставляют при этом?
Уникальность ассоциативных массивов в том, что они индексируются строковыми значениями (в буквальном смысле слова) — хеширования или иного преобразования в численный вид не производится. Это позволяет значительно упрощать многие алгоритмы, раньше выглядевшие громоздкими.
Например, пусть нам требуется составить таблицу, перечисляющую имена сотрудников и получаемую ими зарплату. Традиционно потребовалось бы создать три массива, а потом обвязать все это кодом, гарантирующим согласованность и непротиворечивость данных. Насколько проще решается задача с использованием ассоциативных массивов! Можно придумать массу других примеров.
Архитектура ассоциативных массивов
Ассоциативные массивы представляют собой подкласс разряженных массивов, которые были рассмотрены выше.
Ассоциативные массивы создаются вызовом функции CreateArray (как и обычные разряженные). Любой массив по сути одновременно является и разряженным, и ассоциативным, — точнее, массивов на самом деле все же несколько, но идентификатор (ID) y всех может быть один. Поэтому к ассоциативным массивам применимы описанные выше функции RenameArray и DeleteArray.
Новым является набор менее чем из десятка функций, работающий со строковыми индексами. В терминологии ассоциативных массивов индексы называются ключами. Каждый ключ представляет собой строковое значение без явных ограничений (во всяком случае в контекстной помощи IDA на этот счет


IDA - 0399.htm

Ассоциативные массивы 399
ничего не сказано) и может содержать один элемент типа "строка" или "длинное целое".
Каждый индекс разряженного массива может одновременно содержать элементы двух типов, а каждый ключ ассоциативного массива ссылается лишь на одно значение того или иного типа. В остальном операции с элементами ассоциативных массивов ничем не отличаются от описанных выше.
Всеми манипуляциями с элементами заведуют всего три функции. Можно присвоить значение элементу, выбрать значение элемента, наконец, удалить элемент. На этом предоставляемые IDA возможности заканчиваются.
Более интересны операции,выполняемые над ключами ассоциативных массивов. Поскольку они представляют собой строковые значения, не всегда бывает ясно, как, например, перечислить все элементы массива. Да, существуют традиционные функции GetNext и GetPrev. Очевидно, что они перечисляют ключи (т.е. индексы) массива один за другим, но вот в каком порядке? Это не документировано, поэтому стоит проектировать код скрипта так, чтобы его работоспособность не зависела от подобного рода деталей. Все текущие версии IDA упорядочивают список индексов в алфавитном порядке, независимо от очередности создания элементов, но некоторые языки упорядочивают индексы ассоциативных массивов именно в порядке их создания, и никем не гарантируется, что так не будет и в следующих версиях IDA (хотя это маловероятно). Ваш скрипт не должен зависеть от этих деталей.
success SetHashLong (long Id, char idx, long value)
Функция присваивает значение типа "длинное целое" элементу ассоциативного массива. Ассоциативные массивы выгодно отличаются от остальных тем, что могут индексироваться строковыми значениями. В остальном к ним применяются те же подходы, что и к разряженным массивам. Однако в отличие от разреженных массивов, один и тот же ключ (индекс) не может содержать числовое и строковое значение одновременно.
Пример использования:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",Ox66);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",Ox67);
DeleteArray(a);

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива (строковой!)
value | Присваиваемое значение типа "длинное целое"
Return | ==return, | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка


IDA - 0400.htm

400_____________Ассоциативные массивы_______________
success SetHashString (long id, char idx, char value)
Функция присваивает значение типа "строка" элементу ассоциативного массива. Ассоциативные массивы могут индексироваться строковыми значениями, в остальном они почти не отличаются от разреженных массивов, но один и тот же индекс в ассоциативных массивах не может содержать числовое и строковое значение одновременно.
Пример использования:
auto а;
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 | ==return | Пояснения
 | ==1 | Успешное завершение
 | ==0 | Ошибка
long GetHashLong (long id, char idx)
Функция возвращает значение элемента ассоциативного массива типа "длинное целое". Как уже говорилось выше, один и тот же элемент не может содержать значения "строка" и "длинное целое" одновременно, поэтому возникает возможность присвоить значение одного типа, а попытаться прочитать — другого. Эта операция завершится корректно, и типы будут автоматически преобразованы.
При этом функция GetHashLong всегда возвращает четыре первые байта, сколько бы строка ни занимала на самом деле. Если она была короче, то остаток будет представлять собой мусор и содержать непредсказуемые значения.
Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov","Patron");
Message("%s \n",
GetHashLong(а,"Ivanov")
);
DeleteArray(a);
< ответ IDA:
Patr


IDA - 0401.htm

_______________Ассоциативные массивы_____________401
IDA учитывает регистр индексов. Так, "Ivanov" и "ivanov" — это два разных
ключа, и при попытке прочитать присвоенное значение функция возвратит ноль.
Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",Охбб);
Message("%x \n",
GetHashLong(a,"ivanov")
)/
DeleteArray(a);
< ответ IDA:
0
Возникает неоднозначность, — то ли действительно возникла ошибка, то ли такое значение имеет элемент?
В остальном никаких проблем с использованием этой функции не возникает. И еще один пример:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",Ox66) ;
Message("%x \n",
GetHashLong(a,"Ivanov")
);
DeleteArray(a);
< ответ IDA:
0x66

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива (строковой!)
Return | ==return | Пояснения
 | !=0 | Значение элемента | массива
 | ==0 | Ошибка
 |  | Значение элемента | массива
char GetHashString (long id, char idx)
Функция возвращает значение элемента ассоциативного массива типа "строка". Так как выше один и тот же элемент не может содержать значения "строка" и "длинное целое" одновременно, то возникает возможность присвоить значение одного типа, а попытаться прочитать — другого. Эта операция завершится корректно, и типы будут автоматически преобразованы. При этом функция


IDA - 0402.htm

402 Ассоциативные массивы
GetHashString возвращает четыре первые байта, если ни один из них не является
нулем, в противном случае — весь отрезок до первого нуля.
Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",0x66776677);
Message("%s \n",
GetHashString(a,"Ivanov"));
DeleteArray(a);
< ответ IDA:
Wfwf
IDA учитывает регистр индексов. Так, "Ivanov" и "ivanov" — это два разных индекса, и при попытке прочитать присвоенное значение функция возвратит пустую строку.
Например:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov",0x66);
Message("%s \n",
GetHashLong(a,"ivanov")
) ;
DeleteArray(a);
Возникает неоднозначность: действительно возникла ошибка, то ли такое значение имеет элемент?
В остальном никаких проблем с использованием этой функции не возникает. Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray")) ;
a=CreateArray("MyArray") ;
SetHashString(a,"Ivanov","Patron") ;
Message("%s \n",
GetHashLong(a,"Ivanov")
) ;
DeleteArray(a);

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива (строковой!)


IDA - 0403.htm

Ассоциативные массивы 403

Операнд | Пояснения
Return | ==return | Пояснения
 | !="" | Значение элемента массива
 | == | Ошибка
 |  | Значение элемента массива
success DelHashElement (long id, char idx)
Функция удаляет один элемент ассоциативного массива. Поскольку один и тот же элемент не может содержать значения "строка" и "длинное целое" одновременно, отпадает необходимость указывать теги (см. описание функции DelArrayElement для разряженных массивов).
IDA учитывает регистр индексов. Так, "Ivanov" и "ivanov" — это два разных индекса и представляют собой два разных элемента. В остальном функция ничем не отличается от аналогичной, использующейся для удаления элементов разряженных массивов.
Например:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashString(a,"Ivanov","Patron");
Message("%s \n",
DelHashElement(a,"Ivanov")
) ;
DeleteArray(a);
< ответ IDA:
1

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива (строковой!)
Return | ==return | Пояснения
 | ==1 | Операция выполнена успешно
 | ==0 | Ошибка
char GetFirstHashKey (long id)
Функция возвращает индекс первого элемента ассоциативного массива. Поскольку ассоциативные массивы индексируются строковыми значениями, то привычные способы обращения к элементам не подходят.
Чтобы понять принципы функционирования этой (и некоторых других) функций, необходимо познакомиться с техническими деталями архитектуры ассоциативных массивов.


IDA - 0404.htm

404_____________Ассоциативные массивы_______________
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной последовательности, независимо от порядка добавления в него новых элементов, что и доказывает следующий пример:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov", Охбб) ;
SetHashLong(a,"Cheputilo", 0x77) ;
SetHashLong(a,"Alushta", 0x67) /
Message("%s \n",
GetFirstHashKey(a)
);
DeleteArray(a);
< ответ IDA:
Alushta

Операнд | Пояснения
id | Идентификатор массива
Return | ==return | Пояснения
 | !="" | Индекс первого элемента массива
 | =="" | Ошибка
char GetLastHashKey (long id)
Функция возвращает индекс последнего элемента ассоциативного массива. Ассоциативные массивы индексируются строковыми значениями, поэтому привычные способы обращения к элементам не подходят.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной последовательности, независимо от порядка добавления в него новых элементов, что и доказывает следующий пример:
> ввод пользователя:
auto а;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray");
SetHashLong(a,"Ivanov",Охбб);
SetHashLong(a,"Cheputilo",0x77);
SetHashLong(a,"Alushta",0x67);
Message("%s \n",
GetLastHashKey(а)
) ;
DeleteArray(a);
< ответ IDA:
Ivanov


IDA - 0405.htm

Ассоциативные массивы 405

Операнд | Пояснения
id | Идентификатор массива
Return | ==return | Пояснения
 | !="" | Индекс последнего элемента | массива
 | =="" | Ошибка
char GetNextHashKey (long id,char idx)
Функция возвращает индекс следующего элемента ассоциативного массива. Ассоциативные массивы индексируются строковыми значениями, и привычные способы обращения к элементам не подходят.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной последовательности, независимо от порядка добавления в него новых элементов, и функция 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);
< ответ IDA:
Alushta
Cheputilo
Ivanov

Операнд | Пояснения
id | Идентификатор массива
idx | Индекс массива (строковой!)
Return | ==return | Пояснения
 | |=- | Очередной индекс массива
 | =="" | Ошибка


IDA - 0406.htm

406 Ассоциативные массивы
char GetPrevHashKey (long id, char idx)
Функция возвращает индекс предыдущего элемента ассоциативного массива. Ассоциативные массивы индексируются строковыми значениями, поэтому привычные способы обращения к элементам не подходят. Чтобы понять принципы функционирования этой (и некоторых других) функций, необходимо познакомиться с техническими деталями архитектуры ассоциативных массивов.
Индексы ассоциативных массивов хранятся в списке, упорядоченном в алфавитной последовательности, не зависимо от порядка добавления в него новых элементов, и функция GetPrevHashKey последовательно возвращает элементы ассоциативного массива. Передаваемый ей текущий индекс необязательно существует — функция просматривает список всех элементов и возвращает предшествующий ему в алфавитном порядке. Это дает возможность отказаться от вызовов GetLastHashKey, поскольку GetNextHashKey(,-l) будет его полным эквивалентом.
Например:
> ввод пользователя:
auto a,b;
b=-l;
DeleteArray(GetArrayId("MyArray"));
a=CreateArray("MyArray") ;
SetHashLong(a,"Ivanov", Охбб) ;
SetHashLong(a,"Cheputilo", 0x77) ;
SetHashLong(a,"Alushta", 0x67) ;
for(;;){
b=GetPrevHashKey(a,b);
if (b=="") break;
Message("%s \n",b);}
DeleteArray(a);
< ответ IDA:
Ivanov
Cheputilo
Alushta

Операнд | Пояснения
Id | Идентификатор массива
idx | Индекс массива (строковой!)
Return | ==return Пояснения
 | !="" Очередной | индекс массива
 | =="" Ошибка


IDA - 0407.htm

Глобальные настройки 407
Глобальные настройки
Методы

Функция | Назначение
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 (char processor) | Задет тип процессора для дизассемблирования
long Batch (long batch) | Устанавливает или снимает пакетный режим работы
char GetldaDirectory () | Возвращает путь к директории, в которой расположена IDA
char GetlnputFile () | Возвращает имя дизассемблируемого файла
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, читая строку по четыре байта за раз (это удобнее и быстрее).


IDA - 0408.htm

408 Глобальные настройки
'offset' — это смещение внутри структуры, в которой IDA хранит настройки. Ниже это будет подробно описано. А пока обратим внимание на то, что IDA не запрещает передавать функциям произвольное смещение внутри структуры, что часто становится источником неочевидных ошибок. Такое происходит, когда используются фиксированные смещения элементов структуры вместо определенных IDA значений. Ввиду того, что в следующих версиях эта структура, скорее всего, не останется без изменений, необходимо использовать только определения IDA, иначе за работоспособность скрипта трудно будет поручиться.
Настройки IDA
Первые три 6auma хранят в себе слово 'IDA'. Это не сообщается в документации, но нетрудно убедиться, что это именно так и есть.
> ввод пользователя:
Message("%s%s\n",
GetShortPrm(0),
GetCharPrm(2)
);
< ответ IDA:
IDA
INF_VERSION
Содержит переменную типа Short, хранящую версию базы IDA. Например:
> ввод пользователя:
Message("%x \n",
GetShortPrm(INF_VERSION)
);
< ответ IDA:
22
INF_PROCNAME
Хранит восьмисимвольное имя выбранного типа процессора дизассемблируемого текста. Для процессоров серии 80x86 предусмотрены следующие соответствия:

Intel 8086 | 8086
Intel 80286 real | 80286r
Intel 80286 protected | 80286p
Intel 80386 real | 80386r
Intel 80386 protected | 80386p
Intel 80486 real | 80486r
Intel 80486 protected | 80486p
Intel Pentium real with MMX | 80586r
Intel Pentium protected with MMX | 80586p
Intel Pentium Рго (P6) with MMX | 80686p
Intel Pentium II | P2


IDA - 0409.htm

Глобальные настройки 409

AMD K6-2 with 3DNow! | Кб
Intel Pentium III | рЗ
INF_LFLAGS
Это однобайтовое поле хранит флаги, определяемые по умолчанию в IDP-модуле для конкретной модели процессора, которые могут принимать следующие значения:
LFLG_PC_FPP (0x1)
Декодировать инструкции с плавающей запятой для арифметического сопроцессора.
LFLG_PC_FLAT (0x2)
Плоская модель памяти.
Обратите внимание, что изменение значения параметра в этом поле может повлечь за собой непредсказуемую работу дизассемблера и привести к неверному анализу исследуемого файла! Пример использования:
> ввод пользователя:
auto а;
a=GetCharPrm(INF_LFLAGS);
Message("%x \n",a) ;
if (а & LFLG_PC_FPP)
Message ("Decode FPP \n"); •
if (a & LFLG_PC_FLAT)
Message ("FLAT MODEL \n");
< ответ IDA:
1
Decode FPP
INF_DEMNAMES
Однобайтовое поле, определяющее, каким образом IDA будет "заманглять" имена. Безболезненно может как считываться, так и модифицироваться.
DEMNAM_CMNT (0)
Отображать замангленные имена как комментарии. Например:
> ввод пользователя:
SetCharPrm(INF_DEMNAMES,DEMNAM_CMNT);
Q копия экрана IDA:
.text:00403E79 ?sputc@streambuf@@QAEHH@Z proc near
; streambuf::sputc(int)
DEMNAM_NAME (1)
Заманглять имена. Например:
> ввод пользователя:
SetCharPrm(INF_DEMNAMES,DEMNAM_NAME);
Q копия экрана IDA:
.text:00403E79 public: int _thiscall streambuf::sputc(int) proc near


IDA - 0410.htm

410 Глобальные настройки
DEMNAM_NONE (2)
Не заманглять и представлять имена как есть.
> ввод пользователя:
SetCharPrm(INF_DEMNAMES,DEMNAM_NONE);
Q копия экрана IDA:
.text:00403E79 ?sputc@streambuf@xaQAEHH@Z proc near
При установке нового значения параметра в этом поле IDA автоматически начнет реанализ и внесет все изменения в дизассемблируемый текст.
INF_FILETYPE
Это поле содержит короткое целое, хранящее тип дизассемблируемого файла. Видимо, не используется IDA, поскольку может безболезненно модифицироваться произвольными значениями, что не нарушает работоспособности.
Следует учитывать, что в версии 3.84 функция чтения возвращает неверные значения этого параметра. Дело в том, что IDC.IDC не был изменен, когда был расширен список типов файлов, начиная с середины таблицы, отчего все типы "посыпались".
MSDOS ехе-файл стал определяться как программный файл PalmPilot, что хотя и не нарушало работоспособности IDA, но не позволяло определить тип текущего файла.
B IDC.IDC содержится ссылка на файл 'core.hpp'. На самом деле это опечатка, и нужно смотреть 'ida.hpp', входящий в IDA SDK. Там мы обнаружим прелюбопытную структуру, описывающую типы поддерживаемых файлов. Сравнив ее с IDC.IDC, можно найти различие, которое показано ниже:

f_EXE_old | FT_EXE_OLD | f_SREC | FT_OMF
f_COM_old | FT_COM_OLD | f_ZIP | FT_SREC
f_BIN | FT_BIN | f_OMFLIB | FT_ZIP
f_DRV | FT_DRV | f_AR | FT_OMFLIB
f_WIN | FT_WIN | f_LOADER | FT_AR
f_HEX | FT_HEX | f_ELF | FT_LOADER
f_MEX | FT_MEX | f_W32RUN | FT_ELF
f_LX | FT_LX | f_AOUT | FT_W32RUN
f_LE | FT_LE | f_PRC | FT_AOUT
f_NLM | FT_NLM |  | FT_PRC
f_COFF | FT_COFF |  | FT_EXE
f_PE | FT_PE |  | FT_COM
f_OMF | FT_USER |  | FT_AIXAR
Чтобы функция возвращала правильный результат, необходимо исключить
тип FT_USER и перенумеровать остаток списка.
Расшифровка всех значений приведена ниже:


IDA - 0411.htm

Глобальные настройки 411

FT_EXE_OLD | MS DOS, EXE-файл
FT_COM_OLD | MS DOS, COM-файл
FT_BIN | Двоичный файл (дамп ROM, например)
FT_DRV | MS DOS, драйвер (drv или sys)
FT_WIN | New Executable (NE)
FT__HEX | Intel Hex Object File
FT_MEX | MOS Technology Hex Object File
FT_LX | Linear Executable (LX)
FT_LE | Linear Executable (LE)
FT_NLM | Netware Loadable Module (NLM)
FT_COFF | Common Object File Format (COFF)
FT_PE | Portable Executable (PE)
FT_USER | Файл, загруженный посредством загрузчика IDP
FT_OMF | Object Module Format
FT_SREC | R-records
FT_ZIP | ZIP-file (никогда не бывает загружен в базу IDA)
FT_OMFLIB | Библиотека OMF-Модулей
FT_AR | аг library
FT_LOADER | Файл, загруженный посредством LOADER.DLL
FT_ELF | Executable and Linkable Format (ELF)
FT_W32RUN | Watcom DOS-32 Extender (W32RUN)
FT_AOUT | Linux a. out (AOUT)
FT__PRC | PalmPilot программный файл
FT_EXE | MS DOS, EXE-File
FT_COM | MS DOS, COM-File
FT_AIXAR | AIX ar library
Пример использования:
> ввод пользователя:
.Message("%d \n",
GetShortPrm(INF_FILETYPE)
);
< ответ IDA:
23
INF_OSTYPE
Короткое целое, хранящее тип операционной системы для загруженного
файла (не среды запуска дизассемблера!)
Должно принимать следующие значения:


IDA - 0412.htm

412 Глобальные настройки

OSTYPE_MSDOS | 0x0001 | MS DOS
OSTYPE__WIN | 0x0002 | MS Windows
OSTYPE_OS2 | 0x0004 | OS/2
OSTYPE_NETW | 0x0008 | Novell NetWare
Да, именно должно, ибо MS DOS-файлы возвращают ноль, а не единицу и,
следовательно, OSTYPE__MSDOS не сработает.
Пример использования:
> ввод пользователя:
Message("%d \n",
GetShortPrm(INF_OSTYPE)
< ответ IDA:
0
lNF_APPTYPE
Короткое целое, содержащее информацию о типе дизассемблируемого приложения.
Часть полей (APPT_CONSOLE, APPT_GRAPHIC, APPT_lTHREAD, APPT_MTHREAD) инициализируются FLIRT.
Если исследуемой программе не соответствует ни одна библиотека сигнатур и FLIRT не сработал, то все вышеперечисленные поля будут содержать нулевые значения.
Тип приложения (EXE\DLL\DRIVER) не актуален для MS DOS-программ, как и разрядность (16 или 32 бит). В этом случае функция чтения данного параметра всегда возвращает нулевое значение.

APPT_CONSOLE | 0x0001 | Console
APPT_GRAPHIC | 0x0002 | Graphics
APPT_PROGRAM | 0x0004 | EXE
APPT_LIBRARY | 0x0008 | DLL
APPT_DRIVER | 0x0010 | DRIVER
APPT__1THREAD | 0x0020 | Singlethread
APPT_MTHREAD | 0x0040 | Multithread
APPT_16BIT | 0x0080 | 16 bit application
APPT_32BIT | 0x0100 | 32 bit application
Пример использования:
> ввод пользователя:
Message("%x \n",
GetShortPrm(INF_APPTYPE)
);
< ответ IDA:
104


IDA - 0413.htm

Глобальные настройки 413
INF_START_SP
Длинное целое, содержащее значение регистра SP (ESP) при запуске программы. Для получения этой информации IDA читает соответствующие поля заголовка файла. В противном случае (например, для com или дампов памяти) она устанавливает SP на верхушку сегмента, т.е. присваивает ему значение -1.
Для бинарных файлов и дампов памяти это оказывается не всегда справедливо (в самом деле, откуда IDA может знать значение указателя стека в каждом конкретном случае). Тогда рекомендуется установить требуемое значение вручную, функцией SetLongPrm.
Однако, обычно точное значение SP (ESP) некритично и в общем случае не влияет на правильность дизассемблирования кода.
Пример использования:
> ввод пользователя:
Message("%x \n",
GetShortPrm(INF_START_SP)
) ;
< ответ IDA:
ffff
INF_START_AF
Это поле содержит короткое целое, управляющее настройками анализатора IDA. Иначе к ним можно добраться через меню "Options\Analysis options\Kernel analyser options 1".
Все они доступны как для чтения, так и для модификации. Возможные значения параметра, определяемые битами флагов, приведены сразу за рисунком.
IDA - 0413-1.jpg


IDA - 0414.htm

414 Глобальные настройки

AF_FIXUP | 0x0001 | Создавать сегменты и смещения, используя информацию таблицы перемещаемых элементов | из
AF_MARKCODE | 0x0002 | Автоматически преобразовывать типичные последовательности инструкций в код
AF_UNK | 0x0004 | Удалять инструкции без ссылок
AF_CODE | 0x0008 | Трассировать выполнение
AF_PROC | 0x0010 | Автоматически создавать функции
AF_USED | 0x0020 | Поверхностный анализ программы
AF_FLIRT | 0x0040 | Использовать FLIRT-сигнатуры
AF_PROCPTR | 0x0080 | Создавать функции в 32-битном сегменте, если это ссылка сегмент данных | на
AF_JFUNC | 0x0100 | Переименовывать jump-функции как j_...
AF_NULLSUB | 0x0200 | Переименовывать пустые функции как nullsub_...
AF_LVAR | 0x0400 | Создавать стековые переменные
AF_TRACE | 0x0800 | Отслеживать указатель стека
AF_ASCII | 0x1000 | Автоматически создавать строки
AF_IMMOFF | 0x2000 | Преобразовывать операнды 32-битных инструкций в смещения
AF_DREFOFF | 0x4000 | Преобразовывать 32-битные данные в смещения
AF_FINAL | 0x8000 | Сворачивать все unexplored-регионы
AF_FiXUP
Если этот бит установлен, то IDA будет использовать информацию из таблицы перемещаемых элементов и представлять соответствующие непосредственные операнды в виде смещений или сегментов.
Например:

Код | AF_FIXUP == 1 |  | AF_FIXUP == | 0
В8 01 00 | mov ах, seg dseg | mov | ах, 1001h |
8E D8 | mov ds, ах | mov | ds, ах |
Значение перемещаемого элемента, выделенного жирным курсивом, равно 0x1. В любом случае IDA автоматически суммирует его с адресом загрузки (в нашем примере 0x10000).
Если флаг AF_FIXUP установлен, то IDA преобразует непосредственный операнд в сегмент, в противном случае — оставит его без изменений.
AF_MARKCODE
Установка этого флага приведет к тому, что IDA будет находить все типичные для выбранного процессора последовательности инструкций и преобразовывать их в код, даже если на них отсутствуют явные ссылки.
Такой прием не совсем безгрешен, но позволяет заметно поднять качество дизассемблирования и переложить часть рутинной работы на дизассемблер.


IDA - 0415.htm

Глобальные настройки 415
Например, для процессоров 80x86 типичной последовательностью инструкций будет инициализация регистра BP (EBP) при входе в процедуру.
.text:00401020 push ebp
.text:00401021 8В EC mov ebp, esp
Этот механизм запускается только во время загрузки файла, и его динамическое изменение во время работы дизассемблера будет проигнорировано.
AF_UNK
Установление этого флага приводит к тому, что IDA будет каждый раз при пометке инструкции (инструкций) как unexplored автоматически отслеживать все потерянные перекрестные ссылки, помечая соответствующие регионы как unexplored.
AF_CODE
IDA умеет трассировать следование инструкций, отслеживая условные переходы и вызовы процедур. Например, если встретится:
segOOO:22C3 E8 5F 00 call sub_0_2325
то можно быть уверенным, что IDA преобразует в инструкции и код, находящийся по смещению 0x2325. В противном случае (если бит AF_CODE сброшен) это выполнено не будет.
Более того, при загрузке IDA не дизассемблирует ни одной инструкции, предоставляя пользователю сделать это самостоятельно.
Имеет смысл сбрасывать этот флаг всякий раз, когда IDA неправильно отслеживает ссылки или когда нужно изучить только отдельно взятый фрагмент кода, а дизассемблировать весь файл ни к чему.
AF_PROC
Позволяет автоматически создавать функции на месте вызова инструкцией call. B противном случае функции будут создаваться только для библиотечных процедур. Например:

AF_PROC == 0 | AF_PROC == 1
SegOO:0124 call loc_0_284 | SegOO:0124 call loc_0_284
SegOOO: 0284 loc_0_284: segOOO:0284 push ds segOOO:0285 mov ax, 3500h segOOO:0288 int 21h segOOO:028A ret | SegOOO: 0284 sub__0_284 proc near segOOO:0284 push ds segOOO:0285 mov ax, 3500h segOOO:0288 int 21h segOOO:028A ret segOOO :02C6 sub 0 284 endp
AF_USED
В документации на IDA сообщается, что сброс этого бита приводит к тому, что IDA выполняет поверхностный анализ программы, т.е. примитивное дизассемблирование, без создания перекрестных ссылок и дополнительных проверок.


IDA - 0416.htm

416_____________Глобальные настройки________________
Практически же значение этой опции не влияет на дизассемблируемый текст, и в обоих случаях получаются идентичные листинги.
AF_FLIRT
Уникальная FLIRT-технология позволяет IDA определять имена библиотечных функций наиболее популярных компиляторов по их сигнатурам. Сравните два примера:

AF_FLIRT == 1 | AF_FLIRT == 0
dseg:039A push offset aHelloSailor dseg:039D call _printf dseg:03AO pop cx dseg:03Al retn | dseg:039A pushoffset aHelloSailor dseg:039D call sub_0_1035 dseg:03AO pop ex dseg:03Al retn
AF_PROCPTR
Установка этого флага приведет к тому, что IDA будет проверять все перекрестные ссылки из 32-разрядного сегмента данных в сегмент кода. Если ссылка указывает на машинную инструкцию, то IDA автоматически преобразует ее в код и создаст на этом месте функцию.
Например:

AF_PROCPTR == 1 | AF_PROCPTR == 0
.data:004085EO dd offset sub 0 405AAC | .data:004085EO dd 405AACh
.text:00405AAC sub_0__405AAC proc near .text:00405AAC push ebp .text:00405AAD mov ebp, esp | .text:00405AAC db 55h .text:00405AAD db 8Bh .text:00405AAE db OECh
Данный метод не безгрешен и в некоторых случаях может приводить к ошибкам (тем более, возможно предположить умышленное действие автора дизассемблируемого текста против IDA), поэтому иногда его приходится отключать.
AF_JFUNC
Установка этого флага приведет к тому, что IDA будет переименовывать функции, состоящие из одной только команды jmp somewhere, в j_somewhere. Это заметно улучшает читабельность листинга и ускоряет анализ алгоритма его работы.

AF JFUNC == 1 | AF_jFUNC == 0
segOOO :22DD j_MyJmpTrg proc near segOOO :22DD jmp short MyJmpTrg segOOO :22DD j_MyJmpTrg endp | segOOO :22DD sub_0_22DD proc near segOOO :22DD jmp short MyJmpTrg segOOO :22DD sub_0_22DD endp
AF_NULLSUB
Установка этого флага приведет к тому, что IDA будет переименовывать "пустые", т.е. состоящие только из одной инструкции возврата, процедуры в nullsub_xx. Это облегчает читабельность и восприятие листинга, а также ускоряет анализ исследуемого текста дизассемблера.


IDA - 0417.htm

Глобальные настройки 417

AF_NULLSUB == 1 | AF_NULLSUB == 0
segOOO :22DF null sub 1 proc near segOOO:22DF retn segOOO :22DF nullsub_l endp | segOOO:22DF sub 0 22DF proc near segOOO:22DF retn segOOO :22DF sub_0_22DF endp
AF_LVAR
Механизм отслеживания текущего значения регистра SP (ESP) дает возможность поддержки локальных переменных лежащих в стеке с отрицательным смещением относительно BP (EBP). Это полезно при дизассемблировании кода, сгенерированного оптимизирующими компиляторами, которые уже не опираются на BP (EBP), а адресуют локальные переменные относительно стекового регистра ESP. Это приводит к тому, что невозможно понять, к какой именно переменной обращается та или иная инструкция, до тех пор пока не будет вычислено значение указателя стека в конкретной точке кода.
IDA взяла на себя эту рутиную работу и поддерживает оба типа стековых переменных самостоятельно.

AF_LVAR == 1 | AF__LVAR == 0
.text:0040112A mov ecx, [esp+40h+var_1C] | .text:0040112A mov ecx, [esp+24h]
AF_TRACE
Установка этого флага заставляет IDA отслеживать значение регистра указателя стека в каждой точке кода. Главным образом это необходимо для поддержки локальных переменных (см. AF_LVAR).

AF__ TRACE == 1 | AF_ TRACE == 0
dseg:187A off_0_187A dw offset loc_0_B45 | dseg:187A word_0_187A dw OB45
dseg:OB45 mov dx, 183Ch | dseg:OB45 mov dx, 183Ch
AF_ASCII
IDA может автоматически создавать строки, если элемент, на который указывает ссылка, состоит более чем из четырех символов ASCII для 16-разрядного сегмента (и шестнадцати символов в остальных случаях).
Стиль строки определяется настройками, о которых будет сказано ниже.
AF_MMOFF
Этот флаг имеет смысл только для 32-разрядных сегментов. Если он установлен, то IDA будет преобразовывать 32-разрядные операнды в смещения. Для этого необходимо, чтобы операнд был больше минимально возможного адреса загрузки 0x10000.
Это значительно облегчает дизассемблирование 32-разрядных приложений, автоматически корректно распознавая большинство смещений и указателей. Поскольку большинство приложений редко оперируют подобными величинами, то вероятность ложных срабатываний (ошибочного преобразования константы в смещение) относительно невелика.


IDA - 0418.htm

418 Глобальные настройки

AF_IMMOFF == 1 | AF_IMMOFF == 0
.text:00401000 push offset aHelloSailor .text:00401005 mov ecx, offset ord 0 408900 | .text:00401000 push 408040h .text:00401005 mov ecx, 408900h
AF_DREFOFF
Если этот флаг установлен, то IDA будет автоматически пытаться преобразовать в смещения все двойные слова, содержащие ссылки из 32-разрядных сегментов.
Преобразование в общем случае осуществляется успешно, если содержимое двойного слова больше, чем 0x10000.

AF_DREFOFF == I
.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-фaйлoв с раздельными сегментами кода и данных, эта проблема отсутствует.
Рекомендуется сбрасывать этот флаг (по умолчанию он установлен).
Рассмотрите пример, приведенный ниже. Очевидно, что по адресу segOOO:210D расположены строки:
segOOO:210D aDir db '..',0
segOOO:2110 aMask db '*.*',0
Но IDA, не найдя на них ссылок (поскольку невозможно для 16-разрядных приложений отличить смещения от констант), превратила их в малоосмысленный


IDA - 0419.htm

Глобальные настройки 419
массив. Очевидно, что программа была дизассемблирована неправильно. Поэтому лучше не полагаться на автоматические алгоритмы IDA, а исследовать unexpored-байты самостоятельно.
Замечание. Для некоторых типов файлов (например, PE) значение этого флага
в ряде случаев игнорируется, и остаются unexplored-байты.

AF_FINAL == 1 | AF_FINAL == 0 |
segOOO:210D db 2 dup(2Eh), 0, 2Ah, 2Eh, 2Ah, 0 | segOOO:210D db 2Eh segOOO:210E db 2Eh segOOO:210F db 0 segOOO:2110 db 2Ah segOOO:2111 db 2Eh segOOO:2112 db 2Ah segOOO:2113 db 0 | * *
INF_START_IP
Это длинное поле содержит в себе значение регистра IP (EIP) при запуске программы. Для бинарных файлов не имеет смысла и возвращает ошибку (BADADDR). В остальных случаях IDA извлекает необходимую информацию из соответствующих полей заголовка файла или эмулятора загрузчика (например, для сот-файлов).
Это поле доступно как на чтение, так и на запись, однако модификация начального значения IP (EIP) не приведет к изменению точки входа (Entry point) файла (для этого необходимо изменить значение INF_BEGIN__EA).
Пример использования:
> ввод пользователя:
Message("%x \n",
GetLongPrm(INF_START_IP)
) ;
< ответ IDA:
401020
INF_BEGIN_EA
Это длинное поле хранит линейный адрес точки входа в файл. Доступно для
модификации, однако все изменения не возымеют никакого эффекта.
Пример использования:
> ввод пользователя:
Message("%x \n",
GetLongPrm(INF_BEGIN_EA)
);
< ответ IDA:
401020
INF_MIN_EA
Это длинное поле хранит минимальный линейный адрес, используемый программой.


IDA - 0420.htm

420 Глобальные настройки
Пример использования:
> ввод пользователя:
Message("%x \n",
GetLongPrm(INF_MIN_EA)
);
< ответ IDA:
401000
INF_MAX_EA
Это длинное поле хранит самый старший адрес, используемый программой. Никогда не бывает равно минус единице (несмотря на то, что это число присутствует в IDC.IDC). Независимо от того, загружен файл как бинарный или нет, всегда возвращается максимальный адрес, встретившийся в программе.
> ввод пользователя:
Message("%x \n",
GetLongPrm (INF_MAX_EA)
);
< ответ IDA:
134EA
INF_LOW_OFF
Это длинное поле хранит самый младший из возможных адресов, в котором непосредственные операнды могут трактоваться как тип void. Иными словами, начиная с этой величины IDA будет предполагать, что операнд может являться смещением, и поэтому будет выделять его на экране красным цветом, привлекая к нему внимание пользователя.
Рассмотрим это на следующем примере. Пусть в исследуемом файле минимальный из возможных адресов равен 0x100. Следовательно, можно предположить, что все операнды, входящие в диапазон от 0 до 0xFF, окажутся константами, а свыше 0xFF с равной степенью вероятности могут быть как смещениями, так и константами.
IDA всегда использует беззнаковые значения операндов. Поэтому [BP-2] будет трактоваться как 0xFFFE, а не -2.
Допустима модификация этого поля, в том числе и интерактивно, через меню "~Options\Text representation\void's low limit". По умолчанию IDA использует минимальный линейный адрес. Как правило, первым располагается сегмент кода. Если достоверно известно, что программа не содержит ссылок на него, то значение INF_LOW_OFF можно изменить таким образом, чтобы оно указывало на сегмент данных.
> ввод пользователя:
Message("%x \n",
GetLongPrm(INF_LOW_OFF)
);
< ответ IDA:
401000


IDA - 0421.htm

________________Глобальные настройки______________421
INF__HIGH_OFF
Это длинное поле хранит самый старший из возможных адресов, до которого непосредственные операнды могут трактоваться как тип void. Подробнее об этом было сказано в описании поля INF_LOW_OFF.
По умолчанию INF_HIGH_OFF равно наибольшему адресу, занимаемому программой. Часто этого оказывается недостаточно для тех программ, которые организуют буфера за пределами области статических переменных.
Рассмотрим это на примере типичного ЕХЕ-файла (SMALL модель памяти — Сегмент стека и заголовок для упрощения не показаны). Все переменные, кроме тех, что инициализируются на стадии компиляции, останутся "невидимы" для IDA. Она посчитает ссылки на них константами, а не смещениями. Поэтому необходимо изменить значение поля INF_HIGH_OFF вручную.

Файл на диске | Исполняемая программа
код | Сегмент кода
СТАТИЧЕСКИЕ ПЕРЕМЕННЫЕ | Сегмент данных | Статические переменные
 | Сегмент данных | Динамические переменные
lNF_MAXREF
Это длинное поле хранит максимальную глубину перекрестных ссылок (по умолчанию 10). Это значение можно изменить через меню (~Options\Cross references).
Пример:
Q копия экрана IDA:
Right margin 80 i
Cross reference depth 0x10 i
> ввод пользователя:
Message("%x \n",GetLongPrm(INF_MAXREF));
< ответ IDA:
10
INF_ASCII_BREAK
Это однобайтовое поле содержит в себе символ переноса конца строки. Он не будет использоваться IDA при генерации файлов отчета или при выводе на экран. Не влияет он и на трактовку спецификатора '\n'. Единственное его назначение — форматирование строк в дизассемблируемом листинге. (Ниже это будет показано на конкретном примере).
Поле может как читаться, так и модифицироваться. Изменения вступают в силу немедленно, автоматически переформатируя все строки в дизассемблируемом тексте. Интерактивно это значение можно изменить, вызвав следующий диалог командой меню "~Options\ ASCII strings options". "ASCII next line char" и есть то поле, о котором сейчас идет речь.


IDA - 0422.htm

422 Глобальные настройки
IDA - 0422-1.jpg
Пример использования:
> ввод пользователя:
Message ("Ox%X \n%GetCharPrm(INF_ASCII_BREAK));
< ответ IDA:
0xA
G копия экрана IDA:
.rdata:00407384 aRuntimeErrorPr db 'Runtime Error!',OAh
.rdata:00407384 db OAh
.rdata:00407384 db 'Program: ',0
> ввод пользователя:
SetCharPrm(INF__ASCII_BREAK,0) ;
Message("Ox%X \n",GetCharPrm(INF_ASCII_BREAK));
< ответ IDA:
0x0
G копия экрана IDA:
.rdata:00407384 aRuntimeErrorPr db 'Runtime Error!',OAh,OAh,'Program: ',0
INF_COMMENT
Это однобайтовое поле содержит величину отступа, которым IDA предваряет все комментарии. По умолчанию равно 40. Может быть изменено по вкусу пользователя как интерактивно ("~Options\Text representation\Comments indention"), так и с помощью функции SetCharPrm(INF_COMMENT, nn).

INF_COMMENT == 40 | INF_COMMENT == 0
SetCharPrm (INF_COMMENT, 40) ; | SetCharPrm ( INF_COMMENT, 0) ;
segOOO: 22EO mov ah, 4Ah; 'J' segOOO: 22E2 mov bx, 753h segOOO: 22E5 int 21h ; DOS - 2+ - ADJU segOOO: 22E5 ; ES = segment ad segOOO :22E5 ; BX = new size i | segOOO: 22EO mov ah, 4Ah; 'J' segOOO: 22E2 mov bx, 753h segOOO: 22E5 int 21h;DOS - 2+ - ADJUST MEMORY BLOCK segOOO: 22E5; ES = segment address of block to change segOOO: 22E5; BX = new size in paragraphs | S


IDA - 0423.htm

Глобальные настройки 423
lNF_INDENT
Это однобайтовое поле содержит величину отступа, которым IDA предваря-
ет всe инструкции в дизассемблируемом листинге.

INF_INDENT == 0x10 |  | INF_INDENT == 0
SetCharPrm (INF_INDENT, 0x10) ; |  | SetCharPrm (INF_INDENT, 0x0 );
segOOO :22CO |  | segOOO: 22CO public start
segOOO: 22CO |  | segOOO: 22CO start proc near
segOOO: 22CO | public start | segOOO: 22CO call sub_0_22DD
segOOO: 22CO | start proc near | segOOO: 22C3 call sub_0_2325
segOOO: 22CO | call sub_0_22DD | segOOO: 22C6 call sub_0_235B
segOOO: 22C3 | call sub_0_2325 | segOOO: 22C9 call sub_0_2374
segOOO: 22C6 | call sub_0_235B | segOOO: 22CC call sub_0_23B6
segOOO: 22C9 | call sub_0_2374 | segOOO: 22CF call sub_0_23F8
segOOO: 22CC | call sub_0_23B6 | segOOO: 22D2 jnz loc_0_22DA
segOOO: 22CF | call sub_0_23F8 |
segOOO: 22D2 | jnz loc_0_22DA |
По умолчанию отступ равен 10, однако это значение можно изменять, форматируя листинг по своему вкусу. Для этого необходимо воспользоваться функцией SetCharPrm(INF_INDENT, nn) или интерактивно через меню "~Opti-ons\Text representation\Instructions indention".
IDA - 0423-1.jpg
INF_XREFNUM
Это однобайтовое поле хранит максимально возможное число перекрестных ссылок, которые IDA будет отображать в виде комментариев к инструкции. По умолчанию равно двум.
При этом, если остальные ссылки не отображаются, IDA сигнализирует об их наличии двумя точками, стоящими за последней отображаемой перекрестной ссылкой.


IDA - 0424.htm

424 Глобальные настройки

INF_XREFNUM == 2 | INF_XREFNUM == 4
SetCharPrm (lNF XREFNUM,2); | SetCharPrm (lNF XREFNUM,4);
.text: 00403868 dword_0_403868 dd 5E08458Bh ;CODE XREF: .text:00403745j | .text: 00403868 dword_0_403868 dd 5E08458Bh ; CODE XREF: .text: 00403745 j
 | .text: 00403868 .text:00403858o
Поле может быть изменено как интерактивно ("~Options\ Cross references\ Number
of xrefs to display"), так и с помощью функции SetCharPrm(INF_XREFNUM, xx).
IDA - 0424-1.jpg
INF_ENTAB
Это однобайтовое поле управляет генерацией выходных файлов. Если его значение равно единице, то IDA будет при форматировании использовать символы табуляции. В противном случае все отступы будут выполнены пробелами.
Табуляция позволяет значительно, иногда в два и более раз уменьшить размер файлов. Однако некоторые редакторы и средства просмотра могут неправильно интерпретировать (или вовсе игнорировать) символы табуляции. В этих случаях рекомендуется сбрасывать флаг INF_ENTAB (по умолчанию он установлен). Это можно сделать как интерактивно (~Options\Text representation\Use tabulations in output), так и с помощью следующего вызова функции:
SetCharPrm(INF ENTAB,0);

INF_ENTAB == 1 | INF_ENTAB == 0
SetCharPrm (lNF ENTAB,1); | SetCharPrm (lNF ENTAB,0);
an оор г ос near>g оос al 1 os u b_Q_2 2 D D^gooca1 1 osub_CL. | public start jflst art pr ос near-hg call sub_0_22DD
INF_VOIDS
Это однобайтовое поле содержит флаг, указывающий IDA, выводить ли
после всех непосредственных операндов, "похожих" на смещение (т.е. попадаю-


IDA - 0425.htm

Глобальные настройки 425
щих в интервал INF_LOW_OFF и INF_HIGH_OFF), комментарий "void", сигнализирующий пользователю, что тип не был определен автоматически и должен быть уточнен вручную.
По умолчанию этот флаг сброшен, потому что IDA и без комментариев привлекает внимание к операндам, выделяя их красным цветом. Однако это невозможно осуществить в выходных файлах (ASM и LST), поэтому в этом случае рекомендуется устанавливать флаг INF_VOIDS. Это можно сделать как интерактивно (~Options\ Text representation\ Display 'void' marks), так и с помощью вызова функции SetCharPrm.

INF_VOIDS == 0 |  | INF_VOIDS == 1
SetCharPrm (INF_VOIDS, 0) ; |  | SetCharPrm (INF_VOIDS, 0) ;
mov call mov call | si, 2C51h sub_0_DD si, 2C4Dh sub_0_2E2 | mov call mov call | si, 2C51h ; <void> sub_0_DD si, 2C4Dh ; <void> sub_0_2E2
INF_SHOWAUTO
Это однобайтовое поле содержит флаг, управляющий индикатором автоанализа. По умолчанию он установлен. Если возникнет необходимость, то его можно отключить "~Options\ Analysis options\ Indicator enabled" или вызовом функции SetCharPrm.

INF_SHOWAUTO == 1 | INF_SHOWAUTO == 0
SetCharPrm ( INF_SHOWAUTO, 1 ) ; | SetCharPrm ( INF_SHOWAUTO, 0) ;
AU:llJidlel|j READY | READY
Индикатор может принимать следующие значения:

Вид | Значение
AU: _ idle _ | Автоанализ завершен
AU: disable | Автоанализ выключен
FL: <адрес> | Трассировка порядка исполнения
РR:<адрес> | По указанному адресу была создана | функция
АС: <адрес> | Указатель на текущее положение анализатора
LL: <номер> | Был загружен файл сигнатур
адрес> | Первый проход FLIRT
L2 : <адрес> | Второй проход FLIRT
L3 : <адрес> | Третий проход FLIRT
Р1:<адрес> | Заключительный проход автоанализа
??:<адрес> | Байт по указанному адресу помечен | как unexplored
@ : <номер> | Индикатор различных действий


IDA - 0426.htm

426 Глобальные настройки
INF_AUTO
Это однобайтовое поле содержит флаг, управляющий автоанализом (автоматическим анализом программы). Именно такой режим работы установлен по умолчанию.
Отключать его следуют только в тех случаях, когда результат работы автоматического анализатора не устраивает или вызывает "подвисание" дизассемблера. Такое часто случается с файлами, полученными с помощью ProcDump и подобных утилит.
Сделать это можно как интерактивно (~Options\ Background analysis\Analy-sis enabled), так и вызовом функции SetCharPrm(INF_AUTO,0);
IDA - 0426-1.jpg
INF_BORDER
Это однобайтовое поле хранит флаг, управляющий вставкой линий, разделяющих код и данные в дизассемблере. Значительно улучшает читабельность листинга, поэтому по умолчанию IDA ведет себя именно так.
С другой стороны, дополнительные линии уменьшают число значащих строк, умещающихся на дисплее, а также приводит к излишнему перерасходу бумаги при выводе дизассемблированного текста на принтер, поэтому в этих случаях данную опцию следует отключить вызовом функции SetCharPrm(INF_BORDER,0) или интерактивно ~Options\ Text representation \ Display borders between data/code.

INF_BORDER == 1 |  |  |  | INF_BORDER == 0
SetCharPrm (lNF BORDER, 1); |  |  |  | SetCharPrm |  | (lNF_ BORDER, 0) ;
.text | 00402507 |  |  | .text | 00402507 | jmp | ds:off_0_402630[edx | *4]
.text | 0040250Е | align | 4 | .text | 0040250E | align | 4 |
.text | 00402510 |  |  | .text | 00402510 |  |  |
.text | 00402510 | loc_0_402510: |  | .text | 00402510 loc_0_402510:
.text | 00402510 |  |  | .text | 00402510 |  |  |
.text | 00402510 | neg | ecx | .text | 00402510 | neg | ecx |
.text | 00402512 | jmp | ds:off_0_4025EO[ecx*4] | .text | 00402512 | jmp | ds:off_0_4025EO[ecx | *4]
text | 00402512 |  |  | text | 00402519 |  |  |
 |  |  |  | .text | 0040251C |  |  |
INF_NULL
Это однобайтовое поле хранит флаг, управляющий генерацией пустых строк, вставляемых дизассемблером в различных местах для улучшения читабельности листинга. Однако в ряде случаев эту возможность следует отключать


IDA - 0427.htm

Глобальные настройки 427
(например, при выводе текста на печать). Для этого следует воспользоваться вызовом SetCharPrm(INF_NULL,0) или сбросить флажок ~Options\ Text representation \ Display empty lines.

INF_NULL == 1 |  |  |  | INF_NULL |  |  |  | == 0 |
SetCharPrm (lNF |  |  | _NULL, 1) ; | SetCharPrm |  | (INF_ | _NULL, | 0) ; |
 | 00402507 |  |  |  | 00402507 |  |  |  |
.text | 0040250E |  | align 4 | .text | 0040250E |  |  | align | 4
.text | 00402510 |  |  | .text | 00402510 | loc_0_ | 402510: |  |
.text | 00402510 | loc 0 | 402510: | .text | 00402510 |  |  |  |
.text | 00402510 |  |  | .text | 00402510 |  |  | neg | ecx
.text | 00402510 |  | neg ecx |  |  |  |  |  |
INF_SHOWPREF
Это однобайтовое поле хранит флаг, который управляет выводом префиксов
в дизассемблируемом листинге. Префикс — это адрес текущего байта.
Пример префикса:
.text:004024AC pop edi
По умолчанию этот флаг установлен, и каждая линия предваряется префиксом. Многоточечные структуры (например, массивы) в каждой строке содержат адрес своего первого элемента.
Например:
.text:004023CO dword_0_4023CO dd 68AD123h, 468A0788h,OC102468Ah
.text:004023CO dd 3C68302h, 8303C783h,OCC7208F9h
.text:004023CO dd 3498DOOh
Независимо от значения флага INF_SHOWPREF в ассемблерный листинг (*.asm файл) префиксы не попадают.
Если по какой-то причине генерацию префиксов необходимо отключить, то это можно сделать с помощью вызова функции SetCharPrm(INF_SHOWPREF,0) или интерактивно ~ Options\Text representation\Line prefixes.

INF_SHOWPREF == 1 | INF_SHOWPREF == 0
SetCharPrm (lNF SHOWPREF,1); | SetCharPrm ( INF_SHOWPREF, 0 ) ;
.text:004024AC pop edi .text:004024AC leave .text:004024AC retn | pop edi leave retn
lNF_PREFSEG
Это однобайтовое поле содержит флаг, управляющий выводом имени сегмента в префиксе строки. По умолчанию флаг установлен, и вместо полного адреса выводится имя сегмента.
Если возникнет необходимость видеть полный адрес, то этот флаг можно сбросить. Сделать это можно либо интерактивно "" Options \ Text representation \ Use segment names", либо вызовом функции SetCharPrm(INF_PREFSEG,0).


IDA - 0428.htm

428 Глобальные настройки
При этом листинг будет выглядеть так:

INF_PREFSEG == 1 | INF_PREFSEG == 0
SetCharPrm (INF_PREFSEG, 1) ; | SetCharPrm (lNF PREFSEG, 0);
.text:0040100F xor eax, eax | 0000:0040100F xor eax, eax
INF_ASMTYPE
Это однобайтовое поле хранит номер, начиная с нуля, задающий целевой
ассемблер. Для PC всегда равно нулю и указывает на "Generic for Intel 80x86".
Пример:
> ввод пользователя:
Message("%x \n",GetCharPrm(INF_ASMTYPE));
< ответ IDA:
0
INF_BASEADDR
Это длинное поле хранит базовый параграф программы.
Пример:
> ввод пользователя:
Message ("%x \n",GetLongPrm(INF_BASEADDR) ) ;
< ответ IDA:
1000
lNF_XREFS
Это однобайтовое поле управляет представлением перекрестных ссылок в дизассемблируемом листинге. Может быть представлено комбинацией следующего набора битовых флагов:

IDA - 0428-1.jpg
SW_SEGXRF (0x01)
Установка этого флага приводит к тому, что IDA будет указывать полный адрес, включая сегмент, в перекрестных ссылках (по умолчанию).
Интерактивно этим значением можно управлять "~ Options \ Cross-reference representation \ Display segments in xrefs".


IDA - 0429.htm

Глобальные настройки 429

SW_SEGXRF == 1 | SW_SEGXRF == 0
SetLongPrm (INF_XREF, SW_SEGXRF) ; | SetLongPrm (INF_XREF, !SW_SEGXRF)
DATA XREF: . rdata: 004070COo | DATA XREF: 004070COo
SW_XRFMRK (0x02)
Установка этого флага приводит к тому, что IDA уточняет тип перекрестной ссылки, — представляет ли источник собой код или данные.
Интерактивно этим значением можно управлять "^ Options \ Cross-reference representation \ Display xref type mark".

SW_XRFMRK == 1 | SW_XRFMRK == 0
SetLongPrm (INF_XREF, SW_XRFMRK) ; | SetLongPrm (lNF XREF, !SW XRFMRK)
DATA XREF:. rdata: 004070COo | XREF: 004070COo
SW_XRFFNC (0x04)
Установка этого флага приводит к тому, что IDA выражает адрес ссылки через смещение относительно начала ближайшей функции.
Интерактивно этим значением можно управлять "~ Options \ Cross-reference representation \ Display function offsets".

SW_XRFFNC = | = 1 | SW_XRFFNC == 0
SetLongPrm ( lNF XREF, SW | XRFFNC) ; | SetLongPrm (lNF XREF, ! SW XRFFNC)
CODE XREF: start+AFp |  | CODE XREF: 004010CFp
SW_XRFVAL (0x08)
Установка этого флага приводит к тому, что IDA отображает значение перекрестной ссылки в дизассемблируемом листинге. В противном случае его заменят три точки.

SW_XRFVAL == 1 | SW_XRFVAL == 0
SetLongPrm (INF_XREF, SW_XRFVAL) ; | SetLongPrm (INF_XREF, !SW_XRFFVAL)
CODE XREF: 004010CFp | CODE XREF: ...
INF_BINPREF
Это короткое поле задает число байт, отображающих шестнадцатиричный оп-код инструкции. По умолчанию равно нулю, т.е. IDA дамп не отображает. Однако в ряде случаев потребность в нем все же возникает, кроме того, для кого-то это может быть вопрос удобства или привычки.
Можно воспользоваться вызовом SetShortPrm(INF_BINPREF,OxlO) или изменить значение поля интерактивно "~ Options \ Text representation \ Number of opcode bytes".


IDA - 0430.htm

430 Глобальные настройки

INF_BINPREF == 0 | INF_BINPREF == 0x10
SetShortPrm (lNF BINPREF, 0) ; | SetShortPrm (lNF BINPREF, 0x10) ;
.text: 00401000 sub_0_401000 proc near .text: 00401000 push offset aHelloSailor .text: 00401005 mov ecx, offset dword_0_408900 .text:0040100A call ??6ostream@@QAEAAVO@PBD@Z .text:0040100F xor eax, eax .text:00401011 retn .text: 00401011 sub_0_401000 endp | .text: 00401000 sub_0_401000 proc near .text: 00401000 68 40 80 40 00 push offset HeloSailor .text: 00401005 B9 00 89 40 00 mov ecx, offset dword_408900 .text:0040100A E8 72 2B 00 00 call ostream@@QAEAAVO@PBD@Z .text:0040100F 33 CO xor eax, eax .text: 00401011 C3 Ketn .text: 00401011 sub_0_401000 endp
INF_CMTFLAG
Это короткое поле содержит набор флагов, манипулирующих выводом и представлением комментариев в дизассемблируемом листинге.
SW_RPTCMT
Этот флаг управляет выводом повторяемых комментариев. По умолчанию установлен. Если возникнет необходимость отключить генерацию повторяемых комментариев, то это можно сделать как вызовом функцией SetShortPrm, так и интерактивно "~Options\ Text representation \ Display repeatable comments".

SW_RPTCMT == 1 | SW_RPTCMT == 0
SetShortPrm (lNF CMTFLAG, SW RPTCMT); | SetShortPrm (lNF CMTFLAG, !SW RPTCMT)
Jb short near ptr dword_4023AC ; repeatable comment | Jb short near ptr dword_4023AC
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) пор ; No Operation | call jnb пор | sub_0_2E2 loc_0_2321 |
SW_NOCMT
При установке этого флага IDA не будет отображать комментарии независимо
от состояния остальных настроек. По умолчанию флаг сброшен.
SW_LINNUM
При установке этого флага IDA, при наличии необходимой отладочной инфор-
мации в файле, будет отображать номера строк исходного текста программы.


IDA - 0431.htm

Глобальные настройки 431
INF_NAMETYPE
Это короткое поле содержит флаг, управляющий представлением автогенерируемых имен (в терминологии IDA — dummy names). Эти имена автоматически присваиваются всем созданным меткам и процедурам.

Флаг | Значение | Пояснения
NM_REL_OFF | 0 | Относительная база сегмента и полное смещение
 |  | loc_0_1234
NM_PTR_OFF | 1 | Базовый адрес сегмента и смещение
 |  | loc_1000_1234
NM_NAM_OFF | 2 | Имя сегмента и смещение (по умолчанию)
 |  | loc_dseg_1234
NM_REL_EA | 3 | Сегмент, относительно базового адреса, и полный адрес
 |  | loc_0_11234
NM_PTR_EA | 4 | Базовый адрес сегмента и полный адрес
 |  | loc_1000_11234
NM_NAM_EA | 5 | Имя сегмента и полный адрес
 |  | loc_dseg_11234
NM_EA | 6 | Полный адрес (без нулей слева)
 |  | loc_12
NM_EA4 | 7 | Полный адрес (не менее четырех знаков)
 |  | loc_0012
NM_EA8 | 8 | Полный адрес (не менее восьми знаков)
 |  | loc_00000012
NM_SHORT | 9 | Имя сегмента и смещение без спецификатора типа
 |  | dseg_1234
NM_SERIAL | 10 | Перечисленные имена (1,2,3...)
 |  | loc_l
INF_SHOWBADS
Это однобайтовое поле, будучи установленным, приводит к тому, что IDA будет оставлять в виде дампа все инструкции, которые могут быть неправильно ассемблированы. Например, в исследуемой программе могут встретиться недокументированные команды процессора (подробнее с ними можно ознакомиться на сайте www.x86.org).
Разумеется, что распространенные ассемблеры выдадут ошибку и прекратят работу, однако это не худшая ситуация. Множество команд процессоров 80x86 могут быть ассемблированы по-разному. Например, ADD bx, 0x10 может быть представлена как опкодом 81 C3 01 00, так и 83 C3 10.
Разница здесь в том, что последняя команда добавляет к BX байт, автоматически расширяя его до слова с учетом знака. Следовательно, возникает неодно-


IDA - 0432.htm

432 Глобальные настройки
значность, часто приводящая к неработоспособности программы. Даже если не использовался самомодифицирующийся код, изменение длины инструкции "потянет" за собой все метки и абсолютные адреса в программе. Впрочем, при условии правильного преобразования типов непосредственных операндов это не нарушит работоспособность программы, поэтому по умолчанию эта опция отключена.
Если же в ней возникнет необходимость, то нужно воспользоваться функцией SetCharPrm(INF_SHOWBADS,l) или интерактивно ~Options \ Text representation \ Display bad instructions <BAD> marks.

INF_SHOWBADS == 1 | INF_SHOWBADS == 0
SetCharPrm(INF_SHOWBADS, 1 ) | SetCharPrm(INF_SHOWBADS,0)
segOOO: 0220 db OE9h,0,0 ; <BAD> jmp $+3 | segOOO: 0220 jmp $+3
Замечание. Часть инструкций IDA не помечает как <BAD>, хотя их отказыва-
ются ассемблировать распространенные ассемблеры.
Например, команду JMP FAR segment:offset IDA, в отличие от большинства
ассемблеров, не считает неправильной, и в результате приходится вручную
менять ее в листинге на последовательность:
DB OEah
DW offset
DW segment
INF_PREFFLAG
Это однобайтовое поле хранит флаг, задающий формат вывода префикса на экран. Имеет смысл только тогда, когда вывод префиксов разрешен (т.е. флаг INF_SHOWPREF установлен).
Флаг представляет собой комбинацию из трех битов (см. таблицу ниже). Причем бит PREF_FNCOFF имеет приоритет над PREF_SEGADR, следовательно, комбинация (PREF_FNCOFF | PREF_SEGADR) равносильна PREF_FNCOFF. Однако это не документировано и, возможно, в последующих версиях IDA будет вести себя иначе.
Интерактивно это поле можно изменить с помощью следующих опций диалога настройки:
• Options \ Text representation \ Segment addresses;
• Options \ Text representation \ Function offsets;
• Options \ Text representation \ Display stack pointer.

Флаг | Значение |  | Пример
PREF_SEGADR | 0x01 | Представлять префикс в виде сегментного адреса | segOOO: 2190
PREF_FNCOFF | 0x02 | Представлять префикс в виде смеще- ния внутри функции | Sub_0_22DD+1B
PREF STACK | 0x04 | Завершать префикс указателем стека | SegOOO: 2190 008
 |  |  | Sub_0_22DD+1B 008


IDA - 0433.htm

Глобальные настройки 433
INF_PACKBASE
Это однобайтовое поле хранит тип упаковки базы IDA, предлагаемый по
умолчанию при выходе из дизассемблера.
IDA - 0433-1.jpg
INF_PACKBASE == 0 | INF_PACKBASE == 1 | INF_PACKBASE == 2
SetCharPrm (lNF PACKBASE,0); | SetCharPrm (lNF PACKBASE,1); | SetCharPrm (INF_PACKBASE, 2) ;
(•) Don' t pack database ( ) Pack database (Store) ( ) Pack database (Deflate) | ( ) Don' t pack database (•) Pack database (Store) ( ) Pack database (Deflate) | ( ) Don' t pack database ( ) Pack database (Store) (•) Pack database (Deflate)
Замечание. Оба типа упаковки обладают слабым сжатием, поэтому при возникновении потребности в дисковом пространстве или для передачи базы по коммуникационным каналам рекомендуется ее упаковать с помощью любого подходящего архиватора (например, zip, arj). При этом выигрыш может быть более чем десятикратный.
INF_ASCIIFLAGS
Это однобайтовое поле задает стиль автогенерируемых строковых имен (или вообще запрещает создание таковых).
Представляет собой комбинацию следующих флагов (битов).
ASCF_GEN
Если этот флаг установлен, то IDA автоматически генерирует имена для всех ASCII-строк, состоящие из читабельных символов этой строки.
Так, например, встретив строку "Hello, Word", IDA создаст имя "aHello__Word", в противном случае — что-то наподобие "asc_0_206". Разумеется, что генерация осмысленных имен улучшает читабельность листинга и ускоряет анализ.
По умолчанию IDA ведет себя именно так. Если по какой-то причине возникнет желание отключить эту возможность, то можно воспользоваться функцией SetCharPrm (INF_ASCIIFLAG, !ASCF_GEN) или интерактивночерез "~ Options \ ASCII string options \ Generate names".

ASCF_GEN == 1 | ASCF_GEN == 0
SetCharPrm (lNF ASCIIFLAGS, ASCF GEN); | SetCharPrm (lNF ASCIIFLAGS, !ASCF_GEN) ;
segOOO: 2192 a123456789abcde db ' 123456789ABCDEFG' , 0 | segOOO: 2192 db ' 123456789ABCDEFG' , 0


IDA - 0434.htm

434 Глобальные настройки
ASCF_AUTO
Этот флаг, будучи установленным, приводит к тому, что IDA будет помечать все создаваемые имена как 'autogenerated'.
Это приведет к отображению их другим цветом и автоматическому удалению при преобразовании имени к 'unexplored'.
По умолчанию флаг установлен. Если возникнет необходимость его изменить, то это можно сделать с помощью функции SetCharPrm(INF_ASCII-FLAGS,!ASCF_AUTO) или интерактивно "~Options \ ASCII stringoptions \ Mark as autogenerated".

 | ASCF_AUTO | == 1 |  |  | ASCF_AUTO == 0
segOOO | :2192 | db | 31h | 1 | segOOO | :2192 | db | 31h | 1
segOOO | :2193 | db | 32h | 2 | segOOO | :2193 | db | 32h | 2
segOOO | :2194 | db | 33h | 3 | segOOO | :2194 | db | 33h | 3
segOOO | :2195 | db | 34h | 4 | segOOO | :2195 | db | 34h | 4
segOOO | :2196 | db | 35h | 5 | segOOO | :2196 | db | 35h | 5
segOOO | :2197 | db | 36h | 6 | segOOO | :2197 | db | 36h | 6
segOOO | :2198 | db | 37h | 7 | segOOO | :2198 | db | 37h | 7
segOOO | :2199 | db | 38h | 8 | segOOO | :2199 | db | 38h | 8
segOOO | :219A | db | 39h | 9 | segOOO | :219A | db | 39h | 9
segOOO | :219B | db | 41h | A | segOOO | :219B | db | 41h | A
segOOO | :219C | db | 42h | B | segOOO | :219C | db | 42h | B
segOOO | :219D | db | 43h | С | segOOO | :219D | db | 43h | С
segOOO | :219E | db | 44h | D | segOOO | :219E | db | 44h | D
segOOO | :219F | db | 45h | E | segOOO | :219F | db | 45h | E
segOOO | :21AO | db | 46h | F | segOOO | :21AO | db | 46h | F
segOOO | :21A1 | db | 47h | G | segOOO | :21A1 | db | 47h | G
segOOO | :21A2 | db | 0 |  | segOOO | :21A2 | db | 0 |
Создается имя
segOOO: 2192 a123456789abcde db ' 123456789ABCDEFG |  |  |  | ,0 | segOOO: 2192 a123456789abcde db ' 123456789ABCDEFG' , 0
Преобразуем регион в unexplored
segOOO | :2192 | db | 31h | 1 | segOOO | :2192 a!23456789abcde | db | 31h | 1
segOOO | :2193 | db | 32h | 2 | segOOO | :2193 | db | 32h | 2
segOOO | :2194 | db | 33h | 3 | segOOO | :2194 | db | 33h | 3
segOOO | :2195 | db | 34h | 4 | segOOO | :2195 | db | 34h | 4
segOOO | :2196 | db | 35h | 5 | segOOO | :2196 | db | 35h | 5
segOOO | :2197 | db | 36h | 6 | segOOO | :2197 | db | 36h | 6
segOOO | :2198 | db | 37h | 7 | segOOO | :2198 | db | 37h | 7
segOOO | :2199 | db | 38h | 8 | segOOO | :2199 | db | 38h | 8
segOOO | :219A | db | 39h | 9 | segOOO | :219A | db | 39h | 9
segOOO | :219B | db | 41h | A | segOOO | :219B | db | 41h | A
segOOO | :219C | db | 42h | B | segOOO | :219C | db | 42h | B
segOOO | :219D | db | 43h | С | segOOO | :219D | db | 43h | С
segOOO | :219E | db | 44h | D | segOOO | :219E | db | 44h | D
segOOO | :219F | db | 45h | E | segOOO | :219F | db | 45h | E
segOOO | :21AO | db | 46h | F | segOOO | :21AO | db | 46h | F
segOOO | :21A1 | db | 47h | G | segOOO | :21A1 | db | 47h | G
segOOO | :21A2 | db | 0 |  | segOOO | :21A2 | db | 0 |


IDA - 0435.htm

Глобальные настройки 435
ASCF_SERiAL
Если этот флаг будет установлен, то IDA будет генерировать следующие (в терминологии IDA последовательные) имена 'prefO','preflYpref2'..., где 'pref — префикс имени, который для строк по умолчанию равен 'a'.
По умолчанию этот флаг сброшен, но если возникнет необходимость, то его можно установить с помощью функции SetCharPrm (или интерактивно "~Opti-ons \ ASCII string options\ Generate serial names".

ASCF_SERIAL == 1 | ASCF_SERIAL == 0
SetCharPrm (lNF ASCIIFLAGS, ASCF_SERIAL); | SetCharPrm (INF_ASCI !FLAGS, !ASCF_SERIAL) ;
segOOO: 2192 aO db ' 123456789ABCDEFG' , 0 | segOOO: 2192 a123456789abcde db ' 123456789ABCDEFG' , 0
INF_LISTNAMES
Это однобайтовое поле содержит атрибуты имен, автоматически включае-
мых в Список Имен (Name List).
IDA - 0435-1.jpg
LN_NORMAL | 0x0 Г | Имя без атрибутов (по умолчанию)
LN_PUBLIC | 0x02 | Имя с атрибутом public
LN_AUTO | 0x04 | Автогенерируемое имя
LN_WEAK | 0x08 | Имя с атрибутом weak
INF_START_SS
Это длинное поле хранит значение регистра SS при запуске программы. Для того, чтобы его получить, IDA прибегает к эмуляции загрузки и действует в соответствии с исследуемым типом файла и декларированными правилами загрузки его операционной системой.
Однако это не гарантирует, что полученное значение будет тождественно действительному. Впрочем, такая точность на практике и не требуется. В крайнем случае можно принудительно указать требуемый базовый адрес загрузки или изменить непосредственно само значение INF_START_SS.
Пример:
> ввод пользователя:
Message("Ox%x \n",GetLongPrm(INF_START_SS) ) ;
< ответ IDA:
0x1000


IDA - 0436.htm

436 Глобальные настройки
> ввод пользователя:
SetLongPrm (INF_START__SS, 0) ;
Message("Ox%x \n",GetLongPrm(INF_START_SS));
< ответ IDA:
0
INF_START_CS
Это длинное поле хранит значение регистра CS при запуске программы. Для того, чтобы его получить, IDA прибегает к эмуляции загрузки и действует в соответствии с исследуемым типом файла и декларированными правилами загрузки его операционной системой.
Пример:
> ввод пользователя:
Message ("Ox%x \n",GetLongPrm(INF_START__CS) ) ;
< ответ IDA:
0x1000
lNF_MAIN
В файле определений IDC.IDC сообщается, что это длинное поле содержит адрес процедуры main(), однако при попытке его чтения всегда возвращается ошибка BADADDR.
Например:
> ввод пользователя:
Message("Ox%X \n",GetLongPem(INF_MAIN));
< ответ IDA:
0xFFFFFFFF
INF_SHORT_DN
Это длинное поле хранит указатель на короткую форму вывода "замангленных" имен. Назначение отдельных битов не описано в файле 'idc.idc', в котором содержится ссылка на 'demangle.hpp', входящий в состав IDA SDK.

Флаг | Бит | Значение
MNG_DEFNEAR | 0x00000000 | Подавлять в именах ключевое слово near
MNG_DEFFAR | 0x00000002 | Подавлять в именах ключевое слово far
MNG_DEFHUGE | 0x00000004 | Подавлять в именах ключевое слово huge
MNG_DEFNONE | 0x00000006 | Выводить все
MNG_NODEFINIT | 0x00000008 | Подавлять вывод всего, кроме основного имени
MNG_NOUNDERSCORE | 0x00000010 | Подавлять вывод подчерков для _ ccall, _ pascal...
MNG_NOTYPE | 0x00000020 | Подавлять вывод типа параметров
MNG_NORETTYPE | 0x00000040 | Подавлять вывод типа возвращаемого функцией значения
MNG_NOBASEDT | 0x00000080 | Подавлять вывод базовых типов


IDA - 0437.htm

Глобальные настройки 437

Флаг | Бит | Значение
MNG_NOCALLC | 0x00000100 | Подавлять вывод слов _ pascal \ _ ccall и подобных
MNG_NOPOSTFC | 0x00000200 | Подавлять вывод постфиксного const
MNG_NOSCTYP | 0x00000400 | Подавлять вывод ключевых слов public \ private \ protected
MNG_NOTHROW | 0x00000800 | Подавлять вывод описания throw
MNG_NOSTVIR | 0x00001000 | Подавлять вывод ключевых слов static и virtual
MNG_NOECSU | 0x00002000 | Подавлять вывод ключевых слов class \struct\union\ enum
MNG_NOCSVOL | 0x00004000 | Всюду подавлять вывод ключевых слов const и volatile
MNG_NOCLOSUR | 0x00008000 | Подавлять вывод _ closure (для компиляторов Borland)
MNG_SHORT_S | 0x00010000 | Выводить signed (int) в формате s(int)
MNG_SHORT_U | 0x00020000 | Выводить unsigned (int) в формате u(int)
MNG_ZPT_SPACE | 0x00040000 | Выводить пробел после запятой в аргументах функций
MNG_IGN_ANYWAY | 0x00080000 | Игнорировать постфикс '_nn' в конце строки
MNG_IGN_JMP | 0x00100000 | Игнорировать префикс 'j_' в начале строки
MNG_MOVE_JMP | 0x00200000 | Сохранять префикс 'j_' в Размангленных именах
Подробнее об этом можно прочитать в описании функции Demangle.
lNF_LONG_DN
Это длинное поле хранит полный формат вывода "замангленных" имен. Представляет собой комбинацию флагов, назначение которых описано выше.
INF_DATATYPES
Это длинное поле хранит разрешенные к использованию типы данных, — такие, что будут поочередно перебираться, скажем, при нажатии 'D'.
Файл IDC.IDC не содержит расшифровки отдельных битов этого поля, но в интерактивном диалоге настройки (~Options \ Setup date types) типы данных перечислены в порядке следования флагов в этом поле!
IDA - 0437-1.jpg


IDA - 0438.htm

438 Глобальные настройки

Бит | Значение | Бит | Значение
0x1 | Байт | 0x10 | Четверное слово
0x2 | Слово | 0x20 | Double
0x4 | Двойное слово | 0x40 | Tbyte
0x8 | Float | 0x80 | Упакованное real
Пример использования:
> ввод пользователя:
Message("%b \n", GetLongPrm(INF_DATATYPES));
< ответ IDA:
111
INF_STRTYPE
Это длинное поле хранит текущий стиль ASCII-строк. Первый байт представ-
ляет собой один из следующих флагов:

Флаг | Значение
0 | Строка не предварена полем длины
ASCSTR_PASCAL | Стиль | Pascal — строка предваряется | байтом | длины
 | . data: | 00408040 aHelloSailor db OCh, | 'Hello, | Sailor! '
ASCSTR_LEN2 | Стиль | WinPascal — строка предваряется словом длины
 | .data: | 00408040 aHelloSailor dw OCh, | db 'Hello, Sailor! '
ASCSTR_UNICODE | Стиль | UNICODE |  |
 | 'H' ,0, | 'e',0,'l' ,0,'o',0,V ,0,'S',0, | 'a' ,0,1 | i',0,T ,0,'o',0, 'r',0,1!'
ASCSTR_LEN4 | Стиль | Delphi — 4 байта на длину |  |
 | .data: | 00408040 aHelloSailor dw OCh, | dw 0, db 'Hello, Sailor! '
Если первый байт не равен нулю, то, значит, впереди строки находится поле, определяющее его длину. Иначе используется символ конца строки. Его определяют второй и третий байт поля INF_STRTYPE.
Если второй символ будет равен '\0', то он будет проигнорирован, поэтому если необходимо задать два типа завершающих символов (например, ноль и '$'), то '\0' следует указывать первым из них.
Пример:
> ввод пользователя:
Message("%x \n",GetLongPrm(INF_STRTYPE));
< ответ IDA:
0
SetLongPrm(INF_STRTYPE,'$'>>OxlO);


IDA - 0439.htm

Глобальные настройки 439
INF_AF2
Это длинное поле хранит дополнительные флаги анализатора.

Флаг | Бит | Значение
AF2_JUMPTBL | 0x1 | Выявлять и создавать таблицы переходов
AF2_DODATA | 0x2 | Сворачивать сегмент данных в последнем проходе
AF2_HFLIRT | 0x4 | Автоматически скрывать стандартные библиотечные функции (этот флаг не описан в файле idc. idc)
IDA - 0439-1.jpg
success SetPrcsr (char processor)
Функция позволяет изменить тип процессора, отличный от выбранного при загрузке дизассемблируемого файла, однако возможности динамической смены процессора несколько ограничены: допустим выбор лишь в границах текущей (серии) микропроцессоров.
Это объясняется тем, что на стадии загрузки происходят не устранимые средствами последующих уровней изменения и настройки на конкретный процессор. (Большей частью это относится к невозможности IDA перезагружать микропроцессорные модули, отвечающие за дизассемблирование. Такой модуль может быть загружен один раз, и на весь сеанс работы окажутся доступными лишь поддерживаемые им типы микропроцессоров).
Для серии микропроцессоров Intel в силу их полной обратной совместимости динамический выбор модели процессора некритичен, поскольку можно выбрать самый поздний из доступных представителей и быть уверенным, что все команды будут дизассемблированы. Аналогичный результат можно получить, выбрав тип "meta pc", включающий в себя команды всех моделей микропроцессоров.
Рассмотрим работу этой функции на следующем примере. Загрузим для дизассемблирования бинарный или com-файл (для которого IDA по умолчанию выбирает микропроцессор 8086), содержащий инструкции более поздних моделей.
Разумеется, они окажутся недизассемблированными, и результат работы IDA может выглядеть, например, так:
Q копия экрана IDA:
segOOO:02E9 mov ax, ds:413h
segOOO:02EC db OC1h ; -
segOOO:02ED db OEOh ; p
segOOO:02EE db 6 ;
segOOO:02EF cmp ax, OAOOOh
> ввод пользователя:
SetPrcsr ("metapc");


IDA - 0440.htm

440 Глобальные настройки
Q копия экрана IDA:
segOOO:02E9 mov ax, ds:413h
segOOO:02EC shl ax, 6
segOOO:02EF cmp ax, OAOOOh
Смена типа процессора привела к тому, что IDA заново проанализировала изучаемый файл и автоматически дизассемблировала интересующие нас инструкции. При обратной смене (на модель 8086) листинг будет приведен к первоначальному виду.
При этом IDA может изменять целевой ассемблер, поэтому рекомендуется на всякий случай удостовериться в приемлемости ее выбора, вызвав диалог "~Opti-ons \ Target Assembler". Впрочем, для линии IBM PC он имеется в единственном числе — "Generic for Intel 80x86", и беспокоиться нет никакой необходимости.
Операнд processor может принимать значения, перечисленные в таблице. К регистру функция нечувствительна, поэтому 'metapc' и 'MetaPC' задают один и тот же тип процессора. Отбивка справа указывает, что объединяемые микропроцессоры могут динамически выбираться во время дизассемблирования файла.

Операнд | Процессор | Серия
8086 | Intel 8086 | Линейка IBM PC
80286r | Intel 80286 real mode
80286p | Intel 80286 protected mode
80386r | Intel 80386 real mode
80386p | Intel 80386 protected mode
80486r | Intel 80486 real mode
80486p | Intel 80486 protected mode
80586r | Intel Pentium & MMX real mode
80586p | Intel Pentium & MMX prot mode
80686p | Intel Pentium Pro & MMX
k62 | AMD K6-2 with 3DNow!
Р2 | Intel Pentium II
рЗ | Intel Pentium III
athlon | AMD K7
metapc | Дизассемблировать все инструкции IBM PC
8085 | Intel 8085
z80 | Zilog 80 | Линейка Zilog 80
z8 | Zilog 8 | Линейка Zilog 8
860xr | Intel 860 XR | Линейка Intel 860
860xp | Intel 860 XP
8051 | Intel 8051 | Линейка Intel 51


IDA - 0441.htm

Глобальные настройки 441

Операнд | Процессор | Серия
80196 | Intel 80196 | Линейка Intel 80196
m6502 | 6502 |
m65c02 | 65c02 |
64180 | Hitachi HD64180
pdpll | DEC PDF/ 11 | Линейка PDP line
68000 | Motorola MC68000 | Линейка Motorola 680x0
68010 | Motorola MC68010
68020 | Motorola MC68020
68030 | Motorola MC68030
68040 | Motorola MC68040
68330 | Motorola CPU32 (68330)
68882 | Motorola MC68020 with MC68882
68851 | Motorola MC68020 with MC68851
68020ЕХ | Motorola MC68020 with both
6800 | Motorola MC6800 | Линейка Motorola 8bit
6801 | Motorola MC6801
6803 | Motorola MC6803
6301 | Hitachi HD 6301
6303 | Hitachi HD 6303
6805 | Motorola MC6805
6808 | Motorola MC6808
6809 | Motorola MC6809
6811 | Motorola MC6811
Java | Java | Серия Java
ррс | PowerPC | Линейка PowerPC
arm710a | ARM 7xx серия | Линейка ARM
arm | To же самое, что и arm710a
armb | ARM big endian
tms320c2 | TMS320C2x серия | Серия TMS 16bit адресации
tms320c5 | TMS320C5x серия | Линейка TMS VLIW 1
tms320c6 | TMS320C6x серия
sh3 | Hitachi SH3 (little endian) | Hitachi SH line
sh3b | Hitachi SH3 (big endian)
sh4 | Hitachi SH4 (little endian)
sh4b | Hitachi SH4 (big endian)
avr | ATMEL AVR | Серия ATMEL
mipsl | MIPS little endian | Линейка MIPS: R2000, 3000, R4000,R4200, R4300, 4400, R4600, R8000, R 10000
mipsb | MIPS big endian
mipsr | MIPS & RSP


IDA - 0442.htm

442 Глобальные настройки

Операнд | Процессор | Серия
h8300 | H8/300x in normal mode | Hitachi H8 line
h8300a | H8/300x in advanced mode
h8s300 | H8S in normal mode
h8s300a | H8S in advanced mode
pic!6cxx | Michrochip PIC | Серия микроконтроллеров
Все вышесказанное остается верным и для интерактивного выбора типа
процессора посредством команды меню "~Options \ Processor type".
IDA - 0442-1.jpg
При попытке смены типа процессора 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 | Процессорный | модуль | для | О5\2-версии дизассемблера
dll | Процессорный | модуль | ДЛЯ | MS DOS-версии дизассемблера
w32 | Процессорный дизассемблера | модуль | ДЛЯ | Windows 95\Windows NT-версий
Четвертая версия IDA в полной поставке включает в себя следующие файлы:

Файл | Семейство процессоров
ARM | Семейство ARM (серия ARM 7xx)
AVR | Линейка чипов ATMEL AVR
Н8 | Линейка чипов Hitachi H8 (H8/300x и | H8S серии)
1196 | Микропроцессор Intel 80196
151 | Микропроцессор Intel 8051


IDA - 0443.htm

Глобальные настройки 443

Файл | Семейство процессоров
I860 | Микропроцессор Intel 860 XR
JAVA | Java Virtual Machine
М65 | Микропроцессоры серии 65xx
МС8 | Семейство 8-разрядных микропроцессоров фирмы Motorola (MC6800, MC6801, MC6803, MC6805, MC6808, MC6809, MC6811)
 | Семейство 8-разрядных микропроцессоров фирмы Hitachi (HD 6301, HD 6303)
МС68 | Микропроцессоры серии Motorola 680x0
PC | Микропроцессоры линейки IBM PC
POP 11 | DEC PDP-11
PIC | Микроконтроллеры Microchip серий PIC16C5x PIC16Cxx PIC17Cxx
Z8 | Микропроцессоры линейки Zilog 8
Z80 | Микропроцессоры линейки Zilog 80
Сравнивая эту таблицу с приведенным выше перечнем поддерживаемых IDA процессоров, можно заметить, что часть из них в поставку не входит.
С другой стороны, если вам не нужно дизассемблировать ничего, кроме программ для IBM PC, то все остальные модули можно удалить, освободив немного дискового пространства.
- Если в заголовке загружаемого файла отсутствует информация о типе процессора, то IDA выбирает его, руководствуясь расширением. Соответствие расширений и типов микропроцессора перечислены в файле IDA.CFG в секции DEFA-ULT_PROCESSOR:

Расширение | Тип процессора | Расширение | Тип процессора
"com" | "8086" | "obj" | "metapc"
"exe" | "metapc" | "ргс" | "68000" (PalmPilot-программы)
"dll" | "metapc" | "axf" | "arm710a"
"drv" | "metapc" | "h68" | "68000" (MC68000 для *.Н68-файлов)
"sys" | "metapc" | "i51" | "8051" (i8051 для *.151 файлов)
"bin" | "metapc" | "sav" | "pdpll" (PDP-11 для *.SAV-файлов)
"ovl" | "metapc" | "гот" | "z80" (для *.ROM-файлов)
"ovr" | "metapc" | "class" | "Java"
"ov?" | "metapc" | "ds" | "Java"
"nlm" | "metapc" | "sl9" | "6811"
"lan" | "metapc" | «#» | "metapc"
"dsk" | "metapc" |  |
long Batch (long batch)
Функция позволяет устанавливать (или отменять) пакетный режим работы. В этом режиме IDA не выводит диалоговых окон и не выдает предупреждений. Это может быть полезным при автономной работе и во время выполнения скриптов.


IDA - 0444.htm

444______________Глобальные настройки________________
В версии IDA 4.0 присутствуют некоторые ошибки в реализации пакетного режима. Так, например, попытка вызова калькулятора вызовет зависание IDA, поэтому пользоваться им следует с осторожностью и всегда обращать внимание, чтобы скрипты, использующие его, возвращались в обычный режим при возрате вГОА.

==batch | Режим
0 | Обычный режим
1 | Пакетный режим
Функция возвращает прежний режим работы. Следующий скрипт определяет
его текущее значение без изменений режима работы.
> ввод пользователя:
auto a,s;
s="нормальный" ;
a=Batch(0);
Batch(a);
if (а) s="пакетный";
Мезssаgе("Режим работы %s \n",s);
< ответ IDA:
Режим работы нормальный
char GetldaDirectory ()
Функция возвращает полный путь к директории, в которой расположена IDA,
без завершающего слеша в конце.
Например:
> ввод пользователя:
Message ("%s \n", GetldaDirectory ());
< ответ IDA:
D:\DEBUG\IDA384

Return | Пояснения
 | Полный путь к директории, в которой расположена IDA
Точнее, это путь к исполняемому файлу IDA.EXE (idaw.exe\ idax.exe). Расположение остальных файлов зависит от версии. Так, например, IDA 3.6 хранила все скрипты в базовом каталоге, а последние версии — в каталоге IDC.
Эти различия необходимо учитывать при поиске требуемых файлов. Желательно предусмотреть возможность диалога с пользователем и "ручное" указание путей, а не полагаться на то, что требуемый файл окажется на месте.


IDA - 0445.htm

Глобальные настройки 445
char GetlnputFile ()
Возвращает имя дизассемблируемого файла вместе с расширением. Версия под win32 поддерживает длинные имена файлов, включая пробелы. Дозагрузка файлов не влияет на результат работы этой функции.
Пример использования:
> ввод пользователя:
Message ("%s \n ",
GetlnputFile ()
);
< ответ IDA:
My File.exe

Return Пояснения
 | Имя дизассемблируемого файла вместе с | расширением


IDA - 0446.htm

446 Строки
Строки
К сожалению, встроенный язык IDA не поддерживает даже основных конструкций С для работы со строками. Так, например, невозможно получить посимвольный доступ к строке или указатель на нее.
Зато IDA поддерживает инициализацию и контекцию (слияние) строк, что демонстрирует следующий пример:
> ввод пользователя:
auto a,b;
a="Hello";
b="IDA! \n";
a=a+","+b;
Message("%s \n",a);
< ответ IDA:
Hello,IDA!
Таким образом, строки в IDA представляют собой закрытые объекты, доступные лишь посредством набора манипулирующих с ними функций. Их всего три.
Это определение длины строки (strlen), взятие подстроки (substr) и поиск подстроки (srtsrt). Возможность модификации строки отсутствует, и в том случае, когда возникает потребность изменить хотя бы один символ, приходится перестраивать всю строку целиком.
Для этого могут пригодиться две следующие функции, которые рекомендуется включить в idc.idc, чтобы сделать их доступными для всех пользовательских скриптов.
static setstr(str, pos, ch)
{
auto sO;
sO=substr(str,O,pos);
sO=sO+ch;
sO=sO+substr(str,pos+strlen(ch), strlen (str));
return sO;
}
static insstr(str, pos, ch)
{
auto sO;
sO=substr(str,0,pos);
sO=sO+ch;
sO=sO + substr(str,pos, strlen(str) ) ;
return sO;
}
Первая из них позволяет в строке str заместить любую подстроку ch с позиции pos, а вторая осуществляет вставку с "раздвижкой". Для повседневного исполь-


IDA - 0447.htm

Строки 447
зования обе функции рекомендуется дополнить проверками корректности переда-
ваемых параметров.
Примеры использования даны ниже.
> ввод пользователя:
Message("%s \n",
setstr("Hello World!",5,",")
) ;
< ответ IDA:
Hello, World!
> ввод пользователя:
Message ("%s \n",
insstr("Hello, World!",7,"my ")
);
< ответ IDA:
Hello, my World!
Таким образом, достаточно лишь одной функции взятия подстроки, чтобы обеспечить неограниченный доступ к объекту "строка". Для этого следует скопировать ее в другой объект, доступный нам для чтения и записи, например, виртуальную память, массив и так далее. К сожалению, это медленно работает, но зачастую является единственно возможным выходом.
Другую группу строковых операций представляют функции всевозможного преобразования форматов. Например, перевод символьной строки в двоичное (шестнадцатиричное) число и наоборот. Однако, поскольку IDA поддерживает аналог функции sprintf, то чаще всего пользуются одним вызовом form. Это гораздо удобнее, чем держать в голове имена множества функций.
char substr (char str, long x1, long x2)
Функция осуществляет взятие подстроки. IDA не поддерживает стандартную для С конструкцию str[a], поэтому для любого посимвольного разбора строки приходится вызывать 'substr'.
Функция принимает следующие операнды:

Операнд |
xl | Индекс начала подстроки
х2 | Индекс конца | подстроки
 | Если х2 == - | 1, то возвращается весь остаток строки
Return |
char | Подстрока
В версии 3.84 и более ранних эта функция не имела контроля над границами индексов, и если оказывалось, что x2 < xl, то Windows закрывала приложение IDA как совершившее недопустимую операцию. Наблюдалась непредсказуемая работа приложения и при выходе индексов за границы строки.


IDA - 0448.htm

448 Строки
В версии 4.0 этот недостаток устранен. В случае x2 < xl функция возвращает пустую строку, а при нарушении границ доступа (начальный индекс за границами строки) хоть и выводит диалоговое окно, сообщающее о нарушении границ доступа, но не выходит из дизассемблера, позволяя продолжить работу. (Правда, при этом попытка исполнения любого скрипта заканчивается следующей ошибкой, вплоть до перезапуска IDA.)
IDA - 0448-1.jpg
Если конечный индекс лежит за пределами строки, то IDA просто возвращает остаток строки, и аварийной ситуации не возникает.
Пример использования этой функции для построения простейшего синтаксического анализатора:
> ввод пользователя:
auto a,temp,c;
a="key -Hello";
for (temp=0;temp<strlen(a);temp++)
{
c=substr(a,temp,temp+l);
if (c=="-")
{
Message (substr(a,temp+l,-l));
break;
}
}
< ответ IDA:
Hello
long strstr (char str, char substr)
Функция осуществляет поиск подстроки в строке, принимая следующие операнды:

Операнд | Пояснения
'str' | строка
'substr' | подстрока
Return | ==return | Пояснения
 | !=BADADDR | Индекс подстроки в строке
 | ==BADADDR | Ошибка
При успешном завершении возвращается индекс начала подстроки в строке.
Если подстрока не найдена, то функция возвращает BADADDR.


IDA - 0449.htm

Строки 449
Недокументированной особенностью этой функции является возможность сравнения тождественности числовых выражений. Так, например, strstr(Oxl234,Oxl234) возвратит '0', a strstr(Oxl234,Oxl23) — BADADDR, т.е. ошибку.
Поиск пустой подстроки всегда успешен; поиск в пустой строке всегда возвращает ошибку.
Пример:
> ввод пользователя:
auto a,temp;
a="key -Hello"/
temp=strstr(a, "-") ;
if (temp!=-l) Message (substr(a,temp+l,-l));
< ответ IDA:
Hello
long strlen (char str)
Функция возвращает длину строки. Длина ненулевой строки равна индексу последнего символа плюс единица.

Операнд |
srt | Строка, заканчивающаяся нулем
Return | Пояснения
!=0 | Длина строки
Перенос строки считается за один символ, однако при выводе строки в текстовой файл он представляется последовательностью символов 0xD 0xA, занимая, таким образом, два байта. Это необходимо учитывать при позиционировании внутри файла, а также при оценке необходимого свободного пространства на диске.
Пример:
> ввод пользователя:
Message("%d \n", strlen("Hello, Word! \n"));
< ответ IDA:
14
char form (char format,...)
Ближайший аналог известной функции sprintf. Возвращает форматированную строку и преобразует длинные целые и числа с плавающей запятой в строковые значения.
Например:
> ввод пользователя:
auto a,sO;
a=Oxl23;


IDA - 0450.htm

450 Строки
sO=form("%x \n",a)/
Message (sO);
< ответ IDA:
123
Функция form — не единственное средство для преобразования типов, к тому же достаточно медленно работающее. В большинстве случаев рекомендуется использовать другие функции, преобразующие длинные целые в строку, form выгодно применять только для чисел с плавающей запятой (float).
Управляющие символы стандартные, частично совместимы с printf и полностью совместимы со спецификаторами функции Message встроенного языка IDA.

Сф |
%d | Десятичное длинное знаковое целое. Пример: Message ("%d", 0xF) / 15
%х | Шестнадцатиричное длинное целое строчными символами. Пример: Message ("%x", 10) / а
%Х | Шестнадцатиричное длинное целое заглавными символами. Пример: Message ("%X", 10) ; А
%о | Восьмиричное длинное знаковое целое. Пример: Message ("%o", 11) ; 13
%u | Десятичное длинное беззнаковое целое. Пример: Message ("%u",-l) ; 4294967295
%f | Десятичное с плавающей точкой. Пример: Message ("%f", 1000000) ; I.e6
%с | Символьное значение. Пример: Message ("%с", 33) ; i
%s | Строковое значение. Пример: Message ("%s", "Hello, Word! \n"); Hello, Word!
%е | Вывод чисел в экспоненциальной форме. Пример: Message ("%e", 1000000) ; I.e6


IDA - 0451.htm

Строки 451

Сф |
%g | Вывод чисел в экспоненциальной форме. Замечание. В оригинале спецификатор '%g' заставляет функцию саму решать, в какой форме выводить число — с десятичной точкой или в экспоненциальной форме, из соображений здравомыслия и удобочитаемости. IDA всегда при задании этого спецификатора представляет числа в экспоненциальной форме
%а | Вывод указателя (не поддерживается). Замечание. Вместо спецификатора '%p' IDA использует '%a', преобразующий линейный адрес в строковой сегментный, и автоматически подставляет имя сегмента. Так, например, 'Message("%a \n", 0x10002)' выдаст 'segOOO:2'. Обратите внимание, что таким способом нельзя узнать адрес переменной. Пример: auto а; a="Hello!\n"; Message ("%a \n",a); 0 Возвращается ноль, а не указатель на переменную
%Р | Вывод десятичного целого (всегда со знаком, не опуская плюс)
%+d | В оригинале — вывод шестнадцатиричного целого всегда со знаком, но IDA воспринимает эту конструкцию точно так же, как и 'x'
%+х | 'n' — длина выводимого десятичного числа, при необходимости дополняемого слева пробелами. Например: Message ("Число-%3с1 \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3с1 \п", 10000) ; Число-10000
%nd | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополняемого слева пробелами. Например: Message ( "Число-%3х \n",l); Число- 1 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Например: Message ("Число-%3х \n", 0x1234) ; Число-1234
%nd | 'n' — длина выводимого десятичного числа, при необходимости дополняемого слева незначащими нулями. Пример: Message ("Число-%ОЗсГ, 1) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%03с1", 1000) Число-1000
15*


IDA - 0452.htm

452 Строки

Сф |
%0nx | 'n' — длина выводимого шестнадцатиричного числа, при необходимости дополня- емого слева незначащими нулями. Пример: Message ("Число-%03х", 0x1) ; Число-001 Если выводимое число не укладывается в 'n' позиций, то оно выводится целиком. Пример: Message ("Число-%03х" , 0x1234) ; Число-1234
%#x | Вывод префикса '0x' перед шестнадцатиричными числами. Пример: Message ("%#x", 123) ; 0x123
%#o | Вывод префикса '0' перед восьмиричными числами. Пример: Message ("%#o", 1) ; 01
%n | Количество выведенных символов (не поддерживается)
long xtol (char str)
Функция преобразовывает строковое шестнадцатиричное значение str в длин-
ное целое.

Операнд |
srt | Строковое шестнадцатиричное число
Return | ==return | Пояснения
 | !=0 | Длинное целое
 | ==0 | Ошибка
Разрешается использовать спецификаторы 'x' и 'h' для указания системы исчисления, в противном случае будет использоваться система исчисления по умолчанию (как правило, шестнадцатиричная).
Функция понимает одиночный знак '+' или '-', но "спотыкается" на '+-1 или на префиксе шестнадцатиричных чисел '$', используемых языком Pascal.
Числом считается все до первого нецифрового символа (кроме "А—F"). Так, например:
> ввод пользователя:
Message("Ox%X \n",
xtol("123-2")
);
< ответ IDA:
0x123
В случае ошибки возвращается ноль, например:


IDA - 0453.htm

Строки 453
> ввод пользователя:
Message("Ox%X \n",
xtol("*l") );
< ответ IDA:
0x0
char atoa (long еа)
Функция преобразовывает линейный адрес в строку, используя символьные
имена сегментов, выбирая сегмент с наибольшим из возможных адресов.

Операнд |
еа | 32-разрядный линейный адрес
Return | ==return | Пояснения
 | !="" | Сегментный адрес | в строковом представлении
 | =="" | Ошибка
Так, например:
Q копия экрана IDA:
___________________________ Program Segmentation ------------------------
Name Start End Align Base Type Cls 32es Start EA End EA
segOOO 00000000 000032EA byte 1000 pub CODE N FFFF 00010000 000132EA
> ввод пользователя:
Message("%s \n",
atoa(OxlOOOO)
) ;
< ответ IDA:
segOOO:0
Если указанный адрес не принадлежит ни одному сегменту, то функция
преобразует его к виду segment : offset по стандартной формуле преобразования:
[ segment = еа » 4; offset = еа — (еа » 4). ||
Например:
> ввод пользователя:
Message("%s \n",
atoa(Oxl8)
);
< ответ IDA:
1:00000008
char ltoa (long n, long radix)
Функция преобразовывает длинное целое в символьное с произвольной систе-
мой исчисления.


IDA - 0454.htm

454 Строки
Функция принимает следующие операнды:

Операнд | Назначение
n | Задает операнд
 | ==n | Операнд
 | 0 | Первый слева операнд
 | 1 | Второй, третий и остальные
 | -1 | Все операнды
radix | Требуемая система исчисления. Замечание. В контекстной помощи IDA сообщается, что 'radix' может принимать значения 2, 8, 10, 16. Однако это стандартная С-функция, и она может принимать и другие значения, например, 3 или 11. Точнее, все кроме 0 и 1, а также не более 24, при которых, независимо от аргумента, функция возвращает пустую строку
Return | ==return | Пояснения
 | !="" | Сегментный адрес в строковом представлении
 | =="" | Ошибка
Пример:
> ввод пользователя:
auto а;
for (a=0;a<50;a++)
Message ("%x, %s \n", a, ltoa(a,a));
< ответ IDA:
Of 1, 2,10 3,10 4,10 5,10 6,10 7,10 8,10 9,10 a,10
b,10 c,10 d,10 e,10 f,10 10,10 11,10 12,10 13,10 14,10 15,10
16,10 17,10 18,10 19,10 la,10 lb,10 lc,10 ld,10 le,10 lf,10 20,10
21,10 22,10 23,10 24,10 25, 26, 27, 28, 29, 2a, 2b,
2c, 2d, 2e, 2f, 30, 31,
long atol (char str)
Функция преобразовывает строковое десятичное значение в длинное целое.
Постфикс 'h' она игнорирует, а встретив префикс 'x', возвращает нулевое значение.

Операнд |
srt | Строка, заканчивающаяся нулем
Return | ==return | Пояснения
 |  | Длинное целое
 | ==0 | Ошибка
Пример:
> ввод пользователя:
Message ("%x \n",
atol ("16") );
< ответ IDA:
10


IDA - 0455.htm

Разное 455
Разное
char Compile (char filename)
Очень полезная функция, компилирующая в память указанные файлы (скрипты IDA). Все функции, объявленные в них, будут глобальными и доступными всем остальным скриптам вплоть до завершения сеанса работы с IDA.
Главный модуль скрипта использует эту функцию для подключения всех остальных необходимых ему функций, находящиеся во внешних файлах. Обратите внимание, что Compile только компилирует, но не запускает функцию main(). Если модуль требует инициализации, то 'main' придется вызвать вручную. Однако в компилируемом файле не должно быть функции, совпадающей по имени с текущей (то есть вызвавшей Compile), иначе поведение дизассемблера окажется непредсказуемым. В остальных случаях совпадение имен откомпилированных функций приводит лишь к их замещению.
Системные функции таким образом перекрыть не удастся, и будет выдано сообщение об ошибке. Любопытно, что Compile возвращает не код ошибки, а осмысленную символьную строку, в случае успеха операции — пустую.
Пример:
demo.idc:
static MyFunc()
{
Message("Hello, Sailor! \n");
}
compl.idc
static main()
{
Compile("demo.idc");
MyFunc();
}
Загрузим и запустим на выполнение compl.idc. На экране появится 'Hello, Sailor!'. Нетрудно убедиться, что функция 'MyFunc' доступна и с консоли, и из остальных скриптов.
Заметим, что IDA сложным образом манипулирует с понятием текущего каталога, поэтому если приведенный пример не сработает, попробуйте указать полный путь к файлу demo.idc.
void Exit (long code)
Функция осуществляет выход из IDA в операционную систему, сохраняя все последние изменения и закрывая базу.


IDA - 0456.htm

456___________________Разное______________________
Выполнение скрипта при этом прерывается. Закрытие активных файлов не
гарантируется.

Операнд | Назначение
'code' | Код завершения процесса
long Exec (char command)
Функция позволяет выполнить команду операционной системы, не выходя из IDA.

Операнд |
command | Команда операционной системы (как правило, интерпретатора command.com) | командного
Эта функция расширяет возможности скриптов. Можно, например, удалять временные файлы или склеивать их командой 'copy' и делать многие другие вещи, в том числе писать вирусы и троянские компоненты. (Поэтому с присланными вам скриптами сомнительного происхождения следует вести себя крайне осторожно.)
Для запуска параллельного процесса предусмотрена команда 'start', например, Exec("start command.com"). B Windows приложение запустится в соседнем окне. В этом случае функция возвращает не код завершения процесса (поскольку таковой еще не известен), а результат исполнения программы, использующейся операционной системой для запуска приложения. Для консольных программ это, как правило, 'conagent'.
Проверяйте все скрипты, полученные из чужих рук, на наличие этой команды.
Пример использования:
Exec ("command.com /с del *.tmp");


IDA - 0457.htm

Приложения 457
Приложения
Кратко о загрузке файлов
Опции командной строки
Любопытной особенностью IDA является то, что она поддерживает формат zip и позволяет работать непосредственно с упакованными файлами. Если имя файла не указано в командной строке, то IDA сама запросит его при запуске. В процессе дизассемблирования IDA работает не с выбранным файлом (и его безболезненно можно удалить), а создает набор файлов ID? — собственной базы данных, с которой и взаимодействует.
Поэтому при повторных загрузках файла грузится не он сам, а созданная ранее база. При этом не отслеживаются никакие модификации файла. Исследуемый файл однократно загружается в базу IDA и в дальнейшем не принимает участия в работе.
Для особых ситуаций предусмотрен ключ командной строки '-c', удаляющий перед запуском ранее созданные дизассемблером базы, — вместе со всеми заботливо созданными вами комментариями, именами, метками... уничтожая всю проделанную работу. Конечно, это не решение проблемы — необходимо перегрузить только модифицированный фрагмент, а не уничтожать всю базу! И хотя штатно такая возможность не была предусмотрена, встроенный язык IDA позволяет написать подобный загрузчик самостоятельно.
При первой же загрузке файла появится следующий диалог:
IDA - 0457-1.jpg


IDA - 0458.htm

458 Приложения
( ) Portable executable (PE) [PE.LDW]
( ) MS DOS executable (EXE) [DOS.LDW]
( ) Binary file
B большинстве случаев IDA автоматически определяет тип загружаемого файла, но все же оставляет конечное решение за пользователем. Это бывает полезно во многих случаях — так, вы всегда можете загрузить файл как бинарный и работать с его структурами вручную. Например, текущая версия IDA не поддерживает самозагружающиеся модули и завершает работу при попытке загрузки оных.
IDA - 0458-1.jpg
С такими файлами исследователь рано или поздно сталкивается. Единственный выход — загружать файл как бинарный и дизассемблировать его с помощью собственных скриптов. Не очень вдохновляющая перспектива, но можно утешить себя тем, что другие дизассемблеры не умеют и этого.
Остается только надеяться, что рано или поздно разработчики разрешат эту проблему, но и сегодня IDA обгоняет остальные дизассемблеры числом и качеством поддерживаемых форматов файлов:

EXE | Исполняемый файл | MS DOS
СОМ | Исполняемый файл | MS DOS, CP\M
SYS | Устанавливаемый драйвер | MS DOS
NE | New Executable Format | Windows 3.x;OS/2
LX | Linear Executable Format | OS/2 2.x and OS/2 Warp
LE | Linear Executable Format | Windows VxD
PE | Portable Executable Format | Windows 95; Windows NT
OMF | Intel Object Module Format | MS DOS;Windows 3.x; OS/2
LIB | Library of Object Files | MS DOS, MS Windows 3.x;OS/2.
AR | Library of Object Files | UNIX, MS Windows95; MS Windows NT
COFF | Common Object File Format | UNIX
NLM | Novell Netware Loadable Modules |
ZIP | Archive files |
JAVA | Java classes |
B действительности этот список не исчерпывает возможности IDA, гибкая архитектура которой позволяет работать с произвольными типами файлов и с каждой версией число поддерживаемых форматов все увеличивается.


IDA - 0459.htm

Приложения 459
Loading segment 0x1000 _ _ (EXE & BIN)
Loading offset 0x0 _ _ (BIN)
Сегмент и смещение загрузки на первом этапе освоения IDA лучше не изменять. Смещение актуально только для BIN-файлов, например, для дизассемблирования дампа MBR сектора его необходимо загрузить со смещением 0x7C00, иначе все смещения будут указывать в "космос". Для типизированных файлов IDA игнорирует установленное смещение загрузки, извлекая эту информацию из соответствующих полей заголовка.
Базовый адрес сегмента на самом деле имеет отношение к исследуемому файлу, а не к виртуальной памяти IDA. Его значение необходимо только при написании собственных скриптов. В остальных случаях оно никак не отразится на процессе дизассемблирования.
[X] Create segments
Создавать или не создавать сегмент для бинарных файлов? По умолчанию всегда создается по крайней мере один сегмент. Эту функцию можно выключить когда вы хотите расставить сегменты по своему усмотрению (например, при анализе самозагружамых модулей). Но создать хотя бы один сегмент необходимо в любом случае. Внутренняя архитектура IDA такова, что большинство команд работает только с сегментами. Попытка дизассемблировать фрагмент, не принадлежащий ни к одному из сегментов, вызовет протест со стороны IDA:
This command can't be applied to the addresses without a segment.
Create a segment first.
Однако другие команды, например, чтение\запись памяти, будут успешно работать и могут быть использованы в скрипте расшифровки файла. В этом случае, нет необходимости в создании сегмента.
[ ] Load resources
Команда указывает на необходимость загрузки ресурсов. Актуально только для PE- и NE-файлов. По умолчанию выключено, однако довольно часто в ресурсах расположены текстовые строки или даже некоторые данные, и тогда ресурсы необходимо загрузить. В остальных случаях это лишний расход времени и памяти.
[X] Rename DLL entries
IDA умеет распознавать большинство библиотечных функций (подробнее об этой уникальной возможности читайте в главе, посвященной технологии FLIRT). При этом она может заменять импортируемые по ординалу функции их непосредственными именами. Если по каким-то причинам для вас это неприемлемо, данную опцию необходимо отключить. При этом IDA добавит символьные имена в повторяемые комментарии. Сравните:
; Imports from MFC42.DLL
?DoMessageBox@CWinApp@@UAEHPBDII@Z dd ?
?SaveAllModified@CWinApp@@UAEHXZ dd ?
?InitApplication@CWinApp@@UAEHXZ dd ?


IDA - 0460.htm

460 Приложения
и
; Imports from MFC42.DLL
MFC42_2512 dd ? ; DATA XREF: j_MFC42_2512 r
;?DoMessageBox@CWinApp@@UAEHPBDII@Z:
MFC42_5731 dd ? ; DATA XREF: j_MFC42_5731 r
;?SaveAllModified@CWinApp@@UAEHXZ:
MFC42_3922 dd ? ; DATA XREF: j_MFC42_3922 r
;?InitApplication@CWinApp@@UAEHXZ:
MFC42_1089 dd ? ; DATA XREF: j_MFC42_1089 r
Разумеется, гораздо удобнее, когда IDA заменяет ординалы осмысленными именами, и найдется немного ситуаций, в которых эту опцию приходится отключать.
[ ] Manual load
"Ручная" загрузка некоторых типов файлов полезна для NE\LX\LE форматов. При этом пользователь получает возможность для каждого из объектов файла задать селектор и базовый адрес загрузки. Появится диалог следующего вида:
IDA - 0460-1.jpg
'Start Address' указывает, по какому адресу будет расположен загружаемый объект. Это значение вычисляется дизассемблером автоматически, и обычно нет причин менять его.
Селектор не имеет отношения к исследуемому файлу. Он относится непосредственно к дизассемблеру, а точнее — к организации внутренней (виртуальной) памяти IDA. Сейчас мы не будем задерживать на этом внимание, отметим только, что последний необходим для доступа к сегментам написанных вами скриптов, а в остальных случаях его значение будет "прозрачно" для пользователя.
Базовый адрес связан с виртуальным адресом следующей формулой:
VirtualAddress = LinearAddress - (SegmentBase « 4)


IDA - 0461.htm

Приложения 461
Следовательно, одному и тому же виртуальному адресу могут соответствовать различные пары SegmentBase:LinearAddress. Подробности — в главе, посвященной организации виртуальной памяти IDA.
'Stop loading file' означает прекращение загрузки остальных объектов (сегментов) файла. Команда может быть использована для экономии времени — если остальные объекты\секции вас не интересуют, то к чему тратить время\ресурсы на их загрузку?
IDA - 0461-1.jpg
РЕ-файлы имеют более простую организацию, в первом приближении представляя собой просто образ памяти процесса. Ручная загрузка сводится к произвольному выбору загружаемых секций. Ни базовый адрес, ни адрес загрузки изменить невозможно.
[X] Make imports section
По умолчанию IDA преобразует секцию импорта .idata РЕ-файлов в набор директив 'extern' и усекает ее. Обычно это работает нормально, но никто не гарантирует, что в секции импорта не окажутся размещенными некоторые данные. Так, например, поступают некоторые вирусы, размещая свое тело в таблице адресов. Разумеется, при этом IDA их "не увидит". В таких случаях 'Make imports section' следует отключить.
На этом обзор описания загрузки файлов будем считать законченным.
Интерактивной работе с IDA планируется посвятить второй том этой серии.


IDA - 0462.htm

462 | Приложения
Исходный текст Crypt.com CRYPT.MAKE.BAT tasm Crypt_c.asm tasm Crypt_b.asm tasm Crypt_m.asm tasm Cryptb~l.asm tlink Crypt_c.obj /t tlink Crypt_b.obj /t tlink Crypt_m.obj /t tlink Cryptb~l.obj /t del Crypt*.map del Crypt*.obj CRYPTB-l.COM copy Crypt_c.com /b + Crypt_b.com + Crypt_m.com Crypt.com del Crypt_*.com del Cryptb~l.com
CRYPT.BIN.ASM .MODEL TINY .CODE ORG 100h Start: MOV AX,3D02h LEA DX,sFileName INT 21h XCHG AX,BX MOV AH,3Fh MOV CX,lOOOh LEA DX,Buffer INT 21h PUSH AX XCHG AX,CX MOV SI,DX ADD SI,2 Crypt: XOR Byte ptr [SI],66h INC SI LOOP Crypt MOV AX,3D02h LEA DX,sFileName INT 21h LEA DX,Buffer XCHG AX,BX POP CX MOV AH,40h INT 21h MOV AH,4CH INT 21h sFileName: DB 'Crypt_b.com',0 Buffer: END Start


IDA - 0463.htm

Приложения 463
CRYPT_B.ASM
.MODEL TINY
.CODE
ORG 100h
Start:
DW offset _end - 100h - 2 ; Кол-во байт для шифрования
MOV AH,9 ; Печать строки
LEA DX,sHello-2
INT 21h
RET
sHello DB 'Hello,Sailor!',ODh,OAh,'$'
_end:
END Start
CRYPT_C.ASM
.MODEL TINY
.CODE
ORG 100h
Start:
ADD SI,6
JMP SI
DB OB9h
LEA SI,_end ; На начало зашифрованного фрагмента
LODSW ; Читать слово (кол-во байт для
; расшифровки)
XCHG AX,CX ; AX --> CX
PUSH SI
Decrypt:
XOR Byte ptr [SI],066h
INC SI
LOOP Decrypt
JMP SI
_end:
END Start
CRYPT_M.ASM
.MODEL TINY
.CODE
ORG 100h
Start:
call _next
_next:
POP CX
POP SI
MOV DI,offset Start
PUSH DI
SUB CX,SI
REP MOVSB
RETN
END Start