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].