Почему этот код позволяет мне обнаружить отладчик?

Почему следующий код сборки является средством отладки?

l1:
call l3
l2:
;some code
l3:
mov al, 0c3h
mov edi, offset l3
or ecx, -1
rep stosb

Я знаю, что C3h является RETN, и я знаю, что stobs записывает значение в al в качестве кода операции в соответствии с offset в edi и выполняется для ecx раз из-за rep.

Мне также известно, что stobs и stosw будут выполняться, если они были предварительно учтены в архитектуре Intel в качестве исходного формата.

Если мы запускаем программу в режиме отладки, предварительная выборка не имеет значения, и метка l2 будет работать (поскольку она является одношаговой), иначе если отладчик не будет, он будет пинг-понг между l1 и l3 утра. Я прав

Ответы

Ответ 1

Когда программа отлаживается (т.е. один шаг), очередь предварительной выборки очищается на каждом шаге (когда происходит прерывание). Однако при обычном выполнении это не произойдет с rep stosb. Старые процессоры не сбрасывали его даже тогда, когда в память была записана память, чтобы поддерживать самомодифицирующийся код, который был изменен, кроме rep movs и rep stosb. (IIRC в конечном итоге был исправлен в процессорах i7.)

Вот почему, если есть отладчик (один шаг), код будет выполняться правильно и при замене rep stosb на ret l2. Когда отладочный отладчик rep stosb не будет продолжен, так как ecx является самым большим возможным, он в конечном итоге напишет где-то, что он не должен писать, и произойдет исключение.

Эта технология отладки описана в этой статье.

Ответ 2

Единственное, что делает отладчик, это добавить временную задержку. Это может быть ключом к тому, как это работает. Руководство Intel (и я предполагаю, что AMD) явно заявляет, что самомодифицирующийся код не работает "работать", если программа не сообщит CPU о том, что строка кэша, связанная с измененной инструкцией, изменилась. Это должно сделать логику предварительной выборки дешевой; разработчики чипов не хотят иметь аппаратное обеспечение, которое постоянно проверяет, что каждый байт кэширования инструкций по-прежнему действителен.

Итак, я предполагаю, что происходит с отладчиками, это l1 вызывает l3, который сохраняет возврат после repos, а возврат выполняется, поскольку из-за длительных задержек, вызванных отладчиком, в одиночном шаге, принудительное кэширование, содержащее l3 для повторной проверки после изменения.

Без отладчика, я думаю, что инструкция (не показана) после запуска stosb. Если бы это был переход к "без отладчика", то успех перехода продемонстрировал бы, что не используется одношаговый отладчик.

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