Как программно считывать параметры функции из фреймов стека вызовов в Windows?

Я пытался пройтись по фреймам стека вызовов и извлечь из них некоторую информацию. Я могу извлекать имена файлов, номера строк и имена функций с помощью API StackWalk64, SymGetSymFromAddr64 и SymGetLineFromAddr64 из WinDBG.

Однако DWORD64 Params[4] в STACKFRAME64, которое является значением, возвращаемым из StackWalk64, поддерживает считывание только четырех 64-битных параметров функции из кадра. Хуже того, в 32-битной системе используются только младшие 32 бита Params[4], поэтому один параметр с более чем 32 битами требует двух или более элементов.

typedef struct _tagSTACKFRAME64 {
  ADDRESS64 AddrPC;
  ADDRESS64 AddrReturn;
  ADDRESS64 AddrFrame;
  ADDRESS64 AddrStack;
  ADDRESS64 AddrBStore;
  PVOID     FuncTableEntry;
  DWORD64   Params[4];
  BOOL      Far;
  BOOL      Virtual;
  DWORD64   Reserved[3];
  KDHELP64  KdHelp;
} STACKFRAME64, *LPSTACKFRAME64;

Мне не удалось найти API для чтения ВСЕХ параметров из кадра стека без ограничений.

Я думал использовать _9 _ / _ 10_ для извлечения значений из стека (x86 / x64) и регистров (x64). Но все же, если я это сделаю, можно будет получить только «возможные» значения параметров.

Есть ли какой-нибудь API, который я мог бы использовать для получения точных значений? Было бы даже лучше, если бы я мог получить тип и название параметров.


person stanleyli    schedule 17.04.2014    source источник
comment
Почему голос против? Это кажется разумным вопросом.   -  person Eric J.    schedule 18.04.2014
comment
Вы не можете получить точные значения, потому что компилятор может оптимизировать использование параметров таким образом, чтобы исходные параметры больше не сохранялись в стеке. (Например, на x64 параметры передаются в регистрах и могут никогда не быть записаны в стек.) Значения, которые вы получаете из STACKFRAME64, являются наилучшими.   -  person Raymond Chen    schedule 18.04.2014
comment
@RaymondChen: Верно, но я не могу придумать причину, по которой такой API не мог существовать. Вариант SymGetSymFromAddr64, в котором вы указываете ему стековой фрейм и индекс параметра, а не просто указатель.   -  person Mooing Duck    schedule 18.04.2014
comment
SymGetSymFromAddr64 дает вам символьное имя самой функции, которое является искаженным именем C ++. Если вы запустите это с помощью деманглера, вы можете получить типы параметров, хотя знания одного типа, вероятно, недостаточно для интерпретации члена Params. Но я думаю, что это все, что вам нужно.   -  person Mooing Duck    schedule 18.04.2014
comment
@MooingDuck: Спасибо за информацию. Да, похоже, извлеките имя символа, а затем попытайтесь интерпретировать параметры из регистров (x64), и стек - лучшее решение на данный момент ... Еще подождите, чтобы увидеть, есть ли какое-либо решение со стороны API.   -  person stanleyli    schedule 18.04.2014
comment
Кроме того, в 32-битных системах параметры могут быть только 32-битными. Один параметр с более чем 32 битами был разбит на 32-битные блоки. Таким образом, Params правильный, поскольку это именно то, что происходит внутри, и у него нет способа узнать, как их объединить.   -  person Mooing Duck    schedule 18.04.2014
comment
Если вы знаете соглашение о вызове функции и типы параметров (о которых вам может рассказать демонтаж), у вас есть шанс провести обратное проектирование того, как компилятор будет передавать эти параметры, поскольку многие соглашения о вызовах четко определены и легко найти спецификации.   -  person Remy Lebeau    schedule 18.04.2014
comment
Причина, по которой такой API не может существовать, - это причина, которую дал @RaymondChen. Операционная система не знает, что сделал компилятор.   -  person user207421    schedule 18.04.2014
comment
Параметр может даже не существовать. Например, если параметр не используется после строки 10, компилятор может повторно использовать память для чего-то другого. Вы вызываете другую функцию в строке 14, а затем спрашиваете, каково значение параметра 2? ' Ответ таков: информации больше не существует.   -  person Raymond Chen    schedule 18.04.2014
comment
@RaymondChen: Тогда есть идеи о том, как MSVC и WinDBG реализуют отображение значений параметров? Есть ли скрытые API?   -  person stanleyli    schedule 21.04.2014
comment
Даже MSVC и WinDBG не могут получить параметры, если они больше не существуют.   -  person Raymond Chen    schedule 21.04.2014
comment
@ user3199553 Я не могу найти однозначного ответа, но считаю, что вам нужно создать IMAGEHLP_STACK_FRAME (либо вручную из других структур, либо с помощью некоторого API, который я не смог найти), тогда вы можете использовать SymSetContext и SymEnumSymbols, чтобы найти все (отображается, если не оптимизировано) аргументы функции. это даст вам указатель на SYMBOL_INFO структуру, которую вы можете использовать для получения всей нужной информации.   -  person Raxvan    schedule 22.04.2014


Ответы (1)


Для этого нет API. Почему должны быть какие-то современные операционные системы, которые не интересуют некоторых людей, играющих с этим материалом. Как было сказано ранее, компилятор может выполнять оптимизацию, поэтому у вас не может быть для этого детерминированного инструмента. Но есть эвристика! Вы можете узнать, сколько параметров находится в функции, если вы проанализируете сборку перед вызовом или ret после вызова, у вас всегда есть обратный адрес, по которому вы можете проверить, находится ли он в CS.

Прежде всего - вы должны прочитать о термине «раскрутка стека».

person Roman Smelyansky    schedule 02.05.2014
comment
Таблицы почти-раскрутки стали обычным явлением в наши дни из-за требований EH, и им потребуются данные параметров, чтобы они могли раскручивать передаваемые по значению параметры нетривиального типа. - person LThode; 12.12.2014