从 virtualbox .sav 文件中提取信息?

从 virtualbox .sav 文件中提取信息?

我有一个.savVirtualBox 文件想要恢复。但问题是我只有这个文件(没有磁盘文件和 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 开源标头

相关内容