2

I have a .sav file of VirtualBox which I want to restore. But the problem is I only have this file (without any other info of the disk file and RAM size and so on). I tried to duplicate one of my virtual machines, made a snapshot and replaced the snapshot(with right filename). And when restoring the state, VirtualBox threw an error

Failed to load unit 'mm' (VERR_SSM_LOAD_MEMORY_SIZE_MISMATCH)

Since I don't know the memory size I can not move on.

After googling I find a snapshot file contains all the info and config of a virtual machine. Is there any possible way to extract info from a .sav file so that I can get the right configuration?

2 Answers 2

4

The following relates to the most recent version of VirtualBox at the time of writing (4.3). I can't speak for older versions.

A SAV file is made up of units. If you open your SAV file in a hex editor you can navigate through the units by searching for the following hex string:

0A 55 6E 69 74 0A 00 00

This is the word Unit with some other characters around it. 0x24 (36) bytes after the hit, you'll see some ASCII characters. For example, the first one will probably be: SSM. This is the descriptor for the unit, in this case the 'Saved State Manager'.

You want to find the mm (Memory Manager) unit. For me, it's always been the third unit in the SAV file - so the third result when searching:

In hex:

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

As you can see, the first 8 bytes are the unit header. Then, 0x24 (36) bytes later we see 6D 6D 00 which is mm\0. Skip three bytes (92 10 1E) then you have a uint16 (little endian) which is the amount of system memory at the time the snapshot was taken. In my example: 00 08 = 0x800 = 2048 = 2GB.

0

You can try to execute the vboxmanage adopt state, which , according to the documentation, it will try to change the VM to attach the current snapshot to the proposed save state.

If it doesnt work, parsiya did an interesting blog about parsing the SAV state which can be found here: parsiya blog

According to his blog, the SAVE state is described in SSM.cpp

The new information I found is based on SSMFILEHDRV12 (more recent than parsiya) The unit of RTGCPHYS is in GIM_HV_PAGE_SIZE (4096). It is more a unit and is generaly 08 * 4096 if I understood correctly. In fact there is another Unit for the data created further done

If I understood correctly the logic of the SSM.cpp code as explained at the beggining is to perform live saving state. IE the total size is not known. So there could be multiple unit of memory recorded. If there is only one unit of raw memory, then, yes, you could deduce the size of the VM. Mileage varies

Extract from the beggining of the file

 * 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);
}

He also noticed, like Bridgey, that Units start with ascii "Unit", but also that the last unit finish by "TheEnd"

He parsed some of the structure of the SAV file based on the structure of the UNIT described in SSMInternal.h here: virtualbox opensource header

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .