Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
846 views
in Technique[技术] by (71.8m points)

jvm - Is there a way to soft-change Java version?

I have an old application which refuses to start on versions of Java that it was not tested on. Unfortunately it is an abandonware, so there is little chance for it to be updated. It is complaining that JVM is made by Oracle rather than Sun.

Back in days of MS DOS there was a setver command which allowed one to set a version of the operating system that an application saw, is there something similar to that on JVM?

question from:https://stackoverflow.com/questions/65841455/is-there-a-way-to-soft-change-java-version

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would use the asmtools from the OpenJDK project to disassemble the files to a bytecode assembly format. So for example a helloworld looks like this (jdis NameOfClass.class>NameOfClass.j):

super public class A
    version 59:0
{

public Method "<init>":"()V"
    stack 1 locals 1
{
        aload_0;
        invokespecial   Method java/lang/Object."<init>":"()V";
        return;
}

public static Method main:"([Ljava/lang/String;)V"
    stack 2 locals 1
{
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Hello World!";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
        return;
}

} // end Class A

After that you edit the file:

super public class A
    version 59:0
{

public Method "<init>":"()V"
    stack 1 locals 1
{
        aload_0;
        invokespecial   Method java/lang/Object."<init>":"()V";
        return;
}

public static Method main:"([Ljava/lang/String;)V"
    stack 2 locals 1
{
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Hello Stackoverflow!";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
        return;
}

} // end Class A

And then you can compile it to .class again:

jasm NameOfClass.j

and then you can run it normally with Java.

This was just an example.

You would just have to find the class with the method that checks for the version. For example you could unzip the jar files and if for example the message for a non-matching version is, for example:

"Bad version detected: "

You can just grep for it (grep -R Bad.version) and then you get the class. There you will have to change the method responsible for checking the Java version.

An example:

A.java:

public class A{
        static boolean hasMatchingJavaVersion() {
                return System.getProperty("java.version").matches("1.4.*");//Just Java 1.4
        }
        public static void main(String[] args) {
                if(hasMatchingJavaVersion()) {
                  System.out.println("Matches");
                } else {
                  System.out.println("Bad version");
                  System.exit(-1);
                }
        }
}

Run it: false (On Java 15, e.g.)

As assembly.


super public class A
    version 59:0
{

public Method "<init>":"()V"
    stack 1 locals 1
{
        aload_0;
        invokespecial   Method java/lang/Object."<init>":"()V";
        return;
}

static Method hasMatchingJavaVersion:"()Z"
    stack 2 locals 0
{
        ldc String "java.version";
        invokestatic    Method java/lang/System.getProperty:"(Ljava/lang/String;)Ljava/lang/String;";
        ldc String "1.4.*";
        invokevirtual   Method java/lang/String.matches:"(Ljava/lang/String;)Z";
        ireturn;
}

public static Method main:"([Ljava/lang/String;)V"
    stack 2 locals 1
{
        invokestatic    Method hasMatchingJavaVersion:"()Z";
        ifeq    L17;
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Matches";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
        goto    L29;
    L17:    stack_frame_type same;
        getstatic   Field java/lang/System.out:"Ljava/io/PrintStream;";
        ldc String "Bad version";
        invokevirtual   Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
        iconst_m1;
        invokestatic    Method java/lang/System.exit:"(I)V";
    L29:    stack_frame_type same;
        return;
}

} // end Class A

This method checks for the version:

static Method hasMatchingJavaVersion:"()Z"
    stack 2 locals 0
{
        ldc String "java.version";
        invokestatic    Method java/lang/System.getProperty:"(Ljava/lang/String;)Ljava/lang/String;";
        ldc String "1.4.*";
        invokevirtual   Method java/lang/String.matches:"(Ljava/lang/String;)Z";
        ireturn;
}

So you could for example change it to:

static Method hasMatchingJavaVersion:"()Z"
    stack 2 locals 0
{
        iconst_1;//Load true, always return true
        ireturn;
}

And then compile it again with jasm.

Edit:

that JVM is made by Oracle rather than Sun.

I overread this, but it works the same way as described above, just another system property and some other minor changes,


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...