Статьи

Расшифровка Java Malware с использованием ASM

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

В этой статье мы рассмотрим наиболее распространенный метод шифрования строк в вредоносных программах Java и напишем небольшой инструмент для удаления этого шифрования в целях анализа вредоносного программного обеспечения.

Краткое введение в формат байт-кода файла класса Java

Для дальнейшего понимания необходимо знать, что все константы в Java (строки и типы примитивов) должны храниться в специальной структуре внутри файла класса, который называется Constant Pool. Чтобы получить элемент из пула констант, есть инструкция JVM — ldc.

Давайте посмотрим, как байт-код из

System.out.println("HelloWorld");

вызов метода выглядит так.

Написание простого класса: 

public class Hello {
  public static void main(String[] args) throws Exception {
    System.out.println("HelloWorld");
  }
}

Компиляция:

$ javac Hello.java

И декомпилируем его, используя javap:

javap -c -v Hello

Постоянный пул:

  ... 
    const #3 = String	#20;	//  HelloWorld
    const #20 = Asciz	HelloWorld;
   

Код:

ldc	#3; //String HelloWorld
invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

Темная сторона

В целях шифрования плохие парни обычно применяют следующий подход: 

  • Зашифровать строку с помощью некоторой функции
  • Заменить исходное значение строки на зашифрованное значение
  • Перед использованием строкового вызова сначала расшифруйте функцию

Вот байт-код после применения этого подхода:

ConstanPool:

  ... 
    const #3 = String	#20;	//  CryptedString
    const #20 = Asciz	CryptedString;

Код:

ldc	#3; //String CryptedString
invokestatic	#4; //Method decrypt:(Ljava/lang/String;)Ljava/lang/String;
invokevirtual	#5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

Сторона джедая

Для написания нашего инструмента автоматического дешифрования нам нужен ASM ( http://asm.ow2.org/ ). Идея нашего пути: загрузка класса (который содержит функцию дешифрования), замена зашифрованных строк на дешифрованные и отключение инструкции, которая вызывает функцию дешифрования, из байт-кода.

Основной метод:

        // input file
        JarFile jin = new JarFile(new File(args[0]), false);
        // output file
        JarOutputStream jon = new JarOutputStream(new FileOutputStream(args[1]));
        Enumeration
 
   en = jin.entries();
        byte[] buffer = new byte[1024];
        URLClassLoader jcl = new URLClassLoader(new URL[]{(new File(args[0])).toURI().toURL()});
        Method decMethod = jcl.loadClass(decClassName).getDeclaredMethod(decMethoName, new Class[]{String.class});
        while (en.hasMoreElements()) {
            JarEntry je = en.nextElement();
            if (je.isDirectory()) {
                jon.putNextEntry(new JarEntry(je));
            } else if (je.getName().endsWith(".class")) {
                System.out.println("Processing class " + je.getName());
                ClassReader cr = new ClassReader(jin.getInputStream(je));
                ClassWriter cw = new ClassWriter(0);
                Decryptor transformer = new Decryptor(cw, decMethod);
                cr.accept(transformer, 0);
                jon.putNextEntry(new JarEntry(je.getName()));
                jon.write(cw.toByteArray());
            } else {
                jon.putNextEntry(new JarEntry(je));
                BufferedInputStream bis = new BufferedInputStream(jin.getInputStream(je));
                int readed = bis.read(buffer);
                while (readed > 0) {
                    jon.write(buffer, 0, readed);
                    readed = bis.read(buffer);
                }
                bis.close();
            }
        }
        jon.close();

 

ASM часть:

  class Decryptor extends ClassVisitor implements Opcodes {

        Method decMethod;

        public Decryptor(ClassVisitor cv, Method decMethod) throws Exception {
            super(ASM4, cv);
            this.decMethod = decMethod;
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
            MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
            return new MethodDecryptor(mv, decMethod);
        }

        class MethodDecryptor extends MethodVisitor implements Opcodes {

            Method decMethod;
            String decClassName;
            String decMethodName;

            public MethodDecryptor(MethodVisitor mv, Method decMethod) {
                super(ASM4, mv);
                this.decMethod = decMethod;
                decClassName = decMethod.getDeclaringClass().getName().replace(".","/");
                decMethodName = decMethod.getName();
            }

            @Override
            public void visitMethodInsn(int opcode, String owner, String name, String desc) {
                if (decClassName.equals(owner) && decMethodName.equals(name) && "(Ljava/lang/String;)Ljava/lang/String;".equals(desc)) {
		// skipping decryption function
               } else {
                    super.visitMethodInsn(opcode, owner, name, desc);
                }
            }

            @Override
            public void visitLdcInsn(Object o) {
                if (o instanceof String) {
                     try {
                        // decrypt string
                        o = decMethod.invoke(null, new Object[]{o});
                    } catch (Exception ex) {
                    }
                }
                super.visitLdcInsn(o);
            }
        }
    }

Вывод

Статическое шифрование строк не дает нам никакой защиты. ASM — очень мощный инструмент для манипулирования с помощью байт-кода. Ребята, не пишите вредоносные программы, лучше находите, поддерживайте и помогайте проектам с открытым исходным кодом. Это принесет вам столько удовольствия (и прибыли), поверьте мне!