【问题标题】:Using Java agent (Byte Buddy) to transform existing field from private to public使用 Java 代理 (Byte Buddy) 将现有字段从私有转换为公共
【发布时间】:2017-10-08 23:43:34
【问题描述】:

我正在尝试从我的应用程序代码中的FilePermission 有效地访问cpath 字段。请注意,这应该可以通过安全管理器来完成,所以如果可能的话,我不想求助于调用 setAccessible。

我已经在通过 AgentBuilder 使用 Byte Buddy 支持的代理。以下是 AgentBuilder 的作用:

public static void premain(String arg, Instrumentation inst) {
    install(arg, inst);
}

public static void agentmain(String arg, Instrumentation inst) {
    install(arg, inst);
}

private static void install(String arg, Instrumentation inst) {
    Transformer filePermissionTransformer = (builder, typeDescription, classLoader, module) ->
        builder.field(named("cpath")).transform(ForField.withModifiers(Visibility.PUBLIC));

    new AgentBuilder.Default()
        .with(new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE))
        .with(Listener.StreamWriting.toSystemOut())
        .with(InitializationStrategy.NoOp.INSTANCE)
        .with(RedefinitionStrategy.REDEFINITION)
        .with(TypeStrategy.Default.REDEFINE)
        .ignore(none())
        .type(named("java.io.FilePermission"))
        .transform(filePermissionTransformer)
        .installOn(inst);
}

我可以通过 sysout 监听器看到它确实正在被转换:

[Byte Buddy] DISCOVERY java.io.FilePermission [null, null, loaded=true]
[Byte Buddy] TRANSFORM java.io.FilePermission [null, null, loaded=true]
[Byte Buddy] COMPLETE java.io.FilePermission [null, null, loaded=true]

然后我尝试在应用程序中获取该字段:

if (perm instanceof FilePermission) {
        Field cpathField = perm.getClass().getDeclaredField("cpath");
        String cpath = (String) cpathField.get(perm);
}

但这会导致 IllegalAccessException,原因告诉我它仍然是“私有瞬态”。

只是为了好玩,我尝试了 .annotateField 而不是 .transform 带有弃用注释。这确实有效,并且在运行时我可以从声明的字段中检索注释。所以这至少证明了字段转换的路径是有效的……只是由于某种原因不是这个特定的转换。

仅作为背景,不,这不是我使用 Byte Buddy 的唯一原因...我还使用它来重新定义其他一些东西。我可以自己参考 OpenJDK 代码计算cpath,但我希望它尽可能高效......而且由于 FilePermission 已经在内部完成工作,我宁愿获取价值也不愿做两次工作。由于我已经在其他事情上使用仪器,这似乎是一个更优雅的解决方案。

干杯!

【问题讨论】:

    标签: java bytecode instrumentation javaagents byte-buddy


    【解决方案1】:

    如果您运行程序并将属性-Dnet.bytebuddy.dump 设置为某个文件夹,Byte Buddy 将提取生成的类文件并将它们写入指定的文件夹。如果您使用 javap 调查为 FilePermission 创建的文件,您会得到:

    Compiled from "FilePermission.java"
    public final class java.io.FilePermission extends java.security.Permission implements java.io.Serializable {
      public transient java.lang.String cpath;
      public java.io.FilePermission(java.lang.String, java.lang.String);
      java.io.FilePermission(java.lang.String, int);
      public boolean implies(java.security.Permission);
      boolean impliesIgnoreMask(java.io.FilePermission);
      public boolean equals(java.lang.Object);
      public int hashCode();
      int getMask();
      public java.lang.String getActions();
      public java.security.PermissionCollection newPermissionCollection();
      static java.lang.String access$000(java.io.FilePermission);
    }
    

    如您所见,cpath 字段已公开,但 JVM 似乎并不关心这一点。如果你定义一个具有相同cpath 字段的类并运行上面的转换,这将起作用。

    我只能怀疑 JVM 在某处硬编码了这个属性。你可以在JVM邮件列表上问为什么会这样,我只能猜测。

    【讨论】:

    • 非常有趣...我猜这是因为该类是 Java 安全框架的一部分。感谢您的回复!
    • 也许吧。然而,您可以使用可以毫无问题地更改字段值的建议来更改任何代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多