我有一些方法,其中包含像ILOAD这样的内容,我希望以某种方式在此指令之后获取堆栈的值.不只是打字,而是确切的价值.我知道我需要模拟方法执行才能做到这一点,但我不知道如何正确地做到这一点.
我有这样的测试方法叫main
:
sipush 15649 istore_0 /* c */ getstatic java/lang/System.out:Ljava/io/PrintStream; bipush 45 bipush 11 iload_0 /* c */ ...
我希望得到价值,加载iload_0
.我试图制作分析器,然后看到帧值,但它们只包含值的类型,而不是我想要的.
ClassReader cr = new ClassReader(new FileInputStream(new File("input.class"))); ClassNode cn = new ClassNode(Opcodes.ASM5); cr.accept(cn, 0); Iteratormethods = cn.methods.iterator(); while (methods.hasNext()) { MethodNode mn = methods.next(); if (!mn.name.equals("main")) continue; AbstractInsnNode[] nodes = mn.instructions.toArray(); Analyzer analyzer = new Analyzer(new BasicInterpreter()); analyzer.analyze(cn.name, mn); int i = -1; for (Frame frame : analyzer.getFrames()) { i++; if (frame == null) continue; if (nodes[i].getOpcode() != Opcodes.ILOAD) continue; System.out.print(frame.getStack(0) + "|" + frame.getStack(1)); System.out.print(" - " + nodes[i].getOpcode() + "\n"); } }
它显示了我的结果:R|I - 21
如何获得15649的价值?我试着谷歌那几个小时,找不到任何有用的东西.提前致谢.
您的代码几乎完全忽略了Java 5的优点.当你更新它,你会得到
for(MethodNode mn: cn.methods) { if(!mn.name.equals("main")) continue; Analyzeranalyzer = new Analyzer<>(new BasicInterpreter()); analyzer.analyze(cn.name, mn); int i = -1; for (Frame frame: analyzer.getFrames()) { i++; if(frame == null) continue; int opcode = mn.instructions.get(i).getOpcode(); if(opcode != Opcodes.ILOAD) continue; BasicValue stackValue = frame.getStack(0); System.out.print(stackValue + "|" + frame.getStack(1)); System.out.print(" - " + opcode + "\n"); } }
你可以立即看到你得到的是一个BasicValue
,它适用于验证代码或计算stackmap帧,但不能获得实际值.
这是解释器的一个属性,BasicInterpreter
只保留BasicValue
s(因此得名).另一种方法是SourceInterpreter
允许您跟踪值可能来自哪些指令,这可能是istore_0
您的情况,但这仍然没有给出实际值.
因此,如果您想获得实际值(如果可预测),则需要您自己的解释器.一个相当简单的,只有跟踪值才能真正源于推动常量:
import static org.objectweb.asm.Opcodes.*; import java.util.List; import java.util.Objects; import org.objectweb.asm.Type; import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.analysis.*; public class ConstantTracker extends Interpreter{ static final ConstantValue NULL = new ConstantValue(BasicValue.REFERENCE_VALUE, null); public static final class ConstantValue implements Value { final Object value; // null if unknown or NULL final BasicValue type; ConstantValue(BasicValue type, Object value) { this.value = value; this.type = Objects.requireNonNull(type); } @Override public int getSize() { return type.getSize(); } @Override public String toString() { Type t = type.getType(); if(t == null) return "uninitialized"; String typeName = type==BasicValue.REFERENCE_VALUE? "a reference type": t.getClassName(); return this == NULL? "null": value == null? "unknown value of "+typeName: value+" ("+typeName+")"; } @Override public boolean equals(Object obj) { if(this == obj) return true; if(this == NULL || obj == NULL || !(obj instanceof ConstantValue)) return false; ConstantValue that = (ConstantValue)obj; return Objects.equals(this.value, that.value) && Objects.equals(this.type, that.type); } @Override public int hashCode() { if(this == NULL) return ~0; return (value==null? 7: value.hashCode())+type.hashCode()*31; } } BasicInterpreter basic = new BasicInterpreter() { @Override public BasicValue newValue(Type type) { return type!=null && (type.getSort()==Type.OBJECT || type.getSort()==Type.ARRAY)? new BasicValue(type): super.newValue(type); } @Override public BasicValue merge(BasicValue a, BasicValue b) { if(a.equals(b)) return a; if(a.isReference() && b.isReference()) // this is the place to consider the actual type hierarchy if you want return BasicValue.REFERENCE_VALUE; return BasicValue.UNINITIALIZED_VALUE; } }; public ConstantTracker() { super(ASM5); } @Override public ConstantValue newOperation(AbstractInsnNode insn) throws AnalyzerException { switch(insn.getOpcode()) { case ACONST_NULL: return NULL; case ICONST_M1: case ICONST_0: case ICONST_1: case ICONST_2: case ICONST_3: case ICONST_4: case ICONST_5: return new ConstantValue(BasicValue.INT_VALUE, insn.getOpcode()-ICONST_0); case LCONST_0: case LCONST_1: return new ConstantValue(BasicValue.LONG_VALUE, (long)(insn.getOpcode()-LCONST_0)); case FCONST_0: case FCONST_1: case FCONST_2: return new ConstantValue(BasicValue.FLOAT_VALUE, (float)(insn.getOpcode()-FCONST_0)); case DCONST_0: case DCONST_1: return new ConstantValue(BasicValue.DOUBLE_VALUE, (double)(insn.getOpcode()-DCONST_0)); case BIPUSH: case SIPUSH: return new ConstantValue(BasicValue.INT_VALUE, ((IntInsnNode)insn).operand); case LDC: return new ConstantValue(basic.newOperation(insn), ((LdcInsnNode)insn).cst); default: BasicValue v = basic.newOperation(insn); return v == null? null: new ConstantValue(v, null); } } @Override public ConstantValue copyOperation(AbstractInsnNode insn, ConstantValue value) { return value; } @Override public ConstantValue newValue(Type type) { BasicValue v = basic.newValue(type); return v == null? null: new ConstantValue(v, null); } @Override public ConstantValue unaryOperation(AbstractInsnNode insn, ConstantValue value) throws AnalyzerException { BasicValue v = basic.unaryOperation(insn, value.type); return v == null? null: new ConstantValue(v, insn.getOpcode()==CHECKCAST? value.value: null); } @Override public ConstantValue binaryOperation(AbstractInsnNode insn, ConstantValue a, ConstantValue b) throws AnalyzerException { BasicValue v = basic.binaryOperation(insn, a.type, b.type); return v == null? null: new ConstantValue(v, null); } @Override public ConstantValue ternaryOperation(AbstractInsnNode insn, ConstantValue a, ConstantValue b, ConstantValue c) { return null; } @Override public ConstantValue naryOperation(AbstractInsnNode insn, List extends ConstantValue> values) throws AnalyzerException { List unusedByBasicInterpreter = null; BasicValue v = basic.naryOperation(insn, unusedByBasicInterpreter); return v == null? null: new ConstantValue(v, null); } @Override public void returnOperation(AbstractInsnNode insn, ConstantValue value, ConstantValue expected) {} @Override public ConstantValue merge(ConstantValue a, ConstantValue b) { if(a == b) return a; BasicValue t = basic.merge(a.type, b.type); return t.equals(a.type) && (a.value==null&&a!=NULL || a.value.equals(b.value))? a: t.equals(b.type) && b.value==null&&b!=NULL? b: new ConstantValue(t, null); } }
那么,你可以像使用它一样
private static void analyze() throws IOException, AnalyzerException { ClassReader cr = new ClassReader(new FileInputStream(new File("input.class"))); ClassNode cn = new ClassNode(Opcodes.ASM5); cr.accept(cn, 0); for(MethodNode mn: cn.methods) { if(!mn.name.equals("main")) continue; Analyzeranalyzer = new Analyzer<>(new ConstantTracker()); analyzer.analyze(cn.name, mn); int i = -1; for(Frame frame: analyzer.getFrames()) { i++; if(frame == null) continue; AbstractInsnNode n = mn.instructions.get(i); if(n.getOpcode() != Opcodes.ILOAD) continue; VarInsnNode vn = (VarInsnNode)n; System.out.println("accessing variable # "+vn.var); ConstantTracker.ConstantValue var = frame.getLocal(vn.var); System.out.println("\tcontains "+var); } } }
这适用于所有负载指令不仅ILOAD
,即ALOAD
,LLOAD
,FLOAD
,和DLOAD
当然,解释器还有很大的改进空间,比如用于跟踪int
常量转换short
或者byte
简单数学的简单转换,但我认为现在图片更清晰,这取决于你的实际用例,你想跟踪多少或解释.