Почему этот код позволяет мне обнаружить отладчик?
Почему следующий код сборки является средством отладки?
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. Если бы это был переход к "без отладчика", то успех перехода продемонстрировал бы, что не используется одношаговый отладчик.
Если бы я нашел этот код в приложении, я бы отказался его запускать.