Task 789: .VRB File Format
Task 789: .VRB File Format
File Format Specifications for the .VRB File Format
The .VRB file format is associated with Veeam Backup & Replication software and represents a reverse incremental backup file. It is a proprietary binary format designed to store incremental changes to disk images in reverse order, used specifically in the reverse incremental backup method. The file contains compressed and deduplicated backup data, along with embedded XML metadata blocks in plain text format. These metadata blocks, identified by the root element, provide details about the backup job, restore points, objects, hosts, and extractable files. Multiple blocks may exist in a single file, with the last instance containing the most updated information. No public, detailed byte-level specification or magic header is available, as the format is not openly documented by Veeam. Parsing involves searching for the XML metadata within the binary content.
List of all the properties of this file format intrinsic to its file system:
- Backup: Details about the backup job or policy, including statistics.
- Point: Information on the restore point, such as creation time and state.
- Storage: Metadata about the storage file itself, including backup sizes, compression ratios, and deduplication metrics.
- OIB: Object In Backup details, including attributes like VmName.
- Object: Metadata for the backed-up object, such as Name and Id.
- TargetHost: Host receiving the backup, with attributes like Id, Name, Type, Description, Ip, Reference, Info, ApiVersion, PhysHostId, CredsId, DnsName, HostInstanceId, and HostUniqueId.
- PrevFileName: Path to the previous file in the backup chain.
- BackupVersion: Version of the backup format (e.g., 2).
- OibFiles: List of extractable files, each with attributes including FileName, Size, LastModification, Exist, IsPassThroughDisk, IsDiskProcessing, and PlatformDetails.
- LogBackupInfo: Information related to log backups (may be empty).
- SourceHost: Details about the source host, with attributes like Id, Name, Description, Ip, Type, Reference, Info, ApiVersion, PhysHostId, CredsId, DnsName, HostInstanceId, and HostUniqueId.
Two direct download links for files of format .VRB:
No public direct download links for .VRB files were identified through extensive searches. These files typically contain sensitive backup data from virtual machines and are not shared publicly to avoid security risks. Veeam does not provide sample .VRB files, and no repositories or documentation sites offer them for download.
Ghost blog embedded HTML JavaScript for drag-and-drop .VRB file dumping:
- Python class for .VRB file handling:
import xml.etree.ElementTree as ET
class VRBFile:
def __init__(self, filepath):
self.filepath = filepath
self.properties = {}
def decode_read(self):
with open(self.filepath, 'rb') as f:
data = f.read()
start = b'<OibSummary>'
end = b'</OibSummary>'
positions = []
pos = 0
while True:
pos = data.find(start, pos)
if pos == -1:
break
end_pos = data.find(end, pos) + len(end)
block = data[pos:end_pos]
positions.append(block)
pos = end_pos
if not positions:
raise ValueError("No OibSummary found in the file.")
last_block = positions[-1].decode('utf-8')
root = ET.fromstring(last_block)
tags = ['Backup', 'Point', 'Storage', 'OIB', 'Object', 'TargetHost', 'PrevFileName', 'BackupVersion', 'LogBackupInfo', 'SourceHost']
for tag in tags:
elem = root.find(tag)
if elem is not None:
if tag in ['PrevFileName', 'BackupVersion', 'LogBackupInfo']:
self.properties[tag] = elem.text
else:
self.properties[tag] = {attr: value for attr, value in elem.attrib.items()}
oibfiles = root.find('OibFiles')
if oibfiles is not None:
self.properties['OibFiles'] = [{attr: value for attr, value in file.attrib.items()} for file in oibfiles.findall('File')]
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filepath, new_properties=None):
if new_properties:
self.properties = new_properties
root = ET.Element('OibSummary')
for key, value in self.properties.items():
if key in ['PrevFileName', 'BackupVersion', 'LogBackupInfo']:
sub = ET.SubElement(root, key)
sub.text = value
elif key == 'OibFiles':
oibfiles = ET.SubElement(root, 'OibFiles')
for file_props in value:
file_elem = ET.SubElement(oibfiles, 'File')
for attr, val in file_props.items():
file_elem.set(attr, val)
else:
sub = ET.SubElement(root, key)
for attr, val in value.items():
sub.set(attr, val)
xml_data = ET.tostring(root, encoding='utf-8')
# Note: This writes only the metadata XML, not a full .VRB file, as the complete binary structure is proprietary.
with open(new_filepath, 'wb') as f:
f.write(xml_data)
# Example usage:
# vrb = VRBFile('example.vrb')
# vrb.decode_read()
# vrb.print_properties()
# vrb.write('new.vrb')
- Java class for .VRB file handling:
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
public class VRBFile {
private String filepath;
private Map<String, Object> properties = new HashMap<>();
public VRBFile(String filepath) {
this.filepath = filepath;
}
public void decodeRead() throws Exception {
byte[] data = Files.readAllBytes(Paths.get(filepath));
byte[] start = "<OibSummary>".getBytes("UTF-8");
byte[] end = "</OibSummary>".getBytes("UTF-8");
List<byte[]> positions = new ArrayList<>();
int pos = 0;
while (true) {
pos = findBytes(data, start, pos);
if (pos == -1) break;
int endPos = findBytes(data, end, pos) + end.length;
byte[] block = Arrays.copyOfRange(data, pos, endPos);
positions.add(block);
pos = endPos;
}
if (positions.isEmpty()) {
throw new IllegalArgumentException("No OibSummary found in the file.");
}
byte[] lastBlock = positions.get(positions.size() - 1);
String lastBlockStr = new String(lastBlock, "UTF-8");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = new ByteArrayInputStream(lastBlockStr.getBytes("UTF-8"));
Document doc = builder.parse(is);
String[] tags = {"Backup", "Point", "Storage", "OIB", "Object", "TargetHost", "PrevFileName", "BackupVersion", "LogBackupInfo", "SourceHost"};
for (String tag : tags) {
NodeList nodes = doc.getElementsByTagName(tag);
if (nodes.getLength() > 0) {
Element elem = (Element) nodes.item(0);
if (Arrays.asList("PrevFileName", "BackupVersion", "LogBackupInfo").contains(tag)) {
properties.put(tag, elem.getTextContent());
} else {
Map<String, String> attrs = new HashMap<>();
for (int i = 0; i < elem.getAttributes().getLength(); i++) {
attrs.put(elem.getAttributes().item(i).getNodeName(), elem.getAttributes().item(i).getNodeValue());
}
properties.put(tag, attrs);
}
}
}
NodeList oibFilesNodes = doc.getElementsByTagName("OibFiles");
if (oibFilesNodes.getLength() > 0) {
List<Map<String, String>> oibFiles = new ArrayList<>();
NodeList files = ((Element) oibFilesNodes.item(0)).getElementsByTagName("File");
for (int i = 0; i < files.getLength(); i++) {
Element fileElem = (Element) files.item(i);
Map<String, String> fileProps = new HashMap<>();
for (int j = 0; j < fileElem.getAttributes().getLength(); j++) {
fileProps.put(fileElem.getAttributes().item(j).getNodeName(), fileElem.getAttributes().item(j).getNodeValue());
}
oibFiles.add(fileProps);
}
properties.put("OibFiles", oibFiles);
}
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String newFilepath, Map<String, Object> newProperties) throws Exception {
if (newProperties != null) {
this.properties = newProperties;
}
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.newDocument();
Element root = doc.createElement("OibSummary");
doc.appendChild(root);
for (Map.Entry<String, Object> entry : properties.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
Element sub = doc.createElement(key);
if (key.equals("OibFiles")) {
@SuppressWarnings("unchecked")
List<Map<String, String>> files = (List<Map<String, String>>) value;
for (Map<String, String> fileProps : files) {
Element fileElem = doc.createElement("File");
for (Map.Entry<String, String> attr : fileProps.entrySet()) {
fileElem.setAttribute(attr.getKey(), attr.getValue());
}
sub.appendChild(fileElem);
}
} else if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, String> attrs = (Map<String, String>) value;
for (Map.Entry<String, String> attr : attrs.entrySet()) {
sub.setAttribute(attr.getKey(), attr.getValue());
}
} else if (value instanceof String) {
sub.setTextContent((String) value);
}
root.appendChild(sub);
}
// Note: This writes only the metadata XML, not a full .VRB file, as the complete binary structure is proprietary.
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(newFilepath));
transformer.transform(source, result);
}
private int findBytes(byte[] haystack, byte[] needle, int start) {
outer: for (int i = start; i < haystack.length - needle.length + 1; i++) {
for (int j = 0; j < needle.length; j++) {
if (haystack[i + j] != needle[j]) continue outer;
}
return i;
}
return -1;
}
// Example usage:
// public static void main(String[] args) throws Exception {
// VRBFile vrb = new VRBFile("example.vrb");
// vrb.decodeRead();
// vrb.printProperties();
// vrb.write("new.vrb", null);
// }
}
- JavaScript class for .VRB file handling:
class VRBFile {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
}
async decodeRead() {
// Note: In Node.js, use fs module; this assumes Node.js environment.
const fs = require('fs');
const data = fs.readFileSync(this.filepath);
const start = Buffer.from('<OibSummary>');
const end = Buffer.from('</OibSummary>');
const positions = [];
let pos = 0;
while (true) {
pos = data.indexOf(start, pos);
if (pos === -1) break;
const endPos = data.indexOf(end, pos) + end.length;
const block = data.slice(pos, endPos);
positions.push(block);
pos = endPos;
}
if (positions.length === 0) {
throw new Error('No OibSummary found in the file.');
}
const lastBlock = positions[positions.length - 1].toString('utf-8');
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(lastBlock, 'application/xml');
const tags = ['Backup', 'Point', 'Storage', 'OIB', 'Object', 'TargetHost', 'PrevFileName', 'BackupVersion', 'LogBackupInfo', 'SourceHost'];
tags.forEach(tag => {
const elem = xmlDoc.getElementsByTagName(tag)[0];
if (elem) {
if (['PrevFileName', 'BackupVersion', 'LogBackupInfo'].includes(tag)) {
this.properties[tag] = elem.textContent;
} else {
const attrs = {};
for (let i = 0; i < elem.attributes.length; i++) {
attrs[elem.attributes[i].name] = elem.attributes[i].value;
}
this.properties[tag] = attrs;
}
}
});
const oibFilesElem = xmlDoc.getElementsByTagName('OibFiles')[0];
if (oibFilesElem) {
this.properties['OibFiles'] = [];
const files = oibFilesElem.getElementsByTagName('File');
for (let i = 0; i < files.length; i++) {
const fileProps = {};
for (let j = 0; j < files[i].attributes.length; j++) {
fileProps[files[i].attributes[j].name] = files[i].attributes[j].value;
}
this.properties['OibFiles'].push(fileProps);
}
}
}
printProperties() {
console.log(JSON.stringify(this.properties, null, 2));
}
write(newFilepath, newProperties = null) {
if (newProperties) {
this.properties = newProperties;
}
const doc = new Document();
const root = doc.createElement('OibSummary');
doc.appendChild(root);
for (const [key, value] of Object.entries(this.properties)) {
const sub = doc.createElement(key);
if (key === 'OibFiles') {
value.forEach(fileProps => {
const fileElem = doc.createElement('File');
for (const [attr, val] of Object.entries(fileProps)) {
fileElem.setAttribute(attr, val);
}
sub.appendChild(fileElem);
});
} else if (typeof value === 'object' && value !== null) {
for (const [attr, val] of Object.entries(value)) {
sub.setAttribute(attr, val);
}
} else if (typeof value === 'string') {
sub.textContent = value;
}
root.appendChild(sub);
}
const serializer = new XMLSerializer();
const xmlStr = serializer.serializeToString(doc);
// Note: This writes only the metadata XML, not a full .VRB file, as the complete binary structure is proprietary.
const fs = require('fs');
fs.writeFileSync(newFilepath, xmlStr, 'utf-8');
}
}
// Example usage in Node.js:
// const vrb = new VRBFile('example.vrb');
// await vrb.decodeRead();
// vrb.printProperties();
// vrb.write('new.vrb');
- C class (implemented as C++ class for object-oriented features) for .VRB file handling:
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
#include <sstream>
#include <tinyxml2.h> // Assume tinyxml2 library for XML parsing; include via external dependency if needed.
using namespace std;
using namespace tinyxml2;
class VRBFile {
private:
string filepath;
map<string, variant<string, map<string, string>, vector<map<string, string>>>> properties; // Use variant for mixed types (C++17+).
public:
VRBFile(const string& fp) : filepath(fp) {}
void decodeRead() {
ifstream file(filepath, ios::binary);
if (!file) throw runtime_error("Cannot open file.");
stringstream buffer;
buffer << file.rdbuf();
string data = buffer.str();
string start = "<OibSummary>";
string end = "</OibSummary>";
vector<string> positions;
size_t pos = 0;
while ((pos = data.find(start, pos)) != string::npos) {
size_t endPos = data.find(end, pos) + end.length();
string block = data.substr(pos, endPos - pos);
positions.push_back(block);
pos = endPos;
}
if (positions.empty()) throw runtime_error("No OibSummary found in the file.");
string lastBlock = positions.back();
XMLDocument doc;
doc.Parse(lastBlock.c_str());
if (doc.Error()) throw runtime_error("XML parsing error.");
XMLElement* root = doc.RootElement();
vector<string> tags = {"Backup", "Point", "Storage", "OIB", "Object", "TargetHost", "PrevFileName", "BackupVersion", "LogBackupInfo", "SourceHost"};
for (const string& tag : tags) {
XMLElement* elem = root->FirstChildElement(tag.c_str());
if (elem) {
if (tag == "PrevFileName" || tag == "BackupVersion" || tag == "LogBackupInfo") {
properties[tag] = elem->GetText() ? string(elem->GetText()) : "";
} else {
map<string, string> attrs;
for (const XMLAttribute* attr = elem->FirstAttribute(); attr; attr = attr->Next()) {
attrs[attr->Name()] = attr->Value();
}
properties[tag] = attrs;
}
}
}
XMLElement* oibFilesElem = root->FirstChildElement("OibFiles");
if (oibFilesElem) {
vector<map<string, string>> oibFiles;
for (XMLElement* fileElem = oibFilesElem->FirstChildElement("File"); fileElem; fileElem = fileElem->NextSiblingElement("File")) {
map<string, string> fileProps;
for (const XMLAttribute* attr = fileElem->FirstAttribute(); attr; attr = attr->Next()) {
fileProps[attr->Name()] = attr->Value();
}
oibFiles.push_back(fileProps);
}
properties["OibFiles"] = oibFiles;
}
}
void printProperties() {
for (const auto& [key, val] : properties) {
cout << key << ": ";
if (holds_alternative<string>(val)) {
cout << get<string>(val);
} else if (holds_alternative<map<string, string>>(val)) {
const auto& attrs = get<map<string, string>>(val);
for (const auto& [a, v] : attrs) {
cout << a << "=" << v << " ";
}
} else if (holds_alternative<vector<map<string, string>>>(val)) {
const auto& files = get<vector<map<string, string>>>(val);
cout << "[";
for (const auto& file : files) {
for (const auto& [a, v] : file) {
cout << a << "=" << v << " ";
}
cout << "; ";
}
cout << "]";
}
cout << endl;
}
}
void write(const string& newFilepath, const map<string, variant<string, map<string, string>, vector<map<string, string>>>>& newProperties = {}) {
if (!newProperties.empty()) {
properties = newProperties;
}
XMLDocument doc;
XMLElement* root = doc.NewElement("OibSummary");
doc.InsertFirstChild(root);
for (const auto& [key, val] : properties) {
XMLElement* sub = doc.NewElement(key.c_str());
if (holds_alternative<vector<map<string, string>>>(val)) {
const auto& files = get<vector<map<string, string>>>(val);
for (const auto& fileProps : files) {
XMLElement* fileElem = doc.NewElement("File");
for (const auto& [attr, v] : fileProps) {
fileElem->SetAttribute(attr.c_str(), v.c_str());
}
sub->InsertEndChild(fileElem);
}
} else if (holds_alternative<map<string, string>>(val)) {
const auto& attrs = get<map<string, string>>(val);
for (const auto& [attr, v] : attrs) {
sub->SetAttribute(attr.c_str(), v.c_str());
}
} else if (holds_alternative<string>(val)) {
sub->SetText(get<string>(val).c_str());
}
root->InsertEndChild(sub);
}
// Note: This writes only the metadata XML, not a full .VRB file, as the complete binary structure is proprietary.
ofstream out(newFilepath);
XMLPrinter printer;
doc.Accept(&printer);
out << printer.CStr();
out.close();
}
};
// Example usage:
// int main() {
// VRBFile vrb("example.vrb");
// vrb.decodeRead();
// vrb.printProperties();
// vrb.write("new.vrb");
// return 0;
// }