long SetMemberType(long id,long member_offset,long flag,long typeid,long nitems);
Функция позволяет изменять тип члена структуры. Он определяется флагом flag следующим образом:
Определение
Значение
Пояснения
FF_BYTE 0x00000000L
Байт
FF_WORD 0x10000000L
Слово
FF_DWRD 0x20000000L
Двойное слово
FF_QWRD 0x30000000L
Четвертное слово
FF_TBYT 0x40000000L
Восьмерное слово
FF_ASCI 0x50000000L
ASCII строка
FF_STRU 0x60000000L
Структура
FF_RESERVED 0x70000000L
Зарезервировано
FF_FLOAT 0x80000000L
Float
FF_DOUBLE 0x90000000L
Double
FF_PACKREAL
0xA0000000L
Packed decimal real
FF_ALIGN
0xB0000000L
Директива выравнения
В зависимости от состояния флага, значение аргумента typeid может трактоваться по-разному.
Состояние flag
Значение typeid
FF_STRU
ID структуры
FF_ASCII
Тип ASCII строки (см. таблицу ниже)
Другое
BADADDR
Обратите внимание, что если новый член структуры не представляет собой ни вложенную структуру, ни ASCII строку, то аргумент typeid должен быть равен BADADDR
Определение
Значение
Пояснения
278
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
При этом необходимо, что бы для нового члена хватало места. Если его размер превосходит предыдущий, а следующие за ним смещения принадлежат остальным членам, то тип члена не будет изменен
Пример:
0000 MY_STRUC struc ; (sizeof=0x9)
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("0x%X \n",
SetMemberType(
GetStrucIdByName("MY_STRUC"),
2,
FF_DWRD,
-1,
4));
0000 MY_STRUC struc ; (sizeof=0x9)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
0009 MY_STRUC ends
0009 0x0
Напротив, если новый член занимает места меньше чем старый, то преобразование происходит без проблем, а «лишние» байты помечаются, как неопределенные.
Например:
0000 MY_STRUC struc ; (sizeof=0x9)
0000 field_0 dw ?
0002 field_2 dw ?
0004 field_4 dw ?
0006 field_6 dw ?
0008 MyGoodMember db ?
279 0009 MY_STRUC ends
0009
Message("0x%X \n",
SetMemberType(
GetStrucIdByName("MY_STRUC"),
2,
FF_BYTE,
-1,
1))
0000 MY_STRUC struc ; (sizeof=0x9)
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
Значение
!=BADADDR
Смещение нового члена структуры
Offset
==BADADDR
Добавить новый член в конец flag Новый тип члена typeid Идентификатор структуры или тип ASCII-строки
Nbytes Размер нового члена в байтах
==return Пояснения
==1
Успешное завершение
Return
==0
Ошибка (см коды завершения выше)
long SetMemberComment(long id,long member_offset,char comment,long repeatable); Функция устанавливает комментарий, связанный с членом структуры. IDA поддерживает два типа комментариев – ‘regular’ и ‘repeatable’.
Последний отличается тем, что дублируется по месту обращения к элементу обращения структуры. Однако в случае со структурами и их членами, IDA игнорирует это требование, в чем убеждает следующий пример:
0000 MyStruc struc ; (sizeof=0x4) ; XREF: seg000:0F72r
0000 field_0 dw ? ;
My Repeatable comment
0002 field_1 dw ? ;
0004 MyStruc ends seg000:0F72*stru_0_F72 dw 0 ; field_0
; DATA XREF: sub_0_F56r seg000:0F72* ; sub_0_2456+1Cw ... seg000:0F72* dw 0 ; field_1 seg000:0F56 mov es, stru_0_F72.
field_0
seg000:0F5A assume es:nothing seg000:0F5A xor bx, bx
280
Остается только надеяться, что в будущем рано или поздно такая поддержка появится.
Операнд
Пояснения id Идентификатор (ID) структуры comment
Комментарий члена member_offset
Смещение, лежащее в границах интересующего нас элемента
Флаг
Пояснения
0
Неповторяемый комментарий
Repeatable
1
Повторяемый комментарий
==return Пояснения
==1
Успешное завершение
Return
==0
Ошибка (см коды завершения выше)
ПЕРЕЧИСЛЕНИЯ 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
281
; ------------------------
; enum enum_3 enum_3_14E = 1 enum_3_0 = 2Ch enum_3_2D = 14Dh
Однако, в отличие от структуры элементы перечисления не имеют ни типа, ни размера. Точнее тип определяется только на стадии ассемблирования.
Так, например, если enum_1_0 равен трем, это еще не означает, что он имеет тип байт. Вполне вероятно, что он окажется словом или даже и словом и байтом одновременно, например:
MOV AL, enum_1_0
CMP AX, enum_1_0
Этот код, не смотря на всю его чудаковатость, все же будет успешно ассемблирован!
Но если нет типов, и не возможно вычислить размер членов, то как же тогда осуществить к ним доступ?
Теоретически было можно условиться, что каждый член занимает 32 байта
(двойное слово) и организовать к ним доступ точно так, как и в структурах. И это бы неплохо работало!
Но разработчик IDA пошел по другому пути - он связал каждый член с идентификатором! Разумеется, существует функция, возвращающая идентификатор по имени функции и наоборот.
В свете этого становиться еще более непонятым, какой смысл имеет «группировка» перечислений. Имена членов – глобальные, идентификаторы – тем более. Что же дает принадлежность элемента к той или иной группе?
В каждой группе может существовать
не более одной константы с одним и тем же
значением. С первого взгляда не понятно ни как можно «жить» с этим, ни какие мотивы побудили принять разработчика такое нелепое ограничение.
Однако, на самом деле это следствия выбранной архитектуры. И весьма удачной,
стоит только взглянуть на нее изнутри, чем мы сейчас и займемся.
282
АРХИТЕКТУРА ПЕРЕЧИСЛЕНИЙ Прежде чем углубляться в технические дебри реализации и архитектуры перечислений, зададимся простым вопросом, - что же по сути представляют собой члены перечислений?
Разумеется, это операнды, или еще точнее иная форма представления непосредственных операндов. В главе, посвященной объективной модели IDA уже отмечалось, что один и тот же операнд может быть по-разному отображен на экране дизассемблера. Он может быть не только непосредственным значением, но и смещением, например.
Однако, перечисления – это не просто иная форма отображения операнда на экране – с точки зрения IDA это элемент bTree, который может ссылаться на линейный адрес, объекта… впрочем, не стоит повторяться, об этом уже писалось выше.
Но если каждый сегмент (имя, комментарий, функция) связан только с одним линейным адресом, то
одно и то же перечисление может повторяться в десятке разных мест! И поэтому старые методы для него не подходят!
Поэтому был использован тот же механизм, который был создан для поддержки структур. Каждый объект ссылался на
тег структуры, а операнд указывал на требуемый элемент внутри ее.
Точно то же происходит и с перечислениями. Есть список перечислений, на который ссылается объект. Элементы списка просматриваются до тех пор, пока не найдется элемент совпадающий по значению с операндом, объекта.
Обратите внимание еще раз на тот факт, что и структура и
перечисление связываются не с операндом, а с обладающим им объектом, а точнее линейным адресом его начала.
Представление операнда в виде члена структуры или перечисление происходит на втором этапе, – и жесткой связки тут нет, простой поиск на совпадение значений.
Но если в структуре смещение каждого члена уникально, то есть никакие два члена не могут быть расположены по одному и тому же смещению, то в перечислениях два разных элемента
могут иметь одно и то же значение.
Вот, собственно и ответ на вопрос о необходимости поддержки более чем одного списка перечислений, а заодно и тактика группировки элементов. То есть главным критерием должно быть не родственность каких-то признаков, а гарантия непопадающих значений.
При этом разумно стремиться к уменьшению числа списков, поскольку, как уже говорилось выше, для представления операнда в виде перечисления достаточно сослаться на список, и IDA самостоятельно подберет нужный элемент!
В идеале, если у нас всего один список (что бывает достаточно часто) необходимо перевести курсор на нужную строку и нажать
, как IDA все сделает автоматически.
Программная работа, в отличие от интерактивной, несколько сложнее. Кроме того, теги списков (это не теги, конечно, но иного названия просто нет, - поэтому будет считать, что это как бы теги) вообще практически не фигурируют.
Действительно, все члены связаны с уникальными глобальными идентификаторами, да и имена каждого из них не менее уникальны.
МЕТОДЫ
Функция
Назначение long GetEnumQty(void)
Возвращает число типов перечислений long GetnEnum(long idx)
Возвращает идентификатор перечисления по ее индексу long GetEnumIdx(long enum_id);
Возвращает индекс перечисления по его
283
идентификатору long GetEnum(char name)
Возвращает идентификатор перечисления по его имени char GetEnumName(long enum_id)
Возвращает имя перечисления по его идентификатору 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 SetEnumIdx(long enum_id,long idx)
Задает индекс перечисления в списке
long GetEnumQty(void);
Функция возвращает число типов перечислений. Все они могут быть отображены вызовом списка командой меню View \ Enumeration’s
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
284
Message(“0x%X \n”,
GetEnumQty()
);
0x2
==return
Пояснения
!=0
Число перечислений
Return
==0
Нет ни одного перечисления
long GetnEnum(long idx); Функция возвращает ID перечисления по индексу. Как уже отмечалось выше, индекс не может точно идентифицировать связанное с ним перечисление, поскольку при любых операциях связанных с дополнением или удалением перечислений, список перестраивается, и тот же индекс уже может указывать совсем на другое перечисление.
В отличие от этого, идентификатор (ID) перечисления представляет собой уникальное 32-битное значение, всегда указывающие на одно и ту же перечисление. Более того, даже если перечисление,
связанное с конкретным идентификатором, было удалено, гарантируется, что тот же идентификатор не будет выдан ни одному созданному после этого перечислению.
Это гарантирует непротиворечивость ситуации и позволяет совместно использовать один и тот же идентификатор различным скриптам.
Пример использования:
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
Message(“0x%X 0x%X \n”, a,GetEnumId(a)
);
0x0 0xFF0000F0 0x1 0xFF0000FE
Идентификатор, как и дескриптор, с точки зрения пользователя являются абстрактным «магическим» числом, интерпретировать которое допускается только операционной системе (в качестве которой выступает в данном случае IDA).
Операнд Пояснения index Индекс перечисления в списке (от нуля до GetEnumQty()-1)
Return
==return Пояснения
285
!=BADADDR
Идентификатор (ID) перечисления
==BADADDR
Ошибка
long GetEnumIdx(long enum_id);
Функция возвращает индекс перечисления по ее идентификатору. Необходимо помнить, что индексы не связаны жестко с перечислениями и при каждой операции удаления или добавления новых перечислений тем же индексам уже могут соответствовать новые перечисления.
Пример использования:
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("0x%X \n",
GetEnumIdx(
GetEnum("enum_1")
)
);
0x0
Message("0x%X \n",
GetEnumIdx(
GetEnum("enum_2")
)
);
0x1
Операнд Пояснения
ID Идентификатор перечисления
==return Пояснения
!=BADADDR
Индекс перечисления
Return
==BADADDR
Ошибка
long GetEnum(char name);
Функция возвращает идентификатор перечисления по его имени. Если перечисления с указанным именем не существует, то функция возвращает ошибку –
BADADDR.
Пример использования:
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
286
FFFFFFFF
FFFFFFFF ; ------------------------
FFFFFFFF
FFFFFFFF ; enum enum_2
FFFFFFFF enum_2_0 = 16h
Message("0x%X \n",
GetEnum("enum_1")
);
0xFF000131
Message("0x%X \n",
GetEnum("enum_2")
);
0xFF000132
Message("0x%X \n",
GetEnum("enum_3")
);
0xFFFFFFFF
Операнд Пояснения name Имя перечисления
==return Пояснения
!=BADADDR
Идентификатор перечисления
Return
==BADADDR
Ошибка
char GetEnumName(long enum_id);
Функция возвращает имя перечисления по его идентификатору. Если указанному идентификатору не соответствует ни одно перечисление функция возвращает пустую строку.
Например:
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(
GetnEnum(1)
)
);
enum_2
287
Операнд Пояснения
Enum_id ID перечисления
==return Пояснения
!=””
Имя перечисления
Return
==””
Ошибка
char GetEnumCmt(long enum_id,long repeatable)
Возвращает комментарий перечисления, заданного идентификатором.
Комментарии бывают двух типов – постоянные и повторяемые. Постоянные отображаются только впереди перечисления, а повторяемые при обращении к каждому из его членов.
FFFFFFFF ;
My Enum regulag commnet
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2 seg000:0046 rol bx, enum_1_0
Message(“%s \n”,
GetEnumCmt(
GetEnum(“enum_1”),
0);
My Enum regulag commnet
FFFFFFFF ; My Enum repeatable commnet
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2 seg000:0046 rol bx, enum_1_0 ;
My Enum
Repeatable commnet
Message(“%s \n”,
GetEnumCmt(
GetEnum(“enum_1”),
1);
My Enum Repeatable commnet
Операнд
Пояснения id Идентификатор (ID) перечисления
Флаг
Пояснения
0
Неповторяемый комментарий
Repeatable
1
Повторяемый комментарий
Завершение
Пояснения
!=””
Комментарий
Return
“”
Ошибка
288
long GetEnumSize(long enum_id);
Функция возвращает число членов перечисления, заданного идентификатором.
Обратите внимание, именно число элементов, а не занимаемый ими размер, который вообще вычислить невозможно, поскольку члены перечисления не имеют типа.
Пример:
FFFFFFFF ; enum enum_1
FFFFFFFF enum_1_0 = 1
FFFFFFFF enum_1_2 = 2
FFFFFFFF
Message("0x%X \n",
GetEnumSize(
GetEnum("enum_1")
)
);
0x2
Если перечисление пусто, то функция возвращает ноль, но то же значение возвращается, если указать неверный идентификатор, поэтому возникает неоднозначная ситуация – либо перечисление отсутствует (было удалено?) либо же попросту пусто.
Пример:
FFFFFFFF ; enum enum_2
Message("0x%X \n",
GetEnumSize(
GetEnum("enum_2")
)
);
0x0
Message("0x%X \n",
GetEnumSize(BADADDR)
);
0x0
Операнд Пояснения
Enum_id Идентификатор перечисления
==return Пояснения
!=0
Число членов перечисления
Пустое перечисление
Return
==0
Ошибка