термоядерный инлайн GETORIX | INT3 Спецвыпуск: Хакер, номер #066, стр. 066-068-5 Итак, ядро написано, осталось привязать патч к основной программе, а именно найти для него место вызова. В соответствии с идеей, первый встретившийся в программе вызов функции должен быть подменен и переадресован на наш патч, а сама функция должна быть вызвана внутри патча. Первый переход в программе расположен по адресу 1D60C, там происходит обращение к некоторой функции _cinit. Реализовать такую переадресацию можно только вручную, путем замены в инструкции относительного смещения до этой функции на смещение до нашего inline-патча. Точно таким же образом необходимо рассчитать смещение из тела патча до функции _cinit. О том, как это сделать, можно узнать из врезки. Вычисление относительного адреса ПРИ ПРОГРАММИРОВАНИЕ НА ARM-АССЕМБЛЕРЕ С ИСПОЛЬЗОВАНИЕМ ОПКОДОВ ОЧЕНЬ ВАЖНО ПОНИМАТЬ, КАК РАССЧИТЫВАЕТСЯ ОТНОСИТЕЛЬНЫЙ АДРЕС В ИНСТРУКЦИЯХ ВЕТВЛЕНИЯ ТИПА B,BL. В ОФИЦИАЛЬНОМ ОПИСАНИИ АРХИТЕКТУРЫ ARM О ЕГО ВЫЧИСЛЕНИИ ГОВОРИТСЯ СЛЕДУЮЩЕЕ: THE BRANCH TARGET ADDRESS IS CALCULATED BY: 1 SIGN-EXTENDING THE 24-BIT SIGNED (TWO'S COMPLIMENT) IMMEDIATE TO 32 BITS. 2 SHIFTING THE RESULT LEFT TWO BITS. 3 ADDING THIS TO THE CONTENTS OF THE PC, WHICH CONTAINS THE ADDRESS OF THE BRANCH INSTRICTION PLUS 8. ПЕРЕВЕСТИ МОЖНО ВОТ ТАК: ДЛЯ ПОЛУЧЕНИЯ АБСОЛЮТНОГО АДРЕСА ПЕРЕХОДА 24-БИТОВОЕ СМЕЩЕНИЕ, СОДЕРЖАЩЕЕСЯ В КОМАНДЕ, СДВИГАЕТСЯ ВЛЕВО НА ДВА БИТА, ПОСЛЕ ЧЕГО К НЕМУ ПРИБАВЛЯЕТСЯ ЗНАЧЕНИЕ РЕГИСТРА PC, КОТОРОЕ СОДЕРЖИТ АДРЕС ТЕКУЩЕЙ ИНСТРУКЦИИ ВЕТВЛЕНИЯ, УВЕЛИЧЕННЫЙ НА 8 БИТ. ЭТО УТВЕРЖДЕНИЕ ТАКЖЕ МОЖНО ЗАПИСАТЬ ДВУМЯ ФОРМУЛАМИ: 1. ((da-ba)-8)>>2 [для перехода вперед по коду (на больший адрес)] 2. 0-(((ba-da)+8)>>2) [для перехода назад по коду (на меньший адрес)] ba — адрес команды ветвления (branch address) da — адрес команды назначения (distination address) ДЛЯ ПРОСТОТЫ И ЯСНОСТИ РАЗБЕРЕМ ПРИНЦИП РАБОТЫ ЭТОГО НА ПРИМЕРЕ. ИТАК, НАМ ДАНО: 1D60C: адрес вызова _cinit 1D6EC: адрес функции _cinit 1D840: адрес inline-пачта 1D860: адрес вызова _cinit из тела inline-пачта СНАЧАЛА НЕОБХОДИМО РАССЧИТАТЬ СМЕЩЕНИЕ ОТ БЫВШЕГО ВЫЗОВА ФУНКЦИИ _CINIT ДО НАЧАЛА INLINE-ПАТЧА. ПОСКОЛЬКУ ПАТЧ НАХОДИТСЯ НИЖЕ ПО КОДУ, ИСПОЛЬЗУЕМ ФОРМУЛУ (1): offset = ((1D840-1D60C)-8)>>2 = 8B ТЕПЕРЬ ПО ФОРМУЛЕ (2) РАССЧИТЫВАЕМ СМЕЩЕНИЕ ИЗ ТЕЛА ПАТЧА ДО ФУНКЦИИ _cinit, КОТОРАЯ НАХОДИТСЯ ВЫШЕ ПО КОДУ. offset = 0-(((1D860-1D6EC)+8)>>2) = FFFFA1 После расчета заменяем соответствующие смещения в вызовах и получаем: по адресу 1D60C: 8B 00 00 EB [вызов inline-пачта вместо _cinit] по адресу 1D860: A1 FF FF EB [вызов _cinit из тела патча] Таким образом, конечная версия inline-патча в шестнадцатеричном виде будет выглядеть так, как показано в листинге 5: Листинг 5. HEX-код инлайн-патча .text:0001D840 00 40 2D E9 0F 00 2D E9 EA 00 A0 E3 14 10 9F E5 .@-щ¤.-щъ.ау¶ Ях .text:0001D850 00 00 C1 E5 10 10 9F E5 00 00 C1 E5 0F 00 BD E8 ..+х Ях..+х¤.-ш |