Это вообще можно сделать? Я использовал objdump, но он не дает ассемблерного вывода, который будет принят любым известным мне ассемблером. Я хотел бы иметь возможность изменять инструкции в исполняемом файле, а затем тестировать его впоследствии.
Как разобрать, изменить и собрать исполняемый файл Linux?
Ответы (8)
Я не думаю, что есть какой-либо надежный способ сделать это. Форматы машинного кода очень сложны, сложнее, чем ассемблерные файлы. На самом деле невозможно взять скомпилированный двоичный файл (скажем, в формате ELF) и создать исходную программу сборки, которая будет компилироваться в тот же (или достаточно похожий) двоичный файл. Чтобы понять различия, сравните вывод GCC, компилирующего непосредственно на ассемблере (gcc -S
), с выводом objdump для исполняемого файла (objdump -D
).
Есть два основных осложнения, о которых я могу думать. Во-первых, сам машинный код не соответствует 1-к-1 ассемблерному коду из-за таких вещей, как смещения указателя.
Например, рассмотрим код C для Hello world:
int main()
{
printf("Hello, world!\n");
return 0;
}
Это компилируется в ассемблерный код x86:
.LC0:
.string "hello"
.text
<snip>
movl $.LC0, %eax
movl %eax, (%esp)
call printf
Где .LCO — именованная константа, а printf — символ в таблице символов разделяемой библиотеки. Сравните с выводом objdump:
80483cd: b8 b0 84 04 08 mov $0x80484b0,%eax
80483d2: 89 04 24 mov %eax,(%esp)
80483d5: e8 1a ff ff ff call 80482f4 <printf@plt>
Во-первых, константа .LC0 теперь является просто случайным смещением где-то в памяти - было бы трудно создать исходный файл сборки, содержащий эту константу в нужном месте, поскольку ассемблер и компоновщик могут свободно выбирать места для этих констант.
Во-вторых, я не совсем уверен в этом (и это зависит от таких вещей, как позиционно-независимый код), но я считаю, что ссылка на printf на самом деле вообще не закодирована по адресу указателя в этом коде, но заголовки ELF содержат таблица поиска, которая динамически заменяет свой адрес во время выполнения. Поэтому дизассемблированный код не совсем соответствует исходному ассемблерному коду.
Таким образом, исходная сборка имеет символы, тогда как скомпилированный машинный код имеет адреса, которые трудно обратить вспять.
Вторая серьезная сложность заключается в том, что исходный файл сборки не может содержать всю информацию, которая присутствовала в заголовках исходного файла ELF, например, какие библиотеки динамически компоноваться, а также другие метаданные, помещенные в исходный компилятор. Было бы трудно реконструировать это.
Как я уже сказал, возможно, что специальный инструмент может манипулировать всей этой информацией, но маловероятно, что можно просто создать ассемблерный код, который можно будет собрать обратно в исполняемый файл.
Если вы заинтересованы в изменении только небольшой части исполняемого файла, я рекомендую гораздо более тонкий подход, чем перекомпиляция всего приложения. Используйте objdump, чтобы получить ассемблерный код для интересующих вас функций. Преобразуйте его в «исходный синтаксис ассемблера» вручную (и здесь я бы хотел, чтобы был инструмент, который фактически производил дизассемблирование с тем же синтаксисом, что и ввод) , и измените его по своему усмотрению. Когда вы закончите, перекомпилируйте только эти функции и используйте objdump, чтобы выяснить машинный код для вашей модифицированной программы. Затем используйте шестнадцатеричный редактор, чтобы вручную вставить новый машинный код поверх соответствующей части исходной программы, следя за тем, чтобы ваш новый код имел точно такое же количество байтов, что и старый код (иначе все смещения будут неправильными). ). Если новый код короче, вы можете дополнить его, используя инструкции NOP. Если он длиннее, у вас могут возникнуть проблемы, и вам, возможно, придется создавать новые функции и вызывать их вместо них.
objconv
дизассемблер Agner Fog дизассемблирует в NASM, YASM, MASM или GNU в качестве синтаксиса. Этот вывод можно собрать обратно в аналогичный двоичный файл, но любые предположения, сделанные кодом о выравнивании/смещении, могли измениться. например PLT (таблица связывания процедур) должна использовать кодировку jmp rel32
, чтобы правильное смещение можно было заполнить во время выполнения.
- person Peter Cordes; 10.02.2016
disp32
, даже если инструкция в этом не нуждается: like-s-in-x86-mov-s-documented" title="где находятся суффиксы инструкций ассемблера gnu, такие как s в документированных x86 mov s"> stackoverflow.com/questions/47673177/. Кроме того, NASM обычно может использовать mov eax, [rdi + strict dword 0]
для принудительного создания файла disp32. Проблема в том, что дизассемблеры не используют эти переопределения при дизассемблировании более длинных, чем необходимо, инструкций.
- person Peter Cordes; 07.12.2017
Я делаю это с помощью hexdump
и текстового редактора. Вы должны быть действительно знакомы с машинным кодом и форматом файла, в котором он хранится, и быть гибким в том, что считается дизассемблированием, изменением и повторной сборкой.
Если вам сойдет с рук внесение только точечных изменений (перезапись байтов, но не добавление и удаление байтов), это будет легко (относительно).
Вы на самом деле не хотите замещать какие-либо существующие инструкции, потому что тогда вам придется вручную корректировать любое произведенное относительное смещение в машинном коде для переходов/ветвлений/загрузок/сохранений относительно счетчика программ. , как в жестко запрограммированных немедленных значениях , так и вычисляемых с помощью регистров.
Вы всегда должны иметь возможность избежать удаления байтов. Добавление байтов может быть необходимо для более сложных модификаций, и это становится намного сложнее.
Шаг 0 (подготовка)
После того, как вы фактически правильно дизассемблировали файл с помощью objdump -D
или чего-то, что вы обычно используете в первую очередь, чтобы действительно понять его и найти места, которые нужно изменить, вам нужно принять к сведению следующие вещи, чтобы помочь вы найдете правильные байты для изменения:
- Адрес (смещение от начала файла) байтов нужно изменить.
- Необработанное значение этих байтов в том виде, в котором они есть на данный момент (опция
--show-raw-insn
дляobjdump
здесь очень полезна).
Вам также необходимо проверить, работает ли hexdump -R
в вашей системе. Если нет, то для остальных этих шагов используйте команду xxd
или аналогичную вместо hexdump
во всех шагах ниже (обратитесь к документации по любому инструменту, который вы используете, я сейчас объясняю только hexdump
в этом ответе, потому что это тот Я знаком с).
Шаг 1
Выведите необработанное шестнадцатеричное представление двоичного файла с помощью hexdump -Cv
.
Шаг 2
Откройте файл hexdump
ed и найдите байты по адресу, который вы хотите изменить.
Краткий ускоренный курс в выводе hexdump -Cv
:
- Крайний левый столбец — это адреса байтов (относительно начала самого двоичного файла, как и в
objdump
). - Крайний правый столбец (окруженный символами
|
) представляет собой просто удобочитаемое представление байтов - там записывается символ ASCII, соответствующий каждому байту, а.
заменяет все байты, которые не сопоставляются с печатным символом ASCII. - Важный материал находится между ними — каждый байт в виде двух шестнадцатеричных цифр, разделенных пробелами, по 16 байт на строку.
Осторожно: в отличие от objdump -D
, который дает вам адрес каждой инструкции и показывает необработанный шестнадцатеричный код инструкции в зависимости от того, как она задокументирована как закодированная, hexdump -Cv
выводит каждый байт точно в том порядке, в котором он появляется в файле. Это может немного сбивать с толку, как в первую очередь на машинах, где байты инструкций расположены в противоположном порядке из-за различий в порядке следования байтов, что также может дезориентировать, когда вы ожидаете определенный байт в качестве определенного адреса.
Шаг 3
Измените байты, которые необходимо изменить - вам, очевидно, нужно выяснить исходную кодировку машинных инструкций (а не мнемонику сборки) и вручную записать правильные байты.
Примечание. Вам не нужно изменять удобочитаемое представление в крайнем правом столбце. hexdump
проигнорирует его при распаковке.
Шаг 4
Восстановите дамп измененного файла шестнадцатеричного дампа, используя hexdump -R
.
Шаг 5 (проверка работоспособности)
objdump
ваш только что распакованный файл и убедитесь, что дизассемблированный код, который вы изменили, выглядит правильно. diff
против objdump
оригинала.
Серьезно, не пропускайте этот шаг. Я чаще ошибаюсь, редактируя машинный код вручную, и именно так я ловлю большинство из них.
Пример
Вот реальный рабочий пример, когда я недавно модифицировал двоичный файл ARMv8 (с прямым порядком байтов). (Я знаю, что вопрос помечен x86
, но у меня нет под рукой примера x86, и основные принципы те же, только инструкции разные.)
В моей ситуации мне нужно было отключить конкретную проверку, которую вы не должны выполнять: в моем примере двоичного кода в выводе objdump --show-raw-insn -d
строка, о которой я заботился, выглядела так (одна инструкция до и после дана для контекста):
f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
Как видите, наша программа успешно завершает работу, переходя в функцию error
(которая завершает работу программы). Неприемлемо. Итак, мы собираемся превратить эту инструкцию в запретную операцию. Итак, мы ищем байты 0x97fffeeb
по адресу/смещению файла 0xf44
.
Вот строка hexdump -Cv
, содержащая это смещение.
00000f40 e3 03 15 aa eb fe ff 97 f7 13 40 f9 e8 02 40 39 |..........@...@9|
Обратите внимание, как на самом деле переворачиваются соответствующие байты (кодировка с прямым порядком байтов в архитектуре применяется к машинным инструкциям, как и ко всему остальному) и как это немного неинтуитивно связано с тем, какой байт находится по какому смещению байта:
00000f40 -- -- -- -- eb fe ff 97 -- -- -- -- -- -- -- -- |..........@...@9|
^
This is offset f44, holding the least significant byte
So the *instruction as a whole* is at the expected offset,
just the bytes are flipped around. Of course, whether the
order matches or not will vary with the architecture.
Во всяком случае, я знаю из других разборок, что 0xd503201f
дизассемблируется в nop
, так что это кажется хорошим кандидатом для моей инструкции без операции. Я соответствующим образом изменил строку в файле hexdump
ed:
00000f40 e3 03 15 aa 1f 20 03 d5 f7 13 40 f9 e8 02 40 39 |..........@...@9|
Сконвертировал обратно в бинарник с hexdump -R
, разобрал новый бинарник с objdump --show-raw-insn -d
и проверил правильность изменения:
f40: aa1503e3 mov x3, x21
f44: d503201f nop
f48: f94013f7 ldr x23, [sp, #32]
Затем я запустил двоичный файл и получил желаемое поведение — соответствующая проверка больше не приводила к прерыванию программы.
Модификация машинного кода прошла успешно.
!!! Предупреждение !!!
Или я добился успеха? Вы заметили, что я пропустил в этом примере?
Я уверен, что вы знали - поскольку вы спрашиваете о том, как вручную изменить машинный код программы, вы, вероятно, знаете, что делаете. Но для пользы любых читателей, которые могут читать, чтобы учиться, я уточню:
Я изменил только последнюю инструкцию в ответвлении об ошибке! Переход в функцию, которая выходит из программы. Но, как вы можете видеть, регистр x3
был изменен mov
чуть выше! На самом деле, всего четыре (4) регистра были изменены как часть преамбулы для вызова error
, и один регистр был изменен. Вот полный машинный код для этой ветки, начиная с условного перехода через блок if
и заканчивая тем, куда переходит переход, если условный if
не используется:
f2c: 350000e8 cbnz w8, f48
f30: b0000002 adrp x2, 1000
f34: 91128442 add x2, x2, #0x4a1
f38: 320003e0 orr w0, wzr, #0x1
f3c: 2a1f03e1 mov w1, wzr
f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
Весь код после ветвления был сгенерирован компилятором в предположении, что состояние программы было таким, каким оно было до условного перехода! Но, просто сделав последний переход к коду функции error
бездействующим, я создал путь кода, по которому мы достигаем этого кода с несогласованным/неправильным состоянием программы!
В моем случае это на самом деле казалось не вызывало никаких проблем. Так что мне повезло. Очень повезло: только после того, как я уже запустил свой модифицированный двоичный файл (который, кстати, был критичным с точки зрения безопасности двоичным файлом: он имел возможность setuid
, setgid
и изменять Контекст SELinux!) Я понял, что забыл фактически отследить пути кода того, повлияли ли эти изменения регистра на пути кода, которые появились позже!
Это могло иметь катастрофические последствия — любой из этих регистров мог использоваться в более позднем коде с предположением, что он содержит предыдущее значение, которое теперь было перезаписано! И я тот человек, которого люди знают за дотошное тщательное обдумывание кода, а также как педанта и приверженца за то, что всегда добросовестно относился к компьютерной безопасности.
Что, если бы я вызывал функцию, в которой аргументы высыпались из регистров в стек (что очень часто встречается, например, в x86)? Что, если в наборе инструкций, предшествующих условному переходу, на самом деле было несколько условных инструкций (как это часто бывает, например, в более старых версиях ARM)? Я был бы в еще более безрассудно непоследовательном состоянии после того, как сделал это, казалось бы, самое простое изменение!
Итак, это мое предостерегающее напоминание: манипулирование двоичными файлами вручную буквально сбрасывает каждую безопасность между вами и тем, что позволяют машина и операционная система. Буквально все усовершенствования, которые мы сделали в наших инструментах для автоматического обнаружения ошибок в наших программах, ушли.
Итак, как нам исправить это более правильно? Читать дальше.
Удаление кода
Чтобы эффективно/логически удалить более одной инструкции, вы можете заменить первую инструкцию, которую хотите удалить, безусловным переходом к первой инструкции в конце удаленных инструкций. Для этого двоичного файла ARMv8 это выглядело так:
f2c: 14000007 b f48
f30: b0000002 adrp x2, 1000
f34: 91128442 add x2, x2, #0x4a1
f38: 320003e0 orr w0, wzr, #0x1
f3c: 2a1f03e1 mov w1, wzr
f40: aa1503e3 mov x3, x21
f44: 97fffeeb bl af0 <error@plt>
f48: f94013f7 ldr x23, [sp, #32]
По сути, вы убиваете код (превращаете его в мертвый код). Боковое примечание: вы можете сделать что-то подобное с литеральными строками, встроенными в двоичный файл: если вы хотите заменить его на меньшую строку, вы почти всегда можете избежать перезаписи строки (включая завершающий нулевой байт, если это C-строка ) и, при необходимости, перезаписать жестко закодированный размер строки в машинном коде, который ее использует.
Вы также можете заменить все ненужные инструкции на no-ops. Другими словами, мы можем превратить ненужный код в то, что называется блокировкой:
f2c: d503201f nop
f30: d503201f nop
f34: d503201f nop
f38: d503201f nop
f3c: d503201f nop
f40: d503201f nop
f44: d503201f nop
f48: f94013f7 ldr x23, [sp, #32]
Я ожидаю, что это просто трата циклов процессора по сравнению с их перепрыгиванием, но это проще и, следовательно, защищено от ошибок, потому что вы не Вам не нужно вручную выяснять, как кодировать инструкцию перехода, включая вычисление смещения/адреса для использования в ней - вам не нужно много думать для безоперационных салазок.
Чтобы было ясно, ошибка проста: я ошибся два (2) раза, когда вручную кодировал эту инструкцию безусловного перехода. И это не всегда наша вина: в первый раз это было из-за того, что документация, которая у меня была, была устаревшей/неправильной и в ней говорилось, что один бит игнорируется в кодировке, хотя на самом деле это не так, поэтому я установил его на ноль с первой попытки.
Добавление кода
Теоретически вы можете использовать эту технику для добавления машинных инструкций, но это более сложно, и мне никогда не приходилось этого делать, поэтому у меня нет рабочего примера. В настоящее время.
С точки зрения машинного кода это довольно просто: выберите одну инструкцию в том месте, где вы хотите добавить код, и преобразуйте ее в инструкцию перехода к новому коду, который вам нужно добавить (не забудьте добавить инструкции, которые вы таким образом заменены на новый код, если только вам это не нужно для вашей добавленной логики, и чтобы вернуться к инструкции, к которой вы хотите вернуться в конце добавления). По сути, вы вставляете новый код.
Но вам нужно найти место, куда действительно поместить этот новый код, и это сложная часть.
Если вам действительно повезло, вы можете просто добавить новый машинный код в конец файла, и он просто заработает: новый код загрузится вместе с остальными в тот же ожидаемый машинные инструкции, в пространство вашего адресного пространства, которое попадает на страницу памяти, правильно помеченную как исполняемую.
По моему опыту, hexdump -R
игнорирует не только самый правый столбец, но и самый левый столбец, так что вы можете буквально просто указать нулевые адреса для всех добавленных вручную строк, и это сработает.
Если вам повезло меньше, после добавления кода вам придется фактически настроить некоторые значения заголовков в том же файле: если загрузчик вашей операционной системы ожидает, что двоичный файл будет содержать метаданные, описывающие размер исполняемой секции (по историческим причинам часто называемый текстовым разделом), вам придется найти и настроить его. В прежние времена бинарные файлы были просто машинным кодом, а сейчас машинный код обернут кучей метаданных (например, ELF в Linux и некоторых других).
Если вам все еще немного повезло, у вас может быть некоторое мертвое пятно в файле, которое правильно загружается как часть двоичного файла с теми же относительными смещениями, что и остальная часть кода, который уже находится в файле (и это мертвое пятно может соответствовать вашему коду и правильно выровнена, если ваш ЦП требует выравнивания слов для инструкций ЦП). Затем вы можете перезаписать его.
Если вам действительно не повезло, вы не можете просто добавить код, и нет мертвого пространства, которое вы можете заполнить своим машинным кодом. В этот момент вы в основном должны быть хорошо знакомы с исполняемым форматом и надеяться, что сможете придумать что-то в рамках этих ограничений, что по-человечески возможно сделать вручную за разумное время и с разумным шансом не испортить это. .
hexdump
не узнает -R
. Вместо этого мы можем использовать xxd -r
для обратного шестнадцатеричного дампа, но остерегайтесь, что objdump
не распознает двоичный файл с обратным шестнадцатеричным дампом.
- person Abhishek; 12.02.2020
hexdump
из BusyBox, который был скомпилирован с FEATURE_HEXDUMP_REVERSE
. Я никогда не пробовал xxd -r
, потому что это недоступно в системах, с которыми я работаю подобным образом. (В любом случае, если objdump
не распознает полученный двоичный файл, я заподозрю, что двоичный файл неверен, и что-то пошло не так.)
- person mtraceur; 12.02.2020
hexdump -R
, а если нет, то использовать альтернативу, например xxd
вместо hexdump
для всех команд.
- person mtraceur; 12.02.2020
@mgiuca правильно ответил на этот ответ с технической точки зрения. На самом деле, дизассемблирование исполняемой программы в легко перекомпилируемый исходный код сборки — непростая задача.
Чтобы добавить некоторые детали к обсуждению, есть несколько методов/инструментов, которые было бы интересно изучить, хотя они технически сложны.
- Статические/динамические инструменты. Этот метод включает в себя анализ формата исполняемого файла, вставку/удаление/замену определенных инструкций сборки для заданной цели, исправление всех ссылок на переменные/функции в исполняемом файле и создание нового модифицированного исполняемого файла. Некоторые известные мне инструменты: PIN, Похититель, PEBIL, ДинамоРИО. Учтите, что настройка таких инструментов для целей, отличных от тех, для которых они были разработаны, может быть сложной задачей и требует понимания как исполняемых форматов, так и наборов инструкций.
- Полная декомпиляция исполняемого файла. Этот метод пытается восстановить исходный код полной сборки из исполняемого файла. Вы можете взглянуть на онлайн-дизассемблер, который пытается работа. В любом случае вы теряете информацию о различных исходных модулях и, возможно, об именах функций/переменных.
- Перенацеливаемая декомпиляция. Этот метод пытается извлечь больше информации из исполняемого файла, просматривая отпечатки пальцев компилятора (т. е. шаблоны кода, сгенерированные известными компиляторами) и другие детерминированные данные. Основная цель состоит в том, чтобы восстановить исходный код более высокого уровня, такой как исходный код C, из исполняемого файла. Иногда это может восстановить информацию об именах функций/переменных. Учтите, что компиляция исходников с
-g
часто дает лучшие результаты. Вы можете попробовать Retargetable Decompiler.
Большая часть этого исходит из областей исследований по оценке уязвимостей и анализу выполнения. Это сложные методы, и часто инструменты не могут быть использованы сразу из коробки. Тем не менее, они оказывают неоценимую помощь при попытке обратного проектирования некоторого программного обеспечения.
Для изменения кода внутри бинарной сборки обычно есть 3 способа сделать это.
- Если это просто какая-то тривиальная вещь, например константа, то вы просто меняете местоположение с помощью шестнадцатеричного редактора. Предполагая, что вы можете найти его для начала.
- Если вам нужно изменить код, используйте LD_PRELOAD, чтобы перезаписать некоторые функции в вашей программе. Однако это не работает, если функции нет в таблицах функций.
- Взломайте код функции, которую вы хотите исправить, чтобы она была прямым переходом к функции, которую вы загружаете через LD_PRELOAD, а затем возвращаетесь в то же место (это комбинация двух предыдущих)
Конечно, только второй будет работать, если сборка выполняет какую-либо самопроверку целостности.
Редактировать: если это не очевидно, то игра с бинарными сборками - это ОЧЕНЬ высокоуровневый материал для разработчиков, и вам будет трудно спросить об этом здесь, если только вы не спрашиваете о конкретных вещах.
миазм
https://github.com/cea-sec/miasm
Это представляется наиболее многообещающим конкретным решением. Согласно описанию проекта, библиотека может:
- Открытие/изменение/генерация PE/ELF 32/64 LE/BE с помощью Elffeat.
- Сборка/разборка X86/ARM/MIPS/SH4/MSP430
Таким образом, это должно в основном:
- разобрать ELF во внутреннее представление (разборка)
- изменить то, что вы хотите
- сгенерировать новый ELF (сборка)
Я не думаю, что он генерирует текстовое представление разборки, вам, вероятно, придется пройтись по структурам данных Python.
TODO найти минимальный пример того, как сделать все это с помощью библиотеки. Хорошей отправной точкой кажется example/disasm/full .py, который анализирует данный файл ELF. Ключевой структурой верхнего уровня является Container
, которая читает файл ELF с помощью Container.from_stream
. TODO как его потом собрать? Эта статья, кажется, делает это: http://www.miasm.re/blog/2016/03/24/re150_rebuild.html
Этот вопрос спрашивает, есть ли какие-либо другие такие библиотеки: >https://reverseengineering.stackexchange.com/questions/1843/what-are-the-available-libraries-to-statically-modify-elf-executables
Связанные вопросы:
- https://reverseengineering.stackexchange.com/questions/185/how-do-i-add-functionality-to-an-existing-binary-executable
- https://askubuntu.com/questions/617441/how-can-edit-a-executable-file-linux
Я думаю, что эту проблему нельзя автоматизировать
Я думаю, что общая проблема не является полностью автоматизированной, и общее решение в основном эквивалентно тому, как «перепроектировать» двоичный файл.
Чтобы вставлять или удалять байты осмысленным образом, мы должны убедиться, что все возможные переходы продолжают переходить в одни и те же места.
Формально нам нужно извлечь граф потока управления двоичного файла.
Однако с непрямыми ветвями, например, https://en.wikipedia.org/wiki/Indirect_branch , определить этот график непросто, см. также: Расчет пункта назначения косвенного перехода
Мой «дизассемблер ассемблера ci» — единственная известная мне система, разработанная на основе принципа, что чем бы ни была дизассемблерная сборка, она должна собираться в один и тот же двоичный файл байт за байтом.
https://github.com/albertvanderhorst/ciasdis
Приведены два примера elf-исполняемых файлов с их дизассемблированием и повторной сборкой. Изначально он был разработан для возможности модифицировать систему загрузки, состоящую из кода, интерпретируемого кода, данных и графических символов, с такими тонкостями, как переход из реального режима в защищенный. (Удалось.) В примерах также показано извлечение текста из исполняемых файлов, который впоследствии используется для меток. Пакет debian предназначен для Intel Pentium, но доступны плагины для Dec Alpha, 6809, 8086 и т. д.
Качество разборки зависит от того, сколько усилий вы приложите к ней. Например, если вы даже не предоставите информацию о том, что это файл elf, дизассемблирование будет состоять из отдельных байтов, а повторная сборка будет тривиальной. В примерах я использую скрипт, который извлекает метки и создает по-настоящему удобную реверс-инжиниринговую программу, которую можно модифицировать. Вы можете что-то вставить или удалить, и автоматически сгенерированные символические метки будут пересчитаны. С помощью предоставленных инструментов генерируются метки для всех мест, где заканчиваются прыжки, а затем метки используются для этих прыжков. Это означает, что в большинстве случаев вы можете вставить инструкцию и пересобрать модифицированный исходный код.
Никаких предположений о двоичном блобе не делается, но, конечно, дизассемблирование Intel мало что дает для двоичного файла Dec Alpha.
Еще одна вещь, которая может быть вам интересна:
- бинарная инструментация - изменение существующего кода
Если интересно, проверьте: Pin, Valgrind (или проекты, делающие это: NaCl — собственный клиент Google, может быть, QEmu.)
Вы можете запустить исполняемый файл под наблюдением ptrace (другими словами, отладчиком, таким как gdb) и таким образом контролировать выполнение по ходу дела, не изменяя фактический файл. Конечно, требуются обычные навыки редактирования, такие как поиск того, где в исполняемом файле находятся конкретные инструкции, на которые вы хотите повлиять.