Virtualbox,如何强制客户机使用特定的 CPU

Virtualbox,如何强制客户机使用特定的 CPU

我在 VirtualBox 中有一个 XP 客户机,Windows 8 主机。客户机显示的处理器与主机 (i5 2500k) 完全一样。但是大多数安装程序无法识别此处理器,并且无法继续显示不支持的处理器。

有没有办法欺骗客户机,让其认为这是旧处理器?如果我没记错的话,VMWare 有一个 CPU 屏蔽功能,virtualbox 中也有类似的功能吗?

答案1

VirtualBox 和 CPUID 基础知识

您需要设置VBoxInternal/CPUM/HostCPUID虚拟机的 extradata。这将使 VirtualBox 报告自定义结果CPUID指令给客户机。根据 EAX 寄存器的值,此指令返回有关处理器的信息 - 比如供应商、类型、系列、步进、品牌、缓存大小、功能(MMX、SSE、SSE2、PAE、HTT)等。您弄乱的结果越多,欺骗客户机的机会就越大。

您可以使用vboxmanage setextradata命令来配置虚拟机。例如,

vboxmanage setextradata WinXP VBoxInternal/CPUM/HostCPUID/80000003/ebx 0x50202952

当将 EAX 设置为 80000003₍₁₆₎ 时,将使 CPUID 在 EBX 寄存器中返回 50202952₍₁₆₎。(从现在开始,十六进制数将写为 0xNN 或 NNh。)

设置 CPU 供应商字符串

如果 EAX 为 0(或 AMD 上的 80000000h),则 CPUID 会在寄存器 EBX、EDX、ECX(注意顺序)中以 ASCII 字符串的形式返回供应商。对于 AMD CPU,它们看起来像这样:

| Register | Value      | Description                    |
|----------|------------|--------------------------------|
| EBX      | 6874_7541h | The ASCII characters "h t u A" |
| ECX      | 444D_4163h | The ASCII characters "D M A c" |
| EDX      | 6974_6E65h | The ASCII characters "i t n e" |

(取自AMD CPUID 规格,“CPUID Fn0000_0000_E”小节)

如果将 EBX、EDX 和 ECX 连接起来,您将得到AuthenticAMD

如果您有 Bash 和传统的 Unix 实用程序,则可以使用以下命令轻松设置供应商:

vm='WinXP'  # UUID works as well
# The vendor string needs to have 12 characters!
vendor='AuthenticAMD'
if [ ${#vendor} -ne 12 ]; then
    exit 1
fi
ascii2hex() { echo -n 0x; od -A n --endian little -t x4 | sed 's/ //g'; }

registers=(ebx edx ecx)
for (( i=0; i<${#vendor}; i+=4 )); do
    register=${registers[$(($i/4))]}
    value=`echo -n "${vendor:$i:4}" | ascii2hex`
    # set value to an empty string to reset the CPUID, i.e.
    # value=""
    for eax in 00000000 80000000; do
        key=VBoxInternal/CPUM/HostCPUID/${eax}/${register}
        vboxmanage setextradata "$vm" $key $value
    done
done

设置 CPU 品牌字符串

如果 EAX 为 80000002h、80000003h、80000004h,则 CPUID 在寄存器 EAX、EBX、ECX、EDX 中返回品牌字符串的 16 个 ASCII 字符,共计 3 * 16 = 48 个字符;该字符串为以空字符结束。请注意,此功能是 Pentium 4 处理器引入的。Pentium 4 处理器上的品牌字符串如下所示:

| EAX Input Value | Return Values   | ASCII Equivalent |
|-----------------|-----------------|------------------|
| 80000002h       | EAX = 20202020h | "    "           |
|                 | EBX = 20202020h | "    "           |
|                 | ECX = 20202020h | "    "           |
|                 | EDX = 6E492020h | "nI  "           |
|-----------------|-----------------|------------------|
| 80000003h       | EAX = 286C6574h | "(let"           |
|                 | EBX = 50202952h | "P )R"           |
|                 | ECX = 69746E65h | "itne"           |
|                 | EDX = 52286D75h | "R(mu"           |
|-----------------|-----------------|------------------|
| 80000004h       | EAX = 20342029h | " 4 )"           |
|                 | EBX = 20555043h | " UPC"           |
|                 | ECX = 30303531h | "0051"           |
|                 | EDX = 007A484Dh | "☠zHM"           |
|-----------------|-----------------|------------------|

(取自Intel 架构指令集扩展编程参考,第 2.9 节“CPUID 指令”,表 2-30。☠ 是空字符(数值0)。)

如果将结果放在一起,您将获得Intel(R) Pentium(R) 4 CPU 1500MHz☠

如果您有 Bash 和传统的 Unix 实用程序,则可以使用以下命令轻松设置品牌:

vm='WinXP'  # UUID works as well
# The brand string needs to have 47 characters!
# The null terminator is added automatically
brand='              Intel(R) Pentium(R) 4 CPU 1500MHz'
if [ ${#brand} -ne 47 ]; then
    exit 1
fi
ascii2hex() { echo -n 0x; od -A n --endian little -t x4 | sed 's/ //g'; }

eax_values=(80000002 80000003 80000004)
registers=(edx ecx ebx eax)
for (( i=0; i<${#brand}; i+=4 )); do
    eax=${eax_values[$((${i} / 4 / 4))]}
    register=${registers[$((${i} / 4 % 4 ))]}
    key=VBoxInternal/CPUM/HostCPUID/${eax}/${register}
    value=`echo -n "${brand:$i:4}" | ascii2hex`
    # set value to an empty string to reset the CPUID, i.e.
    # value=""
    vboxmanage setextradata "$vm" $key $value
done

如果您有 Windows 命令提示符,则可以通过运行以下命令将品牌设置为Intel(R) Core(TM)2 CPU 6600 @ 2.40 GHz1 :

set vm=your-vm-name-or-uuid
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/eax 0x65746e49
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/ebx 0x2952286c
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/ecx 0x726f4320
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000002/edx 0x4d542865
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/eax 0x43203229
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/ebx 0x20205550
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/ecx 0x20202020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000003/edx 0x20202020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/eax 0x30303636
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/ebx 0x20402020
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/ecx 0x30342e32
vboxmanage setextradata %vm% VBoxInternal/CPUM/HostCPUID/80000004/edx 0x007a4847

计算机:Intel(R) Core(TM)2 CPU 6600 @ 2.40 GHz

1这些HostCPUID值取自 VirtualBox 错误报告#7865

答案2

这是一种可以将主机 CPU 精确伪装成特定 CPU 的方法,而不必尝试猜测必要的设置。您需要访问在该主机 CPU 上运行 VirtualBox 的机器,以便转储其cpuid寄存器(最好选择与您的实际 CPU 相当相似的架构作为模型)。如果您手边没有,可以四处询问(例如,我在 Reddit 上成功过)。

  1. 从您想要模拟的 CPU 创建一个“模型”文件:

    vboxmanage list hostcpuids > i7_6600U
    
  2. 在目标主机上,确保要修改的虚拟机未运行;您可能需要进行备份以防万一。
  3. 运行以下脚本将模型文件(i7_6600U此处)加载到 VBox VM 的定义中(my_vm_name此处):

    #!/bin/bash
    vm=my_vm_name
    model_file=i7_6600U
    
    egrep -e '^[[:digit:]abcdef]{8} ' $model_file |
    while read -r line; do
        leaf="0x`echo $line | cut -f1 -d' '`"
        # VBox doesn't like applying leaves between the below boundaries so skip those:
        if [[ $leaf -lt 0x0b || $leaf -gt 0x17 ]]; then
            echo "Applying: $line"
            vboxmanage modifyvm $vm --cpuidset $line
        fi
    done
    
  4. 就这样,您现在可以运行您的 VM 并享受伪装的 CPU(注意:您只需运行上述脚本一次)。

如果您需要回滚 CPU 伪装,则可以使用vboxmanage modifyvm $vm --cpuidremove $leaf上述循环中的每个叶子(man vboxmanage是您的朋友)。

对我来说,这已经完美地运行了几个月,在运行 VBox 5.1.22 的 Ubuntu 17.04 主机上将 Kaby Lake CPU(i7_7500U)伪装成 Skylake CPU(i7_6600U)。该方法应该适用于任何主机操作系统,只要您可以为该操作系统创建上述小 bash 脚本的等效项即可。

相关内容