Дізассемблер своїми руками - kildekode

Пісочниця →

Знання структури машинних команд вже багато років не є обов'язковим, для того, щоб людина могла назвати себе програмістом. Природно так було не завжди. До появи перших ассемблеров програмування здійснювалося безпосередньо в машинному коді. Каторжна робота, сполучена з великою кількістю помилок. Сучасні асемблери дозволяють (в розумній мірі) абстрагуватися від заліза, методу кодування команд. Що вже говорити про компіляторах високорівневих мов. Вони вражають складністю своєї реалізації і тією простотою, з якою програмісту дозволяється перетворювати вихідний код в послідовність машинних команд (причому перетворювати, в достатній мірі, оптимально). Від програміста потрібно лише знання улюбленого мови / IDE. Знання того, у що перетворює компілятор вихідний лістинг зовсім не обов'язково.
Тим же, кому цікаво поглянути на короткий опис структури кодування машинних команд, приклад реалізації і вихідний код дизассемблера для x86 архітектури, ласкаво просимо.

Створення дизассемблера для x86 архітектури є, хоча завданням і не особливо складною, але все, же досить специфічною. Від програміста потрібні певного роду знання - знання того, як мікропроцесор розпізнає послідовність "Байтік" в машинному коді. Далеко не в кожному вузі можна отримати такі знання в обсязі достатньому для написання повнофункціонального сучасного дизассемблера - доводиться шукати самому (як правило, англійською мовою). Даний пост не претендує на повноту висвітлення проблеми створення дизассемблера, в ньому лише коротко розповідається то, як був написаний дизассемблер для x86 архітектури, 32-розрядної режиму виконання команд. Так само хотілося б відзначити ймовірність можливих неточностей при перекладі деяких понять з офіційною специфікації.

Структура команд для intel x86

Структура команди наступна:
• Опциональниє префікси (кожен префікс має розмір 1 байт)
• Обов'язковий опкод команди (1 або 2 байти)
• Mod_R / M - Байтік, що визначає структуру операндів команди - опціональний.
• Опциональниє байти, займані операндами команди (іноді розділене як один байт поля SIB [Scale, Index, Base], зміщення і безпосереднього значення).

Існують наступні префікси:
Перші шість змінюють сегментний регістр, який використовується командою при зверненні до комірки пам'яті.
• 0x26 - префікс заміни сегмента ES
• 0x2E - префікс заміни сегмента CS
• 0x36 - префікс заміни сегмента SS
• 0x3E - префікс заміни сегмента BS
• 0x64 - префікс заміни сегмента FS
• 0x65 - префікс заміни сегмента GS

• 0x0F - префікс додаткових команд (іноді його не вважають за справжній префікс - в цьому випадку вважається, що опкод команди складається з двох байт, перший з яких 0x0F)

Кожен з цих префіксів змінює семантику і (або) структуру машинної інструкції (наприклад, її довжину або вибір мнемоніки).

Опкоди команд.
Опкод команди іноді один, іноді вшити з префіксом (ами) однозначно визначає мнемоніку (назва) команди. Команд багато. І при ускладненні сучасних мікропроцесорів їх кількість не зменшується - нові команди з'являються, а застарілі не зникають (зворотна сумісність). Список опкодов і команд асоційованих з ними, як правило, можна скачати на офіційних сайтах виробників мікропроцесорів.

Байт Mod_R / M складається з наступних полів:

• Mod - перші два біта (значення від 0 до 3)
• R / M - такі три біта (значення від 0 до 7)
• Value of ModR / M - такі три біта (значення від 0 до 7)

Перше, що необхідно зробити, це перенести перші дві таблиці в зручні для роботи структури даних. На тому ж сайті можна скачати xml-версії даних таблиць, і вже їх перетворити в красиві сішние структури. Я ж вчинив інакше - завантажив html таблиці в Excel, і вже там, написавши нескладний скриптик на VBA, отримав вихідний сішний код, який, вже після ручних виправлень був необхідні структури даних.

Сам алгоритм дизассемблирования досить простий:

• Чи збирається список префіксів, які використовуються в поточній машинної інструкції
• Шукається в одній з двох таблиць відповідне поле в залежності від опкода, префіксів і покоління (моделі) цільового (шуканого) мікропроцесора.
• Знайдена нами запис характеризується списком полів такими як покоління (модель) мікропроцесора, з якого з'явилася підтримка даної команди або, наприклад, список прапорів, які дана команда може змінити. Нас же, в основному, цікавлять лише мнемоніка (назва) команди і список операндів. Проаналізувавши всі операнди знайденої і поля байта Mod_R / M, ми зможемо дізнатися текстове представлення і довжину команди.

Кількість операндів може коливатися від нуля до трьох. Вихідні таблиці містять більше сотні типів операндів. Деякі операнди дублюються - у них різні назви, але послідовність дій обробки Mod_R / M байта (і можливо наступних байтів) у них однакова.

PS:
Не факт, що комусь із прочитали цей пост, коли-небудь знадобиться інформація, почерпнута з нього (дизасемблери пишуть одиниці), але, в будь-якому випадку цей дизассемблер тестувався і навіть входить до складу досить більшого комерційного протектора. ісподнікі відкриті і поширюються вільно)

Схожі статті