06-17-2011, 12:13 AM
На прошлой неделе довелось реверсить декстопную прогу на java от Microsoft. С виду обычный дистрибутив - папки bin/lib/tmp.
Первым делом бросилось в глаза - был только один jar файл: examengine.jar в папке bin с которого запускалась программа. В папке lib лежали несколько файлов jar и файл exam.exj.set содержащий в себе какие-то-то сериализованные данные.
Первым делом вскрыл examengine.jar. В нем оказались всего 3 привычных .class файла с нормальной сигнатурой (EClassLoader, ExamEnter, ESettings) и ещё несколько десятков .eclass с бинарными данными (ATable.eclass, EEngine.eclass, EEngine$Source.eclass, и так далее).
Первым делом запускаем наш любимый Java Decompiler и вскрываем файл ExamEnter и видим такую конструкцию
так как используемый ресурс класса ETestEngine находится в виде бинарных данных в ETestEngine.eclass то это означает что вся магия находится в EClassLoader.
Вскрываем EClassLoader.
и самое интересное - загрузка класса.
и все становится ясно - весь байткод оказывается зашифрован алгоритмом AES, и получить его можно только расшифровав ключом, который получается после вызова native-метода. Таким образом байткод устойчив к "тупому" дизассемблированию и декомпиляции с помощи всех доступных средств.
Чтобы получить чистый байткод необходимо приложить небольшие усилия в отладчике чтобы выловить ключ и с его помощью расшифровать каждый файл ресурса - но это не каждый сумеет сделать. При использовании дополнительной обфускации незащищенных классов (с оптимизацией) реинжениринг станет ещё сложнее.
Таким образом у меня появилась идея создания относительно стойкой к декомиляции сборки дистрибутива:
1) Обычная компиляция исходных файлов в бинарники и нативных библиотек.
2) Формирование ключа AES / или любого другого алгоритма.
3) Шифрование всех class файлов за исключением загрузчика.
4) Обфускация классов загрузчика и оптимизация остальных классов.
5) Сборка jar-архива приложения с ключом.
6) Создание архива с дистрибутивом.
Понижение производительности за счет введения шифрования ресурсов незначительно. Класс загружается единожды в приложении (частую при старте приложения), поэтому особых задержек не составит.
Сейчас несколько модифицированная система успешно работает на тестовом проекте.
Здесь мой копирайт (с)
P.S. Защитил диплом в этот вторник на "отлично" xD
Первым делом бросилось в глаза - был только один jar файл: examengine.jar в папке bin с которого запускалась программа. В папке lib лежали несколько файлов jar и файл exam.exj.set содержащий в себе какие-то-то сериализованные данные.
Первым делом вскрыл examengine.jar. В нем оказались всего 3 привычных .class файла с нормальной сигнатурой (EClassLoader, ExamEnter, ESettings) и ещё несколько десятков .eclass с бинарными данными (ATable.eclass, EEngine.eclass, EEngine$Source.eclass, и так далее).
Первым делом запускаем наш любимый Java Decompiler и вскрываем файл ExamEnter и видим такую конструкцию
Код:
private void loadMain() throws Exception {
EClassLoader eclassloader = new EClassLoader();
Class var_class = eclassloader
.loadClass("com.mypai.examengine.ETestEngine");
Object object = var_class.newInstance();
Field field = var_class.getDeclaredField("examinatorTime");
field.set(object, new Long(examinatorTime));
Method method = var_class.getDeclaredMethod("createAndShowGUI", null);
method.invoke(object, null);
}
так как используемый ресурс класса ETestEngine находится в виде бинарных данных в ETestEngine.eclass то это означает что вся магия находится в EClassLoader.
Вскрываем EClassLoader.
Код:
final class EClassLoader extends ClassLoader
{
Key ekey = getEKey();
private static native Key getEKey();
....
}
и самое интересное - загрузка класса.
Код:
private byte[] loadClassBytes(String string) throws IOException {
String string_0_ = string.replace('.', '/') + ".eclass";
JarFile jarfile = new JarFile("./bin/examengine.jar");
ZipEntry zipentry = jarfile.getEntry(string_0_);
if (zipentry != null)
{
try {
Cipher cipher = Cipher.getInstance("AES");
cipher.init(2, this.ekey);
CipherInputStream cipherinputstream = new CipherInputStream( jarfile.getInputStream(jarfile.getEntry(string_0_)), cipher);
ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
int i;
while ((i = cipherinputstream.read()) != -1)
{
bytearrayoutputstream.write(i);
}
cipherinputstream.close();
is = bytearrayoutputstream.toByteArray();
}
catch (Exception exception)
{
exception.printStackTrace();
return null;
}
return is;
}
return null;
}
и все становится ясно - весь байткод оказывается зашифрован алгоритмом AES, и получить его можно только расшифровав ключом, который получается после вызова native-метода. Таким образом байткод устойчив к "тупому" дизассемблированию и декомпиляции с помощи всех доступных средств.
Чтобы получить чистый байткод необходимо приложить небольшие усилия в отладчике чтобы выловить ключ и с его помощью расшифровать каждый файл ресурса - но это не каждый сумеет сделать. При использовании дополнительной обфускации незащищенных классов (с оптимизацией) реинжениринг станет ещё сложнее.
Таким образом у меня появилась идея создания относительно стойкой к декомиляции сборки дистрибутива:
1) Обычная компиляция исходных файлов в бинарники и нативных библиотек.
2) Формирование ключа AES / или любого другого алгоритма.
3) Шифрование всех class файлов за исключением загрузчика.
4) Обфускация классов загрузчика и оптимизация остальных классов.
5) Сборка jar-архива приложения с ключом.
6) Создание архива с дистрибутивом.
Понижение производительности за счет введения шифрования ресурсов незначительно. Класс загружается единожды в приложении (частую при старте приложения), поэтому особых задержек не составит.
Сейчас несколько модифицированная система успешно работает на тестовом проекте.
Здесь мой копирайт (с)
P.S. Защитил диплом в этот вторник на "отлично" xD
for(;Forum.getPostCount() < Integer.MAX_VALUE; Forum.writeNewPost()); | TERA Video | GamezTERA Emu