Таблица 6
??? #верстальщику – change table
72
аргумент пояснения ea
любой линейный адрес, принадлежащий сегменту comb Атрибут (смотри определения в таблице 6)
=return Успешность завершения операции
1 Операция завершилась успешно return
0 При выполнении операции произошла ошибка
Родственные функции: SegCreate
Интерактивный аналог: “View\Segments”,
; success SegClass(long ea,char class)
Функция изменяет атрибут класса сегмента, помещая в ассемблерный листинг атрибут класса.
Атрибут класса представляет собой текстовую строку, указывающую линкеру порядок следования сегментов. Если это не запрещено комбинаторным атрибутом (см. описание SegComb), линкер объединяет вместе сегменты с одинаковым именем.
Рекомендуется назначать имена так, чтобы они отображали функциональное назначение сегментов, например, “CODE”, “DATA”, “STACK” и т.д. Общепринятые имена перечислены в таблице ???.
Замечание: большинство линкеров требуют, чтобы в объективном файле
присутствовал хотя бы один сегмент с именем “CODE”, в противном случае,
они могут отказаться обрабатывать такой файл или обработают его
неправильно.
Пример использования:
SegCreate(0x1000,0x2000,0,0,scPub); a) создаем сегмент с атрибутом public seg000:0000 seg000 segment at 100h public '' b) сегмент создан, по умолчанию атрибут класса сегмента отсутствует
SegClass(0x1000,”MySegment”); с) вызываем функцию SegClass для изменения атрибута класса сегмента seg000:0000 seg000 segment at 100h public 'MySegment' d) атрибут класса сегмента изменен (в тексте он выделен жирным шрифтом)
Класс
Пояснения
CODE Pure code
Сегмент кода
DATA Pure data
CONST Pure data
Сегмент данных
BSS Uninitialized data
Неиницилизированные данные
STACK Uninitialized data
Сегмент стека
XTRN Extern definitions segment
Таблица 7 Общепринятые наименования классов сегментов
??? #верстальщику – change table
73
аргумент пояснения ea линейный адрес, принадлежащий сегменту class класс сегмента
=return Успешность завершения операции
1 Операция завершилась успешно return
0 При выполнении операции произошла ошибка
Родственные функции: SegCreate
Интерактивный аналог: “View\Segments”,
; success SegDefReg(long ea,char reg,long value)
Функция определяет значение сегментных регистров, помещая в ассемблерный текст директиву ASSUME. Это указывает дизассемблеру (ассемблеру) к какому именно сегменту обращается тот или иной адресный операнд. Более подробную информацию по этому вопросу можно получить, обратившись к описанию директивы ASSUME в документации, прилагаемой к используемому ассемблеру или справочной (учебной) литературе по языку ассемблера.
Аргумент ea задает любой линейный адрес, принадлежащий сегменту, в которой необходимо поместить директиву ASSUME.
Аргумент reg задает сегментный регистр в символьном представлении, например,
“DS”,”ES”,”SS” и т.д. Строчечные и прописные символы не различаются. Тип процессора при назначении сегментного регистра учитывается.
Аргумент value содержит базовый адрес сегмента, загружаемый в регистр и выраженный в параграфах. Если сегмента с указанным базовым адресом не существует, регистр приобретает неопределенное (“nothing”) значение.
В реализации этой функции допущена одна ошибка – независимо от успешности выполнения, возвращаемое значение никогда не сигнализирует об ошибке.
Пример использования (см. файл “assume.idb”, содержащийся на диске, прилагаемом к книге): пусть имеется один сегмент кода (“seg00”) и два сегмента данных
(“seg001” и “seg002”), содержащих переменные “My666” и “My777” соответственно. По умолчанию значения сегментных регистров неопределенны и IDA не может определить к каким именно сегментам происходит обращения в командах “mov ax,ds:[0]” и “mov dx,es:[0]”, поэтому, вынуждена оставить операнды в виде непосредственных смещений. (в тексте они выделены жирным шрифтом) seg000:0000 seg000 segment byte public 'CODE' seg000:0000 assume cs:seg000 seg000:0000 assume es:nothing, ss:nothing, ds:nothing seg000:0000 mov ax, ds:0 seg000:0003 mov dx, es:0 seg000:0003 seg000 ends seg000: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 seg001:0000 seg001 ends seg001:0000 seg002:0000 ; ═════════════════════════════════════════════════════════════
74
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 seg002:0000 seg002 ends
Задать значения сегментных регистров можно с помощью функции DefSegReg, вызов которой может выглядеть так:
DefSegReg(SegByName(“seg000”), “DS”, SegByName(“seg001”)>>4);
DefSegReg(SegByName(“seg000”), “ES”, SegByName(“seg002”)>>4);
В результате ее выполнения, IDA смогла отследить обращения к переменным, автоматически подставив их имена вместо смещений (в тексте они выделены жирным шрифтом). Поместив курсор в границы того или другого имени, нажатием клавиши
можно перейти к соответствующей ячейке памяти. seg000:0000 seg000 segment byte public 'CODE' seg000:0000 assume cs:seg000 seg000:0000 assume es:seg002, ss:nothing, ds:seg001 seg000:0000 mov ax, My666 seg000:0003 mov dx, es:My777 seg000:0003 seg000 ends seg000: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: seg000:0000r 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: seg000:0003r seg002:0000 seg002 ends seg002:0000
??? #верстальщику – change table аргумент пояснение ea линейный адрес, принадлежащий сегменту reg сегментный регистр в строковом представлении (например, “DS”) val базовый адрес сегмента, загружаемого в регистр, выраженный в параграфах return всегда единица
Родственные функции: нет
Интерактивный аналог: “Edit\Segments\Change segment register value”,
75
success SetSegmentType (long segea,long type)
Функция
изменяет тип сегмента, оказывая влияние на его дизассемблирование.
Аргумент
segea задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент
type указывает на тип сегмента и может принимать одно из значений, перечисленных в таблице ???. При создании сегмента функцией
SegCreate ему присваивается тип «неизвестный» - SEG_NORM. определение
#
Пояснения
SEG_NORM 0 Неизвестный тип
SEG_XTRN 1
Внешний ('extern') сегмент. Инструкции исключены
SEG_CODE 2
Сегмент кода
SEG_DATA 3
Сегмент данных
SEG_IMP 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
Таблица 8 Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0); a) создаем новый сегмент (по умолчанию неизвестного типа) seg000:0000 ; Segment type:
Regular seg000:0000 seg000 segment at 100h private '' seg000:0000 assume cs:seg000 seg000:0000
assume es:nothing, ss:nothing, ds:nothing b) сегмент создан (тип выделен жирным шрифтом), автоматически внедрена директива ASSUME для определения сегментных регистров.
SetSegmentType(0x1000,SEG_DATA); c) вызов функции SetSegnetType для установки типа «сегмент данных» seg000:0000 ; Segment type:
Pure data seg000:0000 seg000 segment at 100h private '' seg000:0000 assume cs:seg000 d) тип сегмента изменен, директива ASSUME, задающая значение регистров DS,
ES и SS удавлена.
??? #верстальщику – change table аргумент пояснения ea любой линейный адрес, принадлежащий сегменту type тип сегмента (возможные значения приведены в таблице ???)
=return пояснения return
!=0 успешное завершение операции
76 0 ошибка
Родственные функции: GetSegmentAttr
Интерактивный аналог: нет long GetSegmentAttr(long segea,long attr)
Функция позволяет узнать следующие атрибуты сегмента: кратность выравнивания, комбинацию, привилегии доступа, разрядность, флаги сегмента, селектор, использующийся для базирования сегмента, тип сегмента и значения сегментных регистров, определенных функцией DefSegReg.
Об атрибуте вырывания можно подробнее прочитать в описании функции SegAlign, об атрибутах комбинации – в описании функции SegComb, о типе сегмента рассказывается в описании функции “SetSegmentType”. Использование селекторов для базирования сегментов подробно описано в главах «Организация сегментов», “SegCreate” и
“SetSelector”.
Аргумент segea задает любой линейный адрес, принадлежащий сегменту. Если передать адрес не принадлежащий никакому сегменту, функция возвратит ошибку.
Аргумент attr указывает функции – содержимое какого атрибута необходимо возвратить. Возможные значения аргумента attr приведены в таблице ???
??? #Верстальщику – Change Table константа
# пояснения функция
SEGATTR_ALIGN 20 получить выравнивание сегмента
SegAlign
SEGATTR_COMB 21 получить атрибуты комбинации SegComb
привилегии доступа
SEGPERM_EXEC 1
исполнение
SEGPERM_WRITE 2
запись
SEGATTR_PERM
22
SEGPERM_READ 4
чтение
Внутренняя используемая только в IDA
SDK
SEGATTR_USE32 23 32 разрядный сегмент
SegAddrnd
флаги сегмента
ADDSEG_NOSREG все сегментные регистры, заданные по умолчанию неопределенны
SEGATTR_FLAGS
24
ADDSEG_OR_DIE невозможно добавить сегмент
Add_seg
(из IDA SDK)
SEGATTR_SEL 26 селектор сегмента
SetSeelctor
SEGATTR_DEF_ES 28 значение регистра ES по умолчанию
SEGATTR_DEF_CS 30 значение регистра CS по умолчанию
SEGATTR_DEF_SS 32 значение регистра SS по умолчанию
SEGATTR_DEF_DS 34 значение регистра DS по умолчанию
SEGATTR_DEF_FS 36 значение регистра FS по умолчанию
SEGATTR_DEF_GS 38 значение регистра GS по умолчанию
DefSegReg
SEGATTR_TYPE 40 тип сегмента
SetSegmentType
Таблица 9 Типы сегментов
??? #верстальщику – change table аргумент пояснения ea линейный адрес, принадлежащий сегменту
Type тип сегмента (возможные значения приведены в таблице 9)
77
=return Успешность завершения операции
!=0 Операция завершилась успешно return
0 При выполнении операции произошла ошибка
Родственные функции: SegAddrng, SegAling, SegComb, SegClass, SegDefReg,
SetSegmentType
Интерактивный аналог: нет char SegName(long ea)
Функция возвращает имя сегмента, заданного любым принадлежащим ему линейным адресом ea. Если передать адрес не принадлежащий никакому сегменту, функция возвратит пустую строку.
Пример использования:
SegCreate(0x1000,0x2000,0x100,0,0,0);
SegRename(0x1000,”MySeg”); a) создаем сегмент и тут же переименовываем его в “MySeg”
MySeg:0000 MySeg segment at 100h private '' b) сегмент успешно создан (имя выделено жирным шрифтом)
Message(">%s\n",SegName(0x1000)); c) вызываем функцию SegName для получения имени сегмента
>MySeg d) результат – имя сегмента
??? #Верстальщику Table Change аргумент пояснения ea линейный адрес, принадлежащий сегменту
=return Пояснение
!=”” имя сегмента return
“” ошибка
Родственные функции: SegRename, SegByName
Интерактивный аналог: по умолчанию имя сегмента отображается в адресе каждой ячейки
long FirstSeg()
Функция возвращает линейный адрес начала сегмента с наименьшим линейным адресом начала. Если не существует ни одного сегмента, функция возвратит значение
BADADDR, сигнализируя об ошибке.
Замечание: FirstSeg обычно используется в паре с NextSeg для получения списка
адресов начала всех существующих сегментов.
Пример использования:
SegCreate(0x1000,0x2000,0x9,0,0,0);
78
SegCreate(0x100,0x200,0x10,0,0,0); a) создаем два сегмента с адресами начала 0x100 и 0x1000.
Message(">%X\n",FirstSeg()); b) вызываем функцию FirstSeg для получения наименьшего линейного адреса сегмента
>100 c) результат – линейный адрес сегмента с наименьшим линейным адресом начала
??? #Верстальщику Change Table аргумент пояснения нет Нет
=return пояснение
!=BADADDR линейный адрес начала сегмента с наименьшим линейным адресом return
==BADADDR ошибка
Родственные функции: NextSeg
Интерактивный аналог: “View\Segments” линейный адрес начала сегмента с наименьшим линейным адресом начала -┐
╔═[■]═══=======═════════=═══===══ Program Segmentation ═════════════════│═══========════3═[↑]═╗
║ Name Start End Align Base Type Cls 32es ss ds ▼ ▲
║ seg000 00000000 00000100 at 0010 pri N FFFF FFFF FFFF 00000100 00000200 ■
║ seg001 00000F70 00001F70 at 0009 pri N FFFF FFFF FFFF 00001000 00002000 ▒
║ ▒
╚═══════════════════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘ long NextSeg(long ea)
Функция возвращает линейный адрес начала сегмента, линейный адрес начала которого больше чем переданный линейный адрес ea. Если сегмента удовлетворяющего такому условию не существует, функция возвращает значение BADADDDR, сигнализируя об ошибке.
Передаваемый функции линейный адрес не обязательно должен быть адресом начала какого-то сегмента, более того, он вообще может не принадлежать ни одному сегменту – это ничуть не помешает функции возвратить линейный адрес следующего сегмента, при условии, что такой сегмент есть.
Замечание: NextSeg обычно используется в паре с FirstSeg для получения списка
адресов начала всех существующих сегментов.
Конструкция NextSeg(0) аналогичная FirstSeg() при условии, что
начальные адреса всех сегментов отличны от нуля (как правило, так
практически всегда и бывает).
Замечание: функции, возвращающей линейный адрес начала сегмента, линейный
адрес которого меньше переданного линейного адреса ea, не существует. Тем
не менее, эта задача может быть решена на основе имеющихся функций
FirstSeg и NextSeg
Ниже приводится пример реализации функции PrevSeg, работающей по
следующему алгоритму: вызовами NextSeg сегменты перебираются один за
другим до тех пор, пока линейный адрес начала очередного сегмента не
превысит переданное значение ea. Затем – возвращается линейный адрес
79
начала предыдущего сегмента.
static PrevSegEx(ea)
{
аuto a;
a=0;
while (SegEnd(NextSeg(a))
return
a;
}
Пример использования NextSeg для получения списка адресов начала всех существующих сегментов:
SegCreate(0x100,.0x200,0x10,0,0,0);
SegCreate(0x1000,0x2000,0x100,0,0,0);
SegCreate(0x10000,.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) результат – линейные адреса начала всех существующих сегментов
??? #Верстальщику Change Table аргумент пояснение ea линейный адрес не обязательно принадлежащий какому-нибудь сегменту
=return Пояснение
!=BADADDR линейный адрес начала следующего сегмента return
==BADADDR ошибка
Родственные функции: FirstSeg
Интерактивный аналог: “View\Segments” линейный адрес начала следующего сегмента–-------------------┐
╔═[■]═══════════════════════════════ Program Segmentation ═══════════════════│═════════5═[↑]═╗
║ Name Start End Align Base Type Cls 32es ss ds │ ▲
║ seg000 00000000 00000100 at 0010 pri N FFFF FFFF FFFF 00000100 │ 00000200 ■
║ seg001 00000000 00001000 at 0100 pri N FFFF FFFF FFFF 00001000 ◄ 00002000 ▒
║ seg002 00000000 00010000 at 1000 pri N FFFF FFFF FFFF 00010000 00020000 ▒
80
║ ▼
╚═════════════════════════════════════◄■▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒►─┘ void SetSelector(long sel,long value)
Функция создает новый селектор или изменяет значение уже существующего селектора. Создаваемый селектор не должен совпадать с базовым адресом ни одного сегмента, иначе для базирования этого сегмента будет автоматически использоваться созданный селектор, а не его базовый адрес, что приведет к искажению всех смещений внутри сегмента.
Максимальное допустимое количество селекторов равно 4096 (0x1000 в шестнадцатеричной системе исчисления), а индексы селекторов могут принимать любые значение от 0x0 до 0xFFFF включительно.
Аргумент sel задает 16-разрядный индекс создаваемого или модифицируемого селектора. Старшее слово, передаваемого 32-битного значения автоматически обрезается, в результате чего существует угроза непреднамеренного искажения другого селектора, что подтверждает следующий эксперимент:
SetSelector(0x1,0x666);
Message(">%X\n",AskSelector(0x1)); a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его значение
>666 b) селектор имеет значение 0x666
SetSelector(0x10001,0x777); c) пытаемся создать селектор с индексом 0x10001 и значением 0x777
Message(">%X\n",AskSelector(0x1)); d) проверяем значение селектора 0x1
>777 e) результат – значение селектора 0x1 искажено! Старшие 16 бит индекса 0x10001 были обрезаны, в результате чего был модифицирован селектор
0x10001 AND 0xFFFF == 0x1
Аргумент value содержит 32-разрядное значение базы сегмента в параграфах. Оно будет автоматически использовано для базирования сегмента базовый адрес которого равен индексу данного селектора.
Замечание: функция DeleteAll (см. описание функции DeleteAll), удаляющая все
сегменты, не уничтожает селекторов и их приходится удалять «вручную»
вызовом DelSelector.
??? #Верстальщику Change Table аргумент пояснения sel 16-разрядный индекс селектора val 32-разрядное значение селектора в параграфах
=return пояснение return void функция не возвращает никакого значения
Родственные функции: AskSelector, DelSelector
Интерактивный аналог: “View\Selector”, - создает новый,
81
изменяет значение уже существующего селектора. void DelSelector(long sel)
Функция удаляет селектор,
если он существует, в противном случае ничего не происходит. Если данный селектор использовался для базирования сегмента, новый базовый адрес сегмента будет равен base = sel * 0x10, а смещение первого байта в сегмента соответственно: startoffset = startea - sel * 0x10. Создав заново уничиженный селектор вызовом SetSelector, можно все вернуть на свои места.
Аргумент
sel задает 16-разрядный индекс удаляемого селектора. Старшее слово, передаваемого 32-битного значения автоматически обрезается, в результате чего существует угроза непреднамеренного уничтожения другого селектора, что подтверждает следующий эксперимент:
SetSelector(0x1,0x666);
Message(">%X\n",AskSelector(0x1)); a) создаем селектор с индексом 0x1 и значением 0x666 и тут же проверяем его значение
>666 b) селектор имеет значение 0x666
DelSelector(0x10001,0x777); c) пытаемся удалить селектор с индексом 0x10001
Message(">%X\n",AskSelector(0x1)); d) проверяем значение селектора 0x1
>FFFFFFFF e) результат – селектора 0x1 был уничтожен! Старшие 16 бит индекса 0x10001 были обрезаны, в результате чего был удален селектор 0x10001 AND 0xFFFF == 0x1
??? #Верстальщику Change Table аргумент пояснения sel 16-разрядный индекс уничтожаемого селектора
=return пояснение return void функция не возвращает никакого значения
Родственные функции: SetSelector
Интерактивный аналог: “View\Selectors”,
long AskSelector(long sel)
Функция возвращает значение селектора в параграфах. Если запрошенный селектор отсутствует, функция возвратит переданное ей значение, сигнализируя об ошибке (внимание! не BADADDR). Поскольку, значение селектора не может быть равно его индексу, никакой неоднозначности не возникает, однако, автору книги совершенно непонятно чем вызвано такое решение – не лучше ли при неуспешном завершении функции возвращать BADADDR – значение, которое не может иметь ни один селектор?
Аргумент sel задает 16-разрядный индекс запрашиваемого селектора. Старшее слово передаваемого 32-битного значения автоматически обрезается, в результате существует возможность обращения совсем к другому селектору, чем предполагалось.
82
Ввиду всего вышесказанного проверка успешности завершения функции должна выглядеть так: if ((selvalue=AskSelector(sel)) == (sel & 0xFFFF))
// ошибка else
// успешное завершение функции
??? #Верстальщику Change Table аргумент пояснения sel 16-разрядный индекс запрашиваемого селектора
=return пояснения sel & 0xFFFF ошибка return
!=(sel & 0xFFF) 32-разрядное значение селектора в параграфах
Родственные функции: SetSelector, FindSelector
Интерактивный аналог: “View\Selectors”
┌─── Индекс селектора
│ ┌─── значение селектора
╔═▼[■]══▼══ Selectors ══4═[↑]═╗
║ Sel Value ▲
║ 0001 00000666 ■
║ 0002 00000999 ▒
║ ▼
╚1/2 ═══════════════─┘ long FindSelector(long val)
Функция возвращнаходит индекс селектора с указанным значением val, выраженном в параграфах.
Если существуют два и более селектора с идентиченым значениями, функция возвращает индекс первого из них в порядке создания. Если же ни одного селектора с таким значением не существет, функция возращает младшие 16 бит переданного ей агрумента обратно.
Поскольку, значение селектора не может быть равно его индексу, никакой неоднозначности не возникает, однако, автору книги совершенно непонятно чем вызвано такое решение – не лучше ли при неуспешном завершении функции возвращать BADADDR
– значение, которое не может иметь ни один селектор?
Ввиду всего вышесказанного проверка успешности завершения функции должна выглядеть так: if ((sel=FindSelector(selvalue)) == (selvalue & 0xFFFF))
// ошибка else
// успешное завершение функции
Замечание: поскольку с данным базовым адресом может существовать только
один сегмент, в создании двух и более селекторов с одинаковыми значениями
никакой необходимости нет. Тем не менее, функция SetSelector не
препятствует этому и существования двух и более селекторов с одинаковыми
индексами в принципе возможно, – один селектор может использоваться для
базирования некоторого сегмента, а остальные простаивать. В такой
83
ситуации функция FindSelector может возвратить неверный результат,
поэтому, перед ее вызовом следует убедиться, что существует не более
одного селектора с каждым значением. Единственный документированный
способ решения этой задачи заключается в последовательном переборе всех
возможных селекторов в интервале от 0x0 до 0xFFFF.
??? #Верстальщику Change Table аргумент пояснения val 32-разрядное значение селектора в параграфах для поиска
=return пояснение
==(val & 0xFFFF) ошибка return
!=(val & 0xFFF) 16-разрядный индекс селектора с указанным значением
Родственные функции: SetSelector, AskSelector
Интерактивный аналог: “View\Selectors”
┌─── Индекс селектора
│ ┌─── значение селектора
╔═▼[■]══▼══ Selectors ══4═[↑]═╗
║ Sel Value ▲
║ 0001 00000666 ■
║ 0002 00000999 ▒
║ ▼
╚1/2 ═══════════════─┘
ЭЛЕМЕМЕНЫ
#Defenition
С каждым адресом виртуальной памяти связано 32-разрядное поле флагов (см. главу «Виртуальная память»). Флаги хранят содержимое ячейки, описывают ее представление и указывают на наличие связанных с ней объектов.
Содержимое ячейки: флаги хранят содержимое ячейки виртуальной памяти (в восьми младших битах) или указывают на то, что ячейка содержит неинициализированное значение (см. главу «Виртуальная память»).
Представление: флаги определяют будет ли отображаться данный байт в виде данных или машинной инструкции. Они так же позволяют уточнить в виде каких именно данных (массива, строки, переменной, непосредственного значения или смещения) он должен отображаться (см. главу «Типы элементов»)
Связанные объект: флаги указывают на наличие на наличие связанных с ячейкой объектов – меток, имен, комментариев и т.д. Сами объекты хранятся в отдельном виртуальном массиве, проиндексированного линейными адресами. В принципе без флагов, ссылающихся на объекты можно было бы и обойтись, но тогда бы пришлось при отображении каждой ячейки просматривать все виртуальные массивы на предмет поиска объектов, ассоциированных с данным линейными адресом, что отрицательно сказалось бы на производительности дизассемблера. Напротив, перенос этой информации в флаги позволяет ускорить работу – обращение к виртуальному массиву происходит только в тех случаях, когда с ячейкой заведомо связан какой-то объект. (см. главу «Объекты»)
Размер машинных инструкций и типов данных различен, и это не дает возможности быстро определить к какой инструкции (переменной) принадлежит ячейка с таким-то линейным адресом. Можно провести следующую аналогию – текст со словами не
84
отделенными друг от друга пробелами из произвольной точки читать невозможно, поскольку неизвестно чем является та или иная буква – началом слова или его серединой.
Образно говоря, при дизассемблировании IDA разбивает исследуемый файл на
«слова», в терминологии разработчиков - элементы.
Элементом называется последовательность смежных ячеек виртуальной памяти, содержащих одну самостоятельную конструкцию языка ассемблера, с которой можно оперировать как с одним целым, – инструкцию, строку, переменную и т.д.
Первая ячейка элемента называется головой, а все, последующие за ней –
хвостом. Элементы единичной длины состоят только из головы и не имеют хвоста.
Признаком головы является ненулевое значение бита FF_DATA (см. таблицу 10) поля флагов. Соответственно признаком хвоста является нулевое значение бита FF_DATA и ненулевое значение бита FF_TAIL.
Свойства элемента определяются свойствами его головы. Свойства головы определяются флагами, связанными с данной ячейкой виртуальной памяти.
Существуют элементы двух видов – элементы кода (CODE) и элементы
данных.(DATE).
Вид элемента задается сочетанием дух битов FF_DATA и FF_TAIL следующим образом (см. таблицу 11) – если бит головы (FF_DATA) установлен, то значение бита хвоста (FF_TAIL) интерпретируется типом элемента – его единичное значение задает тип
CODE, в противном случае – DATA; напротив, если бит головы сброшен, единичное значение бита FF_TAIL трактуется признаком хвоста элемента; если же оба бита FF_DATA и FF_TAIL сброшены – элемент считается неопределенным (unexplored), т.е. несуществующим.
… A
9 8
7
…
1 0 бит поле флагов поле содержимого обозначение …
FF_DATA
FF_TAIL FF_IVL
MS_VAL голова хвост ==1
==0 тип элемента признак хвоста
==0 ==1 назначение …
Голова
DATA CODE
==1 инициализи рованный байт не инициализи рованный байт содержимое ячейки маска …
0x400 0x200 0x100 0xFF
Таблица 10 устройство элемента
FF_DATA
0 1 0 неопределенный элемент
FF_UNK элемент данных, голова
FF_DATA
FF_TAIL
1 хвост элемента
FF_FAIL элемент кода, голова
FF_CODE
Таблица 11 Определение типа элемента
Хвостовые флаги в двенадцати старших битах содержат смещения относительно начала и конца элемента. Флаги, расположенные по четному линейному адресу – относительно конца, а флаги расположенные по нечетному линейному адресу – относительно его начала.
85
Это позволяет легко определить к какому элементу принадлежит такая-то ячейка, а так же узнать линейный адрес начла и конца элемента, на основании которых легко вычислить его длину.
Замечание: в файле , входящим в IDA SDK определена
функция, возвращающая значение хвостовых бит “
inline ushort gettof(flags_t F) { return
ushort((F & TL_TOFF) >> TL_TSFT); }
”
Внимание: созвать новые объекты, использовать или изменять хвостовые
биты может только ядро IDA, но не пользовательские модули! В противном
случае это может привести к некорректной работе и зависанию дизассемблера.
Элементы могут существовать только внутри сегментов – попытка создания элемента по адресу, не принадлежащему ни одному сегменту, обречена на провал.
Навигатор по функциям
Созданием элементов всецело занимается ядро IDA; пользовательские скрипты хотя и имеют достаточные для «ручного» создания элементов привилегии, пользоваться этими привилегиями категорически не рекомендуется - даже незначительная ошибка способна вызвать непредсказуемое поведения дизассемблера вплоть до его полного
«зависания».
Напротив, работая с уже созданными ядром элементами, пользователь может решить ряд задач, облегчающих дизассемблирование исследуемого файла. Пусть, например, требуется отыскать в тексте все условные переходы, стоящие после инструкции
“test”, следующей за вызовом функции, иначе говоря, произвести поиск по шаблону “call
*\test *,*\j? *” и вывести протокол отчета.
Это можно осуществить с помощью следующего листинга, сердцем которого является цикл, вызывающий функцию NextHead, последовательно переходящей от одной машинной инструкции к другой:
#include static main()
{ auto a; a=0; while(a!=BADADDR)
{ if (isCode(GetFlags(a))) if( (GetMnem(a)=="call")
&& (GetMnem(NextHead(a,BADADDR))=="test")
&& (Byte(NextHead(NextHead(a,BADADDR),BADADDR)) > 0x6F)
&& (Byte(NextHead(NextHead(a,BADADDR),BADADDR)) < 0x80))
Message(">%s %4s %s\n>%s %4s %s,%s\n>%s %s %s\n>-------\n", atoa(a),GetMnem(a),GetOpnd(a,0), atoa(NextHead(a,BADADDR)),
GetMnem(NextHead(a,BADADDR)),
GetOpnd(NextHead(a,BADADDR),0),
GetOpnd(NextHead(a,BADADDR),1), atoa(NextHead(NextHead(a,BADADDR),BADADDR)),
GetMnem(NextHead(NextHead(a,BADADDR),BADADDR)),
GetOpnd(NextHead(NextHead(a,BADADDR),BADADDR),0));
86
a=NextHead(a,BADADDR);
}
}
Результат его работы может быть следующим (в данном примере использовался файл first.exe –см. главу «Первые шаги с IDA Pro»)
>004010C0 call ostream::opfx(void)
>004010C5 test eax,eax
>004010C7 jz loc_4010E0
>---------------------------
>0040111F call ios::ios(void)
>00401124 test [esp+4+arg_0],1
>00401129 jz loc_401132
>---------------------------
>004011BE call ios::ios(void)
>004011C3 test [esp+4+arg_0],1
>004011C8 jz loc_4011D1
>---------------------------
Две функции
NextHead и
PrevHead обеспечивают прямую (от младших адресов к страшим) и обратную (от старших к младшим) трассировку элементов, возвращая линейный адрес головы следующего элемента или значение BADADDR если достигнут последний элемент в цепочке.
Это позволяет рассматривать анализируемый код не как поток бессвязных байт, а упорядоченную последовательность инструкций и данных. Разница между ними заключается в том, что поиск байт из интервала 0x70-0x7F в первом случае
обнаружит не только все условные переходы, но и множество других инструкций и данных, частью которых является ячейка с таким значением, например, “DW 6675h”; “MOV AX, 74h”; напротив, во втором случае можно быть уверенным, что анализируется именно начало инструкции, а не нечто иное.
Разбивка потока байт на элементы позволяет решить и другую задачу – определить какой именно инструкции (или данным) принадлежит такой-то линейный адрес, т.е. по линейному адресу ячейки определить адрес головы элемента, которому эта ячейка принадлежит.
Манипулируя с флагами узнать это достаточно просто – достаточно проанализировать старшие 12 бит хвостовых атрибутов – флаги, расположенные по четному линейному адресу – содержат количество оставшихся байт до конца элемента, а флаги расположенные по нечетному линейному адресу – до его начала.
Реализация функции, возвращающей адрес головы элемента по любому принадлежащему элементу адресу может выглядеть так:
#include
static MyGetHead(ea)
{ auto off,F;
F=GetFlags(ea); if (!F) return -1;
//Адрес не принадлежит ни одной ячейке if (!(F & FF_TAIL))
//Это голова, возвращает ее адрес return ea; if (ea & 1)
// ...нечетный линейный адрес return (ea - (F >> 20));
87
// ...четный линейный адрес return MyGetHead(ea-1);
}
Недостатком такого подхода является его потенциальная несовместимость с последующими версиями IDA Pro, но обойтись для решения проблемы одними лишь высокоуровневыми функциями, встроенными в IDA, не получается.
Задача могла бы быть решена при помощи вызовов функций ItemSize и ItemEnd, предназначенных для вычисления длины и адреса конца элемента соответственно, очевидно, адрес начала элемента равен ItemEnd(ea) – ItemSize(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 ea) возвращает значение на единицу превышающее линейный адрес конца элемента
функции трассировки элементов
имя функции краткое описание long NextHead (long ea) long NextHead (long ea, long maxea) возвращает линейный адрес следующей головы элемента long PrevHead (long ea) long PrevHead (long ea, long minea) возвращает линейный адрес предыдущей головы элемента long NextNotTail (long ea) возвращает линейный адрес следующей головы элемента или неопределенного байта long PrevNotTail (long ea) возвращает линейный адрес предыдущей головы элемента или неопределенного байта
88
long ItemSize(long ea)
В контекстной помощи IDA сообщается, что эта функция определяет размер элемента, как и следует из ее названия. На самом же деле она возвращает расстояние в байтах до конца элемента, что доказывает следующий эксперимент: seg000:0000 aHelloIdaPro db 'Hello,IDA Pro!' seg000:000E a1234 db '1234' a) исходные данные auto a,b; a=SegByName("seg000"); for(b=0;b<0x12;b++)
Message(">ea:%X -%c-> %d\n",a+b,Byte(a+b),ItemSize(a+b)); b) скрипт, последовательно передающий функции различные адреса от начала объекта
>ea:1000 -H-> 14
>ea:1001 -e-> 13
>ea:1002 -l-> 12
>ea:1003 -l-> 11
>ea:1004 -o-> 10
>ea:1005 -,-> 9
>ea:1006 -I-> 8
>ea:1007 -D-> 7
>ea:1008 -A-> 6
>ea:1009 - -> 5
>ea:100A -P-> 4
>ea:100B -r-> 3
>ea:100C -o-> 2
>ea:100D -!-> 1
>ea:100E -1-> 4
>ea:100F -2-> 3
>ea:1010 -3-> 2
>ea:1011 -4-> 1 c) результат: функция ItemSize возвращает расстояние до конца объекта в байтах
Минимальное значение, возвращаемое функцией, равно единице. Это же значение возвращается при возникновении ошибки – если функции передать адрес, не принадлежащий ни одному элементу.
Альтернативная реализация функции ItemSize содержится в файле “kpnc.idc”, находящимся на диске, прилагаемом к этой книге. Ее исходный код приведен ниже (см.
MyGetItemSize) static MyGetItemHeadEA(ea)
{ if (GetFlags(ea) & FF_DATA) // голова return ea; if (GetFlags(ea) & FF_TAIL) // хвост return PrevHead(ea,0);
// если не голова и не хвост - ошибка return -1;
}
89
static MyGetItemSize(ea)
{ if (GetFlags(ea) & MS_CLS) // элемент? return ItemEnd(ea) - MyGetItemHeadEA(ea); return
-1;
}
Пример использования: seg000:0000 aHelloIdaPro db 'Hello,IDA Pro!' seg000:000E a1234 db '1234' a) исходные данные auto a,b; a=SegByName("seg000"); for(b=0;b<0x12;b++)
Message(">ea:%X -%c-> %d\n",a+b,Byte(a+b),MyGetItemSize(a+b)); b) скрипт, последовательно передающий функции MyGetItemSize различные адреса от начала объекта
Замечание: перед исполнением скрипта файл “kpnc.idc” должен быть загружен в
память. Это можно осуществить нажатием клавиши
>ea:1000 -H-> 15
>ea:1001 -e-> 15
>ea:1002 -l-> 15
>ea:1003 -l-> 15
>ea:1004 -o-> 15
>ea:1005 -,-> 15
>ea:1006 -I-> 15
>ea:1007 -D-> 15
>ea:1008 -A-> 15
>ea:1009 - -> 15
>ea:100A -P-> 15
>ea:100B -r-> 15
>ea:100C -o-> 15
>ea:100D -!-> 15
>ea:100E -1-> 5
>ea:100F -2-> 5
>ea:1010 -3-> 5
>ea:1011 -4-> 5 c) результат – корректное выполнение функции
??? #Верстальщику – Change Table аргумент пояснение ea линейный адрес, принадлежащий элементу
=return Пояснения
!=0 расстояние до конца элемента в байтах (не его размер!) return
==1 ошибка
Родственные функции: нет
90
Интерактивный аналог: нет long ItemEnd(long ea)
Функция возвращает значение на единицу превышающее линейный адрес конца элемента, заданного линейным адресом ea.
Если переданный адрес не принадлежит ни одному сегменту, функция возвратит единицу, сигнализируя об ошибке.
Замечание: если по указанному адресу расположен бестиповой байт (т.е.
переданный адрес не принадлежит ни одному элементу), функция возвратит не
ошибку, а линейный адрес следующей ячейки.
Пример использования: seg000:0000 aHelloIdaPro db 'Hello,IDA Pro!' seg000:000E a1234 db '1234' a) исходные данные
Message(“>%s\n”,atoa(ItemEnd(SegByName(“seg000”)))); b) вызываем функцию ItemEnd, передавая ей адрес, принадлежащий элементу
“Hello, IDA Pro!”.
>seg000:000E c) результат – функция вернула значение, на единицу превышающее адрес конца данного элемента
??? #Верстальщику Change Table аргумент пояснение ea линейный адрес, принадлежащий элементу или бестиповому байту return
=return пояснения
!=1 значение на единицу превышающее линейный адрес конца элемента
==1 ошибка
Родственные функции: нет
Интерактивный аналог: нет long NextHead(long ea)
(версия IDA 3.85 и младше)
Возвращает линейный адрес головы следующего элемента, в противном случае –
BADADDR, сигнализируя об ошибке. Переданный функции линейный адрес ea не обязательно должен принадлежать какому-то элементу – он может даже вообще не существовать.
Пример использования: seg000:0000 aHelloIdaPro db 'Hello,IDA Pro!' seg000:000E a1234 db '1234' a) исходные данные
91
Message(“>%s\n”,atoa(NextHead(SegByName(“seg000”)))); b) вызываем функцию NextHead, передавая ей адрес, принадлежащий элементу
“Hello, IDA Pro!”.
>seg000:000E c) результат – функция вернула адрес головы следующего элемента.
Другой пример использования функции NextHead приведен в файле