Распознавание кадров стека в стеке с использованием сохраненных значений EBP - PullRequest
1 голос
/ 08 февраля 2011

Я хотел бы разделить стек на кадры стека, просматривая необработанные данные в стеке.Я подумал об этом, найдя «связанный список» сохраненных указателей EBP.

  1. Можно ли предположить, что (стандартный и часто используемый) компилятор C (например, gcc) всегда будет обновляться исохранить EBP при вызове функции в прологе функции?

    pushl% ebp
    movl% esp,% ebp

    Или есть случаи, когда некоторые компиляторы могут пропуститьэта часть для функций, которые не получают никаких параметров и не имеют локальных переменных?

    x86 соглашения о вызовах и статья в вики о пролог функции don 'С этим сильно помочь.

  2. Есть ли лучший способ разделить стек на кадры стека, просто взглянув на его необработанные данные?

Спасибо!

Ответы [ 2 ]

3 голосов
/ 08 февраля 2011

В некоторых версиях gcc есть опция оптимизации -fomit-frame-pointer.Если память служит, она может использоваться даже с параметрами / локальными переменными (они индексируются непосредственно вне ESP вместо использования EBP).Если я не ошибаюсь, MS VC ++ может делать примерно то же самое.

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

2 голосов
/ 10 февраля 2011

Даже при оптимизированном указателе фреймов стековые кадры часто можно различить, просматривая в стеке сохраненные адреса возврата . Помните, что последовательность вызова функции в x86 всегда состоит из:

    call someFunc             ; pushes return address (instr. following `call`)
    ...
someFunc:
    push EBP                  ; if framepointer is used
    mov EBP, ESP              ; if framepointer is used
    push <nonvolatile regs>
    ...

так что ваш стек всегда - даже если указатели фреймов отсутствуют - там будут адреса возврата.

Как узнать обратный адрес?

  • для начала, на x86, инструкции имеют разную длину. Это означает, что адреса возврата - в отличие от других указателей (!) - имеют тенденцию быть смещенными значениями. Статистически 3 / 4 из них заканчиваются не кратными четырем.
    Любой неправильный указатель является хорошим кандидатом на обратный адрес.
  • затем, помните, что call инструкции для x86 имеют определенные форматы кода операции; прочитайте несколько байтов до обратного адреса и проверьте, есть ли там код операции call (в большинстве случаев 99%, это пять байтов назад для прямого вызова и три байта назад для вызова через регистр). Если это так, вы нашли обратный адрес.
    Это также способ отличить v ++ таблицы V ++ от адресов возврата по пути - точки входа vtable, которые вы найдете в стеке, но, оглядываясь назад, по тем адресам, которые вы не найдете call в инструкциях.

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

Подробности того, как собрать фактическую последовательность вызовов вместе из этих кандидатов, менее просты, хотя вам нужен дизассемблер и некоторая эвристика для отслеживания потенциальных потоков вызовов от самого низкого найденного адреса возврата вплоть до последней известной программы. место нахождения. Может быть, однажды я напишу об этом в блоге ;-), хотя в этот момент я бы скорее сказал, что поле для публикации в потоке стека слишком мало, чтобы содержать это ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...