我有一个.sav
VirtualBox 文件想要恢复。但问题是我只有这个文件(没有磁盘文件和 RAM 大小等任何其他信息)。我尝试复制我的一个虚拟机,制作快照并替换快照(使用正确的文件名)。恢复状态时,VirtualBox 抛出错误
无法加载单位‘mm’(VERR_SSM_LOAD_MEMORY_SIZE_MISMATCH)
由于我不知道内存大小,所以我无法继续。
经过谷歌搜索,我发现快照文件包含虚拟机的所有信息和配置。有没有可能从文件中提取信息,.sav
以便我获得正确的配置?
答案1
以下内容与本文撰写时的最新版本 VirtualBox 有关(4.3)。我无法代表旧版本。
文件SAV
由 组成units
。如果您SAV
在十六进制编辑器中打开文件,则可以通过搜索以下十六进制字符串来浏览各个单元:
0A 55 6E 69 74 0A 00 00
这是单词Unit
及其周围的一些其他字符。命中后 0x24(36)个字节,您将看到一些 ASCII 字符。例如,第一个字符可能是:SSM
。这是单元的描述符,在本例中为“已保存状态管理器”。
您想找到mm
(内存管理器)单元。对我来说,它一直是 SAV 文件中的第三个单元 - 因此搜索时的第三个结果:
十六进制:
0A 55 6E 69 74 0A 00 00 52 01 00 00 00 00 00 00 7C 36 11 91 CE B0 E2
CE 02 00 00 00 01 00 00 00 FF FF FF FF 00 00 00 00 03 00 00 00 6D 6D
00 92 10 1E 00 08 00 00 00 00 00 00 00 00 80 00 00 00 00 91 0E 01 00
38 7E D4 06 22 00 00 00 00 00 00 00
如您所见,前 8 个字节是unit
标头。然后,0x24(36)字节后,我们看到6D 6D 00
的是mm\0
。跳过三个字节(92 10 1E
),然后您得到一个 uint16(小端),这是拍摄快照时的系统内存量。在我的示例中:00 08
= 0x800
= 2048
= 2GB
。
答案2
您可以尝试执行vboxmanage 采用状态根据文档,它将尝试更改虚拟机以将当前快照附加到建议的保存状态。
如果它不起作用,parsiya 写了一篇关于解析 SAV 状态的有趣博客,可以在这里找到:帕西亚博客
根据他的博客,SAVE 状态的描述如下安全管理器
我发现的新信息基于 SSMFILEHDRV12(比 parsiya 更新)RTGCPHYS 的单位是 GIM_HV_PAGE_SIZE(4096)。如果我理解正确的话,它更像是一个单位,通常是 08 * 4096。事实上,对于进一步创建的数据,还有另一个单位
如果我理解正确的话,SSM.cpp 代码的逻辑正如开头所解释的那样,是执行实时保存状态。也就是说,不知道总大小。因此可能会记录多个内存单元。如果只有一个原始内存单元,那么,是的,您可以推断出虚拟机的大小。里程各不相同
从文件开头提取
* The live snapshots feature (LS) is similar to teleportation (TP) and was a
* natural first step when implementing TP. The main differences between LS and
* TP are that after a live snapshot we will have a saved state file, disk image
* snapshots, and the VM will still be running.
* * Compared to normal saved stated and snapshots, the difference is in that the
* VM is running while we do most of the saving. Prior to LS, there was only
* one round of callbacks during saving and the VM was paused during it. With
* LS there are 1 or more passes while the VM is still running and a final one
* after it has been paused. The runtime passes are executed on a dedicated
* thread running at at the same priority as the EMTs so that the saving doesn't
* starve or lose in scheduling questions (note: not implemented yet). The final
* pass is done on EMT(0).
* The saved state units each starts with a variable sized header
* (SSMFILEUNITHDRV2) that contains the name, instance and pass. The data
* follows the header and is encoded as records with a 2-8 byte record header
* indicating the type, flags and size. The first byte in the record header
* indicates the type and flags:
* * - bits 0..3: Record type:
* - type 0: Invalid.
* - type 1: Terminator with CRC-32 and unit size.
* - type 2: Raw data record.
* - type 3: Raw data compressed by LZF. The data is prefixed by a 8-bit
* field containing the length of the uncompressed data given in
* 1KB units.
* - type 4: Zero data. The record header is followed by a 8-bit field
* counting the length of the zero data given in 1KB units.
* - type 5: Named data - length prefixed name followed by the data. This
* type is not implemented yet as we're missing the API part, so
* the type assignment is tentative.
* - types 6 thru 15 are current undefined.
* - bit 4: Important (set), can be skipped (clear).
* - bit 5: Undefined flag, must be zero.
* - bit 6: Undefined flag, must be zero.
* - bit 7: "magic" bit, always set.
/**
* Writes a record header for the specified amount of data.
*
* @returns VBox status code. Sets pSSM->rc on failure.
* @param pSSM The saved state handle
* @param cb The amount of data.
* @param u8TypeAndFlags The record type and flags.
*/
static int ssmR3DataWriteRecHdr(PSSMHANDLE pSSM, size_t cb, uint8_t u8TypeAndFlags)
{
size_t cbHdr;
uint8_t abHdr[8];
abHdr[0] = u8TypeAndFlags;
if (cb < 0x80)
{
cbHdr = 2;
abHdr[1] = (uint8_t)cb;
}
else if (cb < 0x00000800)
{
cbHdr = 3;
abHdr[1] = (uint8_t)(0xc0 | (cb >> 6));
abHdr[2] = (uint8_t)(0x80 | (cb & 0x3f));
}
else if (cb < 0x00010000)
{
cbHdr = 4;
abHdr[1] = (uint8_t)(0xe0 | (cb >> 12));
abHdr[2] = (uint8_t)(0x80 | ((cb >> 6) & 0x3f));
abHdr[3] = (uint8_t)(0x80 | (cb & 0x3f));
}
else if (cb < 0x00200000)
{
cbHdr = 5;
abHdr[1] = (uint8_t)(0xf0 | (cb >> 18));
abHdr[2] = (uint8_t)(0x80 | ((cb >> 12) & 0x3f));
abHdr[3] = (uint8_t)(0x80 | ((cb >> 6) & 0x3f));
abHdr[4] = (uint8_t)(0x80 | (cb & 0x3f));
}
else if (cb < 0x04000000)
{
cbHdr = 6;
abHdr[1] = (uint8_t)(0xf8 | (cb >> 24));
abHdr[2] = (uint8_t)(0x80 | ((cb >> 18) & 0x3f));
abHdr[3] = (uint8_t)(0x80 | ((cb >> 12) & 0x3f));
abHdr[4] = (uint8_t)(0x80 | ((cb >> 6) & 0x3f));
abHdr[5] = (uint8_t)(0x80 | (cb & 0x3f));
}
else if (cb <= 0x7fffffff)
{
cbHdr = 7;
abHdr[1] = (uint8_t)(0xfc | (cb >> 30));
abHdr[2] = (uint8_t)(0x80 | ((cb >> 24) & 0x3f));
abHdr[3] = (uint8_t)(0x80 | ((cb >> 18) & 0x3f));
abHdr[4] = (uint8_t)(0x80 | ((cb >> 12) & 0x3f));
abHdr[5] = (uint8_t)(0x80 | ((cb >> 6) & 0x3f));
abHdr[6] = (uint8_t)(0x80 | (cb & 0x3f));
}
else
AssertLogRelMsgFailedReturn(("cb=%#x\n", cb), pSSM->rc = VERR_SSM_MEM_TOO_BIG);
Log3(("ssmR3DataWriteRecHdr: %08llx|%08llx/%08x: Type=%02x fImportant=%RTbool cbHdr=%u\n",
ssmR3StrmTell(&pSSM->Strm) + cbHdr, pSSM->offUnit + cbHdr, cb, u8TypeAndFlags & SSM_REC_TYPE_MASK, !!(u8TypeAndFlags & SSM_REC_FLAGS_IMPORTANT), cbHdr));
return ssmR3DataWriteRaw(pSSM, &abHdr[0], cbHdr);
}
和 Bridgey 一样,他还注意到,Units 以 ASCII 码“Unit”开头,但最后一个单元以“TheEnd”结尾
他根据这里SSMInternal.h中描述的UNIT的结构解析了SAV文件的一些结构:virtualbox 开源标头