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));

生成的asm代码如下图,有这些信息,我们就可以方便写出ClassFileTransformer。
[img]http://dl.iteye.com/upload/attachment/275724/d563d65f-f24c-39f5-b5d8-c1bbb4f6112f.jpg[/img]

下面是实例,具体是参考ayufox的[url=http://ayufox.iteye.com/blog/655619][Java性能剖析]JPDA 4)Java Instrutment[/url],在此表示感谢,写了不少好的文章,希望以后又更多的分享。我这篇文章就是自己的一个学习笔记。:)

1)定义入口

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");
}
}

我们的入口类格式必须是如上的premain(对应JVM TI的Agent_OnLoad方法),与JVM TI类似,JVM启动的时候回回调这个入口函数。在premain中我们最常见就是增加Transform,Transform允许我们在类加载器修改bytecode。一般性能剖析程序都是通过修改字节码,在方法进入和退出时收集时间数据来得出剖析数据的。
2)编写Transform

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);
}
}

这里简单地使用一个编译好的新的class的字节替换掉旧的class的字节,
3)修改META-INF/ MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: com.starit.StubPreMain

4)打成InstrutmentTest.jar,在启动参数中增加如下启动参数,启动JVM,当加载com/starit/ch02/MethodTest这个类的时候,字节码会变成我们传进去的新的字节码
-javaagent:F:/Genuitec/workspace/ASM/InstrutmentTest.jar

5)MethodTest代码:

public class MethodTest {
public void hello() {
System.out.println("Hello World");
}
}

5)运行后显示的为:

StubPreMain:Add StubTransformer
Load MethodTdest From Transformer
File name:com.starit.ch02.MethodTest
Hello World
消耗时间为(纳秒):31569


了解以后,需要我们找到他的应用点,才能发挥其作用。

参考资料;http://www.fasterj.com/articles/hotpatch2.shtml
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值