我想知道如何获取jvm运行的操作系统类型。同时,这个方法必须是“安全”的,所以System.getProperty("os.name")
并不是一个好的选择,因为它可以轻松地通过-D指令规避。
所谓“安全”,是指难以被规避。这是为桌面应用程序而设计的。用户总是可以对代码进行反混淆、反编译、修改和重新编译,但这比传递-D给jvm要困难得多。我们希望让调整变得更加困难,而不是不可能(因为这是不可能的)。
我想知道如何获取jvm运行的操作系统类型。同时,这个方法必须是“安全”的,所以System.getProperty("os.name")
并不是一个好的选择,因为它可以轻松地通过-D指令规避。
所谓“安全”,是指难以被规避。这是为桌面应用程序而设计的。用户总是可以对代码进行反混淆、反编译、修改和重新编译,但这比传递-D给jvm要困难得多。我们希望让调整变得更加困难,而不是不可能(因为这是不可能的)。
首先,无法防止运行时环境任意操纵代码。但为了让检查更难被欺骗,最好的方法可能是基于文件系统的操作系统指纹识别。
File.listRoots() 是你的起点;在类Unix系统上,它将返回一个包含特征目录(如/etc、/usr等)的单个根目录。在Windows上,它将返回多个结果,但据我所知,操作系统安装驱动器不一定是C:,而且特征目录在Windows版本和区域设置之间有所不同-请注意不要假设每个人都运行英文版的Vista。
你可以投入大量工作来识别不同版本的Windows和Linux,以及BSD或MacOS-但一旦编译代码发布出去后,从中删除检查可能需要更少的工作。
你为什么担心这个问题?如果终端用户够蠢,去乱动os.*属性,何不让应用程序崩溃呢?
话虽如此,这些属性可能对你的目的足够好用。
//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;
}
}
Apache commons-vfs 抽象化了某些处理过程,所以你只需处理Os和OsFamily。然而,内部仍然使用 System.getProperty(..) 来获取这些值。我不知道 JVM 获取这些值的其他方法。
如果有人能够更改传递给 JVM 的属性,那么你将面临比更改奇怪属性更大的问题。
您能否详细说明您所说的安全性?安全性是指对谁的安全?
你可以使用exec来尝试运行一些无害的程序,这些程序可以在一个操作系统或另一个操作系统中存在,例如在Windows中使用"c:\windows\system\dir.exe",在*nix系统中使用"/bin/ls",看看它们是否成功运行或失败。但当然,如果有人知道你正在做这件事并试图破坏它,他们可能会创建带有这些名称的可执行文件。
你究竟想要保护什么?如果有人故意搞砸了你的应用程序的启动,然后它因为你试图运行不存在的命令而崩溃,那么适当的反应不是 "如果你的脚痛,就别对自己开枪" 吗?如果你只是在运行操作系统命令,那么用户显然能够在你的应用程序之外访问这些命令,所以我不明白撒谎关于操作系统如何影响安全性。
/proc/version
,它提供了内核版本ID。因此,如果该文件存在并且与合适的模式匹配(例如,以单词Linux
开头),你就可以增加在Linux计算机上运行的可能性。相反,如果不存在或不符合该模式,则可以确定你没有在Linux上运行。除了使用您提到的属性之外,VM中没有公共API可用。
我希望,由于Java应该是安全的,VM会忽略从命令行覆盖这些值的任何尝试,但事实并非如此。
您可以尝试使用SecurityManager来禁止写入这些值,但我猜想,那么该值将变为空(因为VM也无法更改它)。
你也可以通过System.getenv()检查环境变量,但用户在启动之前也可以自由修改这些变量。只是在Windows上,将变量作为命令行的一部分进行修改并不那么容易,以便启动应用程序。