Ломаем структуры Мысла Владислав Спецвыпуск Xakep, номер #045, стр. 045-036-2 Переполнение классов Все сказанное касается также и классов. Возьмем предыдущий пример, но на этот раз все данные будут храниться не в структуре, а в классе. Более того, пусть теперь можно будет указать данные не для одного, а для двух пользователей. Реализация аутентификации с помощью классов Давай опять проверим работу получившегося приложения, которое также чувствительно к длине передаваемых ему строк. Приложение работает корректно, если длина строки не превышает 16 символов; если нарушить это правило, то перед вами откроются другие возможности, совсем не предусмотренные программистом :-). Так, например, при передаче в качестве пароля для первого пользователя строку из 30 байт приложение критически завершит свою работу. Причина происшедшего в том, что для хранения класса используется динамическая память. Переполняя буфер, ты можешь изменить значения статических переменных, это верно, но ошибка не из-за этого. Посмотри на отладочную информацию. Видно, что после того как первый экземпляр класса получил логин и пароль, у второго изменилось значение VTable. А VTable – это адрес таблицы методов класса. В ней находятся все методы, помеченные как виртуальные, а все остальные находятся в сегменте кода. Адрес этой таблицы хранится по указателю объекта, а поскольку они создаются в стеке, то адрес таблицы лежит там же. Но на самом деле все просто: функции хранятся в области кода, переменные фиксированной длины лежат в той же области памяти, где расположен сам объект. Чтобы перезаписать область кода, необходимо копировать буфер очень большей длины, и поэтому, даже если есть возможность записи в сегмент кода, ей не всегда можно воспользоваться из-за расстояния от него до стека или кучи. Однако для большей гибкости и реализации наследования программисты используют виртуальные функции. Они ничем не отличаются от обычных за исключением метода доступа к ним. Если точнее, адрес функции не жестко прописан в коде, а находится в таблице, которая может меняться в ходе выполнения программы. Адрес таблицы хранится в первых 4 байтах объекта, а поэтому, если есть переполнение, то его можно перезаписать. Именно это и произошло в рассмотренном примере. Когда ты передал строку «name AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA name pass», пароль, хранящийся в первом экземпляре, перезаписал виртуальную таблицу методов второго. Чтобы в этой ситуации получить контроль над приложением, необходимо найти такой адрес в памяти, который не содержит в себе нулей и указывает на адрес, по которому находится твой shell-код. Надеюсь, теперь тебе понятно, в чем дело. Существует еще множество методов переполнения объектов в памяти, но эта тема заслуживает отдельного рассмотрения, а пока что мы остановимся на переполнении структурированных данных. |