views:

1636

answers:

11

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).

A: 

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));
    }
}
Kevin Boyd
`java -Dos.name=foo -Dos.version=foo -Dos.arch=foo OpertingSystemInfo` – and now?
Bombe
+1  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?

Rich Seller
+7  A: 

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.

Kevin
A: 

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).

Aaron Digulla
I'm pretty sure that the settings done by the VM are not prevented by any SecurityManager.
Michael Borgwardt
+11  A: 

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.

Michael Borgwardt
+2  A: 

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"

techzen
A: 

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.

Jonathan
+3  A: 

Why are you worried about this? If an end-user is dumb enough to futz 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;
}
}
KitsuneYMG
A: 

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.

Marian
A: 

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.

tulskiy
+1  A: 

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.

Jay
The security concern is "copy protection". It should be nontrivial to run the application on a medium (usb stick) other than the one it is delivered on.
Wouter Lievens
I suggest you reword your question to ask for what you really wants to know.
Thorbjørn Ravn Andersen
I'm just curious now. This probably isn't relevant to answering your question, but how does knowing the OS help? If someone inserts the USB, copies the program to their hard drive, and tries to run it, they're still under the same OS. If on the USB you code what OS you expect and sell different versions for different OSs, I suppose you could then prevent someone buying the Windows version and running on Linux, but that would be a tiny subset of piracy candidates. I suspect that most people who want to make illegal copies would be making them for the same OS.
Jay
Oh, wait, maybe I get what you're up to now. You have some program you run, with Windows, Linux, Mac, whatever versions, that checks if you're actually running off the memory stick, and if it fails, you refuse to run. So if someone can trick you about the operating system and your "stick check" doesn't run, they bypass your copy protection. Is that it? But if so, can't you simply write your program so that if the stick check doesn't run successfully, you call it a failure? Then if they lie about the OS, it's a fail.
Jay
You describe the problem well. And yes, I can use the tips from this thread to find out if they are lying about the OS and fail in that case.
Wouter Lievens
So why go to great lengths to validate the OS? Just say, If the stick check does not run successfully, it's a failure. If they lie about the OS so your program won't work, then it will be a fail.
Jay