Stringer — новый подход к защите Java приложений
Обычно, для защиты Java-программ используются обфускаторы. Обфускаторы позволяют переименовать классы, методы, переменные, изменить поток управления (control flow) байт-кода. Эти функции умеют делать все обфускаторы включая бесплатные и платные.
Целью обфускации байт-кода является построение такого набора команд JVM из которого декомпилятор не мог бы построить корректный исходный код на языке Java.
Противостояние обфускаторов и декомпиляторов продолжается постоянно.
- Реверс-инжиниринг проприетарного API
- Модификация байт-кода для отключения различных механизмов лицензирования
- Получение доступа к «чувствительной» информации
Классический обфускатор относительно хорошо справляется с защитой от второго типа атак. От реверс-инжиниринга он помогает слабо, потому что Java-программы используют Java API, которое публично и обфускатор не может его изменить. Есть одно исключение, но это даже не обфускатор, а полноценная виртуальная машина с нативной реализацией Java API — Excelsior JET.
Ни для кого не секрет, что в настоящее время наблюдается «бум» троянов, которые, используя уязвимости в JVM, проникают на компьютер пользователя для осуществления своих зловредных действий, в наибольшей степени по отношению к программам, которые работают с финансовой и другой ценной информацией.
Современные средства защиты Java кода должны скорее не защищать его от декомпиляции, а организовывать «песочницу» внутри «песочницы» Java 🙂
С этими проблемами мы столкнулись, разрабатывая систему лицензирования для Java/Java FX приложений — C3. И так родился Stringer.
Теперь вернемся к реальности. Большая часть логики которую хотели бы защищать раработчики коммерческих Java-приложений (в том числе и различные приложения для Android) завязана на строки… Ох уж эти java.lang.String’s.
Например. У нас есть графический клиент, который получает с интернет-ресурса данные в определенном формате, парсит и отображает их пользователю. Злоумышленнику достаточно получить доступ к констант-пулу Java класса и для него не составит никакого труда сделать программу-клон.
Обычно разработчики для защиты от этих атак используют свои функции шифрования строк или пользуются обфускаторами с поддержкой этих функций.
- Шифрование реализовано на базе алгоритма AES с динамическими ключами шифрования для каждого защищаемого Java-пакета
- Логика функций дешифрования динамически встраивается в уже имеющиеся class-файлы:
Фрагмент декомпилированного class-файла до встраивания защиты:
Плюс упомянутая выше возможность организации песочницы: защита от использования reflection, защита private-полей классов и методов, защита final-полей классов от модификации.
Мы считаем, что функция шифрования строк нужна практически каждому разработчику, а для компаний которые хотят защитить пользователей своих продуктов от кражи информации Stringer позволяет организовывать защищенную среду внутри Java-приложения, но эта тема достойна отдельного поста…
В настоящее время мы занимаемся разработкой Stringer для Android-приложений.
P.S.
Кстати, наш CrackMe так никто и не взломал.
Кроме пользователей Хабра, очень активное участие принимали пользователи Javalobby. Суммарно скачиваний было около тысячи, и только один из участников дошел до этапа взаимодействия с сервером лицензирования.
How to protect jar from «simple/easy» decompilation
I know this is a very common question, and a good topic is actually here:
How to lock compiled Java classes to prevent decompilation? But I’m asking something different, not an absolute protection, but at least some basic protection against decompilers. I have tried some decompilers like JD and Jode with some .jar files, and they couldn’t decompile all the .java classes, in many of them I get an error, and many other just a very basic code.
How are they protecting the jar files from those decompilers? when I use the same decompilers with my jar all the classes are decompiled with full detail. So basically my question is, what could be causing this error on those decompilers? is because the java classes are encrypted? EDIT:
I have noted Jode is showing this error in some java files:
Exception while decompiling:java.lang.NoClassDefFoundError: [package.ClassName] at jode.bytecode.ClassInfo.loadInfo(ClassInfo.java:631) at jode.decompiler.ClassAnalyzer.(ClassAnalyzer.java:86) at jode.decompiler.ClassAnalyzer.(ClassAnalyzer.java:123) at jode.decompiler.Decompiler.decompile(Decompiler.java:191) at jode.swingui.Main.run(Main.java:204) at java.lang.Thread.run(Unknown Source)
java.lang.ClassCastException: g cannot be cast to T at bK.a(Unknown Source) at T.a(Unknown Source) at bK.(Unknown Source) at bW.a(Unknown Source) at bW.(Unknown Source) at bW.(Unknown Source) at i.a(Unknown Source) at i.a(Unknown Source) at i.a(Unknown Source) at de.fernflower.main.decompiler.ConsoleDecompiler.addSpace(Unknown Source) at de.fernflower.main.decompiler.ConsoleDecompiler.main(Unknown Source)
Java Tip 22: Protect your bytecodes from reverse engineering/decompilation
If you are writing Java classes and distributing them over the Internet, you should know that people can reverse-engineer, disassemble, or decompile your classes into Java source code. The most widely used decompiler (at least publicly) is Mocha. Mocha reads one or more files of bytecodes (classes) and converts them back to Java source code. Although the code generated by Mocha is not exactly the same as the original source code, it is close enough for someone to understand and modify. If you are interested in developing Java classes and distributing them over the Internet — and you would like to protect them from being decompiled — read on.
Mocha: an example
Before introducing Crema, we will walk through an example using Mocha. The following simple program displays the string «Hi there» on the screen:
If the above four lines were saved in a file, test.java , then compiling test.java would generate a new file, test.class , that contains the Java bytecodes representing that Java source code. Now let’s run Mocha on the class file and see the Mocha output:
% java mocha.Decompiler test.class // the % is my C shell prompt on UNIX.
The above command generates a file called test.mocha , which contains the Java source code generated by Mocha:
% more test.mocha /* Decompiled by Mocha from test.class */ /* Originally compiled from test.java */ import java.io.PrintStream; class test < public static void main(String astring[]) < System.out.println("Hi there"); >test() < >>
As you can see from the example above, Mocha has given us Java source code that is easy to read and understand. If you copy this file to test.java , compile it again, and run it, it will compile and run just fine.
Crema to the rescue!
So how can you protect your classes from being decompiled? One answer is Crema. Crema scrambles the symbolic information in your .class files so that they will become less vulnerable to decompilation. The symbolic information that Crema scrambles includes the name of the class, its superclass, interfaces, variable names, methods, and so on. These symbolic names are needed by the Java virtual machine (JVM) to link your classes with library packages. Crema scrambles these symbolic names and makes references to them in the same way so that the JVM can still achieve the correct linking between classes and packages.
So how does Crema work? Basically, before distributing your class files on the Internet, run Crema on them. Crema will scramble the symbolic information contained in them, and will place each new class in the file 1.crema . Your job then is to rename 1.crema to something like filename.class before distributing it on the Internet.
Let’s run Crema on our test.class example shown above, and then try to decompile it with Mocha:
The above command has generated a new file, 1.crema , which contains the bytecodes with scrambled symbolic information. Note that Crema has many command-line option parameters that you can use; for more information on Crema, see the Resources section.
Now let’s move that file into test.class again and decompile it using Mocha:
% mv 1.crema test.class % java mocha.Decompiler test.class java.lang.NullPointerException SIGSEGV 11* segmentation violation si_signo [11]: SIGSEGV 11* segmentation violation si_errno [0]: Error 0 si_code [1]: SEGV_ACCERR [addr: 0x0] stackbase=EFFFF35C, stackpointer=EFFFF040 Full thread dump: "Finalizer thread" (TID:0xee3003b0, sys_thread_t:0xef490de0) prio=1 "Async Garbage Collector" (TID:0xee300368, sys_thread_t:0xef4c0de0) prio=1 "Idle thread" (TID:0xee300320, sys_thread_t:0xef4f0de0) prio=0 "clock handler" (TID:0xee3001f8, sys_thread_t:0xef5b0de0) prio=11 "main" (TID:0xee3000a0, sys_thread_t:0x835a0) prio=5 *current thread* java.lang.Throwable.printStackTrace(Throwable.java) java.lang.ThreadGroup.uncaughtException(ThreadGroup.java) java.lang.ThreadGroup.uncaughtException(ThreadGroup.java) Monitor Cache Dump: Registered Monitor Dump: Finalize me queue lock: unowned Thread queue lock: unowned Class lock: unowned Java stack lock: unowned Code rewrite lock: unowned Heap lock: unowned Has finalization queue lock: unowned Monitor IO lock: unowned Child death monitor: unowned Event monitor: unowned I/O monitor: unowned Alarm monitor: unowned Waiting to be notified: "clock handler" Sbrk lock: unowned Monitor cache lock: unowned Monitor registry: monitor owner: "main" Thread Alarm Q: Abort (core dumped)
As you can see in the code above, the first thing Mocha complains about is a NullPointerException because it was confused about the symbolic information. Hence, our goal of making it difficult to decompile our code is achieved.
It should be noted that the author of Mocha, Hanpeter van Vliet, is also the author of Crema! Mocha is distributed without charge. An evaluation copy of Crema is available without charge, but the full version is a commercial product.
When distributing Java classes over the Internet, you can protect your Java bytecode from the risk of being reverse-engineered. The code examples above show how Mocha is used to effect decompilation and how Crema can come to the rescue by preventing such activity.
Qusay H. Mahmoud is a graduate student in computer science at The University of New Brunswick, Saint John campus, Canada.
This story, «Java Tip 22: Protect your bytecodes from reverse engineering/decompilation» was originally published by JavaWorld .
Next read this: