Декомпилатор за Java
Java декомпилаторът е вид декомпилатор – програмен инструмент, който превръща изпълними файлове на компютърни програми в изходен код, разбираем от хора[1][2]. Операцията е обратна на тази на компилатора, който превежда изходния код на машинен език. Качеството на декомпилацията варира значително в зависимост от сложността на програмата и наличие на метаданни. Начина по който декомпилаторът анализира изпълнението на програмата също има ключова роля.
Декомпилирането се използва най-често за възстановяване на изгубен изходен код, когато е необходимо да се разбере функционалността на дадена програма или за подобряване на нейната сигурност.
Java компилаторът компилира в двоичен код, който се изпълнява в JVM (Java виртуална машина) и се съхранява в .class файлове. Този двоичен код не представя едно към едно оригиналния Java код написан от програмиста и включва няколко оптимизации приложени от компилатора. Определена част от информацията се губи, когато се изпълни тази оптимизация. Поради тази причина декомпилаторите не могат да възстановят точно първоначалния вид на кода.
Как работи Декомпилатора
[редактиране | редактиране на кода]Декомпилацията може да се разбие на няколко поредни етапа:
Зареждане на машинния код
[редактиране | редактиране на кода]При зареждането трябва да се открият основни точки за зададената програма като архитектура и входната точка. В повечето случаи трябва да намери еквивалентни функции с основните функции в Java.
Преобразуване
[редактиране | редактиране на кода]Състои се в преобразуването на инструкции от машинен код в машинно междинно независимо представяне (IR).
Идиоми
[редактиране | редактиране на кода]Идиоматични поредици от машинен код са поредици код, чиято комбинирана семантика не е непосредствено очевидна от инструкциите на индивидуалните семантики. Или като част от преобразуването, или като част от по-късни анализи, тези идиоматични поредици трябва да бъдат преведени в познат еквивалент в машинно междинно независимо представане(IR).
Анализ на програмата
[редактиране | редактиране на кода]Различни анализи на програмата могат да бъдат приложени на (IR). По-точно събиране, комбиниране на няколко инструкции в по-сложен израз. Резултатът е език на по-високо ниво.
Анализиране на потока от данни
[редактиране | редактиране на кода]Местата, където съдържанието на регистъра се дефинират и използват, трябва да бъдат проследени с анализ на потока от данни. Този анализ трябва да бъде приложен също така и на места, които се използват за временни и локални данни. Възможно е на едно и също място в паметта да се използва от различни променливи в различни части от оригиналната програма.
Анализ на типовете данни
[редактиране | редактиране на кода]Чрез наблюдение на записването в паметта, декомпилаторът понякога може да разбере типа данни.
Структуриране
[редактиране | редактиране на кода]В този етап машинно междинно независимо представане(IR) структурира в конструкция от по-високо ниво, като цикли while
и условни конструкции (if, else
, then
).
Генериране на код
[редактиране | редактиране на кода]Това е последният етап. Генерира се код от по-високо ниво в back end-а на декомпилатора. Декомпилаторът може да има опция да представи кода в други езици.
Разлика между декомпилатор и дизасемблър
[редактиране | редактиране на кода]Декомпилаторът превръща изпълнимия бинарен файл в лесно четима форма. По-точно той трансформира бинарния код в по-високо ниво програмен език (C, C++ или Java), който програмисти и разработчици могат да четат и променят. Много често цялата индустрията за софтуерна защита разчита на тази трансформация, за да анализира и одобри определени софтуерни продукти. Анализът се прави върху двоичния код, ползвайки декомпилатори, просто защото изходният програмен код традиционно не е достъпен, най-често поради търговска тайна.
Дизасемблерите от своя страна превръщат простите двоични инструкции на процесора едно към едно в мнемонични инструкции (код). Обикновено мнемоничният код представлява символно наименование на една изпълнима на машинен език инструкция (операционен код) и съществува поне по един операционен мнемоничен код, дефиниран за всяка инструкция на машинен език.
Декомпилаторите се различават от дизасемблерите по един много важен показател – и двата създават лесен за четене от хората последователност от текстови команди, но декомпилаторите създават доста по-високо ниво текстови инструкции, които са по-смислени и доста по-лесни за четене. Сравнен с ниско ниво асемблерните езици, езикът от високо ниво като Java има следните предимства:
- Значително по-смислен е
- Структуриран е
- Не изисква от разработчиците да знаят асемблерен език
- По-малко объркващ е следователно е по-лесен за разбиране
- Разпознава и преобразува идиомите от ниско ниво в понятия от високо ниво
- По-малко повторяем е и съответно не толкова разсейващ
- Използва анализ на потока от данни
Да разгледаме тези точки по-детайлно.
Обикновено изходният код от декомпилаторите е от пет до десет пъти по-къс от този на дизасемблерите. За пример най-често една програма съдържа от 400 KB до 5 MB бинарен (двоичен) код. Съответно изходният код, който дизасемблерът ще генерира, ще включва 5 – 100 MB текст, което би отнело от няколко седмици до няколко месеца, за да се анализира напълно. Докато при декомпилаторите кодът за същата програма би бил от 400 KB до 10 MB, като в този случай времето за анализ би било 10 пъти по-малко.
Втората голяма разлика е, че при декомпилаторите изходният код е структуриран и текстът е отместен в една или друга посока за по-лесно проследяване на логическата връзка, вместо да е последователност от редове, в които всеки следващ ред е много подобен на останалите.
Последователността от код създава условни изрази, цикли и променливи, които са обозначени със съответните доста по-смислени и съответстващи думи от нормалния човешки език.
За да може да се ползва дизасемблер, анализаторите на код трябва добре да познават асемблерния език на дадения процесор. Разбира се основната част от програмистите не използват асемблер език всекидневно, а по-скоро по-високо ниво езици като Java. По този начин декомпилаторът прескача границата между обикновените програмни езици от високо ниво до изходния език.
Декомпилаторът преобразува идиомите от асемблер в по-високо ниво абстрактен програмен език. Някои идиоми могат да бъдат доста дълги и времеемки за анализ. Следната последователност от код:
x = y / 2;
може да бъде трансформирана от компилатора в серия от 20 – 30 процесорни инструкции. Това би отнело между 15 – 30 секунди на един опитен анализатор да разпознае модела и мислено да го замени с оригиналния ред. Ако кодът включва много такива изрази, съответно анализаторът трябва да си води бележки и да описва всеки модел с неговия къс израз. Всичко това би забавило анализатора значително. Декомпилаторът просто премахва това неудобство от анализатора на кода.
Обикновено обемът от асемблерни инструкции е огромен. Всички те изглеждат доста подобни една на друга и техният модел е доста повторяем. 95% от кода, създаден от компилатор, е сравнително отегчителен и е много лесно за анализатора на този код да се обърка, от примерно две много подобни парчета код, като съответно изгуби нишката към крайния резултат.
Различни Java Декомпилатори
[редактиране | редактиране на кода]Procyon [3]
[редактиране | редактиране на кода]Декомпилаторът Procyon е съвместим с всички версии на езика за компилиране Java по-нови от Java версия 5 (включително Java версия). Написан на Java версия 6, този декомпилатор е особено надежден в ситуации, включващи:
Enum
иString
базираниswitch
изрази- Локални класове (анонимни и именувани)
- Анотации
- Java версия 8 ламбда изрази и референции към методи (например:
::
оператор).
Често срещан проблем е, че класовете компилирани чрез средата Eclipse, може да не дадат напълно правилни резултати. Това се забелязва най-често при String
базирани switch
изрази.
CFR
[редактиране | редактиране на кода]Безплатен, няма сорс код на разположение.[4]
Обновен през 2015 година и написан изцяло на езика за програмиране Java 6, декомпилаторът CFR поддържа Java версия 6,7 и 8 и е способен да декомпилира модерни Java функции като:
- Ламбда изрази от Java версия 8
String
базираниswitch
изрази от Java версия 7
AndroChef[5]
[редактиране | редактиране на кода]Декомпилаторът AndroChef е собственост на компанията Microsoft и е способен да декомпилира клас файлове с разширение:
- .apk
- .dex
- .jar
- .java
Поддържа всички версии на езика за програмиране Java до Java версия 6 включително. Съвместим е със следните операционни системи:
- Windows XP
- Windows 2003
- Windows Vista
- Windows 7
- Windows 8
- Windows 8.1
- Windows 10
Декомпилаторът AndroChef е способен да декомпилира бинарни кодове и сложни приложения, написани на програмния език Java версия 6. Често се наблюдават проблеми при съвместна работа с Java JRE 64-bit пакет.
JAva_Decompiler
[редактиране | редактиране на кода]JAD дълго време е един от най-разпространените декомпилатори на програмния език Java. Написан изцяло на програмния език C++, JAD не е обновяван от 2011 година и отдавна е изгубил функционалността си сред съвременните езици като Java версия 5 и нагоре.
Fernflower[6][7]
[редактиране | редактиране на кода]Отворен код. Създател: Egor Ushakov.
Oбновен последно през 2015 година, този бързо развиващ се аналитичен декомпилатор скоро ще е интегрирана част от средата за програмиране Intellij версия 14. Fernflower e съвместим и съпортва всички версии на програмния език Java до Java версия 6 включително.
Безплатен софтуер за некомерсиално използване. Създател: Emmanuel Dupuy.
Обновен последно през 2014 година, този декомпилатор притежава собствен визуален интерфейс и приставки към средите за разработка Eclipse и Intellij. JD е написан изцяло на езика за програмиране C++ и поддържа само версия 5 на езика за програмиране Java.