Благодаря языкам виртуальных машин на основе байт-кода, таким как Java, VB.NET, C #, ActionScript 3.0 и т. Д., Вы иногда слышите о том, как легко просто загрузить какой-то декомпилятор из Интернета, запустить байт-код через него в одно удобное время и часто за несколько секунд придумывает что-то не слишком далекое от исходного исходного кода. Предположительно, этот тип языка особенно уязвим для этого.
Я недавно начал задаваться вопросом, почему вы больше не слышите об этом, касающемся нативного двоичного кода, когда вы хотя бы знаете, на каком языке он был написан изначально (и, следовательно, на каком языке пытаться декомпилироваться). Долгое время я полагал, что это просто потому, что родной машинный язык намного более безумный и более сложный, чем типичный байт-код.
Но как выглядит байт-код? Это выглядит так:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
И как выглядит машинный код (в шестнадцатеричном формате)? Это, конечно, выглядит так:
1000: 2A 40 F0 14
1001: 2A 50 F1 27
1002: 4F 00 F0 F1
1003: C9 00 00 F2
И инструкции исходят из несколько схожего настроения:
1000: mov EAX, 20
1001: mov EBX, loc1
1002: mul EAX, EBX
1003: push ECX
Итак, учитывая язык, который пытается декомпилировать какой-то собственный двоичный файл, скажем, в C ++, что в этом сложного? Единственные две идеи, которые сразу приходят на ум: 1) на самом деле все гораздо сложнее, чем байт-код, или 2) что-то о том, что операционные системы имеют тенденцию разбивать программы на части и разбрасывать их части, вызывает слишком много проблем. Если одна из этих возможностей верна, пожалуйста, объясните. Но так или иначе, почему ты никогда не слышишь об этом в принципе?
НОТА
Я собираюсь принять один из ответов, но сначала хочу кое-что упомянуть. Почти все ссылаются на тот факт, что разные части исходного кода могут отображаться на один и тот же машинный код; имена локальных переменных теряются, вы не знаете, какой тип цикла изначально использовался и т. д.
Однако примеры, подобные двум, которые только что были упомянуты, кажутся мне тривиальными. Некоторые ответы, как правило, утверждают, что разница между машинным кодом и исходным кодом значительно больше, чем что-то тривиальное.
Но, например, когда дело доходит до таких вещей, как имена локальных переменных и типы циклов, байт-код также теряет эту информацию (по крайней мере, для ActionScript 3.0). Я извлек эту штуку обратно через декомпилятор раньше, и мне было все равно, была ли вызвана переменная strMyLocalString:String
или loc1
. Я все еще мог бы заглянуть в эту маленькую локальную область и увидеть, как она используется без особых проблем. И for
цикл - это почти то же самое, что иwhile
цикл, если вы думаете об этом. Кроме того, даже если бы я запускал исходный код через irrFuscator (который, в отличие от secureSWF, не делает намного больше, чем просто рандомизирует имена переменных и функций-членов), все равно выглядело, как если бы вы могли просто начать изолировать определенные переменные и функции в меньших классах, узнайте, как они используются, присвойте им свои собственные имена и работайте оттуда.
Для того, чтобы это имело большое значение, машинный код должен был бы потерять намного больше информации, чем это, и некоторые из ответов действительно идут в это.