2010-05-15 1 views
8

Рассмотрим это (имхо) простой пример:Есть ли какой-нибудь Java Decompiler, который может правильно декомпилировать вызовы перегруженным методам?

public class DecompilerTest { 
    public static void main(String[] args) { 
     Object s1 = "The", s2 = "answer"; 
     doPrint((Object) "You should know:"); 
     for (int i = 0; i < 2; i++) { 
      doPrint(s1); 
      doPrint(s2); 
      s1 = "is"; 
      s2 = new Integer(42); 
     } 
     System.out.println(); 
    } 

    private static void doPrint(String s1) { 
     System.out.print("Wrong!"); 
    } 

    private static void doPrint(Object s1) { 
     System.out.print(s1 + " "); 
    } 
} 

Собирать с источником/целевого уровня 1.1 без информации отладки (т.е. не локальной переменной информации не должно присутствовать) и попытаться декомпилировать его. Я попробовал Jad, JD-GUI и Fernflower, и все они получили хотя бы один из неправильных вызовов (например, программа напечатала «Неправильно!» Хотя бы один раз)

Действительно ли нет декомпилятора java, который может вывести правильное бросает так, чтобы он не вызывал неправильную перегрузку?

Редактировать: Целевой уровень 1.1, чтобы не было никакой информации о быстрой проверке Java6. Это может дать декомпилятору ключ к тому, что s1 объявлен как Object, а не как String. Декомпилятор должен иметь возможность декомпилировать код даже без этой информации (необязательно получить исходный тип переменной, но показывать то же поведение), тем более, что множество обфускаторов также разделяет его.

Что декомпиляторов ошибались:

  • Они пропустили бросок к (Object) в первом вызове.
  • Они вывели тип s1 как String, но забыли добавить приведение к вызову doPrint (так что вместо версии объекта вызывается версия String).
  • Один из дерьмовых (я даже не перечислял) даже указывает тип s2 на строку, вызывающую несовместимый код.

В любом случае этот код никогда не вызывает перегрузку String, но декомпилированный код сделал.

+0

Что значит «они ошиблись»? Какой источник они генерируют после декомпиляции? – skaffman

+1

hm, и почему 1.1? – Bozho

ответ

4

Krakatau корректно обрабатывает все перегруженные методы, даже методы перегруженных на примитивных типов, большинство декомпиляторов получить неправильно. Он всегда аргументирует точный тип вызываемого метода, поэтому код может быть более загроможден, чем необходимо, но, по крайней мере, это правильно.

Раскрытие информации: Я являюсь автором Кракатау.

3

Плагин JadClipse Eclipse для декомпиляции также предоставляет декомпилятор JODE, который вы можете попробовать. Я использую его, когда Джад сдаётся.

Также декомпилятор Dava использует Soot, который - в последний раз, когда я смотрел - был очень амбициозен в восстановлении исходного кода Java. Я не пробовал с вашим примером, но вы можете посмотреть. http://www.sable.mcgill.ca/dava/

+0

Dava потерпел неудачу, как и другие. Джод работал, но так как Джод имеет тенденцию к сбою почти в каждой программе, которая проходила через какой-либо обфускатор, это на самом деле не помогает мне :(Угадайте, я задал неправильный вопрос.;) – mihi

+3

Downvote _and_ принять ответ? Это было ново :) –

+0

изменил мой принятый ответ, если вам не нравится, когда вас не привлекают и принимают :) (я сначала отказался от вас, потому что ваши предложения тоже не сработали, но потом решили, что из всех ответов с нисходящими ответами ваш был лучшим) - btw где вы можете видеть, что это я был и тем, и другим, и лидером? – mihi

9

Hallo Михи,

извините за поздний ответ. Я копирую свой ответ от http://www.reversed-java.com/fernflower/forum?threadfolder=2_DE

Ваша проблема на самом деле хорошо известна. Давайте посмотрим:

1) Чистый байт-код не содержит информации о типе переменных объекта, поэтому в первом проходе s1 и s2 объявляются как Object.

2) Декомпилятор прилагает все усилия, чтобы назначить наилучший возможный тип каждой переменной (= «самый узкий принцип типа», реализованный в Fernflower). Таким образом, s1 и s2 правильно идентифицируются как экземпляры String.

3) Призывание doPrint дают нам прямую ссылку на правильный метод
частный статический аннулируются doPrint (s1 Object)

4) Все ОК до сих пор, не так ли?Теперь мы получили переменную String s1, переданную функции, которая ожидает объект. Нужно ли его бросать? Не как таковой, вы бы подумали, так как Object является супертипом String. И все же мы делаем - потому что в одном классе есть еще одна функция с тем же именем и другой сигнатурой параметра. Поэтому нам нужно проанализировать весь класс, чтобы узнать, нужен ли бросок или нет.

5) Вообще говоря, это означает, что нам нужно проанализировать ВСЕ ссылочные классы во ВСЕХ библиотеках, включая java runtime. Огромная работа! Действительно, эта функция была реализована в какой-то альфа-версии Fernflower, но еще не сделала ее в производстве из-за производительности и памяти. Другие упомянутые декомпиляторы не обладают этой способностью по дизайну.

Надежда Я осветленные вещи немного :)

+0

Да, я знаю общую проблему (поэтому мне было нелегко сократить намного более длинный класс до образца выше). Но, насколько я понимаю, если у вас нет информации о классе для целевого класса, вы всегда можете добавить (лишний) приведение к точному типу параметра, не так ли? Вы все равно можете использовать quickfix Eclipse для удаления всех лишних бросков позже. (т.е.Я бы предпочел правильность красоты при декомпиляции кода) – mihi

+0

Вы правы, это можно сделать. Единственным недостатком было бы то, что мы генерируем очень уродливый код со всеми этими ненужными бросками. Я буду реализовывать вариант для этого поведения, спасибо за предложение. – Stiver

+0

«Чистый байт-код не содержит информации о типе переменных объекта» –

3

Procyon должен правильно обрабатывать вызовы перегруженного метода. Как и Krakatau, Procyon изначально вставляет броски для каждого аргумента метода, который точно не соответствует целевому методу. Однако большинство из них будут удалены во время более поздней фазы декомпиляции, которая идентифицирует и устраняет избыточные броски. Procyon будет удалять только отбрасывания на аргументы вызова, если он может убедиться, что это не приведет к привязке вызова к другому методу. Например, если .class, объявляющий метод, не может быть разрешен, он не будет вообще пытаться удалить броски, потому что он не знает, что может вызвать перегрузки.

2

Добавление к предыдущим ответам: Вот список современных декомпиляторов по состоянию на март 2015 года:

  • Процион
  • CFR
  • JD
  • Fernflower

ВСЕ они поддерживают перегруженные методы.

Вы можете протестировать вышеупомянутые декомпиляторы онлайн, не требуется установка и сделать свой собственный образованный выбор. Разделение устройств в облаке: http://www.javadecompilers.com/

+0

Не уверен, что JD и JDCore одинаковы, но для записи: JDCore с этого сайта не может декомпилировать образец из этого вопроса – mihi

+0

JD и JDCore - это то же самое. Плохо, что это провалилось. Надейся ли другие декомпиляторы? –