Skip to content

Commit

Permalink
Add support for Veeam Backup files (VBK)
Browse files Browse the repository at this point in the history
  • Loading branch information
Schamper committed Feb 27, 2024
1 parent a3ea169 commit 76ca0ff
Show file tree
Hide file tree
Showing 11 changed files with 1,627 additions and 178 deletions.
3 changes: 2 additions & 1 deletion dissect/hypervisor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dissect.hypervisor.backup import vma, xva
from dissect.hypervisor.backup import vbk, vma, xva
from dissect.hypervisor.descriptor import hyperv, ovf, pvs, vbox, vmx
from dissect.hypervisor.disk import hdd, qcow2, vdi, vhd, vhdx, vmdk
from dissect.hypervisor.util import envelope, vmtar
Expand All @@ -10,6 +10,7 @@
"ovf",
"pvs",
"qcow2",
"vbk",
"vbox",
"vdi",
"vhd",
Expand Down
287 changes: 287 additions & 0 deletions dissect/hypervisor/backup/c_vbk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
from dissect.cstruct import cstruct

vbk_def = """
#define PAGE_SIZE 4096
/* Storage header */
struct StorageHeader {
uint32 FormatVersion; /* 0x0000 */
uint32 Initialized; /* 0x0004 */
uint32 DigestTypeLength; /* 0x0008 */
char DigestType[251]; /* 0x000C */
uint32 SnapshotSlotFormat; /* 0x0107 format > 5 -> crc32c */
uint32 StandardBlockSize; /* 0x010B */
uint8 ClusterAlign; /* 0x010F */
char Unk0[16]; /* 0x0120 */
char ExternalStorageId[16]; /* 0x0130 */
};
/* Snapshot header */
struct SnapshotSlotHeader {
uint32 CRC;
uint32 ContainsSnapshot;
};
struct DirectoryRootRecord {
int64 RootPage; /* Root page of the directory */
uint64 Count; /* Number of children */
};
struct BlocksStoreHeader {
int64 RootPage; /* Root of the blocks store */
uint64 Count; /* Number of blocks store entries */
int64 FreeRootPage; /* Root of the free blocks tree */
int64 DeduplicationRootPage; /* Root of the deduplication tree */
int64 Unk0;
int64 Unk1;
};
struct CryptoStoreRecord {
int64 RootPage; /* Root of the crypto store */
};
struct SnapshotDescriptor {
uint64 Version; /* Acts as a sequence number, highest is active slot */
uint64 StorageEOF; /* End of file, aka file size */
uint32 BanksCount; /* Number of banks */
DirectoryRootRecord DirectoryRoot; /* Directory root record */
BlocksStoreHeader BlocksStore; /* Blocks store header */
CryptoStoreRecord CryptoStore; /* Crypto store record */
uint64 Unk0;
uint64 Unk1;
};
struct BankDescriptor {
uint32 CRC;
uint64 Offset;
uint32 Size;
};
struct BanksGrain {
uint32 MaxBanks;
uint32 StoredBanks;
// BankDescriptor Banks[StoredBanks];
};
/* Block headers */
struct BankHeader {
uint16 PageCount;
uint16 Flags;
char Unk0[3064];
uint64 Unk1;
char Unk2[1020];
};
struct BankHeaderV71 {
uint16 PageCount;
uint16 Flags; /* 2 == encrypted */
char Unk0[3072];
char KeySetId[16];
char Unk1[16];
char Unk2[16];
uint32 Unk3;
char Unk4[968];
};
struct MetaBlobHeader {
int64 NextPage;
int32 Unk0;
};
struct Lz4BlockHeader {
uint32 Magic; /* 0xF800000F */
uint32 CRC; /* CRC32C of the compressed data */
uint32 SourceSize;
};
/* DirItem headers */
struct BlocksVectorHeader {
uint64 RootPage;
uint64 Count;
};
struct SubFolderHeader {
uint64 RootPage; /* 0x94 */
uint32 Count; /* 0x9C */
char Data[32]; /* 0xA0 */
}; /* 0xC0 */
struct ExtFibHeader {
uint16 UpdateInProgress; /* 0x94 */
uint8 Unk3; /* 0x96 */
uint8 Format; /* 0x97 Bit 3 == 1 */
BlocksVectorHeader BlocksVector; /* 0x98 */
uint64 FibSize; /* 0xA8 */
uint64 Size; /* 0xB0 */
uint8 FsObjAttachState; /* 0xB8 */
char Data[7]; /* 0xB9 */
}; /* 0xC0 */
struct IntFibHeader {
uint16 UpdateInProgress; /* 0x94 */
uint8 Unk3; /* 0x96 */
uint8 Format; /* 0x97 Bit 3 == 1 */
BlocksVectorHeader BlocksVector; /* 0x98 */
uint64 FibSize; /* 0xA8 */
uint64 Size; /* 0xB0 */
uint8 FsObjAttachState; /* 0xB8 */
char Data[7]; /* 0xB9 */
}; /* 0xC0 */
struct PatchHeader {
uint32 Unk0; /* 0x94 */
BlocksVectorHeader BlocksVector; /* 0x98 */
uint64 FibSize; /* 0xA8 Source file size */
uint64 Unk4; /* 0xB0 */
char Data[8]; /* 0xB8 */
}; /* 0xC0 */
struct IncrementHeader {
uint32 Unk0; /* 0x94 */
BlocksVectorHeader BlocksVector; /* 0x98 */
uint64 FibSize; /* 0xA8 Original FIB size */
uint64 Unk4; /* 0xB0 */
char Data[8]; /* 0xB8 */
}; /* 0xC0 */
enum DirItemType : uint32 {
None = 0,
SubFolder = 1,
ExtFib = 2,
IntFib = 3,
Patch = 4,
Increment = 5,
};
struct DirItemRecord {
DirItemType Type; /* 0x00 */
uint32 NameLength; /* 0x04 */
char Name[128]; /* 0x08 */
int64 PropsRootPage; /* 0x88 */
uint32 Unk1; /* 0x90 */
union { /* 0x94 */
char Data[44];
SubFolderHeader SubFolder;
ExtFibHeader ExtFib;
IntFibHeader IntFib;
PatchHeader Patch;
IncrementHeader Increment;
};
};
/* Block descriptors */
flag BlockFlags : uint8 {
None = 0x00,
Updated = 0x01,
CommitInProgress = 0x02,
};
enum BlockLocationType : uint8 {
Normal = 0x00,
Sparse = 0x01,
Reserved = 0x02,
Archived = 0x03, /* CompressedSize | (CompressionType << 32) */
BlockInBlob = 0x04, /* BlockId? & 0x3FFFFFF | (BlobId << 26) | ((Offset >> 9) << 42) */
BlockInBlobReserved = 0x05, /* BlockId? | 0xFFFFFFFFFC000000 */
};
enum CompressionType : int8 {
Plain = -1,
RL = 2,
ZLH = 3,
ZLL = 4,
LZ4 = 7,
};
struct MetaTableDescriptor {
int64 RootPage;
uint64 BlockSize;
uint64 Count;
};
struct StgBlockDescriptor {
uint8 Format; /* Format != 4 == legacy */
uint32 UsageCounter;
uint64 Offset;
uint32 AllocatedSize;
uint8 Deduplication;
char Digest[16];
CompressionType CompressionType;
uint8 Unk0;
uint32 CompressedSize;
uint32 SourceSize;
};
struct StgBlockDescriptorV7 {
uint8 Format; /* Format != 4 == legacy */
uint32 UsageCounter;
uint64 Offset;
uint32 AllocatedSize;
uint8 Deduplication;
char Digest[16];
CompressionType CompressionType;
uint8 Unk0;
uint32 CompressedSize;
uint32 SourceSize;
char KeySetId[16];
};
struct FibBlockDescriptor {
uint32 BlockSize;
BlockLocationType Type;
char Digest[16];
// union {
// struct {
// uint32 ArchiveUsedSize;
// uint8 ArchiveCompressionType;
// uint8 Unk3;
// uint16 Unk4;
// } Archived;
// uint64 Offset;
// };
uint64 BlockId; /* For performance reasons we just put a uint64 here, but this is actually a union */
BlockFlags Flags;
};
struct FibBlockDescriptorV7 {
uint32 BlockSize;
BlockLocationType Type;
char Digest[16];
// union {
// struct {
// uint32 ArchiveUsedSize;
// uint8 ArchiveCompressionType;
// uint8 Unk3;
// uint16 Unk4;
// } Archived;
// uint64 Offset;
// };
uint64 BlockId; /* For performance reasons we just put a uint64 here, but this is actually a union */
BlockFlags Flags;
char KeySetId[16];
};
struct PatchBlockDescriptor {
};
struct PatchBlockDescriptorV7 {
};
/* Property dictionary */
enum PropertyType : int32 {
UInt32 = 1,
UInt64 = 2,
AString = 3,
WString = 4,
Binary = 5,
Boolean = 6,
End = -1,
};
""" # noqa: E501
c_vbk = cstruct()
c_vbk.load(vbk_def)
Loading

0 comments on commit 76ca0ff

Please sign in to comment.