Я робив це багато разів і продовжую це робити. У цьому випадку, коли вашою основною метою є читання, а не написання асемблера, я вважаю, що це стосується.
Напишіть власний розбирач. Не для того, щоб зробити наступний найбільший розбирач, цей суто для вас. Мета - вивчити набір інструкцій. Я навчаюсь асемблеру на новій платформі, запам'ятовуючи асемблер для платформи, якою я колись знав. Почніть з лише декількох рядків коду, додаючи наприклад регістри та ping pong-ing між розбиранням бінарного виводу та додаванням все більш складних інструкцій на стороні введення:
1) вивчити набір інструкцій для конкретного процесора
2) дізнайтеся нюанси того, як писати код у збірці для згаданого процесора, щоб ви могли змішувати кожен біт опкоду в кожній інструкції
3) ви засвоїте набір інструкцій краще, ніж більшість інженерів, які використовують цей набір, щоб заробити собі на життя
У вашому випадку є декілька проблем, як правило, я рекомендую розпочати інструкцію щодо ARM, сьогодні є більше продуктів на основі ARM, ніж будь-які інші (в комплекті є комп'ютери x86). Але ймовірність того, що ви зараз використовуєте ARM і не знаєте достатньої кількості асемблера, щоб написати стартовий код або інші підпрограми, знаючи ARM, може допомогти або не допомогти тому, що ви намагаєтеся зробити. Друга і важливіша причина спочатку ARM полягає в тому, що довжини інструкцій мають фіксований розмір та вирівнювання. Розбирання інструкцій змінної довжини на зразок x86 може бути кошмаром як ваш перший проект, і мета тут - навчитися набору інструкцій не створювати дослідницький проект. Третя ARM - це добре зроблений набір інструкцій, регістри створюються рівними і не мають індивідуальних особливих нюансів.
Тож вам доведеться розібратися, з якого процесора ви хочете почати. Я пропоную спочатку msp430 або ARM, потім спочатку ARM або другий, а потім хаос x86. Незалежно від того, на якій платформі варто використовувати будь-яку платформу, дані, що містять інструкції, а також кодування опкодів (біт і байтів машинної мови), не мають постачальників даних або довідників програмістів. Для того, щоб дізнатися, що робить компілятор, і як писати код, з яким компілятор не повинен боротися, добре знати декілька наборів інструкцій і бачити, як однаковий код високого рівня реалізується для кожного набору інструкцій з кожним компілятором з кожною оптимізацією. налаштування. Ви не хочете вникати в оптимізацію свого коду лише для того, щоб виявити, що ви зробили це краще для одного компілятора / платформи, але набагато гірше для кожного іншого.
О, для розбирання наборів інструкцій змінної довжини, замість того, щоб просто починати на початку та розбирати кожне чотири байтове слово лінійно через пам'ять, як це було б з ARM або кожні два байти, як msp430 (msp430 має інструкції зі змінною довжиною, але ви все одно можете отримати лінійно проходить через пам'ять, якщо ви починаєте з точок входу з таблиці переривань вектора). Для змінної довжини потрібно знайти точку входу на основі векторної таблиці або знань про те, як завантажується процесор і слідувати коду в порядку виконання. Вам потрібно повністю розшифрувати кожну інструкцію, щоб знати, скільки байтів використовується, якщо ця інструкція не є безумовною гілкою, припустимо, що наступний байт після цієї інструкції - інша. Ви також повинні зберігати всі можливі адреси філій та припускати, що це відправні байтові адреси для отримання додаткових інструкцій. Одного разу, коли я був успішним, я здійснив кілька проходів через бінарне. Починаючи з точки входу, я позначив цей байт як початок інструкції, потім лінійно розшифровувався через пам'ять, поки не потрапляв на безумовну гілку. Всі цілі філій були позначені як вихідні адреси інструкції. Я зробив кілька проходів через двійковий файл, поки не знайшов нових цілей гілки. Якщо в будь-який час ви знайдете, скажімо, 3-байтну інструкцію, але чомусь ви позначили другий байт як початок інструкції, у вас є проблеми. Якщо код був створений компілятором високого рівня, цього не повинно відбуватися, якщо компілятор не робить щось зло, якщо в коді є рукописний асемблер (як, скажімо, стара аркадна гра), цілком можливо, що існуватимуть умовні гілки, які ніколи не можуть трапитися, як r0 = 0 з подальшим стрибком, якщо не нулем. Можливо, вам доведеться відредагувати ті, які випадають з двійкового файлу, щоб продовжити. З ваших найближчих цілей, які я припускаю, будуть на x86, я не думаю, що у вас виникнуть проблеми.
Я рекомендую інструменти gcc, mingw32 - це простий спосіб використання інструментів gcc для Windows, якщо x86 - ваша мета. Якщо не mingw32 plus msys - це відмінна платформа для створення перехресного компілятора з binutils та gcc-джерел (як правило, досить просто). mingw32 має деякі переваги перед cygwin, як значно швидші програми, і ви уникаєте dg пекла cygwin. gcc і binutils дозволять писати на C або збирати і розбирати код, і там більше веб-сторінок, ніж ви можете прочитати, показуючи, як зробити будь-яку одну або всі три. Якщо ви будете робити це за допомогою набору інструкцій зі змінною довжиною, я настійно рекомендую використовувати набір інструментів, що включає в себе розбиральник. Наприклад, сторонній розбирач для x86 стане проблемою для використання, оскільки ви ніколи не знаєте, чи правильно його розібрали. Частина цього також залежить від операційної системи, мета полягає в тому, щоб компілювати модулі у двійковий формат, який містить вказівки щодо маркування інформації з даних, щоб розбиральник міг зробити більш точну роботу. Вашим іншим вибором для цієї основної мети є створення інструменту, який може збирати безпосередньо асемблер для вашої перевірки, а потім сподіватися, що коли він компілюється у двійковий формат, він створює ті самі інструкції.
Коротка (гаразд трохи коротша) відповідь на ваше запитання. Напишіть розбирач, щоб вивчити набір інструкцій. Я б почав з чогось RISCy та легкого для навчання, як ARM. Після того, як ви знаєте один набір інструкцій, інші набагато простіше підібрати, часто через кілька годин, за третім набором інструкцій, ви можете почати писати код майже відразу, використовуючи таблицю / посібник для синтаксису. Усі процесори, які варто використовувати, мають таблицю даних або довідкове керівництво, яке описує інструкції до бітів і байтів опкодів. Вивчіть процесор RISC, як ARM, і CISC, як x86, достатньо, щоб відчути відмінності, такі речі, як проходження реєстрів для всього або можливість виконувати операції безпосередньо на пам'яті з меншою кількістю реєстрів або відсутністю. Три інструкції операнда проти двох тощо. Під час налаштування коду високого рівня, компілювати для більш ніж одного процесора та порівняти вихід. Найважливіше, що ви дізнаєтесь, це те, що незалежно від того, наскільки добре написаний код високого рівня, якість компілятора і зроблений вибір оптимізації мають величезну різницю в фактичних інструкціях. Я рекомендую llvm та gcc (з бінутами), не вироблятичудовий код, але вони є багатоплатформенними та багатоцільовими, і обидва мають оптимізатори. І обидва, і безкоштовні, і ви можете легко створити крос-компілятори з джерел для різних цільових процесорів.