使用Java代理(Byte Buddy)将现有字段从私有转换为公共

时间:2017-10-08 23:43:35

标签: java bytecode instrumentation javaagents byte-buddy

我正在尝试从应用程序代码中的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 ......我也用它来重新定义其他一些东西。我可以自己计算cpath引用OpenJDK代码,但我希望它尽可能高效...而且由于FilePermission已经在内部完成工作,我宁愿抓住价值而不是工作两次。由于我已经在其他方面使用仪器,这似乎是一个更优雅的解决方案。

干杯!

1 个答案:

答案 0 :(得分: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邮件列表上询问为什么会出现这种情况,我只能猜测。