4

我想知道如何获取 jvm 运行的操作系统类型。它也必须是“安全的”,所以System.getProperty("os.name")它不是一个真正的选择,因为它可以用 -D 指令轻松规避。

我所说的“安全”是指规避的重要性。它适用于桌面应用程序。用户总是可以对代码进行反混淆、反编译、编辑和重新编译,但这比将 -D 传递给 jvm 困难得多。我们想让修补变得不平凡,而不是不可能(因为那是做不到的)。

4

12 回答 12

15

首先,不可能保护代码不被其运行时环境任意操纵。但为了至少让您的支票难以被骗,最好的选择可能是某种基于文件系统的操作系统指纹识别

File.listRoots()是您的起点;在类 Unix 系统上,它将返回包含 /etc、/usr 等特征目录的单个根目录。在 Windows 上,它将返回多个结果,但 AFAIK 操作系统安装驱动器不一定是 C: 并且特征目录不同Windows 版本和语言环境 - 注意不要假设每个人都运行英文版的 Vista。

您可以投入大量工作来识别不同版本的 Windows 和 Linux,以及 BSD 或 MacOS——一旦从编译代码中删除检查,可能需要的工作会少得多。

于 2009-08-18T12:50:20.650 回答
8

系统属性是我所知道的获取操作系统信息的唯一方法。甚至 OperatingSystemMXBean 也从 os.X 系统属性中获取其值。

如果有人可以修改您的应用程序的启动方式,那么您会遇到比 os.name 正确时更大的问题。

但是,如果您担心在应用程序运行时恶意代码设置该属性,您可以使用 Java 安全管理器来确保安全地保护这些属性。

于 2009-08-18T12:47:37.890 回答
8

你为什么担心这个?如果最终用户愚蠢到可以弄乱 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;
    }
}
于 2009-08-18T14:25:58.663 回答
2

您可以通过运行时类(exec 方法)运行外部 cmd,如“ver”来获取 os 的版本(如果在 windows 中)或“uname -a”

于 2009-08-18T12:54:09.557 回答
1

您可以使用 exec 来尝试运行一些您可以期望存在于一个或另一个操作系统上的无害程序——比如 windows 的“c:\windows\system\dir.exe”和 *nix 的“/bin/ls”—— - 看看他们是否成功运行或轰炸。但是当然有人知道你正在这样做并试图破坏它,他们可以创建具有这些名称的可执行文件。

你到底想保护的是什么?如果有人故意搞砸了你的应用程序的启动,然后它因为你试图运行不存在的命令而炸毁,那么适当的反应不是“如果你的脚受伤了,别再往自己的脚上开枪了”。如果您所做的只是运行操作系统命令,那么用户可能能够访问您的应用程序之外的命令,所以我看不出在操作系统上撒谎会如何损害安全性。

于 2009-08-18T16:25:05.817 回答
1

Apache commons-vfs抽象了一些处理,因此您可以只处理OsOsFamily。但是在内部,这仍然使用 System.getProperty(..) 来获取值。我不知道 JVM 获取这些值的任何其他方式。

如果有人能够更改传递给 JVM 的属性,那么与更改奇数属性相比,您需要处理更大的问题。

您能否详细说明安全的含义。从谁那里得到保障?

于 2009-08-18T12:44:49.513 回答
0

如果安全性是您的主要目标,您可能需要尝试添加一些 JNI。首先,如果您尝试在 linux 或 OS X 中加载 DLL,我敢打赌它会崩溃/无法加载。其次,您可以从该 DLL 调用操作系统的 API,因此您不受 Java 环境提供的限制。

于 2009-08-18T15:10:00.960 回答
0

除了使用您提到的属性外,VM 中没有公共 API。

我的希望是,由于 Java 应该是安全的,VM 会忽略从命令行覆盖这些值的任何尝试,但事实并非如此。

您可能会尝试使用不允许写入这些值的 SecurityManager,但我的猜测是,该值将只是空的(因为 VM 也无法更改它)。

于 2009-08-18T12:48:50.043 回答
0

您还可以通过 System.getenv() 检查环境变量,但用户也可以在启动之前自由修改这些变量。将变量作为命令行的一部分来启动应用程序并不那么容易(至少在 Windows 上)。

于 2009-08-18T14:08:17.220 回答
0

我同意 jni/jna 是一个更好的选择。只需调用 Native.loadLibrary("myLib") 之类的东西,在 Windows 上它将加载 myLib.dll,在 linux 上 - myLib.so。这是最安全的方法,不能乱来。将所有特定于操作系统的代码放入库中,并将编译后的版本保存在您的 jar 中。

于 2009-08-18T15:44:16.430 回答
0

现代 Linux 系统有一个特殊的文件/proc/version,它给出了内核版本 ID。因此,如果该文件与合适的模式匹配(例如,以单词 开头Linux),则增加了您在 Linux 计算机上运行的可能性。相反,如果它不存在或与该模式不匹配,您可以确定您没有在 Linux 上运行。

于 2013-03-22T12:19:52.820 回答
-2

为什么不结合以下内容并使用它进行插值,那样规避可能很困难。

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));
    }
}
于 2009-08-18T12:36:58.323 回答