通过 ASM Java 字节码访问外部类的私有字段时出现 java.lang.IllegalAccessError

发布于 2024-09-19 22:50:12 字数 552 浏览 3 评论 0原文

在反射中,可以通过 getDeclaredField() 和 setAccessible(true) 访问私有字段。如何通过Objectweb ASM字节码API访问外部类的私有字段? 我设置从类似的东西获取私有字段,通过

Field current = sourceObject.getDeclaredField(privateFieldName);
Field.setAccessible(true);
Type sourceType = Type.getType(sourceObject.getClass());
mv.visitFieldInsn(Opcodes.GETFIELD,
                  sourceType.getInternalName(),
                  privateFieldname,
                  Type.getDescriptor(current.getType()));

当执行字节代码并获取私有字段时,我总是收到错误“java.lang.IllegalAccessError”

任何线索?谢谢一包,

in reflection, the private field can be access via getDeclaredField() and setAccessible(true). How to access the private field of a outside class via Objectweb ASM bytecode API?
I set to get the private field from something like, via

Field current = sourceObject.getDeclaredField(privateFieldName);
Field.setAccessible(true);
Type sourceType = Type.getType(sourceObject.getClass());
mv.visitFieldInsn(Opcodes.GETFIELD,
                  sourceType.getInternalName(),
                  privateFieldname,
                  Type.getDescriptor(current.getType()));

When the byte code is executed and to get the private field, I always got an error "java.lang.IllegalAccessError "

Any clue? Thanks a bundle,

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

递刀给你 2024-09-26 22:50:12

你不能那样做。 setAccessible(true) 只会影响程序当前执行中的当前字段引用(也就是说,它不会影响最终修改程序的执行) 。

要在运行修改后的程序时访问私有字段,您基本上必须将相应的反射步骤嵌入到程序中。

要访问存储在局部变量 varId 中的某个对象的私有字段 YourClass.thePrivatefield,您可以执行以下操作:

// Get hold of the field-reference
mv.visitLdcInsn(Type.getType("LYourClass;"));
mv.visitLdcInsn("thePrivateField");
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/Class",
                   "getDeclaredField",
                   "(Ljava/lang/String;)Ljava/lang/reflect/Field;");

// Duplicate the reference
mv.visitInsn(DUP);

// Call setAccessible(true) using the first reference.
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "setAccessible",
                   "(Z)V");

// Call get(yourObject) using the second reference to the field.
mv.visitInsn(ALOAD, varId);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "get",
                   "(Ljava/lang/Object;)Ljava/lang/Object;");

如果您尝试访问的字段是 cobe 基础的一部分如果您要重写,您显然也可以使用 ACC_PUBLIC 代替 ACC_PRIVATE 来将该字段公开。

You can't do it like that. The setAccessible(true) will only affect the current field-reference in the current execution of your program (that is, it will not affect the execution of the resulting modified program).

To access a private field when running your modified program, you basically have to embed the corresponding reflection-steps into the program.

To access a private field YourClass.thePrivatefield of some object stored in local variable varId you do something like

// Get hold of the field-reference
mv.visitLdcInsn(Type.getType("LYourClass;"));
mv.visitLdcInsn("thePrivateField");
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/Class",
                   "getDeclaredField",
                   "(Ljava/lang/String;)Ljava/lang/reflect/Field;");

// Duplicate the reference
mv.visitInsn(DUP);

// Call setAccessible(true) using the first reference.
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "setAccessible",
                   "(Z)V");

// Call get(yourObject) using the second reference to the field.
mv.visitInsn(ALOAD, varId);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "get",
                   "(Ljava/lang/Object;)Ljava/lang/Object;");

If the field you're trying to make accessible is part of the cobe base that your'e rewriting, you could obviously also make that field public by using ACC_PUBLIC instead of ACC_PRIVATE.

疧_╮線 2024-09-26 22:50:12

实际问题是您无法合法访问这些变量。这是因为 JVM 在 Java 拥有内部类之前定义了其访问规则,因此 javac 为在 JVM 中无法合法访问但在 Java 中可以合法访问的字段创建合成访问器。例如,

class Sample {
    private int i = 0;
    class Inner {
        int foo = i;
    }
}

然后我们可以使用javap来反编译生成的类。

fowles@morbo:/tmp$ javap -private Sample
Compiled from "Sample.java"
class Sample extends java.lang.Object{
    private int i;
    Sample();
    static int access$000(Sample);
}

fowles@morbo:/tmp$ javap -c Sample.Inner
Compiled from "Sample.java"
class Sample$Inner extends java.lang.Object{
int foo;

final Sample this$0;

Sample$Inner(Sample);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #1; //Field this$0:LSample;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  aload_0
   11:  getfield    #1; //Field this$0:LSample;
   14:  invokestatic    #3; //Method Sample.access$000:(LSample;)I
   17:  putfield    #4; //Field foo:I
   20:  return

}

请注意在 Sample 中生成并从 Sample.Inner 使用的 access$000(Sample) 方法。遗憾的是,您的选择是要么

  1. 使字段可访问
  2. 使用反射
  3. 生成合成访问器

The actual problem is that you cannot legally access those variables. This is because the JVM defined its access rules before Java had inner classes, so javac creates synthetic accessors for fields that it cannot legally access in the JVM but can in Java. For example,

class Sample {
    private int i = 0;
    class Inner {
        int foo = i;
    }
}

Then we can use javap to decompile the generated classes.

fowles@morbo:/tmp$ javap -private Sample
Compiled from "Sample.java"
class Sample extends java.lang.Object{
    private int i;
    Sample();
    static int access$000(Sample);
}

fowles@morbo:/tmp$ javap -c Sample.Inner
Compiled from "Sample.java"
class Sample$Inner extends java.lang.Object{
int foo;

final Sample this$0;

Sample$Inner(Sample);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #1; //Field this$0:LSample;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  aload_0
   11:  getfield    #1; //Field this$0:LSample;
   14:  invokestatic    #3; //Method Sample.access$000:(LSample;)I
   17:  putfield    #4; //Field foo:I
   20:  return

}

Notice the access$000(Sample) method that got generated in Sample and used from Sample.Inner. Sadly, your options are to either

  1. Make the field accessible
  2. Use reflection
  3. Generate synthetic accessors
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文