我正在对Java客户端应用程序进行反向工程,我想跟踪该应用程序对某些字段的修改,以查看在解决混淆的名称的操作之后进行了哪些更改。
我可能可以使用某种调试器,但是我决定将其调试以进行学习。
我使用ASM创建了一个类适配器,该适配器正在检查每个方法的每个指令,当它碰到PUTFIELD指令时,它应该调用该对象的fireCallback方法
只要我可以期望PUTFIELD目标是工作类,它就非常简单,但是如果在其他对象上调用它,我将无法弄清楚如何检测到它在哪个对象上被调用
我在阅读JVM规范,发现有一条DUP和POP指令可用于堆栈操作,并且当调用PUTFIELD时,在堆栈上有对象引用和设置值,所以我想为什么我不能在之前复制堆栈putfield,然后简单地弹出设置值,并在其余objectref上调用fireCallback
但这不是那么简单,不幸的是,如果设置值的类型为double或long,则PUTFIELD会使用两个单词,而且我无法简单地确定如何处理此异常?
还是有一些更简单的方法?我如何知道在putfield之前将哪些对象加载到堆栈?
我当前的代码:
for (MethodNode method : (List<MethodNode>) methods) {
if (ADD_FIELD_CALLBACKS) {
Iterator<AbstractInsnNode> insIt = method.instructions
.iterator();
while (insIt.hasNext()) {
AbstractInsnNode ins = insIt.next();
int opcode = ins.getOpcode();
if (ins.getOpcode() == Opcodes.PUTFIELD) {
FieldInsnNode fieldInsNode = (FieldInsnNode) ins;
System.out.println(name + "'s updating "
+ fieldInsNode.owner + "'s "
+ fieldInsNode.name + " in method "
+ method.name);
InsnList onFieldEditInsList = new InsnList();
method.instructions.insertBefore(ins, new InsnNode(
Opcodes.DUP2));
onFieldEditInsList.add(new InsnNode(Opcodes.POP));
onFieldEditInsList.add(new LdcInsnNode(
fieldInsNode.name));
onFieldEditInsList
.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL,
fieldInsNode.owner, "fireCallback",
"(Ljava/lang/String;)V", false));
method.maxStack += 2;
method.instructions.insert(ins, onFieldEditInsList);
}
}
}
}
如果此时堆栈中有不同的项目集,则会导致带有一条消息“ Expecting to find object / array on stack”的VerifyError。
最佳答案
您的问题看起来有些奇怪,因为putfield
指令已经包含了您需要的所有信息。它具有reference to the owner class和field signature telling the type of the field。
因此,对于遇到putfield
指令时的有效字节码,最高的堆栈值必须与该字段的类型兼容,而下一个值必须是对与所有者类兼容的实例的引用。换句话说,如果字段的签名是"J"
或"D"
,则您的值将占据堆栈顶部的两个单词,否则为一个单词。
请注意,对于此类任务,使用GeneratorAdapter
更为方便。用作访问者时,默认情况下,它将复制遇到的代码,并且可以计算maxStack和maxLocal值等。因此,您只需要覆盖访问方法即可获得要注入代码的指令,而无需实现迭代逻辑。
例如。用于为每个public static void fieldWrite(Object owner, String fieldName)
指令在类mypackage.MyDebugger
中调用类似putField
的方法的代码如下:
// i.e. class MyCodeTransformer extends GeneratorAdapter
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if(opcode==Opcodes.PUTFIELD) {
final Type fieldType = Type.getType(desc);
super.swap(Type.getObjectType(owner), fieldType);
if(fieldType.getSize()==1) super.dupX1(); else super.dupX2();
super.visitLdcInsn(name);
super.visitMethodInsn(Opcodes.INVOKESTATIC, "mypackage/MyDebugger",
"fieldWrite", "(Ljava/lang/Object;Ljava/lang/String;)V", false);
}
super.visitFieldInsn(opcode, owner, name, desc);
}