Содержание статьи
Фундаментальные основы хакерства
Пятнадцать лет назад эпический труд Криса Касперски «Фундаментальные основы хакерства» был настольной книгой каждого начинающего исследователя в области компьютерной безопасности. Однако время идет, и знания, опубликованные Крисом, теряют актуальность. Редакторы «Хакера» попытались обновить этот объемный труд и перенести его из времен Windows 2000 и Visual Studio 6.0 во времена Windows 10 и Visual Studio 2019.
Ссылки на другие статьи из этого цикла ищи на странице автора.
Идентификация констант и смещений
Микропроцессоры серии 80x86 поддерживают операнды трех типов: регистр, непосредственное значение, непосредственный указатель. Тип операнда явно задается в специальном поле машинной инструкции, именуемом mod
, поэтому никаких проблем в идентификации типов операндов не возникает. Регистр — ну, все мы знаем, как выглядят регистры; указатель по общепринятому соглашению заключается в квадратные скобки, а непосредственное значение записывается без них. Например:
MOV ECX, EAX; ← регистровые операндыMOV ECX, 0x666; ← левый операнд регистровый, правый — непосредственныйMOV [0x401020], EAX; ← левый операнд — указатель, правый — регистр
Кроме этого, микропроцессоры серии 80x86 поддерживают два вида адресации памяти: непосредственную и косвенную. Тип адресации определяется типом указателя. Если операнд — непосредственный указатель, то и адресация непосредственна. Если же операнд‑указатель — регистр, то такая адресация называется косвенной. Например:
MOV ECX,[0x401020] ← непосредственная адресацияMOV ECX, [EAX] ← косвенная адресация
Для инициализации регистрового указателя разработчики микропроцессора ввели специальную команду, вычисляющую значение адресного выражения addr
и присваивающую его регистру REG
, — LEA
. Например:
LEA EAX, [0x401020] ; Регистру EAX присваивается значение указателя 0x401020MOV ECX, [EAX] ; Косвенная адресация — загрузка в ECX двойного слова, ; расположенного по смещению 0x401020
Правый операнд команды LEA
всегда представляет собой ближний (near) указатель (исключение составляют случаи использования LEA
для сложения констант — подробнее об этом см. в одноименном пункте). И все было бы хорошо... да вот, оказывается, внутреннее представление ближнего указателя эквивалентно константе того же значения. Отсюда LEA
равносильно MOV
. В силу определенных причин MOV
значительно обогнал в популярности LEA
, практически вытеснив последнюю инструкцию из употребления.
Отказ от LEA
породил фундаментальную проблему ассемблирования — проблему OFFSET’a. В общих чертах ее суть заключается в синтаксической неразличимости констант и смещений (ближних указателей). Конструкция MOV
может грузить в EAX
и константу, равную 0x401020
(пример соответствующего C-кода: a=0x401020
), и указатель на ячейку памяти, расположенную по смещению 0x401020
(пример соответствующего C-кода: a=&x
). Согласись, a=0x401020
совсем не одно и то же, что a=&x
! А теперь представь, что произойдет, если в повторно ассемблированной программе переменная х
окажется расположена по иному смещению, а не 0x401020
? Правильно — программа рухнет, ибо указатель a
по‑прежнему указывает на ячейку памяти 0x401020
, но здесь теперь «проживает» совсем другая переменная!
Почему переменная может изменить свое смещение? Основных причин тому две. Во‑первых, язык ассемблера неоднозначен и допускает двоякую интерпретацию. Например, конструкции ADD
соответствуют две машинные инструкции: 83
и 05
длиной три и пять байт соответственно. Транслятор может выбрать любую из них, и не факт, что ту же самую, которая была в исходной программе (до дизассемблирования). Неверно «угаданный» размер вызовет смещение всех остальных инструкций, а вместе с ними и данных. Во‑вторых, смещение не замедлит вызвать модификацию программы (разумеется, речь идет не о замене JZ
на JNZ
, а о настоящей адаптации или модернизации), и все указатели тут же «посыплются».
Вернуть работоспособность программы помогает директива offset
. Если MOV
действительно загружает в EAX
указатель, а не константу, по смещению 0x401020
следует создать метку, именуемую, скажем, loc_401020
. Также нужно MOV
заменить на MOV
. Теперь указатель EAX
связан не с фиксированным смещением, а с меткой!
А что произойдет, если предварить директивой offset
константу, ошибочно приняв ее за указатель? Программа откажет или станет работать некорректно. Допустим, число 0x401020
выражало собой объем бассейна, в который вода втекает через одну трубу, а вытекает через другую. Если заменить константу указателем, то объем бассейна станет равен... смещению метки в заново ассемблированной программе и все расчеты полетят к черту.
Таким образом, очень важно определить типы всех непосредственных операндов, и еще важнее определить их правильно. Одна ошибка может стоить программе жизни (в смысле работоспособности), а в типичной программе тысячи и десятки тысяч операндов!
Отсюда возникает два вопроса:
- Как вообще определяют типы операндов?
- Можно ли их определять автоматически (или на худой конец хотя бы полуавтоматически)?
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»