SetFuctionEnd(0x12930,0x12935)
);
0 seg000:292F sub_0_292F proc near seg000:292F inc bx seg000:2930 loop loc_0_292F seg000:2932 nop seg000:2933 retn seg000:2933 seg000:2933 ; ---------------------------------- seg000:2934*word_0_2934 dw 0 seg000:2934* seg000:2936*byte_0_2936 db 0
Однако, в действительности, то, что конец функции не отображается на экране, еще ничего не значит. Попробуем убедиться, что IDA действительно не выполнила округления, и адрес 0x12936 не принадлежит функции.
Message(“0x%X \n”,
SetFuctionEnd(0x12936,0x12933)
);
1
Ага, вызов SetFunctionEnd возвратил ошибку, следовательно, адрес 0x12936 действительно не принадлежит функции. Попробуем теперь уменьшить его на единицу:
Message(“0x%X \n”,
SetFuctionEnd(0x12935,0x12933)
164
);
0 seg000:292F sub_0_292F proc near seg000:292F inc bx seg000:2930 loop loc_0_292F seg000:2932 nop seg000:2933 retn seg000:2933 seg000:2933 ; ---------------------------------- seg000:2934*word_0_2934 dw 0 seg000:2934*
sub_0_292F endp seg000:2936*byte_0_2936 db 0
Выходит, что как это ни парадоксально, но линейный адрес конца функции лежал посередине ячейки word_02934, где он, разумеется, не мог быть отображен. Описание этой ошибки IDA (которая должна быть устранена в последующих версиях), вероятно, не стоил бы такого пристального внимания, если бы эти таинственные исчезновения конца функций не случались так часто.
Это не нарушает работоспособности дизассемблера, но сбивает с толку пользователя и вызывает в нем мнимое подозрение, что в базе IDA серьезные сбои или ошибки, и что лучше ее удалить и начать проект заново, чем работать с ошибками, которые еще не известно чем могут обернуться в будущем.
На самом деле никаких поводов для беспокойства нет. Необходимо поправить адрес конца функции и можно продолжить работать дальше.
Операнд
Пояснения ea
Любой линейный адрес, принадлежащий функции end
Новый линейный адрес конца функции.
Завершение
Пояснения
0
Вызов завершился не успешно. Функция не была создана
Return
1
Вызов завершился Успешно
long NextFunction(long ea); Вызов возвращает линейный адрес начала функции следующий за ‘ea’. Что бы получить адрес первой функции необходимо вызвать NextFunction(0).
Если
больше ни одной функции возвратить невозможно, то функция возвращает ошибку BADADDR.
Пример использования: seg000:0000
sub_0_0 proc near seg000:0000 push ax seg000:0001 push bx
…………….. seg000:0027 pop bx seg000:0028 pop ax seg000:0029 retn seg000:0029 sub_0_0 endp
165
seg000:0029 seg000:002A seg000:002A ; ___________ S U B R O U T I N E
____________________ seg000:002A seg000:002A seg000:002A
sub_0_2A proc near seg000:002A mov si, 211h seg000:002D call sub_0_DD seg000:0030 mov si, 2BAh seg000:0033 call sub_0_DD seg000:0036 retn seg000:0036 sub_0_2A endp seg000:0036 seg000:0037 seg000:0037 ; _______________ S U B R O U T I N E
________________ seg000:0037 seg000:0037 seg000:0037
sub_0_37 proc near seg000:0037 seg000:0037 seg000:0037 push ax seg000:0038 push bx auto a; a=0; while ((a=NextFunction(a))!=-1)
Message("%x \n",a);
10000 1002a
10037
Операнд
Пояснения ea
Линейный адрес
Завершение
Пояснения
!=BADADDR
Линейный адрес начала следующей функции
Return
BADADDR
Ошибка
long PrevFunction(long ea)
Вызов возвращает линейный адрес предыдущий функции. Что бы получить адрес последней функции необходимо вызвать PrevFunction(BADADDR). seg000:0000
sub_0_0 proc near seg000:0000 push ax seg000:0001 push bx
…………….. seg000:0027 pop bx seg000:0028 pop ax seg000:0029 retn seg000:0029 sub_0_0 endp
166
seg000:0029 seg000:002A seg000:002A ; ___________ S U B R O U T I N E
____________________ seg000:002A seg000:002A seg000:002A
sub_0_2A proc near seg000:002A mov si, 211h seg000:002D call sub_0_DD seg000:0030 mov si, 2BAh seg000:0033 call sub_0_DD seg000:0036 retn seg000:0036 sub_0_2A endp seg000:0036 seg000:0037 seg000:0037 ; _______________ S U B R O U T I N E
________________ seg000:0037 seg000:0037 seg000:0037
sub_0_37 proc near seg000:0037 seg000:0037 seg000:0037 push ax seg000:0038 push bx auto a; a=0x10038; while ((a=PrevFunction(a))!=-1)
Message("%x \n",a);
10037 1002a
10000
Операнд
Пояснения
Ea
Линейный адрес
Завершение
Пояснения
!=BADADDR
Линейный адрес начала предыдущей функции
Return
BADADDR
Ошибка
long GetFunctionFlags(long ea);
Вызов GetFunctionFlags позволяет узнать атрибуты функции. Назначение отдельных битов в возвращаемом значении показано в таблице ниже.
Определение
Значение
Пояснения
FUNC_NORET 0x00000001
L
Функция не возвращает управления
FUNC_FAR 0x00000002
L
FAR (далекая) функция
FUNC_LIB 0x00000004
Библиотечная функция
167
L
FUNC_STATIC 0x00000008
L
Статическая функция
FUNC_FRAME 0x00000010L
Функция использует для указателя кадра стека регистр BP
FUNC_USERFAR 0x00000020
L
Функция определена пользователем как далекая
(FAR)
FUNC_HIDDEN 0x00000040
L
Скрытая функция
Подробнее о каждом атрибуте будет рассказано ниже.
FUNC_NORET
Этот атрибут устанавливается тем функциям, что не возвращают управления командой ret. Однако в большинстве случаев IDA автоматически не присваивает его.
Так, например, в следующей функции он не установлен. seg000:2305 sub_0_2305 proc near seg000:2305 sti seg000:2306 call sub_0_1CA seg000:2309 mov ah, 4Ch seg000:230B int 21h seg000:230B sub_0_2305 endp
Message(“%b \n”,
GetFunctionFlags(0x12305) );
0
Если в вашей ситуации это обстоятельство окажется критичным, то можно написать собственный скрипт, выполняющий такие проверки и устанавливающий атрибуты через вызов SetFunctionFlags.
FUNC_FAR
«Далекая» функция. IDA считает далекими все функции, оканчивающиеся командой далекого возврата – retf.
Этот механизм достаточно несовершенен и может давать сбои. Так, например, в следующем фрагменте эмуляцию вызова
CALL FAR \ RET IDA интерпретировала, как далекий возврат из функции.
Это не упрек в сторону IDA, поскольку ради таких частных случаев бессмысленно вносить
дополнительные усовершенствования в дизассемблер, но учитывать этот факт всегда стоит, вручную корректируя адрес конца и атрибуты функции. seg000:048B sub_0_48B proc far seg000:048B seg000:048B pushf seg000:048C push cs seg000:048D push offset locret_0_499 seg000:0490 push word ptr ds:74Dh seg000:0494 push word ptr ds:74Bh seg000:0498 retf
168
seg000:0498 sub_0_48B endp ; sp = -0Ah seg000:0498 seg000:0499 ; ------------------------------------------ seg000:0499 seg000:0499 locret_0_499: seg000:0499 retn
Message(“%b \n”,
GetFunctionFlags(0x1048B)
);
10
FUNC_LIB
Таким флагом отмечены стандартные «библиотечные» функции. То есть те, чьи сигнатуры известны FLIRT.
.text:004010FF ;
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 0FFh
.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)
);
100
FUNC_FRAME
Функция использует в качестве указателя на кадр стека регистр BP (EBP).
IDA определяет это отслеживая последовательность
PUSH BP
MOV BP, SP
169
Большинство современных оптимизирующих компиляторов отказались от такого подхода, и адресуют локальные переменные и аргументы непосредственно по регистру ESP. IDA распознает такую ситуацию и отслеживает значение ESP по ходу исполнения процедуры, освобождая пользователя от львиной доли работы. seg000:20B8 ;
Attributes: bp-based frame seg000:20B8 seg000:20B8 sub_0_20B8 proc near seg000:20B8 seg000:20B8 seg000:20B8 var_80 = byte ptr -80h seg000:20B8 var_6B = byte ptr -6Bh seg000:20B8 var_62 = byte ptr -62h seg000:20B8 seg000:20B8
push bp
seg000:20B9 mov ah, 2Fh seg000:20BB int 21h seg000:20BB seg000:20BD push bx seg000:20BE
mov bp, sp
seg000:20C0 sub sp, 80h seg000:20C4 mov ah, 1Ah seg000:20C6 lea dx, [bp+
var_80
] seg000:20C9 int 21h
Message(“%b \n”,
GetFunctionFlags(0x4010FF)
);
10000
Обратите внимание, что IDA распознала эту комбинацию, даже когда команды push bp и mov bp,sp оказались достаточно разнесены.
FUNC_USERFAR
Этот атрибут IDA устанавливает, когда пользователь вручную меняет тип функции с NEAR на FAR, вызывая диалог ‘Modify Function’ с помощью команды меню Edit \ Function \ Edit Function.
170
Обратите внимание, что это не относится к вызовам SetFunctionFlags!
Последняя устанавливает именно тот набор атрибутов, который ей передается.
FUNC_HIDDEN
Этот атрибут устанавливается у «свернутых» функций. Свернуть любую функцию можно однократным нажатием Gray ‘-‘, переместив курсов в ее границы.
Кроме этого, в зависимости от настоек, IDA может автоматически сворачивать все библиотечные функции для экономии места. dseg:027B ; [00000009 BYTES: COLLAPSED FUNCTION sub_0_27B. PRESS KEYPAD "+" TO EXPAND]
Message(“%b \n”,
GetFunctionFlags(0x4010FF) );
100000
Заметим, что в результате упущения этот тип не определен в IDC.IDC и что бы им пользоваться необходимо это сделать самостоятельно, внеся новую строчку:
#define FUNC_HIDDEN 0x00000040L // a hidden function
Все атрибуты функция IDA
отображает в виде комментариев, расположенных в начале функции. dseg:0271 ;
Attributes: library function dseg:0271 dseg:0271 __checknull proc near
171
dseg:0271 retn dseg:0271 __checknull endp dseg:0272 ;
Attributes: library function bp-based frame dseg:0272 dseg:0272 __terminate proc
Операнд
Пояснения
Ea Линейный адрес, принадлежащий функции
Завершение
Пояснения
!=BADADDR
Набор атрибутов функции (смотри таблицу выше)
Return
BADADDR
Ошибка
success SetFunctionFlags(long ea,long flags);
Позволяет устанавливать атрибуты функции. Подробнее об этом было сказано в описании функции GetFunctionFlags.
Операнд
Пояснения
Ea
Линейный адрес, принадлежащий функции flag
Атрибуты функции (смотри таблицу в описании GetFunctionFlags)
Завершение
Пояснения
!=BADADDR
Набор атрибутов функции (смотри таблицу выше)
Return
BADADDR
Ошибка
Как уже отмечалось в описании функции SetFunctionFlags, часто IDA автоматически не распознает функции, не возвращающие управления (нет команды ref в завершении функции).
Если это критично, то нужный атрибут можно установить вручную. Покажем это на следующем примере: 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
SetFunctionFilegs
(
0x10272,
GetFunctionFlags(0x10272)
+
1
)
172
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
В большинстве случаев атрибуты никакого влияния на функции не оказывают. Так, например, если в приведенном примере сбросить флаг FUNC_FRAME, то это не повлечет за собой автоматического удаления всех локальных переменных, адресуемых через BP.
SetFunctionFilegs
(
0x10272,
GetFunctionFlags(0x10272)
–
0x10;
) 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+ - QUIT WITH EXIT dseg:0279 __terminate endp ; AL = exit code
Но вот установка флага FUNC_HIDDEN приведет к незамедлительному сворачиванию функции и сброс соответственно, наоборот.
SetFunctionFilegs
(
0x10272,
GetFunctionFlags(0x10272)
+
0x40;
) dseg:0272 ; [00000009 BYTES: COLLAPSED FUNCTION __terminate. PRESS KEYPAD "+" TO EXPAND]
char GetFunctionName(long ea);
Возвращает имя функции. Если указанный адрес не принадлежит ни одной из функций, то возвращается пустая строка.
Поскольку функции без имени не бывает, то неоднозначной ситуации не возникает.
Операнд
Пояснения
Ea Любой линейный адрес, принадлежащий функции
Return
Завершение
Пояснения
173
!=””
Имя функции
“”
Ошибка
Пример использования: dseg:025E
__cleanup proc near dseg:025E mov es, cs:DGROUP@ dseg:0263 push si dseg:0264 push di
Message(“%s \n”,
GetFunctionName(0x10263) );
__cleanup
void SetFunctionCmt(long ea, char cmt, long repeatable); Задает комментарий, который размещается впереди функции. IDA поддерживает символ переноса, поэтому комментарий может располагаться на нескольких строках.
Существует возможность повторять тот же комментарий в точке вызова функции
(так называемый repeatable comment). Для этого необходимо установить флаг ‘repeatable’ равным единице.
Например:
SetFunctionCmt(0x10271,”Hello IDA 4.0”,1);
dseg:0271 ;
Hello IDA 4.0
dseg:0271 ; Attributes: static dseg:0271 dseg:0271 __checknull proc near ; CODE XREF: sub_0_3C7+2Cp dseg:0271 retn dseg:0271 __checknull endp
Если перейти по перекрестной ссылке к месту вызова функции, то там будет можно обнаружить следующее: dseg:03F0 call __restorezero dseg:03F3 call __checknull ;
Hello IDA
4.0
dseg:03F6 cmp [bp+arg_2], 0 dseg:03FA jnz loc_0_40F
Если в строке
комментария будет присутствовать символ переноса, то экран будет выглядеть так:
SetFunctionCmt(0x10271,”Hello \nIDA 4.0”,1);
174
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
Не рекомендуется перегружать листинг повторяемыми комментариями. Ведь всегда можно обратиться за разъяснениями к самой функции.
Наиболее полезны они на начальной стадии дизассемблирования, когда назначение большинства функций плохо понятны и дать им осмысленное имя никак не удается. Тогда в повторяемом комментарии отражают все, что на данный момент известно о каждой из функций и по мере исследования текста, уточняют. На финальной же стадии дизассемблирования, повторяемые комментарии обычно убирают.
Обратите внимание, каждая функция может обладать комментариями сразу двух типов, но в заголовке будет отображаться только один из них – ‘regular’.
Например:
SetFunctionCmt(0x10271,”Hello IDA 4.0”,1);
SetFunctionCmt(0x10271,”Hello World”,0); 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 Строка комментария, включая символ переноса
Флаг
Пояснения
0 Неповторяемый комментарий
Repeatable
1 Повторяемый комментарий
char GetFunctionCmt(long ea, long repeatable);
Позволяет получить как повторяемый, так и постоянный комментарии. Для этого необходимо задать любой линейный адрес, принадлежащий функции.
Подробнее о повторяемых комментариях можно прочитать в описании функции
SetFunctionCmt
Например: 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(0x010271,1)
175
);
Hello, IDA 4.0
Message(“%s \n”,
GetFunctionCmt(0x010271,0)
);
Обратите внимание, что необходимо правильно указывать тип комментария
(повторяемый или нет) иначе функция вернет совсем не то, что от нее ожидается.
Операнд
Пояснения
Ea Любой линейный адрес, принадлежащий функции
Флаг
Пояснения
0 Неповторяемый комментарий
Repeatable
1 Повторяемый комментарий
Завершение
Пояснения
!=””
Комментарий
Return
“”
Ошибка
long ChooseFunction(char title);
Создает диалоговое окно содержащие список всех существующих функций с краткой сводной информацией о каждой из них.
Возвращает линейный адрес начала выбранной функции или BADADDR, если ни одна функция не была выбрана.
Пример использования:
Message(“0x%X \n”,
ChooseFunction(“List”)
);
176 0x401020
Поле
Function Name
Имя функции
Segment
Сегмент, владеющий функцией
Start
Линейный адрес начала
Length
Длина функции в байтах
Атрибут
Определение
Пояснение
R
!FUNC_NORET
Функция, возвращающая управление
F
FUNC_FAR
FAR (Далекая) функция
L
FUNC_LIB
Библиотечная функция
S
FUNC_STATIC
Static – функция
B
FUNC_FRAME
BP используется как указатель на кадр стека
* M
FUNC_MEMBER member function
* I
FUNC_VIRTUAL
Виртуальная функция
* C
FUNC_CTR
Конструктор
* D
FUNC_DTR
Деструктор
RFLSBMICDV
* V
FUNC_VARARG
Функция с переменным числом аргументов
* Не поддерживается в текущих версиях. Зарезервировано для будущего использования.
Подробнее узнать об атрибутах функции можно в описании SetFunctionFlags.
Операнд
Пояснения title Заголовок дианового окна
177
Завершение
Пояснения
!=BADADDR
Линейный адрес начала выбранной функции
Return
BADADDR
Ошибка
По умолчанию подсвечивается функция, в границах которой находится курсор. В противном случае подсвечивается ближайшая функция, лежащая
«выше» курсора (то есть в более младших адресах)
Для поиска подстроки в именах функции предусмотрена специальная клавишная комбинация
Регистр символов при этом будет игнорироваться.
Для продолжения поиска необходимо нажать .
В контекстной помощи сообщатся, что с помощью клавиш и можно соответственно добавлять или удалять функции. Но на самом деле в данном случае эти возможности недоступны.
или двойной щелчок мышью выбирают функцию и возвращают управление, закрывая диалоговое окно.