Created
August 21, 2020 23:27
-
-
Save comp500/a752b4b5cd13f30e5e4ecbc07dabc511 to your computer and use it in GitHub Desktop.
Terrible technique for dumping class files using JDI/JDWP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package link.infra.jdwp; | |
import com.sun.jdi.Field; | |
import com.sun.jdi.LocalVariable; | |
import com.sun.jdi.Method; | |
import com.sun.jdi.ReferenceType; | |
import com.sun.jdi.*; | |
import com.sun.jdi.connect.AttachingConnector; | |
import com.sun.jdi.connect.Connector; | |
import com.sun.jdi.connect.IllegalConnectorArgumentsException; | |
import org.apache.bcel.classfile.*; | |
import org.apache.bcel.generic.Type; | |
import org.apache.bcel.generic.*; | |
import java.io.*; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Map; | |
public class App { | |
public static void main(String[] args) throws IOException, IllegalConnectorArgumentsException { | |
List<AttachingConnector> connectors = Bootstrap.virtualMachineManager().attachingConnectors(); | |
for (AttachingConnector connector : connectors) { | |
if (connector.name().equals("com.sun.jdi.SocketAttach")) { | |
Map<String, Connector.Argument> connArgs = connector.defaultArguments(); | |
connArgs.get("port").setValue("5005"); | |
VirtualMachine vm = connector.attach(connArgs); | |
for (ReferenceType refType : vm.allClasses()) { | |
if (!(refType instanceof ClassType)) { | |
continue; | |
} | |
ClassType clazz = ((ClassType) refType); | |
// TODO: test with j9+ | |
if (clazz.classLoader() == null) { | |
continue; | |
} | |
// TODO: workaround for lambdas? | |
String sourceName; | |
try { | |
sourceName = clazz.sourceName(); | |
} catch (AbsentInformationException ignored) { continue; } | |
String name = clazz.name().replace('.', '/'); | |
byte[] hmmm = clazz.constantPool(); | |
ByteArrayOutputStream baos = new ByteArrayOutputStream(hmmm.length + 2); | |
DataOutputStream dos = new DataOutputStream(baos); | |
// TODO: unsigned? | |
dos.writeShort(clazz.constantPoolCount()); | |
dos.write(hmmm); | |
ConstantPoolGen poolGen = new ConstantPoolGen(new ConstantPool(new DataInputStream(new ByteArrayInputStream(baos.toByteArray())))); | |
String superClassName; | |
ClassType superClass = clazz.superclass(); | |
if (superClass != null) { | |
superClassName = superClass.name().replace('.', '/'); | |
} else { | |
superClassName = "java/lang/Object"; | |
} | |
String[] implementedInterfaces = clazz.interfaces().stream().map(ReferenceType::name).toArray(String[]::new); | |
ClassGen classGen = new ClassGen(name, | |
superClassName, | |
sourceName, | |
clazz.modifiers(), | |
implementedInterfaces, poolGen); | |
classGen.setMinor(clazz.minorVersion()); | |
classGen.setMajor(clazz.majorVersion()); | |
String classGenericSignature = clazz.genericSignature(); | |
if (classGenericSignature != null && classGenericSignature.length() > 0) { | |
classGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(classGenericSignature), poolGen.getConstantPool())); | |
} | |
// TODO: the necessary metadata for bootstrap methods isn't available!! :( | |
// TODO: inner classes | |
// TODO: enclosing method? | |
for (Field field : clazz.fields()) { | |
FieldGen fieldGen = new FieldGen(field.modifiers(), | |
Type.getType(Utility.getSignature(field.typeName())), | |
field.name(), | |
poolGen); | |
String genericSignature = field.genericSignature(); | |
if (genericSignature != null && genericSignature.length() > 0) { | |
fieldGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(genericSignature), poolGen.getConstantPool())); | |
} | |
classGen.addField(fieldGen.getField()); | |
} | |
for (Method method : clazz.methods()) { | |
String[] argNameList = null; | |
try { | |
argNameList = method.arguments().stream().map(LocalVariable::name).toArray(String[]::new); | |
} catch (AbsentInformationException ignored) {} | |
String[] argTypeList = Utility.methodSignatureArgumentTypes(method.signature()); | |
if (argNameList == null || argTypeList.length != argNameList.length) { | |
// TODO: better names | |
argNameList = argTypeList; | |
} | |
MethodGen methodGen = new MethodGen(method.modifiers(), | |
Type.getReturnType(method.signature()), | |
Type.getArgumentTypes(method.signature()), | |
argNameList, | |
method.name(), | |
name, | |
new InstructionList(method.bytecodes()), | |
poolGen); | |
// TODO: line numbers | |
// TODO: LVT | |
// TODO: LVTT | |
//method.variables(); | |
methodGen.setMaxStack(); | |
methodGen.setMaxLocals(); | |
String genericSignature = method.genericSignature(); | |
if (genericSignature != null && genericSignature.length() > 0) { | |
methodGen.addAttribute(new Signature(poolGen.lookupUtf8("Signature"), 2, poolGen.lookupUtf8(genericSignature), poolGen.getConstantPool())); | |
} | |
classGen.addMethod(methodGen.getMethod()); | |
} | |
if (name.endsWith("class_310")) { | |
classGen.getJavaClass().dump(new File("test.class")); | |
break; | |
} | |
} | |
vm.dispose(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment