在只有 jar包 的情况下,动态获取 claaa
OS: Linux
JDK: 1.8.0_201、21.07
Arthas
https://github.com/alibaba/arthas/releases
java -jar burpsuite_pro.jar // 启动目标 jar包
java -jar arthas-boot.jar // 启动 Arthas,并选择 目标进程
sc burp.* > classList // 查看指定 class列表,并输出到 classList
dump burp.qzo -d CLASS // dump 指定class,到 CLASS目录下
心得
1、比JDK自带的顺手,8 或 21 下运行,终端输出,差异明显
sa-jdi.jar
// JDK9之前,位置:JAVA_HOME/lib/sa-jdi.jar
// CLHSDB(命令行) HSDB(图形版)
java -cp $JAVA_HOME/lib/sa-jdi.jar sun.jvm.hotspot.CLHSDB // 启动 Hotspot Debugger
java -jar burpsuite_pro.jar
jps -l
hsdb> attach 5011
hsdb> classes > classList // 查看所有 class,输出到 classList
hsdb> class burp.qzo // 查看指定 class
burp/qzo @0x00000007c03fdc28
hsdb> dumpclass burp.qzo // dump 指定class ./burp/qzo.class
心得
1、只支持 JDK8
2、dump出来的class,与 Arthas的差异明显
jhsdb
// JDK8之后,位置:JAVA_HOME/bin/jhsdb // 用于取代 sa-jdi.jar
jhsdb --help
clhsdb command line debugger
hsdb ui debugger
debugd --help to get more information
jstack --help to get more information
jmap --help to get more information
jinfo --help to get more information
jsnap --help to get more information
jhsdb clhsdb // 启动命令行 hsdb
java -jar burpsuite_pro.jar
jcmd -l
hsdb> attach 2197
hsdb> classes > class.txt
hsdb> class burp/Zto
burp/Zto @0x00007f0b404bdab8
hsdb> dumpclass burp/Zto
心得
1、只 dumpclass,与 sa-jdi.jar 差异不大,也有图形版
2、jhsdb 其它工具功能挺强大,值得研究
ClassFileTransformer
import java.io.File;
import java.io.FileOutputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
public class ClassDumpAgent implements ClassFileTransformer {
private static final String OUT_DIR = "dumpdir";
private static final String[] EXCLUDED_PREFIXES = { "java/", "javax/", "sun/", "com/", "org/", "net/", "io/",
"jdk/", "javafx/" };
static {
File dir = new File(OUT_DIR);
if (!dir.exists()) {
boolean created = dir.mkdir(); // 使用mkdir()创建目录
if (!created) {
System.err.println("无法创建目录: " + OUT_DIR);
}
}
System.out.println("创建目录 " + OUT_DIR);
}
private static void saveClass(String className, byte[] classfileBuffer) throws Exception {
System.out.println("开始saveClass()");
// 文件路径转为类名,连接符(win、linux 用 / 连接)
String fileName = OUT_DIR + "/" + className.replace("/", ".") + ".class";
File classFile = new File(fileName);
try (FileOutputStream fos = new FileOutputStream(classFile)) {
fos.write(classfileBuffer);
}
}
// 注册 ClassFileTransformer
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) {
// loader == null 会过滤JDK系统核心类(java、javax、sun、com ......)
if (loader == null) {
return null;// 告诉JVM,不修改
}
// loader == null 与 EXCLUDED_PREFIXES 有重复,但更好用
for (String prefix : EXCLUDED_PREFIXES) {
if (className.startsWith(prefix)) {
return null;
}
}
if (classfileBuffer.length == 111989) {
System.out.println("过滤后 " + loader + " " + className + " " + classfileBuffer.length);
} else {
return null;
}
try {
saveClass(className, classfileBuffer);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void premain(String agentArgs, Instrumentation inst) throws Exception {
System.out.println("进入 premain()");
ClassDumpAgent cda = new ClassDumpAgent();
inst.addTransformer(cda);
}
}
// MANIFEST.MF
Premain-Class: ClassDumpAgent
............... // 此处需有1空行
// JDK8
javac -g ClassDumpAgent.java
jar cfmv ClassDumpAgent.jar MANIFEST.MF ClassDumpAgent.class
java -javaagent:ClassDumpAgent.jar -jar burpsuite_pro.jar // 会在当前目录生成指定 class
心得
可在 transform()中进行过滤,自定义性强
java.lang.ClassLoader
// JDK8,获取源码
cp $JAVA_HOME/src.zip .
// 粘出 java/lang/ClassLoader.java,并改写 defineClass(){}
protected final Class<?> defineClass(String name, byte[] b, int off, int len)
throws ClassFormatError {
if (len == 111989) {
String classFileName = name.replace("/", ".") + ".class";
File classFile = new File(classFileName);
try (java.io.FileOutputStream fos = new java.io.FileOutputStream(classFile)) {
fos.write(b, off, len);
} catch (IOException e) {
System.err.println("Error writing class file: " + e.getMessage());
e.printStackTrace();
}
}
return defineClass(name, b, off, len, null);
}
// 创建目录,放入 ClassLoader.java
mkdir -p java/lang mv ClassLoader.java java/lang/
// 创建 out,并编译
mkdir out
javac -g -bootclasspath $JAVA_HOME/jre/lib/rt.jar -sourcepath . java/lang/ClassLoader.java -d out
// 进入 out,并打包
jar cfv Loader.jar .
// dump 指定 .class
java -noverify -Xbootclasspath/p:Loader.jar -jar burpsuite_pro.jar // 会在当前目录生成指定 class
心得
自定义性强,目前在 JDK8上测试通过
最终心得
1、dump all or one,java.lang.ClassLoader 或 ClassFileTransformer 都行
2、java.lang.ClassLoader、ClassFileTransformer、Arthas 与 JDK自带工具dump的结果,差异挺大
3、猜测这3种方式是在 JVM之外进行 dump,JDK自带的是在 JVM之内进行 dump