This page hosts a formal specification of Microsoft Compound File Binary (CFB), AKA OLE (Object Linking and Embedding) file format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.
For C++, the easiest way is to clone the runtime library sources and build them along with your project.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
std::istream
). One can open local file for that, or use existing std::string
or char*
buffer.
#include <fstream>
std::ifstream is("path/to/local/file.bin", std::ifstream::binary);
#include <sstream>
std::istringstream is(str);
#include <sstream>
const char buf[] = { ... };
std::string str(buf, sizeof buf);
std::istringstream is(str);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
microsoft_cfb_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.header() // => get header
#pragma once
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
class microsoft_cfb_t : public kaitai::kstruct {
public:
class cfb_header_t;
class fat_entries_t;
class dir_entry_t;
microsoft_cfb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, microsoft_cfb_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~microsoft_cfb_t();
class cfb_header_t : public kaitai::kstruct {
public:
cfb_header_t(kaitai::kstream* p__io, microsoft_cfb_t* p__parent = nullptr, microsoft_cfb_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~cfb_header_t();
private:
std::string m_signature;
std::string m_clsid;
uint16_t m_version_minor;
uint16_t m_version_major;
std::string m_byte_order;
uint16_t m_sector_shift;
uint16_t m_mini_sector_shift;
std::string m_reserved1;
int32_t m_size_dir;
int32_t m_size_fat;
int32_t m_ofs_dir;
int32_t m_transaction_seq;
int32_t m_mini_stream_cutoff_size;
int32_t m_ofs_mini_fat;
int32_t m_size_mini_fat;
int32_t m_ofs_difat;
int32_t m_size_difat;
std::unique_ptr<std::vector<int32_t>> m_difat;
microsoft_cfb_t* m__root;
microsoft_cfb_t* m__parent;
public:
/**
* Magic bytes that confirm that this is a CFB file
*/
std::string signature() const { return m_signature; }
/**
* Reserved class ID field, must be all 0
*/
std::string clsid() const { return m_clsid; }
uint16_t version_minor() const { return m_version_minor; }
uint16_t version_major() const { return m_version_major; }
/**
* In theory, specifies a byte order. In practice, no other values besides FE FF (which imply little endian order) are used.
*/
std::string byte_order() const { return m_byte_order; }
/**
* For major version 3, must be 0x9 (sector size = 512 bytes). For major version 4, must be 0xc (sector size = 4096 bytes).
*/
uint16_t sector_shift() const { return m_sector_shift; }
uint16_t mini_sector_shift() const { return m_mini_sector_shift; }
std::string reserved1() const { return m_reserved1; }
/**
* Number of directory sectors in this file. For major version 3, must be 0.
*/
int32_t size_dir() const { return m_size_dir; }
/**
* Number of FAT sectors in this file.
*/
int32_t size_fat() const { return m_size_fat; }
/**
* Starting sector number for directory stream.
*/
int32_t ofs_dir() const { return m_ofs_dir; }
/**
* A transaction sequence number, which is incremented each time the file is saved if transactions are implemented, 0 otherwise.
*/
int32_t transaction_seq() const { return m_transaction_seq; }
int32_t mini_stream_cutoff_size() const { return m_mini_stream_cutoff_size; }
/**
* Starting sector number for mini FAT.
*/
int32_t ofs_mini_fat() const { return m_ofs_mini_fat; }
/**
* Number of mini FAT sectors in this file.
*/
int32_t size_mini_fat() const { return m_size_mini_fat; }
/**
* Starting sector number for DIFAT.
*/
int32_t ofs_difat() const { return m_ofs_difat; }
/**
* Number of DIFAT sectors in this file.
*/
int32_t size_difat() const { return m_size_difat; }
std::vector<int32_t>* difat() const { return m_difat.get(); }
microsoft_cfb_t* _root() const { return m__root; }
microsoft_cfb_t* _parent() const { return m__parent; }
};
class fat_entries_t : public kaitai::kstruct {
public:
fat_entries_t(kaitai::kstream* p__io, microsoft_cfb_t* p__parent = nullptr, microsoft_cfb_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~fat_entries_t();
private:
std::unique_ptr<std::vector<int32_t>> m_entries;
microsoft_cfb_t* m__root;
microsoft_cfb_t* m__parent;
public:
std::vector<int32_t>* entries() const { return m_entries.get(); }
microsoft_cfb_t* _root() const { return m__root; }
microsoft_cfb_t* _parent() const { return m__parent; }
};
class dir_entry_t : public kaitai::kstruct {
public:
enum obj_type_t {
OBJ_TYPE_UNKNOWN = 0,
OBJ_TYPE_STORAGE = 1,
OBJ_TYPE_STREAM = 2,
OBJ_TYPE_ROOT_STORAGE = 5
};
enum rb_color_t {
RB_COLOR_RED = 0,
RB_COLOR_BLACK = 1
};
dir_entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, microsoft_cfb_t* p__root = nullptr);
private:
void _read();
void _clean_up();
public:
~dir_entry_t();
private:
bool f_mini_stream;
std::string m_mini_stream;
bool n_mini_stream;
public:
bool _is_null_mini_stream() { mini_stream(); return n_mini_stream; };
private:
public:
std::string mini_stream();
private:
bool f_child;
std::unique_ptr<dir_entry_t> m_child;
bool n_child;
public:
bool _is_null_child() { child(); return n_child; };
private:
public:
dir_entry_t* child();
private:
bool f_left_sibling;
std::unique_ptr<dir_entry_t> m_left_sibling;
bool n_left_sibling;
public:
bool _is_null_left_sibling() { left_sibling(); return n_left_sibling; };
private:
public:
dir_entry_t* left_sibling();
private:
bool f_right_sibling;
std::unique_ptr<dir_entry_t> m_right_sibling;
bool n_right_sibling;
public:
bool _is_null_right_sibling() { right_sibling(); return n_right_sibling; };
private:
public:
dir_entry_t* right_sibling();
private:
std::string m_name;
uint16_t m_name_len;
obj_type_t m_object_type;
rb_color_t m_color_flag;
int32_t m_left_sibling_id;
int32_t m_right_sibling_id;
int32_t m_child_id;
std::string m_clsid;
uint32_t m_state;
uint64_t m_time_create;
uint64_t m_time_mod;
int32_t m_ofs;
uint64_t m_size;
microsoft_cfb_t* m__root;
kaitai::kstruct* m__parent;
public:
std::string name() const { return m_name; }
uint16_t name_len() const { return m_name_len; }
obj_type_t object_type() const { return m_object_type; }
rb_color_t color_flag() const { return m_color_flag; }
int32_t left_sibling_id() const { return m_left_sibling_id; }
int32_t right_sibling_id() const { return m_right_sibling_id; }
int32_t child_id() const { return m_child_id; }
std::string clsid() const { return m_clsid; }
/**
* User-defined flags for storage or root storage objects
*/
uint32_t state() const { return m_state; }
/**
* Creation time, in Windows FILETIME format (number of 100-nanosecond intervals since January 1, 1601, UTC)
*/
uint64_t time_create() const { return m_time_create; }
/**
* Modification time, in Windows FILETIME format (number of 100-nanosecond intervals since January 1, 1601, UTC).
*/
uint64_t time_mod() const { return m_time_mod; }
/**
* For stream object, number of starting sector. For a root storage object, first sector of the mini stream, if the mini stream exists.
*/
int32_t ofs() const { return m_ofs; }
/**
* For stream object, size of user-defined data in bytes. For a root storage object, size of the mini stream.
*/
uint64_t size() const { return m_size; }
microsoft_cfb_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
private:
bool f_sector_size;
int32_t m_sector_size;
public:
int32_t sector_size();
private:
bool f_fat;
std::unique_ptr<fat_entries_t> m_fat;
public:
fat_entries_t* fat();
private:
bool f_dir;
std::unique_ptr<dir_entry_t> m_dir;
public:
dir_entry_t* dir();
private:
std::unique_ptr<cfb_header_t> m_header;
microsoft_cfb_t* m__root;
kaitai::kstruct* m__parent;
std::string m__raw_fat;
std::unique_ptr<kaitai::kstream> m__io__raw_fat;
public:
cfb_header_t* header() const { return m_header.get(); }
microsoft_cfb_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
std::string _raw_fat() const { return m__raw_fat; }
kaitai::kstream* _io__raw_fat() const { return m__io__raw_fat.get(); }
};
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "microsoft_cfb.h"
#include "kaitai/exceptions.h"
microsoft_cfb_t::microsoft_cfb_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, microsoft_cfb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_header = nullptr;
m_fat = nullptr;
m__io__raw_fat = nullptr;
m_dir = nullptr;
f_sector_size = false;
f_fat = false;
f_dir = false;
_read();
}
void microsoft_cfb_t::_read() {
m_header = std::unique_ptr<cfb_header_t>(new cfb_header_t(m__io, this, m__root));
}
microsoft_cfb_t::~microsoft_cfb_t() {
_clean_up();
}
void microsoft_cfb_t::_clean_up() {
if (f_fat) {
}
if (f_dir) {
}
}
microsoft_cfb_t::cfb_header_t::cfb_header_t(kaitai::kstream* p__io, microsoft_cfb_t* p__parent, microsoft_cfb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_difat = nullptr;
_read();
}
void microsoft_cfb_t::cfb_header_t::_read() {
m_signature = m__io->read_bytes(8);
if (!(signature() == std::string("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1", 8), signature(), _io(), std::string("/types/cfb_header/seq/0"));
}
m_clsid = m__io->read_bytes(16);
if (!(clsid() == std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16), clsid(), _io(), std::string("/types/cfb_header/seq/1"));
}
m_version_minor = m__io->read_u2le();
m_version_major = m__io->read_u2le();
m_byte_order = m__io->read_bytes(2);
if (!(byte_order() == std::string("\xFE\xFF", 2))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\xFE\xFF", 2), byte_order(), _io(), std::string("/types/cfb_header/seq/4"));
}
m_sector_shift = m__io->read_u2le();
m_mini_sector_shift = m__io->read_u2le();
m_reserved1 = m__io->read_bytes(6);
m_size_dir = m__io->read_s4le();
m_size_fat = m__io->read_s4le();
m_ofs_dir = m__io->read_s4le();
m_transaction_seq = m__io->read_s4le();
m_mini_stream_cutoff_size = m__io->read_s4le();
m_ofs_mini_fat = m__io->read_s4le();
m_size_mini_fat = m__io->read_s4le();
m_ofs_difat = m__io->read_s4le();
m_size_difat = m__io->read_s4le();
m_difat = std::unique_ptr<std::vector<int32_t>>(new std::vector<int32_t>());
const int l_difat = 109;
for (int i = 0; i < l_difat; i++) {
m_difat->push_back(std::move(m__io->read_s4le()));
}
}
microsoft_cfb_t::cfb_header_t::~cfb_header_t() {
_clean_up();
}
void microsoft_cfb_t::cfb_header_t::_clean_up() {
}
microsoft_cfb_t::fat_entries_t::fat_entries_t(kaitai::kstream* p__io, microsoft_cfb_t* p__parent, microsoft_cfb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_entries = nullptr;
_read();
}
void microsoft_cfb_t::fat_entries_t::_read() {
m_entries = std::unique_ptr<std::vector<int32_t>>(new std::vector<int32_t>());
{
int i = 0;
while (!m__io->is_eof()) {
m_entries->push_back(std::move(m__io->read_s4le()));
i++;
}
}
}
microsoft_cfb_t::fat_entries_t::~fat_entries_t() {
_clean_up();
}
void microsoft_cfb_t::fat_entries_t::_clean_up() {
}
microsoft_cfb_t::dir_entry_t::dir_entry_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, microsoft_cfb_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_child = nullptr;
m_left_sibling = nullptr;
m_right_sibling = nullptr;
f_mini_stream = false;
f_child = false;
f_left_sibling = false;
f_right_sibling = false;
_read();
}
void microsoft_cfb_t::dir_entry_t::_read() {
m_name = kaitai::kstream::bytes_to_str(m__io->read_bytes(64), std::string("UTF-16LE"));
m_name_len = m__io->read_u2le();
m_object_type = static_cast<microsoft_cfb_t::dir_entry_t::obj_type_t>(m__io->read_u1());
m_color_flag = static_cast<microsoft_cfb_t::dir_entry_t::rb_color_t>(m__io->read_u1());
m_left_sibling_id = m__io->read_s4le();
m_right_sibling_id = m__io->read_s4le();
m_child_id = m__io->read_s4le();
m_clsid = m__io->read_bytes(16);
m_state = m__io->read_u4le();
m_time_create = m__io->read_u8le();
m_time_mod = m__io->read_u8le();
m_ofs = m__io->read_s4le();
m_size = m__io->read_u8le();
}
microsoft_cfb_t::dir_entry_t::~dir_entry_t() {
_clean_up();
}
void microsoft_cfb_t::dir_entry_t::_clean_up() {
if (f_mini_stream && !n_mini_stream) {
}
if (f_child && !n_child) {
}
if (f_left_sibling && !n_left_sibling) {
}
if (f_right_sibling && !n_right_sibling) {
}
}
std::string microsoft_cfb_t::dir_entry_t::mini_stream() {
if (f_mini_stream)
return m_mini_stream;
n_mini_stream = true;
if (object_type() == microsoft_cfb_t::dir_entry_t::OBJ_TYPE_ROOT_STORAGE) {
n_mini_stream = false;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek(((ofs() + 1) * _root()->sector_size()));
m_mini_stream = io->read_bytes(size());
io->seek(_pos);
f_mini_stream = true;
}
return m_mini_stream;
}
microsoft_cfb_t::dir_entry_t* microsoft_cfb_t::dir_entry_t::child() {
if (f_child)
return m_child.get();
n_child = true;
if (child_id() != -1) {
n_child = false;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek((((_root()->header()->ofs_dir() + 1) * _root()->sector_size()) + (child_id() * 128)));
m_child = std::unique_ptr<dir_entry_t>(new dir_entry_t(io, this, m__root));
io->seek(_pos);
f_child = true;
}
return m_child.get();
}
microsoft_cfb_t::dir_entry_t* microsoft_cfb_t::dir_entry_t::left_sibling() {
if (f_left_sibling)
return m_left_sibling.get();
n_left_sibling = true;
if (left_sibling_id() != -1) {
n_left_sibling = false;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek((((_root()->header()->ofs_dir() + 1) * _root()->sector_size()) + (left_sibling_id() * 128)));
m_left_sibling = std::unique_ptr<dir_entry_t>(new dir_entry_t(io, this, m__root));
io->seek(_pos);
f_left_sibling = true;
}
return m_left_sibling.get();
}
microsoft_cfb_t::dir_entry_t* microsoft_cfb_t::dir_entry_t::right_sibling() {
if (f_right_sibling)
return m_right_sibling.get();
n_right_sibling = true;
if (right_sibling_id() != -1) {
n_right_sibling = false;
kaitai::kstream *io = _root()->_io();
std::streampos _pos = io->pos();
io->seek((((_root()->header()->ofs_dir() + 1) * _root()->sector_size()) + (right_sibling_id() * 128)));
m_right_sibling = std::unique_ptr<dir_entry_t>(new dir_entry_t(io, this, m__root));
io->seek(_pos);
f_right_sibling = true;
}
return m_right_sibling.get();
}
int32_t microsoft_cfb_t::sector_size() {
if (f_sector_size)
return m_sector_size;
m_sector_size = (1 << header()->sector_shift());
f_sector_size = true;
return m_sector_size;
}
microsoft_cfb_t::fat_entries_t* microsoft_cfb_t::fat() {
if (f_fat)
return m_fat.get();
std::streampos _pos = m__io->pos();
m__io->seek(sector_size());
m__raw_fat = m__io->read_bytes((header()->size_fat() * sector_size()));
m__io__raw_fat = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_fat));
m_fat = std::unique_ptr<fat_entries_t>(new fat_entries_t(m__io__raw_fat.get(), this, m__root));
m__io->seek(_pos);
f_fat = true;
return m_fat.get();
}
microsoft_cfb_t::dir_entry_t* microsoft_cfb_t::dir() {
if (f_dir)
return m_dir.get();
std::streampos _pos = m__io->pos();
m__io->seek(((header()->ofs_dir() + 1) * sector_size()));
m_dir = std::unique_ptr<dir_entry_t>(new dir_entry_t(m__io, this, m__root));
m__io->seek(_pos);
f_dir = true;
return m_dir.get();
}