декомпилировать код C с информацией отладки?

Байт-код Java и Python относительно легко декомпилировать, чем скомпилированный машинный код, сгенерированный компилятором C / C ++.

Я не могу найти убедительный ответ, почему информация из опции -g недостаточна для декомпиляции, но достаточна для отладки?
Что такое дополнительный материал, содержащийся в байт-коде Python / Java, который облегчает декомпиляцию?

3

Решение

Я не могу найти убедительный ответ, почему информация из опции -g недостаточна для декомпиляции, но достаточна для отладки?

Отладочная информация в основном содержит только отображение между адресами в сгенерированном коде и номерами строк исходных файлов. Отладчику не нужно декомпилировать код — он просто показывает исходные коды. Если исходные файлы отсутствуют, отладчик не будет магически показывать их.

Тем не менее, наличие отладочной информации облегчает декомпиляцию. Если информация отладки включает в себя макет используемых типов и прототипов функций, декомпилятор может использовать ее и обеспечить гораздо более точную декомпиляцию. Однако во многих случаях он все равно будет отличаться от исходного источника.

Например, вот функция, декомпилированная с помощью декомпилятора Hex-Rays без использования информации отладки:

int __stdcall sub_4050A0(int a1)
{
int result; // eax@1

result = a1;
if ( *(_BYTE *)(a1 + 12) )
{
result = sub_404600(*(_DWORD *)a1);
*(_BYTE *)(a1 + 12) = 0;
}
return result;
}

Поскольку он не знает тип a1доступ к его полям представлен в виде дополнений и приведений.

И вот та же функция после загрузки файла символов:

void __thiscall mytree::write_page(mytree *this, PAGE *src)
{
if ( src->isChanged )
{
cache::set_changed(this->cache, src->baseAddr);
src->isChanged = 0;
}
}

Вы можете видеть, что это было улучшено довольно много.

Что касается того, почему декомпилирование байт-кода обычно проще, в дополнение к проверке ответа NPE также этот.

1

Другие решения

Вот несколько причин для этого:

  1. Байт-коды Java и Python являются относительно простыми и высокоуровневыми, в то время как набор команд некоторых процессоров (например, x86) чрезвычайно сложен.
  2. Байт-коды близко имитируют структуру языка, для которого они были разработаны.
  3. При генерации байт-кодов Java и Python мало что делают для оптимизации. Это приводит к байт-кодам, которые близко соответствуют структуре исходного исходного кода. Хороший оптимизирующий компилятор C или C ++ способен создавать ассемблер, который далек от исходного исходного кода.
  4. Существует несколько компиляторов Java и Python и много компиляторов C и C ++. Легче создать высококачественный декомпилятор, если вы нацелены на один известный компилятор (или небольшой набор известных компиляторов).
  5. Python и Java являются относительно простыми языками по сравнению с C ++ (этот пункт не относится к C).
  6. Шаблоны C ++ создают много проблем для качественной декомпиляции (этот пункт также не относится к C).
  7. Препроцессор C / C ++.
  8. В Python существует взаимно-однозначная связь между исходными файлами и файлами байт-кода. В Java отношение является одним источником для одного или нескольких файлов байт-кода. В C и C ++ отношения многие-ко-многим, с большим количеством совпадений на фронте исходного кода (думаю, заголовки).
8

Некоторые процессоры, такие как x86, имеют инструкции переменной длины. Если управление передается в середину (= где-нибудь после первого байта) инструкции, это также может быть допустимой командой (или несколькими инструкциями). Это затрудняет однозначную разборку машинного кода. Код C / C ++ может использовать эту функцию.

На некоторых процессорах и ОС можно выполнять данные, как если бы они были кодом, и использовать код, как если бы они были данными. Это затрудняет однозначное разделение двух. И, опять же, это то, что программы на C / C ++ часто могут делать легко.

На некоторых процессорах и операционных системах легко генерировать код на лету и выполнять его, а также можно изменять существующий код во время выполнения. Это также способствует неоднозначности в декомпиляции кода. И программы на C / C ++ часто могут это делать.

РЕДАКТИРОВАТЬКроме того, некоторые процессоры имеют несколько разных кодировок для одной и той же инструкции. Например, процессоры x86 имеют 2 инструкции mov reg, reg/mem а также mov reg/mem, reg, Они позволяют перемещать данные между регистром и ячейкой памяти (в любом направлении) и между двумя регистрами. Обе эти инструкции могут использоваться для передачи данных между двумя регистрами, но они имеют разные кодировки. Если программа каким-то образом полагается на определенную кодировку (например, с целью проверки ее целостности с помощью контрольных сумм), то при разборке, например, mov eax, ebx Вы не сможете сказать, какой из двух mov инструкции, как это было изначально, и поэтому, если вы попытаетесь собрать разборку, вы можете сломать программу.

Вы можете использовать отладчик для отладки программы с информацией об отладке / символах или без нее. Эта информация только облегчает человеку навигацию по коду и данным, поскольку многие (но не обязательно все) подпрограммы и переменные могут быть идентифицированы и показаны с использованием их имен и типов, а не только необработанных адресов и необработанных данных без типов.

Я предполагаю, что различные байт-коды менее двусмысленны и более ограничены в том, что они могут делать, и это облегчает их декомпиляцию.

0
По вопросам рекламы [email protected]