2

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

whme
  • 4,908
  • 5
  • 15
  • 28
  • Obviously, (since `parsingBlockOrSF` is not static), you need an existing instance of `JarVerifier` to work on. And what would be the use case for setting that variable? Variables are usually `private` for a reason. – Gyro Gearless Dec 04 '18 at 11:16
  • @GyroGearless you are totally right, I edited the post ^^ For the use case: If you want a deeper insight on my problem maybe go take a look at [this](https://stackoverflow.com/questions/53482698/how-to-sign-a-jar-file-using-xmss-pqc-signature-scheme-with-jarsigner) . Briefly explained: I managed to sign .jar files programmatically with pqc-signature schemes through the use of BCPQC-Provider (which I had to change a bit) which is why I need to make a verifier now. Sadly the verifying process concerning xmss signed .jars has to be redone.. which is why im here – whme Dec 04 '18 at 11:25
  • @NicolasBrauer field.setBoolean(field1.get(jf), newValue); Shouldn't the first parameter be the actual object on which you want to modify the value. You are instead supplying the field object itself i.e. field1.get(jf) Also, please paste the code how you derive the jf parameter from where you call this method. – A_C Dec 04 '18 at 11:33
  • @AnkurChrungoo so it should be JarVerifier.parsingBlockOrSF while I am currently giving JarVerifier ? (as field1.get(jf) should return the JarVerifier Object) – whme Dec 04 '18 at 11:37
  • @NicolasBrauer, first parameter should be the Object on which you are setting the value, i.e. not the field because field is what you are invoking setBoolean() on. – A_C Dec 04 '18 at 11:39
  • @AnkurChrungoo actually I thought `field1.get(jf)` would return the object, however field1.getType() returns the JarVerifier on which I want to set the variable `parsingBlockOrSF` to true. – whme Dec 04 '18 at 11:44
  • @NicolasBrauer field1.get(jf) will return the value of the field jv represented as an Object (wrapped) . https://docs.oracle.com/javase/9/docs/api/java/lang/reflect/Field.html – A_C Dec 04 '18 at 11:47
  • @AnkurChrungoo thank you, so how do I get what I actually need ? – whme Dec 04 '18 at 11:51
  • @NicolasBrauer I am not exactly sure what you are trying to achieve now, but the syntax should be like this : field.setBoolean(jf, newValue); – A_C Dec 04 '18 at 11:57
  • @AnkurChrungoo thank you, sadly it gives me `java.lang.IllegalArgumentException: Can not set boolean field java.util.jar.JarVerifier.parsingBlockOrSF to java.util.jar.JarFile` so i think i need to get an isntance of the object `java.util.jar.JarVerifier.parsingBlockOrSF` it self – whme Dec 04 '18 at 12:00
  • @AnkurChrungoo thank you this helped a lot. So I need to get the instance of the JarVerifier that gets initialized in the JarFile jf as I want to change the value of its variable parsingBlockOrSF. However this is what I am trying to do by calling `Field field1 = clacc.getDeclaredField("jv"); ` – whme Dec 04 '18 at 12:17
  • @NicolasBrauer request you to please paste the code in the question, where you are creating the jf instance. It would be helpful for me to understand. – A_C Dec 04 '18 at 12:19
  • @AnkurChrungoo I edited it, however its nothing special – whme Dec 04 '18 at 12:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/184691/discussion-between-ankur-chrungoo-and-nicolas-brauer). – A_C Dec 04 '18 at 12:23
  • 3
    You already know the way to do it. 1) Store the `Constructor` returned by `clazz.getConstructor(byte[].class)` in a variable. 2) Call `setAccessible(true)` on it like you did for the other inaccessible members. 3) Now call `newInstance(new byte[1])` on it. Needless to say, accessing private implementation details like this is fragile and may break with the next Java version. Problems like those you already have (e.g. a field is unexpectedly `null`) give an idea of some of the trouble you may run into. – Holger Dec 04 '18 at 15:23
  • 4
    Hacking JarVerifier in this way creates huge potential to cause breakage and/or introduce security bugs. I think it would be better to start with an explanation as to why you think you need to hack this field. If there is a good case for a new API in java.util.jar then it would be useful to post a link to the issue in the Java Bug System. – Alan Bateman Dec 05 '18 at 08:39
  • @AlanBateman In order to be prepared in case Quantum Computers get viable our Security Research team has to find a way of signing and verifying .jar files with the use of PQC-Signature schemes, such as XMSS, XMSSMT, SPHINCS, etc. The JDK provides a feature called JCA/JCE which at first look seems perfect for such use-cases especially as [BouncyCastle](https://www.bouncycastle.org/java.html) does provider a PQC-Provider for JCA. Sadly neither the JarSigner is made to work with PQC nor the BCPQC-Provider to work with JarSigner through JCA, which is why we need to find a way to get this to work. – whme Dec 05 '18 at 08:53
  • 2
    Nicolas - you should bring this topic to the OpenJDK security-dev list for discussion. Hacking JarVerifier's fields is too fragile and could break at any time. – Alan Bateman Dec 05 '18 at 10:44

2 Answers2

2

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

A_C
  • 905
  • 6
  • 18
  • Thank you very much, I added my post. – whme Dec 04 '18 at 11:22
  • As I get `class JarVerifier cannot access a member of class java.util.jar.JarVerifier (in module java.base) with modifiers "public"` by using your code example, should I presume Mohammad Hossin Samadian answer is right, and you cannot change variables from java.util.jar package ? – whme Dec 04 '18 at 11:58
  • @NicolasBrauer mind sharing the code how you are creating the instance using my code above? – A_C Dec 04 '18 at 12:10
  • I used it exactly as you wrote: `Class> clazz = Class.forName("java.util.jar.JarVerifier"); Class> clacc = Class.forName("java.util.jar.JarFile"); Field field = clazz.getDeclaredField(variableName); Field field1 = clacc.getDeclaredField("jv"); field.setAccessible(true); field1.setAccessible(true); field.setBoolean(clazz.getConstructor(byte[].class).newInstance(new byte[1]), newValue);` – whme Dec 04 '18 at 12:15
0

You cannot change object from java.lang and java.util.jar package. these packages is system package and if you can't change it.these protects by jvm.