Yury Bendersky et al.
Replies
0
Voices
1

 

Memory Dumping

There are some interesting classes in the JDK [1],[2] –
com.sun.tools.attach.VirtualMachine, java.lang.instrument.ClassFileTransformer,
and java.lang.instrument.Instrumentation, which hackers have tried
unsuccessfully to use to crack Java byte code encryption for years. The idea
behind these attempts is pretty simple. Below is the Attacher
class that allows you to connect to an already running Java program according
to the PID, as well as implicitly load the so-called ClassDumperAgent

 

Attacher

 

import com.sun.tools.attach.VirtualMachine;

 

public class Attacher {

       public static void main(String[] args) throws Exception {

             String pid = “1234”;

             VirtualMachine vm = VirtualMachine.attach(pid);

             vm.loadAgent(“c:/workspaces/dumperagent.jar”);

       }

}

 

ClassDumperAgent

 

import java.io.File;

import java.io.FileOutputStream;

import java.lang.instrument.ClassFileTransformer;

import java.lang.instrument.Instrumentation;

import java.lang.instrument.UnmodifiableClassException;

import java.security.ProtectionDomain;

import java.util.ArrayList;

import java.util.List;

import java.util.regex.Pattern;

 

public class ClassDumperAgent implements ClassFileTransformer {

       private static String dumpDir;

       private static Pattern classes;

 

       public static void premain(String agentArgs, Instrumentation inst) {

             agentmain(agentArgs, inst);

       }

 

       public static void agentmain(String agentArgs, Instrumentation inst) {

             parseArgs(agentArgs);

             inst.addTransformer(new ClassDumperAgent(), true);

             Class<?>[] classes = inst.getAllLoadedClasses();

             List<Class<?>> candidates = new ArrayList<Class<?>>();

             String fileName = dumpDir + File.separator + class.list;

             try {

                    FileOutputStream fos = new FileOutputStream(fileName);

                    for (Class<?> c : classes) {

                           fos.write((c.getName() + “\n\r”).getBytes());

                           if (isCandidate(c.getName())) {

                                 candidates.add(c);

                           }

                    }

                    fos.close();

             } catch (Exception e) {

                    e.printStackTrace();

             }

             System.out.println(candidates.size() + ” of “

                                         classes.length + ” classes are dumped.”);

             try {

                    if (!candidates.isEmpty()) {

                           inst.retransformClasses(candidates.toArray(

                                                   new Class[candidates.size()]));

                    }

             } catch (UnmodifiableClassException uce) {}

       }

 

       public byte[] transform(ClassLoader loader, String className,
                     Class<?> rc, ProtectionDomain pd,
 byte[] classBytes) {

             if (isCandidate(className)) {

                    dumpClass(className, classBytes);

             }

             return null;

       }

 

       private static boolean isCandidate(String className) {

             if (className.charAt(0) == ‘[‘) {

                    return false;

             }

 

             if (className.startsWith(“java.”) || className.startsWith(“com.”) || 

                 className.startsWith(“org.”) || className.startsWith(javax.”) ||

                 className.startsWith(“sun.”) || className.startsWith(javafx.”))

                    return false;

 

             className = className.replace(‘/’, ‘.’);

             return classes.matcher(className).matches();

       }

 

       private static void dumpClass(String className, byte[] classBuf) {

             try {

                    className = className.replace(“/”, File.separator);

                    StringBuilder buf = new StringBuilder();

                    buf.append(dumpDir);

                    buf.append(File.separatorChar);

                    int index = className.lastIndexOf(File.separatorChar);

                    if (index != -1) {

                           buf.append(className.substring(0, index));

                    }

                    String dir = buf.toString();

                    new File(dir).mkdirs();

                    String fileName = dumpDir + File.separator + className + “.class”;

                    FileOutputStream fos = new FileOutputStream(fileName);

                    fos.write(classBuf);

                    fos.close();

             } catch (Exception exp) {

                    exp.printStackTrace();

             }

       }

 

       private static void parseArgs(String agentArgs) {

             if (agentArgs != null) {

                    String[] args = agentArgs.split(“,”);

                    for (String arg : args) {

                           String[] tmp = arg.split(“=”);

                           if (tmp.length == 2) {

                                 String name = tmp[0];

                                 String value = tmp[1];

                                 if (name.equals(dumpDir)) {

                                        dumpDir = value;

                                 } else if (name.equals(“classes”)) {

                                        classes = Pattern.compile(value);

                                 }

                           }

                    }

             }

 

             if (dumpDir == null) {

                    dumpDir = “c:/workspaces/dump”;

             }

 

             if (classes == null) {

                    classes = Pattern.compile(“.*”);

             }

       }

}

 

ClassDumperAgent should be compressed to a jar file with the following Manifest

Manifest

 

Manifest-Version: 1.0

Premain-Class: ClassDumperAgent

Agent-Class: ClassDumperAgent

Can-Redefine-Classes: true

Can-Retransform-Classes: true


A well-known program Stylepad from the JDK demo was selected for testing:
http://www.java2s.com/Code/JavaDownload/Stylepad.zip

 

 

Stylepad.jar

 

40 of 2332 classes are dumped.

and indeed, taking an arbitrary Java program, we will get a dump of all the classes
of interest to us in the directory
c:/workspaces/dump

 

However, if the program that we wanted to crack is protected, then we
will receive an error message.

 

 

 

 

Exception in thread "main" com.sun.tools.attach.AttachNotSupportedException:

                                      The VM does not support the attach mechanism

at sun.tools.attach.HotSpotAttachProvider.testAttachable(HotSpotAttachProvider.java:162)

at sun.tools.attach.WindowsAttachProvider.attachVirtualMachine(

                                                  WindowsAttachProvider.java:67)

at com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:208)

at Attacher.main(Attacher.java:6)

 

Defense is as simple as an attack. If the attacking classes have access to all the classes
of the program being executed, then the attacked program can also have access to
foreign classes and blocks the Java dumping. For an explanation see [3].

 

  1. Understanding Java Agents
  2. Dumping Embedded Java Classes
  3. Werner Heisenberg, Observer effect (Wikipedia)

Leave a comment