Set private field of package-private Class from outer package Class with reflection

Nicolas Brauer :

I need to change the value of a private variable in a package-private class from a outer-package class.

  • package: java.util.jar from jdk-9.0.1
  • class: JarVerifier.java
  • variable: parsingBlockOrSF (private boolean)

I tried this:

private void writePrivateJarVerifierField(boolean newValue) throws Exception {
    Class<?> clazz = Class.forName("java.util.jar.JarVerifier");
    Field field = clazz.getDeclaredField("parsingBlockOrSF");
    field.setAccessible(true);
    field.setBoolean(clazz.newInstance(), newValue);
}

It gives me Exception in thread "main" java.lang.InstantiationException: java.util.jar.JarVerifier

I have seen this, this, this and this question already, though I am not able to derive a solution for my problem.

Can someone give me a hint please ?


Edit 1: as I want to modify the value of parsingBlockOrSF during runtime (as if I changed it through debugger) I need an existing instance of JarVerifier thus of JarFile (thank you Gyro Gearless) By taking a look at the approach proposed by Ankur Chrungoo, I figured out I would need to get an already existing Instance of JarVerifier, thus I tried this:

    private void writePrivateJarVerifierField(boolean newValue, JarFile jf) throws Exception {
    Class<?> clazz = Class.forName("java.util.jar.JarVerifier");
    Class<?> clacc = Class.forName("java.util.jar.JarFile");
    Field field = clazz.getDeclaredField("parsingBlockOrSF");
    Field field1 = clacc.getDeclaredField("jv");
    field.setAccessible(true);
    field1.setAccessible(true);
    field.setBoolean(field1.get(jf), newValue);
}

Where the JarFile jf creates a new instance of JarVerifier and saves it in the variable called jv. Thats why I am aiming to get both classes JarVerifier and JarFile, in order to get both variable I want to access (one beeing the actual boolean parsingBlockOrSF from the JarVerifier, the other beeing the JarVerifier instance jv from the JarFile instance. In my mind the code shown above make sense and should work, but it doesn't, so where is my mistake ?

The exception I get: java.lang.NullPointerException at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:57)


Edit 2: changing the last line of the above code to : field.setBoolean(field1.getType(), newValue);

It seems as the object is recognized, though it Can not set boolean field java.util.jar.JarVerifier.parsingBlockOrSF to java.lang.Class


Edit 3: by using this Code:

    Class<?> clazz = Class.forName("java.util.jar.JarVerifier");
Class<?> clacc = Class.forName("java.util.jar.JarFile");
Field field = clazz.getDeclaredField("parsingBlockOrSF");
Field field1 = clacc.getDeclaredField("jv");
field.setAccessible(true);
field1.setAccessible(true);
field.setBoolean(clazz.getConstructor(byte[].class).newInstance(new byte[1]), newValue);

I get this error: Exception in thread "main" java.lang.IllegalAccessException: class JarVerifier cannot access a member of class java.util.jar.JarVerifier (in module java.base) with modifiers "public"

Here I want to change the value of the parsingBlockOrSF in the jarVerifier inside the JarFile, so I first must work on the jarFile instance, get the jarVerifier out of it (what I am trying to do with field1 = calcc.getDeclaredField("jv") as the jarVerifier is stored in the jv variable inside the JarFile), and then using that object, modify its property


The Code where the JarFile is created:

    JarFile jf = null;
    jf = new JarFile(jarName, true);

jarName is a String representing the path to the .jar File

Ankur Chrungoo :

I could see that the JarVerifier class does not have a default Constructor. The constructor it has is something like this:-

public JarVerifier(byte rawBytes[]) {
        manifestRawBytes = rawBytes;
        sigFileSigners = new Hashtable<>();
        verifiedSigners = new Hashtable<>();
        sigFileData = new Hashtable<>(11);
        pendingBlocks = new ArrayList<>();
        baos = new ByteArrayOutputStream();
        manifestDigests = new ArrayList<>();
    }

So, you would have to get the non-default Constructor using reflection and then use it to create the instance. So, your code should be something like this:-

field.setBoolean(clazz.getConstructor(byte[].class).newInstance(new byte[1]), newValue);

Reference for JarVerifier class: https://github.com/netroby/jdk9-dev/blob/master/jdk/src/java.base/share/classes/java/util/jar/JarVerifier.java

Assumption: Your application has the required security permissions to modify access using reflection.

Further reference: Java: newInstance of class that has no default constructor

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=132144&siteId=1