1

У меня есть файл .sav VirtualBox, который я хочу восстановить. Но проблема в том, что у меня есть только этот файл (без какой-либо другой информации о файле диска, размере ОЗУ и т.д.). Я попытался скопировать одну из моих виртуальных машин, сделал снимок и заменил снимок (с правильным именем файла). А при восстановлении состояния VirtualBox выкинул ошибку

Не удалось загрузить единицу измерения «мм» (VERR_SSM_LOAD_MEMORY_SIZE_MISMATCH)

Так как я не знаю объем памяти, я не могу двигаться дальше.

После поиска в Google я обнаружил, что файл снимка содержит всю информацию и конфигурацию виртуальной машины. Есть ли какой-нибудь возможный способ извлечь информацию из файла .sav чтобы я мог получить правильную конфигурацию?

2 ответа2

4

Нижеследующее относится к самой последней версии 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 (little-endian), который представляет собой объем системной памяти на момент создания снимка. В моем примере: 00 08 = 0x800 = 2048 = 2GB .

0

Вы можете попытаться выполнить состояние принятия vboxmanage, которое, согласно документации, попытается изменить виртуальную машину, чтобы прикрепить текущий снимок к предлагаемому состоянию сохранения.

Если это не сработало, parsiya сделал интересный блог о разборе состояния SAV, который можно найти здесь: parsiya blog

Согласно его блогу, состояние SAVE описано в SSM.cpp

Новая информация, которую я нашел, основана на SSMFILEHDRV12 (более поздняя, чем парсия). Блок 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);
}

Он также заметил, как и Бриджи, что Юниты начинаются с ascii "Юнит", но также и то, что последний юнит заканчивается "TheEnd"

Он проанализировал некоторые структуры SAV-файла на основе структуры UNIT, описанной в SSMInternal.h здесь: virtualbox opensource header

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .