使用 ASM 选择和修改 `if` 语句

2024-02-15

我要更新if在特定行上的现有类中声明,而不更改整个方法。

这是目标代码(类、方法的名称和一些代码已更改,因为它们不相关):

public class Target extends Something {
   public Target(){
        super();
        //some code...
   }

    public Result targetMethod(Data firstPar, Data secondPar){
        if(someStatement()) {
            return Result.FAIL;
        } else {
            if(firstPar.check()){ //here is the line I want to change
                 firstPar.doSomething()
            }
            secondPar.doSomething();
            return Result.SUCCESS;
        }

    }
}

在这段代码中我想改变if(firstPar.check())像这样的事情:

if(firstPar.check() && !Utilities.someOtherCheck(firstPar, secondPar))

这是我尝试解决这个问题的方法:

ClassNode node = new ClassNode();
ClassReader reader = new ClassReader(bytes); //bytes - original target class
reader.accept(node, 0);
ClassWriter cw = new ClassWriter(0);
node.accept(cw);
MethodVisitor visitor = cw.visitMethod(ACC_PUBLIC, "targetMethod", "<it's signature>", null, null);
visitor.visitCode();
//No idea what to do next

我不明白的事情:

  1. 如何在 MethodVisitor 中正确选择一行?

  2. 如何只更改一半if陈述?

  3. 如何获取新类的字节码(将生成 在我们改变目标类别之后)?

如果您能提供某种示例代码,那就太好了。

Thanks!


问题 1 也许是最困难的。您需要通过识别某种模式来找出插入指令的位置。如果你假设firstPar.check()仅被调用一次,那么您可以查找以下字节码指令if(firstPar.check()):

ALOAD 1
INVOKEVIRTUAL Data.check ()Z
IFEQ L3

where L3是跳转标签如果check return false.

对于问题 2,请注意字节码指令if(firstPar.check() && !Utilities.someOtherCheck(firstPar, secondPar)) is:

ALOAD 1
INVOKEVIRTUAL Data.check ()Z
IFEQ L3
ALOAD 1
ALOAD 2
INVOKESTATIC Utilities.someOtherCheck (LData;LData;)Z
IFNE L3

因此,您需要在后面插入 4 条新指令IFEQ L3.

您可以通过使用Tree API http://asm.ow2.org/asm50/javadoc/user/org/objectweb/asm/tree/package-summary.html,您可以在其中创建一个适配器targetMethod通过子类化ClassVisitor and MethodNode:

private static class ClassAdapter extends ClassVisitor {
    public ClassAdapter(ClassVisitor cv) {
        super(Opcodes.ASM5, 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("targetMethod"))
            return new MethodAdapter(access, name, desc, signature, exceptions, mv);
        else
            return mv;
    }
}

private static class MethodAdapter extends MethodNode {
    public MethodAdapter(int access, String name, String desc,
            String signature, String[] exceptions, MethodVisitor mv) {
        super(Opcodes.ASM5, access, name, desc, signature, exceptions);
        this.mv = mv;
    }
    // More to come ...
}

Inside MethodAdapter,你可以覆盖visitEnd迭代所有instructions在方法内部,尝试检测上述 3 条指令,并在它们后面插入 4 条新指令:

@Override
public void visitEnd() {
    // Iterates all instructions in the method
    ListIterator<AbstractInsnNode> itr = instructions.iterator();
    while (itr.hasNext()) {
        // Checks whether the instruction is ALOAD 1
        AbstractInsnNode node = itr.next();
        if (node.getOpcode() != Opcodes.ALOAD
                || ((VarInsnNode) node).var != 1)
            continue;

        // Checks whether the next instruction is INVOKEVIRTUAL
        if (node.getNext() == null
                || node.getNext().getOpcode() != Opcodes.INVOKEVIRTUAL)
            continue;

        // Checks the invoked method name and signature
        MethodInsnNode next = (MethodInsnNode) node.getNext();
        if (!next.owner.equals("Data")
                || !next.name.equals("check")
                || !next.desc.equals("()Z"))
            continue;

        // Checks whether the next of the next instruction is IFEQ
        AbstractInsnNode next2 = next.getNext();
        if (next2 == null
                || next2.getOpcode() != Opcodes.IFEQ)
            continue;

        // Creates a list instructions to be inserted
        InsnList list = new InsnList();
        list.add(new VarInsnNode(Opcodes.ALOAD, 1));
        list.add(new VarInsnNode(Opcodes.ALOAD, 2));
        list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, 
            "Utilities", "someOtherCheck", 
            "(LData;LData;)Z", false));
        list.add(new JumpInsnNode(Opcodes.IFNE, ((JumpInsnNode) next2).label));

        // Inserts the list, updates maxStack to at least 2, and we are done
        instructions.insert(next2, list);
        maxStack = Math.max(2, maxStack);
        break;
    }
    accept(mv);
}

要使用适配器,您可以将其与ClassReader and a ClassWriter。下面我也链一个TraceClassVisitor打印 tmp 目录中的日志文件:

ClassReader reader = new ClassReader("Target");
ClassWriter writer = new ClassWriter(reader, 0);
TraceClassVisitor printer = new TraceClassVisitor(writer, 
        new PrintWriter(System.getProperty("java.io.tmpdir") + File.separator + "Target.log"));
ClassAdapter adapter = new ClassAdapter(printer);
reader.accept(adapter, 0);
byte[] b = writer.toByteArray(); // The modified bytecode
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 ASM 选择和修改 `if` 语句 的相关文章

随机推荐

  • 停止所有子元素的 jQuery 动画

    祝大家新年快乐 我只是想问是否有任何方法可以停止元素的所有子节点的 jquery 动画 我的意思是 如果我在文档中有一个下面的 html 结构 并且我已经对容器内的所有 div 应用了不同的动画 有些也在 queue false 等中 di
  • 哪种嵌入式消息传递系统 -> ActiveMQ 或 HornetQ [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我希望得到一些关于这两个消息系统中哪一个的一般性指示和意见 更容易管理 需要了解和避免的陷阱或神奇的东西
  • 如何在 JUnit 5 中参数化 beforeEach()?

    我使用 JUnit 5 作为我的测试运行程序 在设置方法中 我硬编码了 3 个参数 platformName platformVersion and deviceName 我有一个测试方法 应该测试各种组合 这意味着 当运行我的testLo
  • ffmpeg在特定时间混合音频

    我想将 2 个音频文件混合在一起 一个文件的长度为 2 分钟 另一个文件的长度为 10 秒 我希望两个文件混合 这样仍然可以听到两个声音 我希望这个 10 秒的剪辑恰好在 10 秒的 30 秒处出现 这样它就会在 40 秒处结束 我知道如何
  • R 中的 apply() 与用户定义函数

    我有一个数据框 其中有如此排列的选票和政党标签 dat lt data frame v1 c 25 0 70 v2 c 75 100 20 v3 c 0 0 10 l1 c pA pB l2 c pB pC pC l3 c pD 这样每一行
  • 在 Angular 2/材质的对话框模式中禁用自动对焦

    我正在使用 Angular Material 2 中的对话框 问题是 尤其是在 iPhone 或平板电脑中打开模态对话框时 我无法禁用自动对焦 在 iOS 中 它会自动聚焦对话框中的第一个输入字段 我尝试使用 tabindex 并在其他输入
  • 如何在 Django-Rest-Framework 序列化器中获取 Request.User?

    我已经尝试过类似的方法 但它不起作用 class PostSerializer serializers ModelSerializer class Meta model Post def save self user self contex
  • 通过基于事件的通信做出反应

    我正在尝试在 a 中使用 Reactvscode 网页视图面板 https code visualstudio com api extension guides webview 我认为自己是 React 中的一个不错的组件 但我习惯于通过
  • 如何识别应用程序在 Linux 上以深色主题运行?

    我开发了一个使用 qscintilla 作为文本编辑器的应用程序 我还实现了自定义词法分析器来突出显示特定于语言的关键字 到目前为止 突出显示关键字的样式已硬编码在我的应用程序中 并且在 Windows Linux Ubuntu Mac 上
  • iOS 应用内购买:沙盒产品 ID 无效

    在我解决这个问题之前 先介绍一下稍微奇怪的设置的背景 为客户开发一个应用程序 我们使用的 iTunes 开发者帐户与最终发布的用于开发和临时构建应用程序的帐户不同 具有游戏中心和 IAP 集成 显然 我们最终将不得不在最终发布帐户上复制我们
  • 在OpenGL中从矩阵获取位置、旋转和缩放

    目前我正在学习用于 Android 开发的 OpenGL ES 现在我正处于必须处理的时刻ModelMatrix和CameraMatrix但我对此有一些疑问 在 OpenGL 中 我们总是使用 4x4 矩阵 我理解为什么要这样做 但我不知道
  • 所有测试均通过,但 TFS 将构建标记为部分成功

    我们当前的项目涉及构建一个由 Net 应用程序控制的机器人盒子 我们与相当多的硬件库进行交互 并且我们确实设置了一个集成服务器 所有硬件都连接到它来运行夜间回归测试 不幸的是 并非系统中的所有硬件库都能与 TFS 和 MSTest 很好地集
  • kcachegrind:如何绘制完整的调用图?

    我喜欢 kcachegrind 的调用图 但我无法让它绘制完整的调用图 我想我只需要设置 图表 gt 呼叫者深度 gt 无限 图形 gt 被调用者深度 gt 无限制 图表 gt 最小值节点成本 gt 无最低成本 图表 gt 最小值通话费用
  • R 和 RStudio 不显示希腊字母和其他符号 - 显示方块

    我似乎无法在 r 中显示希腊字母 我正在使用 RStudio 无论我如何尝试 我似乎都无法显示希腊字符 最初我尝试在里面显示希腊字母bquote 使用我上的一堂课的语法 教授 发表并在课堂上证明它有效 当它显示的只是正方形 时 我尝试了更基
  • 如何使用 PHP 的 OpenSSL 扩展验证 CA?

    在命令行中 我可以通过输入来验证证书是否由受信任的 CA 颁发 openssl 验证 mycert pem 我如何使用 PHP 的 OpenSSL 库做同样的事情 PHP 有一个openssl verify https www php ne
  • 在控制器操作中执行 SQL 查询

    我有 5 个单独的 SQL 查询 正在控制器操作中按顺序执行 这是我用来执行它们的方法 var entity new TestEntities entity Database ExecuteSqlCommand SQL Query 所以 基
  • 为什么在 ASP.NET Web 应用程序上调用 WebMethod 时出现不明确的类型错误?

    发送到浏览器的消息如下 My API Class 类型不明确 它可能来自程序集 在临时 ASP NET 文件上 或来自程序集 在 bin 文件夹上 调试 Web 应用程序时 特别是向 WebService 的 WebMethod 发出请求时
  • ActionView::Template::Error:ActionView::Template::Error:nil:NilClass 未定义方法“[]”

    我有一个基本的静态网页 class StaticPagesController lt ApplicationController def home end end 还有一个只有标题的 home html erb 这在开发中工作得很好 但在测
  • %time wait main() 抛出语法错误:jupyter 中的“await”外部函数

    我尝试使用来计算时间 time在 Jupyter notebook 中 一些 SyntaxError 只是让我感到困惑 这是一个可以演示问题的简单代码 import asyncio async def main print 1 time a
  • 使用 ASM 选择和修改 `if` 语句

    我要更新if在特定行上的现有类中声明 而不更改整个方法 这是目标代码 类 方法的名称和一些代码已更改 因为它们不相关 public class Target extends Something public Target super som