Я ранее отмечал, что версии Windows для x86-32 и x86-64 осторожно начинают каждую функцию с точки для патчинга. А как насчёт AArch64 (в Windows известного как arm64)?
Windows также вставляет места для патчинга перед функциями на AArch64, но они намного проще из‑за набора команд фиксированной длины. Не нужно беспокоиться о патчинге команды, когда счётчик команд оказывается посередине последовательности байтов, потому что счётчик команд никогда не находится посередине: он всегда выровнен по четырём байтам.
Поэтому для первой инструкции функции нет специальных ограничений. Все инструкции удовлетворяют требованию атомарного обновления без риска того, что счётчик команд окажется в середине инструкции.
Перед каждой функцией имеется пространство для патча размером 12 байт, чего как раз достаточно для трёхинструкционного транполятора (trampoline).
Регистровый псевдоним xip0 — один из двух временных регистров для внутриепроцедурных вызовов, и соглашение таково, что этот регистр может быть перезаписан любой командой ветвления. Поскольку вызывающая сторона уже использовала команду ветвления, чтобы попасть в точку входа функции, она не может использовать xip0 ни для чего важного, поэтому мы можем перезаписать xip0 в составе нашего транполятора.
Бонусный комментарий:
Первая инструкция в точке входа функции почти наверняка pacibsp — инструкция аутентификации указателя, подписывающая адрес возврата, чтобы сделать код более устойчивым к ROP‑атакам и атакам, перезаписывающим адрес возврата.
Raymond участвует в развитии Windows более 30 лет. В 2003 году он начал веб‑сайт, известный как The Old New Thing, который стал значительно популярнее, чем он мог себе представить, что до сих пор вызывает у него нервозность. Веб‑сайт породил книгу с тем же названием (Addison Wesley, 2007). Он время от времени появляется в аккаунте Windows Dev Docs в Twitter, рассказывая истории, не всегда несущие полезную информацию.
Обсуждение закрыто. Войдите в систему, чтобы редактировать/удалять существующие комментарии.
Кроме патчабельных первых инструкций, функция также не должна прыгать обратно к первой инструкции, например в составе условного цикла. Я видел, как это ломало хуки довольно неприятным образом.
Внутрипроцедурный временный регистр был одной из лучших идей ARM. Он облегчает реализацию целого набора таких «магических» исправлений.
Ещё один бонусный комментарий:
Когда вы видите эту дружелюбную инструкцию pacibsp, помните, что в венгерском языке «paci» — уменьшительно‑ласкательное слово для лошади, вроде «конёк», только намного милее. Поэтому автоматически представляйте себе 🐎bsp.
Малоизвестное правило венгерской нотации: если тип данных — лошадь, имя указателя должно быть pAci.
Windows на Itanium тоже поддерживал hot‑patching, и делал это ещё проще.
Почему нет поддержки Windows hot‑patching для других архитектур, таких как 32‑битный ARM и MIPS?
