Итак, вот сделка. Я работаю над отладчиком, используя инструмент булавки и информацию о дварфе из процесса, к которому я присоединяюсь. Pin — это фреймворк, который позволяет создавать инструментальные средства для уже запущенных процессов, я присоединяюсь к процессу, а затем анализирую из него информацию DWARF.
Очевидно, что пин-код позволяет мне захватить регистры в тот момент, когда я присоединяюсь к программе. Я могу получить ebp
, esp
, а также eip
, Но информация из стека не совпадает с информацией из информации DWARF.
Например:
Пин говорит мне, что ebp
является: bfe0abe8
Я предполагаю, что это фактический адрес регистра, а не значение, которое находится внутри него.
Если я иду в стек, у меня есть это:
bfe0abb5 -> 00000020 \\this is esp
bfe0abb6 -> 0804855d
bfe0abb7 -> 08048720
bfe0abb8 -> 0000000a
bfe0abb9 -> bfe0abe8 \\this is what I think is ebp, but according to pin is not
bfe0abba -> 0015711f
bfe0abbb -> bfe0ac28
bfe0abbc -> 4236b852 \\this is a local float variable named jos
bfe0abbd -> 42c0947b \\this is a local float variable named tib
bfe0abbe -> 0000000a
bfe0abbf -> 08048724
bfe0abc0 -> bfe0abf8
bfe0abc1 -> bfe0ac28
bfe0abc2 -> 08048612
bfe0abc3 -> 00000000
bfe0abc4 -> 0000000a
bfe0abc5 -> 00006680
bfe0abc6 -> 08048689
bfe0abc7 -> 00266324
bfe0abc8 -> 00265ff4
bfe0abc9 -> 936498a8
bfe0abca -> 4072464d
bfe0abcb -> 0013f4a5
bfe0abcc -> 424ab852 \\this is another local variable
bfe0abcd -> 42bc947b \\this is another local variable
bfe0abce -> 0000000a
bfe0abcf -> 08048670
bfe0abd0 -> 00000000
bfe0abd1 -> bfe0aca8
И многое другое, я не знаю, что на самом деле является дном стека, поэтому я показываю только его часть.
Если я перейду к информации DWARF, то это функция, в которой в настоящее время находится вершина стека, и на которую должен указывать ebp:
<1>< 962> DW_TAG_subprogram
DW_AT_external yes(1)
DW_AT_name add
DW_AT_decl_line 36
DW_AT_prototyped yes(1)
DW_AT_low_pc 0x8048513
DW_AT_high_pc 0x804855f
DW_AT_frame_base <loclist with 3 entries follows>
[ 0]<lowpc=0x2f><highpc=0x30>DW_OP_breg4+4
[ 1]<lowpc=0x30><highpc=0x32>DW_OP_breg4+8
[ 2]<lowpc=0x32><highpc=0x7b>DW_OP_breg5+8
Я знаю это, потому что локальные переменные, которые я вижу в стеке, находятся внутри этой функции. Эти локальные переменные:
<2>< 1029> DW_TAG_variable
DW_AT_name tib
DW_AT_decl_line 38
DW_AT_type <837>
DW_AT_location DW_OP_fbreg -24
<2>< 1043> DW_TAG_variable
DW_AT_name jos
DW_AT_decl_line 39
DW_AT_type <837>
DW_AT_location DW_OP_fbreg -28
Я знаю, что в соответствии с DWARF (и это: Как увидеть переменные, хранящиеся в стеке с помощью GDB), ebp в этой функции должен быть текущим ebp + 8 (который, поскольку я работаю с DWORD, должен быть +2). И тогда я должен вычесть от -24 до 8, делая его ebp-16 (ebp-4 в реальности). На бумаге это должно работать, но при переходе в стек я сталкиваюсь с множеством проблем:
bfe0abb9
это текущий ebp, и этот вывод фактически возвращает значение, а не адрес регистра, если я вычту 4 из этого регистра, я не получу значение tib, так как, как я понимаю стек, я буду вверх не вниз. И даже если бы я упал при попытке получить Йос (ebp-28+8 = ebp -20 = ebp -5
) Я бы точно не получал иос, но 000000a
,Я что-то здесь упускаю? У меня неправильное понимание стека и / или регистров или информации DWARF? Или Пин меня облажает и дает неверную информацию?
Какие-либо предложения?
Я проигнорирую дамп стека, потому что я не уверен, что вы пытаетесь показать там.
Упомянутый вами DWARF говорит, что основа фрейма функции изначально $esp + 4
затем он меняется на $esp + 8
затем он меняется на $ebp + 8
, Это стандартpush $ebp; mov $esp, $ebp
«последовательность команд, которая открывает большинство функций i386. При входе в функцию основой кадра является значение esp
плюс 4. Как только мы сохранили абонента ebp
зарегистрируйтесь в стеке, основа кадра теперь esp
плюс 8, потому что esp
изменилось благодаря push
, Наконец значение в esp
копируется в ebp
и это значение останется неизменным для остальной части функции (в то время как esp
дополнительно корректируется, как правило).
То, что вы видите здесь — «основа фрейма» — также называется каноническим адресом фрейма или CFA в разделе debug_frame. CFA — это адрес в стеке, который одинаков для всей продолжительности функции. На i386 адрес CFA является абонентом esp
значение, прежде чем он выполняет call
Инструкция для вызова функции, которую вы смотрите.
DWARF говорит, что переменная tib
находится у основания рамы -24. Основа для вашей функции ebp + 8
так что да, ценность ebp
регистр, минус 16, является адресом в стеке. Переменная хранится по этому адресу.
Я часто нахожу полезным прочитать фактический код сборки для вашей функции, если DWARF неясен, при условии, что вы достаточно хорошо понимаете язык ассемблера. Вы также можете выполнять пошаговые инструкции в своей программе, проверяя регистры и адреса стеков по мере прохождения через нее. Небольшое количество практических экспериментов с реальным кодом почти всегда может ответить на любые вопросы.
Это странно, потому что это не похоже на адресацию стека, bfe0abb5
, bfe0abb6
, bfe0abb7
, bfe0abb8
, bfe0abb9
, bfe0abba
….
Это должно быть в 4-байтовом выравнивании в 32-битной машине. Основываясь на указанном вами стековом адресе, я также не могу рассчитать реальный адрес, даже если вы предоставили правильную информацию DWARF.