Java Instrutment后门,偷着乐
早上六点多就醒,睡不着。本人没有睡懒觉的能力,杯具....
可能大家对btrace比较熟悉,是一款就能在不改动当前程序的情况下,运行时的去监控Java程序的执行状况,例如可以做到内存状况的监控、方法调用的监控等等。实现机制是attach api + asm + instrumentation。Java Instrutment一个是允许在类加载之前,修改类字节,从JDK5中开始提供,随JVM启动的Agent,另外一个是在类加载之后,触发JVM重新进行类加载,JDK6中开始提供,用于JVM启动之后通过Attach去加载Agent。这里目前只说明第一种。
为了能够在加载前方便修改字节码,选择Asm,这需要jvm字节码结构和语法有一定了解。自己目前也不是很太熟悉,写出复杂的修改字节码的代码还是有一定难度,但幸运的是asm有一个eclipse插件,一段java代码,能够看到asm生成这段代码字节码的asm代码。在下面我要演示获取一段代码执行的时间,
long startTime = System.nanoTime();long endTime = System.nanoTime();System.out.println("消耗时间为(纳秒):" + (endTime - startTime));
public class StubPreMain {//另外一种入口格式是public static void premain(String agentArgs) public static void premain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException { inst.addTransformer(new StubTransformer()); System.out.println("StubPreMain:Add StubTransformer"); }} public class StubTransformer implements ClassFileTransformer {public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {if ("com/starit/ch02/MethodTest".equals(className)) {System.out.println("Load MethodTdest From Transformer");return readByte(className.replace("/", "."));}return null;}public byte[] readByte(String fileName) {System.out.println("File name:" + fileName);ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);AddTimeClassAdapter ca = new AddTimeClassAdapter(cw);try {ClassReader cr = new ClassReader(fileName);cr.accept(ca, ClassReader.SKIP_DEBUG);} catch (IOException e) {e.printStackTrace();}return cw.toByteArray();}}class AddTimeClassAdapter extends ClassAdapter {public AddTimeClassAdapter(ClassVisitor cv) {super(cv);}@Override public MethodVisitor visitMethod(int access, String name,String desc, String signature, String[] exceptions) {MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);if (name.equals("hello")) {mv = new AddTimerMethodAdapter(mv);}return mv;}}class AddTimerMethodAdapter extends MethodAdapter {public AddTimerMethodAdapter(MethodVisitor mv) {super(mv);}@Override public void visitCode() {mv.visitCode();Label l0 = new Label();mv.visitLabel(l0);Label l1 = new Label();mv.visitLabel(l1);Label l4 = new Label();mv.visitLabel(l4);mv.visitLocalVariable("this", "Lcom/starit/ch02/MethodTest;", null, l0, l4, 0);mv.visitLocalVariable("startTime", "J", null, l1, l4, 1);mv.visitMethodInsn(INVOKESTATIC, "java/lang/System","nanoTime", "()J");mv.visitVarInsn(Opcodes.LSTORE, 1);}@Override public void visitInsn(int opcode) {Label l2 = new Label();mv.visitLabel(l2);Label l4 = new Label();mv.visitLabel(l4);mv.visitLocalVariable("endTime", "J", null, l2, l4, 3);mv.visitMethodInsn(INVOKESTATIC, "java/lang/System","nanoTime", "()J");mv.visitVarInsn(Opcodes.LSTORE, 3);mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");mv.visitTypeInsn(NEW, "java/lang/StringBuilder");mv.visitInsn(DUP);mv.visitLdcInsn("\u6d88\u8017\u65f6\u95f4\u4e3a\uff08\u7eb3\u79d2\uff09\uff1a");mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");mv.visitVarInsn(LLOAD, 3);mv.visitVarInsn(LLOAD, 1);mv.visitInsn(LSUB);mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;");mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");mv.visitInsn(opcode);}}Manifest-Version: 1.0Premain-Class: com.starit.StubPreMain
-javaagent:F:/Genuitec/workspace/ASM/InstrutmentTest.jar
public class MethodTest {public void hello() {System.out.println("Hello World");}}StubPreMain:Add StubTransformerLoad MethodTdest From TransformerFile name:com.starit.ch02.MethodTestHello World消耗时间为(纳秒):31569