Ivan has posted 22 posts at DZone. View Full User Profile

Decrypting Java Malware using ASM

05.18.2013
| 5044 views |
  • submit to reddit

Nowadays, a Java malware, which is using various vulnerabilities in Java, is very widespread. Many of them are using string encryption to hide its presence and actions on an infected host. As a rule  strings contain registry keys, some commands to execute various programs, etc.

In this article,  we will examine the most widespread string encryption method in Java malware, and we will write a little tool for removing this encryption for the purposes of malicious software analysis.

Short introduction to Java class file bytecode format

For further understanding,  it is necessary to know that all the constants in Java (strings and primitive types) to be stored in the special structure inside a class file which is called Constant Pool. To get an element from Constant Pool, there is the JVM instruction - ldc.

Let's see how the bytecode of the

System.out.println("HelloWorld");
method call is looking like.

Writing simple class: 

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

Compiling:

$ javac Hello.java

And decompiling it using javap:

javap -c -v Hello
Constant Pool:
  ... 
    const #3 = String	#20;	//  HelloWorld
    const #20 = Asciz	HelloWorld;
   
Code:
ldc	#3; //String HelloWorld
invokevirtual	#4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V

The dark side

For the encryption purposes,  bad guys mostly apply the following approach: 

  • Encrypt string using some function
  • Replace the source value of the string to the encrypted value
  • Before using the string call first a decrypt function

Here is the bytecode after applying this approach:

ConstanPool:
  ... 
    const #3 = String	#20;	//  CryptedString
    const #20 = Asciz	CryptedString;
Code:
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

Jedi side

To write our automatic decryption tool we need ASM (http://asm.ow2.org/). The idea of our way: loading the class (which contain decrypt function), replacing encrypted strings to decrypted and cutting off an instruction which is call decrypt function from the bytecode.

Main method:

        // 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 part:

  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);
            }
        }
    }

Conclusion

Static string encryption doesn't bring us any protection. ASM is very powerful tool to manipulate with the bytecode. Guys, do not write malware, better found, maintain and help to open source projects. It will bring you so much fun (and profit), believe me! 

Published at DZone with permission of its author, Ivan Kinash.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)