У меня есть .sav
файл VirtualBox, который я хочу восстановить. Но проблема в том, что у меня есть только этот файл (без какой-либо другой информации о файле на диске и размере ОЗУ и т. д.). Я попытался продублировать одну из моих виртуальных машин, сделал снимок и заменил снимок (с правильным именем файла). И при восстановлении состояния VirtualBox выдал ошибку
Не удалось загрузить единицу измерения «мм» (VERR_SSM_LOAD_MEMORY_SIZE_MISMATCH)
Поскольку я не знаю объёма памяти, я не могу двигаться дальше.
После поиска в Google я нахожу файл снимка, содержащий всю информацию и конфигурацию виртуальной машины. Есть ли какой-либо возможный способ извлечь информацию из .sav
файла, чтобы я мог получить правильную конфигурацию?
решение1
Следующее относится к последней версии VirtualBox на момент написания статьи (4.3). Я не могу говорить о более старых версиях.
Файл SAV
состоит из units
. Если вы откроете SAV
файл в шестнадцатеричном редакторе, вы сможете перемещаться по единицам, выполняя поиск следующей шестнадцатеричной строки:
0A 55 6E 69 74 0A 00 00
Это слово Unit
с некоторыми другими символами вокруг него. Через 0x24 (36) байт после попадания вы увидите несколько символов ASCII. Например, первым, вероятно, будет: SSM
. Это дескриптор для блока, в данном случае «Saved State Manager».
Вы хотите найти 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
.
решение2
Вы можете попробовать выполнитьvboxmanage принимает состояние, который, согласно документации, попытается изменить виртуальную машину, чтобы прикрепить текущий снимок к предлагаемому состоянию сохранения.
Если это не сработает, parsiya сделал интересный блог о разборе состояния SAV, который можно найти здесь:блог парсия
Согласно его блогу, состояние SAVE описано вSSM.cpp
Новая информация, которую я нашел, основана на 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);
}
Он также заметил, как и Бриджит, что единицы начинаются с ascii "Unit", но также и то, что последняя единица заканчивается "TheEnd"
Он проанализировал часть структуры файла SAV на основе структуры UNIT, описанной в SSMInternal.h здесь:заголовок virtualbox с открытым исходным кодом