Question

I'd like to know how to get hold of the kind of Operating System the jvm is running on. It has to be "secure" as well, so System.getProperty("os.name") is not really an option because it can be trivially circumvented with the -D directive.

By "secure" I mean nontrivial to circumvent. It's for a desktop application. The user could always deobfuscate, decompile, edit and recompile the code, but that is significantly harder than passing -D to the jvm. We want to make tinkering nontrivial, not impossible (since that can't be done).

Was it helpful?

Solution

First, it's impossible to protect code from being manipulated arbitrarily by its runtime environment. But in order to make it at least hard to fool your check, the best bet is probably some sort of file system based OS fingerprinting.

File.listRoots() is your starting point; on a Unix-like system it will return a single root containing characteristic directories like /etc, /usr, etc. On Windows, it will return multiple results, but AFAIK the OS installation drive is not necessarily C: and the characteristic directories differ across Windows versions and locales - be careful not to assume that everyone runs an English version of Vista.

You could invest a lot of work into recognizing different versions of Windows and Linux, as well as BSD or MacOS - and it would probably take a lot less work to remove the check from the compiled code once it's out there.

OTHER TIPS

The system properties are the only way that I know of to obtain operating system information. Even the OperatingSystemMXBean takes its values from the os.X system properties.

If someone can tinker with how your application is launched, you have bigger problems than if os.name is correct.

However, if you're worried about malicious code setting that property while your application is running, you can use the Java Security Manager to ensure that the properties are guarded safely.

Why are you worried about this? If an end-user is dumb enough to mess with os.* properties why not let the app explode?

That being said, these may work well enough for your purposes

//can be defeated by adding com.apple.eawt.Application to the classpath
public boolean isMac() {
    try {
        Class.forName("com.apple.eawt.Application");
        return true;
    } catch(Exception e) {
        return false;
    }
}

//can be defeated by creating a cmd.exe in PATH
public boolean isWin() {
    try{
        Runtime.getRuntime().exec( new String[]{"cmd.exe","/C","dir"} ).waitFor();
        return true;
    } catch (Exception e) {
        return false;
    }
}

public boolean isLinux() {
    if(isMac()) return false;
    try{
        Runtime.getRuntime().exec(  new String[]{"sh","-c","ls"} ).waitFor();
        return true;
    } catch (Exception e) {
        return false;
    }
}

You can run through the Runtime class (exec method) an external cmd like "ver" to get the version of os (if in windows) or "uname -a"

Apache commons-vfs abstracts some of the processing so you can just deal with Os and OsFamily. However internally this still uses System.getProperty(..) to obtain the values. I don't know of any other way for the JVM to obtain these values.

If someone is in a position to change the properties passed to the JVM you've got bigger problems to deal with than them changing the odd property.

Can you elaborate on what you mean by secure. Secure from whom?

You could use exec to try to run some harmless programs that you can expect to exist on one OS or another -- like "c:\windows\system\dir.exe" for windows and "/bin/ls" for *nix -- and see if they run successfully or bomb out. But of course is someone knows you're doing that and is trying to undermine it, they could create executables with those names.

What exactly is it that you're trying to be secure from? If someone deliberately screws up the launch of your app and then it blows up because you're trying to run non-existent commands, wouldn't the appropriate response be, "If your feet hurt, stop shooting yourself in the foot." If all you're doing is run OS commands, presumably the user is able to access those outside of your app, so I don't see how lying about the OS compromises security.

There is no public API in the VM other than using the properties you mentioned.

My hope was that, since Java is supposed to be secure, that the VM would ignore any attempts to overwrite these values from the command line but that is not the case.

You might try a SecurityManager which disallows to write these values but my guess is that then, the value will be simply empty (since the VM can't change it, either).

You can also examine environment variables through System.getenv(), but the user is free to modify these as well prior to startup. It's just not quite as easy (at least on Windows) to modify the variables as part of the command-line to start your application.

If security is your primary goal, you may want to try to add in some JNI. First, if you try to load a DLL in linux or in OS X, I bet it will crash/not load. Second, from that DLL you can call the OS' API, so you are not restricted to what the Java environment is offering.

I agree that jni/jna is a better alternative. Just call something like Native.loadLibrary("myLib"), and on windows it will load myLib.dll, on linux - myLib.so. It's the safest way, one just cannot mess with it. Put all os specific code in libraries and keep compiled versions in your jar.

Modern Linux systems have a special file /proc/version, which gives the kernel version ID. So if that file is present an matches a suitable pattern (for example, starting with the word Linux), you have increased the probability that you are running on a Linux computer. Conversely, if it is absent or does not match that pattern, you can be sure you are not running on Linux.

Why dont you combine the following and use it to interpolate, that way circumventing may be difficult.

public class OpertingSystemInfo 
{
    public static void main(String[] args)
    {
        String nameOS = "os.name";        
        String versionOS = "os.version";        
        String architectureOS = "os.arch";
    System.out.println("\n    The information about OS");
        System.out.println("\nName of the OS: " + 
System.getProperty(nameOS));
        System.out.println("Version of the OS: " + 
System.getProperty(versionOS));
        System.out.println("Architecture of THe OS: " + 
System.getProperty(architectureOS));
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top