我有一个从以前的系统备份的旧 Windows XP Mode vhd,但是在新系统上运行它时遇到了困难。
vhd 是一个差异磁盘,其父磁盘是标准 Windows XP Mode 基础;我仍然有旧的父磁盘,并且已经验证它与我新安装的 XP Mode 中的 XP Mode 基础二进制相同。
但在新系统中,差异磁盘父级的路径与旧系统不同。
当我打开旧 XP 模式的 .vmcx 设置并选择“硬盘 1”时,“虚拟硬盘文件”设置正确,但“父磁盘:”字段指向错误的路径,我看不到任何编辑它的方法。
有人知道怎么做吗?
答案1
好吧,我自己解决了这个问题,但是它太难了。
父路径以几种不同的格式硬编码在 .vhd 中;在我的例子中,有相对路径和绝对路径,以 ASCII 和 UTF-16 编码,总共 4 条路径。您可以获取 .vhd 文件格式规范来自 Microsoft。
但快速扫描字符串或者使用类似的实用程序(我在这里使用 Cygwin)来搜索字符串数据,可以比遍历 VHD 结构更容易地找到这些偏移量:
$ strings -t x Windows\ XP\ Mode-X.vhd | head -200 | grep vhd
40000 ..\..\..\..\..\..\..\other\xp-mode\Windows XP Mode base.vhd
40200 C:\other\xp-mode\Windows XP Mode base.vhd
$ strings -t x -e l Windows\ XP\ Mode-X.vhd | head -200 | grep vhd
40400 ..\..\..\..\..\..\..\other\xp-mode\Windows XP Mode base.vhd
40600 C:\other\xp-mode\Windows XP Mode base.vhd
这些字符串存储了大量空字节填充;我使用十六进制编辑器将路径更新到新位置。
但它不起作用;在 Virtual PC 设置编辑器中,路径显示为截断。字符串长度在其他地方编码;在我的情况下为 0x4AB、0x4C3 等。(我对旧路径长度进行了十六进制搜索)。我更新了这些长度,但 Virtual PC 设置编辑器认为 vhd 无效。我认为现在涉及校验和,下载了 VHD 规范,安装了 Word 文档查看器,以便我可以阅读 .doc,然后我编写了下面的程序。当将路径传递给 vhd 时,它的输出是更新的校验和以及校验和的位置;示例输出:
opening Windows XP Mode-X.vhd
dynamic header seems to be at 0x200
new checksum: ffffda30 at 0x224
使用十六进制编辑器输入这个新的校验和使得 vhd 工作,并且旧的 XP 模式成功启动。
校验和计算程序:
#include <fcntl.h>
#include <stdio.h>
#include <io.h>
#include <stdlib.h>
#include <unistd.h>
const int checksum_offset = 36;
long long flip_endian_ll(long long x)
{
return ((x & 0x00000000000000FFULL) << 56)
| ((x & 0x000000000000FF00ULL) << 40)
| ((x & 0x0000000000FF0000ULL) << 24)
| ((x & 0x00000000FF000000ULL) << 8)
| ((x & 0x000000FF00000000ULL) >> 8)
| ((x & 0x0000FF0000000000ULL) >> 24)
| ((x & 0x00FF000000000000ULL) >> 40)
| ((x & 0xFF00000000000000ULL) >> 56);
}
int die(const char *msg)
{
fprintf(stderr, "error: %s\n", msg);
exit(1);
}
int main(int argc, const char *const *args)
{
int f, r;
long long dyn_ofs;
unsigned char dyn_header[1024];
if (argc != 2)
{
fprintf(stderr, "expected: .vhd argument\n");
return 1;
}
printf("opening %s\n", args[1]);
f = open(args[1], O_RDONLY | O_BINARY);
if (f < 0)
die("failed open");
r = lseek(f, 0x10, SEEK_SET);
if (r < 0)
die("seek failed");
r = read(f, &dyn_ofs, sizeof(dyn_ofs));
if (r != 8)
die("failed read");
dyn_ofs = flip_endian_ll(dyn_ofs);
printf("dynamic header seems to be at 0x%llx\n", dyn_ofs);
r = lseek(f, dyn_ofs, SEEK_SET);
if (r < 0)
die("seek failed");
r = read(f, dyn_header, sizeof(dyn_header));
if (r != sizeof(dyn_header))
die("read of dynamic header failed");
if (memcmp("cxsparse", dyn_header, 8) != 0)
die("dynamic header didn't start with cxsparse");
{
unsigned long sum = 0;
int i;
for (i = checksum_offset; i < checksum_offset + 4; ++i)
dyn_header[i] = 0;
for (i = 0; i < sizeof(dyn_header); ++i)
sum += dyn_header[i];
sum = ~sum; // flip_endian_l(~sum);
printf("new checksum: %.8x at 0x%llx\n", sum, dyn_ofs + checksum_offset);
}
return 0;
}
这花了我几个小时,但最终我还是完成了。我相信最终会有人发布一个更容易完成此操作的 VHD 编辑器,但至少我再次进入了我的旧 XP 模式。
更新:有一种更简单的方法可以做到这一点,使用 PowerShell 编写一些 COM 对象脚本:https://obligatorymoniker.wordpress.com/2010/08/21/how-to-rebase-differencing-disk-vhds-or-how-to-change-the-parent-of-a-differencing-vhd-when-the-parent-has-moved/
在本质上:
$vpc=new-object -com VirtualPC.Application
$VHDChild = $vpc.GetHardDisk("C:\NAV2010.06.10_Diff")
$VHDParent = $vpc.GetHardDisk("\\devnas\Data_Backups SQLCluster1\NAV2010.06.10.vhd")
$VHDChild.Parent = $VHDParent
答案2
或者,如果您不想编写 C++ 代码来操作 .VHD 文件:
...从虚拟 PC 中,选择“虚拟磁盘向导”、“编辑现有虚拟磁盘”并选择有故障的差异磁盘,您将收到以下消息:
“父虚拟硬盘已被删除或已从其先前位置移走。请选择父虚拟硬盘的新位置。”
单击[更改...]并找到父磁盘。