8086 Сборочный вопрос, что делает этот код - PullRequest
3 голосов
/ 13 июня 2011

я получил этот вопрос в моем курсе по сборке:

что делает эта процедура
и как она должна называться?

push ebp
mov ebp, esp    
push esi

mov esi, [ebp+4]
mov eax, [esi]
sub eax, [esi+4]
add esi, 8
mov [ebp+4], esi

pop esi
pop ebp
ret

похоже на [ebp+4]является аргументом, а не адресом возврата, поэтому его следует вызывать с помощью «jmp», а не «call».я не совсем понимаю, что нужно найти на [esi+4] и на esi+8 (обратный адрес) хм, я действительно запутался, надеюсь, вы поможете мне
заранее спасибо.

Ответы [ 3 ]

2 голосов
/ 16 июня 2011

Как вы правильно заметили, если эта подпрограмма вызывается с call, то [ebp+4] является адресом возврата. Это не значит, что это плохая идея.

Предположим, что процедура вызывается с call. В этот момент «адрес возврата», который помещается в стек, является адресом байта, который следует сразу за кодом операции call. Давайте назовем этот адрес x . Затем процедура извлекает из адреса x два 32-битных слова, одно по адресу x и одно по адресу x + 4 . Вычитает второе слово из первого, сохраняя результат в eax. Наконец, процедура сохраняет обратно значение x + 8 в слоте стека [ebp+4], чистый эффект состоит в том, что при достижении ret выполнение возобновляется по адресу x + 8 . С этой гипотезой подпрограмма выглядит как способ вычитать целые числа, которые находятся в середине кода, что-то вроде этого:

call yourroutine
dd   56478634
dd   18943675
mov  ebx, eax  ; an example instruction

Здесь call возвращается к инструкции mov, и в этот момент eax содержит значение 37534959 (это 18943675, вычтенное из 56478634).

Как подпрограмма кода, она не очень полезна, поскольку это сложный способ загрузки eax с постоянным значением, которое жестко закодировано (кодовое пространство обычно доступно только для чтения во время выполнения). Можно подумать, что такая подпрограмма может появиться как часть поддержки динамического связывания во время выполнения в некоторой архитектуре (динамическое связывание является проблематичным предметом).

Теперь давайте предположим, что вместо этого подпрограмма вызывается с jmp. [ebp+4] теперь обозначает все, что было на вершине стека в этой точке. Процедура принимает это значение (назовем его y ), получает два слова по адресам y и y + 4 , вычисляет результат вычитания в eax и затем сохраняет y + 8 обратно в слот [ebp+4]. Наконец, ret интерпретирует этот слот как адрес возврата, то есть адрес некоторого кода, к которому должно перейти выполнение. Неважно, что при создании этого адреса не был задействован call код операции; ret все равно прыгнет к нему. На этот раз код вызова может выглядеть следующим образом:

    push   foobar
    jmp    yourroutine
    ...  ; unreached code

foobar:
    dd 56478634
    dd 18943675
    mov  ebx, eax  ; an example instruction

На этот раз это выглядит как параметризованный скачок с некоторой присущей ему нагрузкой eax. Подобный код может существовать в реализации некоторых интерпретаторов для многопоточного кода . Однако, как домашнее задание, я совершенно уверен, что это не то, что было задумано (многопоточные интерпретаторы кода даже более привлекательны, чем динамические ссылки).

1 голос
/ 13 июня 2011

Нет, он обязательно должен вызываться с call - в конце есть ret.

С точки зрения того, что он делает, вы должны сесть с листом бумаги со списком регистров и пошагово, что код через вашу голову, обновляя регистры по мере продвижения. Тогда должно быть очевидно, что происходит:

eax:
esp:
ebp:
esi:

, а также другая соответствующая память (например, область вокруг вершины стека).

Это идеальный способ узнать о программировании (для небольших программ в любом случае), поскольку вы учитесь анализировать детали подробно и на самом деле понимать их. И я боюсь, что это примерно столько же, сколько я собираюсь оказать для домашнего задания: -)

1 голос
/ 13 июня 2011

Когда вы вызываете эту функцию, адрес возврата (eip адреса инструкции после вызова) помещается в стек, а после первой преамбулы функции [ebp+4] ссылается на этот адрес возврата. Затем тело функции рассматривает этот адрес как 2 целых числа, которые вычтены, результат помещается в eax, а затем адрес возврата увеличивается на 8, т.е. ret просто возвращает нас по этому новому адресу возврата (который, мы надеемся, является действительной инструкцией ....).

Странная функция, она выглядит в некотором самом изменяющемся коде и т.д ...

...