Главная страница
Навигация по странице:

  • SetFunctiinEnd(0x12305,0x12310)

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


    Скачать 2.86 Mb.
    НазваниеОписание функций встроенного языка
    Анкорobraz_mihlenia_-_dizassembler_IDA
    Дата29.09.2022
    Размер2.86 Mb.
    Формат файлаpdf
    Имя файлаobraz_mihlenia_-_dizassembler_IDA.pdf
    ТипСправочник
    #704305
    страница16 из 39
    1   ...   12   13   14   15   16   17   18   19   ...   39
    процедурой, а возвращающую функцией. Именно такая классификация и была использована в языке Pascal.
    Разумеется, было много путаницы. Мало того, что трудно представить себе процедуру, совсем ничего не возвращающую. По крайней мере она выводит что-то на экран, или в порты ввода-вывода, пускай даже меняет значения глобальных переменных – то есть все-таки что-то возвращает.
    Потом, любая процедура имеет доступ к аргументам, передаваемым по ссылке, и может их модифицировать, даже если она ничего не возвращает с точки зрения языка.
    Иными словами выражение:
    Resultant := MyProc (arg1, ard2);
    С точки зрения языка Pascal бессмысленно, процедура не может ничего возвращать.
    Если опуститься на уровень реализации компилятора Turbo-Pascal, то грубо говоря, функции возвращают результат своей работы в регистре AX, а процедуры оставляют его неопределенным.
    То есть другими словами, функцией называется то, что возвращает результат своей работы в регистре AX.
    В языке Си все совсем иначе. Не зависимо от того, возвращает что ни будь подпрограмма или нет, она называется функцией.
    Процедур в Си нет. Функция всегда что-то возвращает, по крайней мере бестиповое значение void, которые можно присвоить переменной.
    Не будем вспоминать сейчас те языки, где классификация подпрограмм еще более запутана или вовсе не развита.
    Сейчас же важно представить себе, как будет работать со всем этим IDA. Знает ли она что-нибудь об этих языковых конструкциях?
    Для ответа на этот вопрос необходимо уточнить, что нужно понимать под термином
    «знает».
    Прежде всего она, как и любой другой дизассемблер явно поддерживает только те конструкции, которые «понимает» целевой ассемблер.
    С другой стороны, автоматический анализатор способен распознавать различные языковые конструкции, которые могут напрямую и не поддерживаться ассемблером.
    Популярные ассемблеры MASM и TASM кроме низкоуровневой поддержки подпрограмм, обеспечивают ряд типовых операций с ними, такими как, например, передача аргументов через стек и обращения к ним, абстрагируясь от модели памяти (все вычисления ложатся на плечи ассемблера).
    Однако, эту конструкцию создатели языка почету-то окрестили процедурой, чем окончательно всех запутали. С одной стороны ассемблер не может автоматически присваивать переменным и регистрам результат, возвращенный процедурой, но с другой это еще никак не означает, что процедура не может ничего возвращать.
    Воистину, «называемое дао, не есть дао». Впрочем, само по себе такой расклад вещей достаточно приемлем – поскольку, ассемблер, как и любой другой язык, имеет свои соглашения и может совсем не интересоваться, как та же конструкция называется у других.
    Но ведь IDA это не просто дизассемблер, но и немного декомпилятор, то есть мотивом ее использования, зачастую служит необходимость получить хотя бы отдаленное представление об исходном тексте программы, пусть и в другой форме.

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

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

    156
    success MakeFunction(long start,long end);
    Создает функцию success DelFunction(long ea);
    Удаляет функцию success SetFunctionEnd(long ea,long end);
    Изменяет линейный адрес конца функции long NextFunction(long ea);
    Возвращает линейный адрес начала следующей функции long PrevFunction(long ea)
    Возвращает линейный адрес начала предыдущей функции long GetFunctionFlags(long ea);
    Возвращает атрибуты функции success SetFunctionFlags(long ea,long flags);
    Устанавливает атрибуты функции char GetFunctionName(long ea);
    Возвращает имя функции void SetFunctionCmt(long ea, char cmt, long repeatable);
    Устанавливает комментарий функции
    (постоянный и повторянымый) char GetFunctionCmt(long ea, long repeatable);
    Возвращает комментарий функции long ChooseFunction(char title);
    Открывает диалоговое окно со списком всех функций char GetFuncOffset(long ea);
    Преобразует линейный адрес к строковому смещению от начала функции long GetFrame(long ea);
    Возвращает ID фрейма функции long GetFrameLvarSize(long ea);
    Возвращает размер фрейма функции long GetFrameLvarSize(long ea);
    Возвращает размер локальных переменных функции в байтах long GetFrameArgsSize(long ea)
    Возвращает размер аргументов функции в байтах long GetFrameSize(long ea);
    Возвращает полный размер стекового фрейма в байтах long MakeFrame(long ea,long lvsize,long frregs,long argsize)
    Создает фрейм функции или модифицирует уже существующий long GetSpd(long ea);
    Возвращает значение регистра SP в произвольной точке функции long GetSpDiff(long ea);
    Возвращает относительное изменение регистра SP указанной инструкцией

    157
    success SetSpDiff(long ea,long delta);
    Корректирует изменение регистра SP, указанной командой long FindFuncEnd(long ea)
    Определяет линейный адрес конца функции
    success MakeFunction(long start,long end);
    Вызов MakeFunction позволяет создавать функцию. IDA не различает функций и процедур – в ее терминологии все это функции.
    Каждая функция обладает принадлежащим ей непрерывным диапазонов адресов.
    В его границах может отслеживаться значения указателя стека, создаются ссылки на следующие инструкции и так далее. То есть ряд вызовов API работает исключительно с функциями.
    Для создания функции достаточно только указать линейный адрес ее начала и конца. Функции могут создаваться только внутри сегментов и располагаться только с начала, а не середины машинных инструкций.
    В то же время допускается в качестве конца задавать адрес, приходящейся на середину инструкции. IDA все равно его округлит до адреса конца предыдущей инструкции.
    Например: 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
    MakeFunction(0x1002A,0x10037); 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
    При этом функции автоматически дается имя, вид которого зависит от настоек. По умолчанию оно предваряется префиксом ‘sub’ (от subroutine - то есть процедура; забавно
    – ведь в терминологии IDA она называется функцией) и последующим смещением внутри сегмента.
    Если вместо адреса конца функции указать константу BADADDR, то IDA попытается самостоятельно определить его.
    Этот механизм довольно бесхитростен (концом функции считается инструкция ret или jmp) и довольно часто приводит к ошибкам и занижает адрес.

    158
    Разберем, например, такой случай. Путь некая функция имеет более одной точки выхода. В этом случае IDA часто принимает за конец функции первый встретившийся их них, а второй так и остается в «хвосте».
    Это не упрек в несовершенстве алгоритма, а просто предостережения от всецелого доверия ему. В действительности же машинный анализ никогда не станет настолько совершенным, что бы полностью заменить человека.
    Обратите внимание, что вызов MakeFunction провалится, если выделенная под функцию область будет помечена как undefined. Что и видно из следующего примера: seg000:002A db 0BEh seg000:002B db 11h seg000:002C db 2 seg000:002D db 0E8h seg000:002E db 0ADh seg000:002F db 0 seg000:0030 db 0BEh seg000:0031 db 0BAh seg000:0032 db 2 seg000:0033 db 0E8h seg000:0034 db 0A7h seg000:0035 db 0 seg000:0036 db 0C3h
    Message(“0x%X \n”,MakeFunction(0x1002A,0x10037));
    0
    Но в то же время, если в качестве адреса конца указать константу BADADDR, то функция будет успешно создана! seg000:002A db 0BEh seg000:002B db 11h seg000:002C db 2 seg000:002D db 0E8h seg000:002E db 0ADh seg000:002F db 0 seg000:0030 db 0BEh seg000:0031 db 0BAh seg000:0032 db 2 seg000:0033 db 0E8h seg000:0034 db 0A7h seg000:0035 db 0 seg000:0036 db 0C3h
    Message(“0x%X \n”,MakeFunction(0x1002A,-1));
    1 seg000:002A ; _______________ S U B R O U T I N E
    _______________________________________ seg000:002A seg000:002A seg000:002A sub_0_2A proc near seg000:002A mov si, 211h

    159
    seg000:002D call sub_0_DD seg000:0030 mov si, 2BAh seg000:0033 call sub_0_DD seg000:0036 retn seg000:0036 sub_0_2A endp seg000:0036 seg000:0037 seg000:0037 ; _______________ S U B R O U T I N E
    _______________________________________
    Операнд
    Пояснения
    Start
    Линейный адрес начала функции. Функция не может начинаться с середины инструкции
    ==end
    Пояснения
    !=-1
    Линейный адрес конца функции. Может приходиться на середину инструкции. IDA его округлит до адреса конца предыдущей инструкции.
    End
    ==-1 IDA автоматически вычисляет адрес конца функции и при необходимости преобразует undefined в инструкции
    Завершение
    Пояснения
    0
    Вызов завершился не успешно. Функция не была создана
    Return
    1
    Вызов завершился Успешно
    success DelFunction(long ea);
    Вызов DelFunction позволяет удалять функцию, указав любой, принадлежащий ей адрес. Вместе с функцией уничтожаются все локальные переменные, и аргументы, если они есть. Все остальное (инструкции, перекрестные ссылки, метки) остается нетронутым.
    Например:
    .text:00400FFF ; _____________ S U B R O U T I N E
    ____________________________________
    .text:00400FFF
    .text:00400FFF ; Attributes: library function
    .text:00400FFF
    .text:00400FFF
    __amsg_exit proc near
    ; CODE
    XREF: __setenvp+4Ep
    .text:00400FFF ;
    __setenvp+7Dp ...
    .text:00400FFF
    .text:00400FFF arg_0 = dword ptr 4
    .text:00400FFF
    .text:00400FFF cmp dword_0_408758, 2
    .text:00401006 jz short loc_10_40100D
    .text:00401008 call __FF_MSGBANNER
    .text:0040100D
    .text:0040100D loc_10_40100D: ; CODE
    XREF: __amsg_exit+7j
    .text:0040100D push [esp+
    arg_0
    ]

    160
    .text:00401011 call __NMSG_WRITE
    .text:00401016 push 0FFh
    .text:0040101B call off_0_408050
    .text:00401021 pop ecx
    .text:00401022 pop ecx
    .text:00401023 retn
    .text:00401023
    __amsg_exit endp
    DelFuncton(0x400FFF);
    .text:00400FFF __amsg_exit: ; CODE
    XREF: __setenvp+4Ep
    .text:00400FFF ;
    __setenvp+7Dp ...
    .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 0FFh
    .text:0040101B call off_0_408050
    .text:00401021 pop ecx
    .text:00401022 pop ecx
    .text:00401023 retn
    Операнд
    Пояснения ea
    Любой линейный адрес, принадлежащий функции
    Завершение
    Пояснения
    0
    Вызов завершился не успешно. Функция не была создана
    Return
    1
    Вызов завершился Успешно
    success SetFunctionEnd(long ea,long end);
    Позволяет изменить линейный адрес конца функции. Для этого достаточно лишь передать любой адрес, принадлежащий функции и новое значение конца.
    Например: seg000:22C0 start proc near seg000:22C0 call sub_0_22DD seg000:22C3 call sub_0_2325 seg000:22C6 call sub_0_235B seg000:22C9 call sub_0_2374 seg000:22CC call sub_0_23B6 seg000:22CF call sub_0_23F8 seg000:22D2 jnz loc_0_22DA seg000:22D4 nop seg000:22D5 nop

    161
    seg000:22D6 nop seg000:22D7 call sub_0_2412 seg000:22DA seg000:22DA loc_0_22DA: seg000:22DA call sub_0_2305 seg000:22DA start endp
    SetFunctionEnd(0x122C3,0x122СF); seg000:22C0 start proc near seg000:22C0 call sub_0_22DD seg000:22C3 call sub_0_2325 seg000:22C6 call sub_0_235B seg000:22C9 call sub_0_2374 seg000:22CC call sub_0_23B6 seg000:22CF call sub_0_23F8 ;
    Æ источник seg000:22CF
    start endp seg000:22D2 jnz loc_0_22DA ;
    Å приемник seg000:22D4 nop seg000:22D5 nop seg000:22D6 nop seg000:22D7 call sub_0_2412 seg000:22DA seg000:22DA loc_0_22DA: seg000:22DA call sub_0_2305
    Однако при этом не удаляется перекрестная ссылка на следующую команду, что может иметь неприятные последствия, например, при попытке пометить функцию как undefined, что и видно на следующем примере:
    MakeUnkn(0x122C0,1); seg000:22C0 start db 0E8h ; ш seg000:22C1 db 1Ah ; seg000:22C2 db 0 ; seg000:22C3 db 0E8h ; ш seg000:22C4 db 5Fh ; _ seg000:22C5 db 0 ; seg000:22C6 db 0E8h ; ш seg000:22C7 db 92h ; Т seg000:22C8 db 0 ; seg000:22C9 db 0E8h ; ш seg000:22CA db 0A8h ; и seg000:22CB db 0 ; seg000:22CC db 0E8h ; ш seg000:22CD db 0E7h ; ч seg000:22CE db 0 ; seg000:22CF db 0E8h ; ш seg000:22D0 db 26h ; & seg000:22D1 db 1 ; seg000:22D2 db 75h ; u seg000:22D3 db 6 ; seg000:22D4 db 90h ; Р seg000:22D5 db 90h ; Р seg000:22D6 db 90h ; Р

    162
    Кроме того, если в качестве нового конца указать адрес, уже принадлежащий какой-нибудь функции (разумеется, кроме текущей), то вызов провалиться. 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 seg000:230B seg000:230D seg000:230D ; _______________ S U B R O U T I N E seg000:230D seg000:230D seg000:230D sub_0_230D proc near seg000:230D mov si, 2C51h seg000:2310 call sub_0_DD seg000:2313 mov si, 2C4Dh seg000:2316 call sub_0_2E2 seg000:2319 jnb loc_0_2321 seg000:231B nop seg000:231C nop seg000:231D nop seg000:231E mov si, 2A2Dh seg000:2321 seg000:2321 loc_0_2321: seg000:2321 call sub_0_DD seg000:2324 retn seg000:2324 sub_0_230D endp
    Message(“0x%X \n”,
    SetFunctiinEnd(0x12305,0x12310)
    );
    1
    Если функция возвращает отличное от нуля число, то это признак не успешности завершения операции. Следовательно, адрес конца не был изменен. (Повторный дамп для экономии не приводится)
    Напротив, если за концом функции расположены данные (можно даже массив), то новый адрес конца будет успешно установлен. seg000:292F sub_0_292F proc near seg000:292F inc bx seg000:2930 loop loc_0_292F seg000:2932 nop seg000:2933 retn seg000:2933 sub_0_292F endp seg000:2933 seg000:2933 ; ---------------------------------- seg000:2934*word_0_2934 dw 0 seg000:2934* seg000:2936*byte_0_2936 db 0

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


    написать администратору сайта