Как декомпилировать запутанные Java-программы, избегая конфликтов имен классов и пакетов

Я хочу декомпилировать программу Java и перекомпилировать производный (запутанный) источник. Я распаковал архив .jar и получил такую ​​структуру каталогов:

com/
com/foo/A/
com/foo/A/A.class
com/foo/A/B.Class
com/foo/B/A.class
...
com/foo/A.class
com/foo/B.class
org/foo/Bar.class
...

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

package org.foo;
import com.foo.A; // <-- name collision error

class Bar {
    ...
}

Есть ли способ решить эти проблемы с именами без переименования файлов классов?

РЕДАКТИРОВАТЬ: Это не проблема декомпилятора, а вопрос о том, как можно иметь рабочий файл .jar с классами, которые нарушают соглашения об именах.

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


person johnd    schedule 28.12.2010    source источник
comment
Я не уверен, что вы спрашиваете, но вы всегда можете декомпилировать запутанный код, но тогда вы не получите читаемый код,   -  person jmj    schedule 28.12.2010
comment
Зачем вы декомпилируете код?   -  person duffymo    schedule 28.12.2010
comment
@duffymo: я все время декомпилирую код, чтобы украсть его. Это бизнес-модель целой нации, имя которой нельзя называть.   -  person President James K. Polk    schedule 28.12.2010


Ответы (3)


Механизм импорта Java обеспечивает сокращение для именования вещей, но вы, очевидно, не можете использовать его при возникновении коллизий. Вы всегда можете использовать полное имя в своем коде, например.

package org.foo;  

class Bar { 
    private com.foo.Bar aDifferentBar;
    ...
}

РЕДАКТИРОВАТЬ:

Я предполагаю, что могут быть файлы классов, которые соответствуют спецификации JVM, но которые не могут быть созданы программой Java, соответствующей спецификации JLS. Если это так, вам определенно понадобится более умный декомпилятор.

person President James K. Polk    schedule 28.12.2010
comment
Я знаю, но это не решает проблему, если com.foo.Bar неоднозначен. - person johnd; 28.12.2010
comment
Я не знаю, что вы имеете в виду. Может быть только один com.foo.Bar, верно? Есть ли какой-нибудь трюк с обфускатором, который даст вам несколько com.foo.Bar? - person President James K. Polk; 28.12.2010
comment
Если в пакете org.foo есть класс Bar, то пакета org.foo.Bar обычно быть не может. Однако такие конфликты имени класса/пакета происходят в банке, которую я хочу декомпилировать. - person johnd; 28.12.2010

Вам действительно нужно распаковать всю банку и перекомпилировать все? Вместо перекомпиляции всего декомпилированного исходного кода используйте исходный jar-файл в качестве пути к классам, а также извлекайте и перекомпилируйте только те классы, которые необходимо изменить. Затем, когда вам нужно упаковать перекомпилированный код, просто скопируйте оригинальный jar и используйте jar -uf для замены измененных файлов классов на место:

jar -uf ./lib/copy_of_original_jar_file.jar -C ./bin com/foo/A.class com/foo/B.class [...]

... и ./lib/copy_of_original_jar_file.jar станет вашей новой библиотекой.

Одно можно сказать наверняка: исходный jar-файл должен правильно работать с загрузчиком классов Java, чтобы программа могла работать. Он должен работать так же хорошо для компиляции ваших одноразовых файлов .class.

Вы должны испытывать гораздо меньше проблем с конфликтами имен при использовании исходного jar-файла, потому что вы сохраняете тот же порядок сканирования путей к классам, который использовало бы работающее приложение. Мало того, декомпиляторы Java не идеальны. Исключая большую часть декомпилированного кода из перекомпиляции, вы избегаете большинства проблем, которые возникают у декомпиляторов, таких как перекрытие обработчиков исключений, специальные символы в запутанных символах, проблемы с областью действия переменных и т. д.

person smallfire    schedule 28.12.2010
comment
Перекомпилировать только некоторые классы было именно то, что я имел в виду, но если я декомпилирую класс A, а этот класс импортирует класс B с таким неоднозначным именем, то я не могу скомпилировать класс A. Я мог бы переименовать класс B но тогда мне пришлось бы исправить все ссылки на B в A и других классах. - person johnd; 29.12.2010
comment
Я не совсем уверен, что ошибка обязательно означает, что у вас проблема с двусмысленностью. Вы пытались импортировать другой запутанный класс, который не является двусмысленным? - person smallfire; 30.12.2010
comment
Данг. Я надеялся, что мой анекдотический опыт превзойдет твою реальность. Я думаю, это так не работает. Возможно, вы можете попробовать использовать что-то вроде JDO (Java Deobfuscator). sourceforge.net/projects/jdo Я нашел это здесь: stackoverflow.com/questions/1662766/ - person smallfire; 30.12.2010
comment
Это был один из немногих деобфускаторов, которые я не пробовал... и он довольно умен, поскольку переименовывает классы с конфликтами имен. К сожалению, есть еще много ошибок, но это хорошая отправная точка. Спасибо :) - person johnd; 30.12.2010

Вы не можете импортировать пакеты в Java, так почему это должно быть конфликтом имен? Какое сообщение об ошибке вы получаете от компилятора?

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

person Reboot    schedule 28.12.2010
comment
Сообщение об ошибке - импорт ... не может быть разрешен. В com/foo/A.class может быть общедоступный внутренний класс с именем A, поэтому import com.foo.A.A может означать этот внутренний класс или com/foo/A/A.class. Вот почему такое именование запрещено в Java, но программа должна быть каким-то образом скомпилирована... - person johnd; 28.12.2010
comment
Внутренние классы не были частью вопроса. В файлах классов классы загружаются с использованием своего пути, поэтому внутренние классы — это A$A, а классы в пакетах — A/A. То, что эти классы имеют одинаковую нотацию в исходном файле, является проблемой языка Java. Таким образом, либо декомпилятор должен разрешить конфликт имен и переименовать классы, если он должен генерировать компилируемый код, либо ему придется генерировать код, использующий отражение, что сделает код в значительной степени нечитаемым, что обычно не является целью декомпиляции. . - person Reboot; 28.12.2010