Task 470: .OCX File Format
Task 470: .OCX File Format
1. Properties of the .OCX File Format Intrinsic to Its File System
The .OCX file format adheres to the Compound File Binary Format (CFBF), also known as the OLE Compound Document Format, which structures the file as a hierarchical file system within a single binary container. This format emulates a FAT-like file system with sectors, chains, storages (directories), and streams (files). The intrinsic properties are derived from the file's header and directory structures, defining the allocation, organization, and metadata of the internal file system. Below is a comprehensive list of these properties:
- Header Signature: An 8-byte fixed magic number identifying the format (offset 0x00, value: 0xD0CF11E0A1B11AE1).
- Class ID (CLSID): A 16-byte GUID for the root storage (offset 0x08, often zero or specific to the control).
- Minor Version: A 2-byte unsigned integer indicating the minor format version (offset 0x18, typically 0x0021 or 33 decimal for reference implementations).
- Major Version (DLL Version): A 2-byte unsigned integer indicating the major format version (offset 0x1A, 3 for 512-byte sectors, 4 for 4096-byte sectors).
- Byte Order: A 2-byte unsigned integer specifying endianness (offset 0x1C, 0xFFFE for little-endian).
- Sector Shift: A 2-byte unsigned integer defining sector size as 2^value bytes (offset 0x1E, typically 9 for 512-byte sectors).
- Mini Sector Shift: A 2-byte unsigned integer defining mini-sector size as 2^value bytes (offset 0x20, typically 6 for 64-byte mini-sectors).
- Reserved Field: A 2-byte field that must be zero (offset 0x22).
- Reserved Field 1: A 4-byte field that must be zero (offset 0x24).
- Number of Directory Sectors: A 4-byte unsigned integer indicating the count of directory chain sectors (offset 0x28, zero for 512-byte sector files in version 3).
- Number of FAT Sectors: A 4-byte unsigned integer indicating the count of File Allocation Table (FAT) sectors (offset 0x2C).
- First Directory Sector Location: A 4-byte unsigned integer pointing to the starting sector of the directory chain (offset 0x30).
- Transaction Signature Number: A 4-byte unsigned integer for transaction support (offset 0x34, must be zero as transactions are not supported).
- Mini Stream Cutoff Size: A 4-byte unsigned integer specifying the maximum size for mini-streams (offset 0x38, typically 4096 bytes).
- First MiniFAT Sector Location: A 4-byte unsigned integer pointing to the starting sector of the MiniFAT chain (offset 0x3C).
- Number of MiniFAT Sectors: A 4-byte unsigned integer indicating the count of MiniFAT sectors (offset 0x40).
- First DIFAT Sector Location: A 4-byte unsigned integer pointing to the starting sector of the Double-Indirect FAT (DIFAT) chain (offset 0x44).
- Number of DIFAT Sectors: A 4-byte unsigned integer indicating the count of DIFAT sectors (offset 0x48).
- DIFAT Array: A 436-byte array (109 entries of 4 bytes each) listing the first 109 FAT sector locations (offset 0x4C).
Additional properties derived from the directory entries (each 128 bytes, starting from the first directory sector) include, for each storage or stream:
- Entry Name: A variable-length Unicode string (up to 64 bytes, including null terminator).
- Entry Type: A 1-byte value (e.g., 0x00 invalid, 0x01 storage, 0x02 stream, 0x05 root storage).
- Node Color: A 1-byte value for red-black tree balancing (0x00 red, 0x01 black).
- Left Sibling SID: A 4-byte signed integer pointing to the left child directory entry.
- Right Sibling SID: A 4-byte signed integer pointing to the right child directory entry.
- Child SID: A 4-byte signed integer pointing to the first child directory entry (for storages).
- CLSID: A 16-byte GUID for the entry.
- State Bits: A 4-byte unsigned integer for user-defined flags.
- Creation Time: An 8-byte FILETIME structure.
- Modification Time: An 8-byte FILETIME structure.
- Starting Sector Location: A 4-byte unsigned integer for the first sector of the stream.
- Stream Size: An 8-byte unsigned integer indicating the size of the stream.
These properties collectively define the file system's allocation tables (FAT, MiniFAT, DIFAT), sector chains, and hierarchical organization.
2. Direct Download Links for .OCX Files
3. HTML JavaScript for Drag-and-Drop .OCX File Property Dump
Below is a self-contained HTML page with embedded JavaScript that enables drag-and-drop functionality for a .OCX file. Upon dropping the file, it parses the header and directory structures to display the properties listed in section 1.
4. Python Class for .OCX File Handling
import struct
import datetime
import os
class OCXFile:
def __init__(self, filepath):
self.filepath = filepath
self.header = None
self.properties = {}
def read(self):
with open(self.filepath, 'rb') as f:
data = f.read()
self._parse_header(data)
self._parse_root_directory(data)
return self.properties
def _parse_header(self, data):
self.header = struct.unpack_from('<8s16sHHHHHHIIIIIQIII109I', data, 0)
self.properties['Header Signature'] = self.header[0].hex()
self.properties['CLSID'] = self.header[1].hex()
self.properties['Minor Version'] = self.header[2]
self.properties['Major Version'] = self.header[3]
self.properties['Byte Order'] = self.header[4]
self.properties['Sector Shift'] = self.header[5]
self.properties['Mini Sector Shift'] = self.header[6]
self.properties['Reserved'] = self.header[7]
self.properties['Reserved1'] = self.header[8]
self.properties['Number of Directory Sectors'] = self.header[9]
self.properties['Number of FAT Sectors'] = self.header[10]
self.properties['First Directory Sector Location'] = self.header[11]
self.properties['Transaction Signature Number'] = self.header[12]
self.properties['Mini Stream Cutoff Size'] = self.header[13]
self.properties['First MiniFAT Sector Location'] = self.header[14]
self.properties['Number of MiniFAT Sectors'] = self.header[15]
self.properties['First DIFAT Sector Location'] = self.header[16]
self.properties['Number of DIFAT Sectors'] = self.header[17]
self.properties['DIFAT Array'] = list(self.header[18:])
def _parse_root_directory(self, data):
sector_size = 1 << self.properties['Sector Shift']
dir_start = self.properties['First Directory Sector Location']
dir_offset = (dir_start + 1) * sector_size
name = ''
for i in range(0, 64, 2):
char = struct.unpack_from('<H', data, dir_offset + i)[0]
if char == 0: break
name += chr(char)
self.properties['Root Name'] = name
self.properties['Root Type'] = struct.unpack_from('<B', data, dir_offset + 66)[0]
self.properties['Root Node Color'] = struct.unpack_from('<B', data, dir_offset + 67)[0]
self.properties['Root Left Sibling SID'] = struct.unpack_from('<i', data, dir_offset + 68)[0]
self.properties['Root Right Sibling SID'] = struct.unpack_from('<i', data, dir_offset + 72)[0]
self.properties['Root Child SID'] = struct.unpack_from('<i', data, dir_offset + 76)[0]
self.properties['Root CLSID'] = struct.unpack_from('<16s', data, dir_offset + 80)[0].hex()
self.properties['Root State Bits'] = struct.unpack_from('<I', data, dir_offset + 96)[0]
creation_low, creation_high = struct.unpack_from('<II', data, dir_offset + 100)
creation_time = (creation_high << 32) + creation_low
self.properties['Root Creation Time'] = self._filetime_to_datetime(creation_time)
mod_low, mod_high = struct.unpack_from('<II', data, dir_offset + 108)
mod_time = (mod_high << 32) + mod_low
self.properties['Root Modification Time'] = self._filetime_to_datetime(mod_time)
self.properties['Root Starting Sector'] = struct.unpack_from('<I', data, dir_offset + 116)[0]
self.properties['Root Stream Size'] = struct.unpack_from('<Q', data, dir_offset + 120)[0]
def _filetime_to_datetime(self, filetime):
return datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=filetime / 10)
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filepath=None):
# For demonstration, this rewrites the original file or to a new path without modification.
# In practice, implement modifications to header or directories as needed.
if new_filepath is None:
new_filepath = self.filepath
with open(self.filepath, 'rb') as f_in:
data = f_in.read()
with open(new_filepath, 'wb') as f_out:
f_out.write(data)
# Example usage:
# ocx = OCXFile('example.ocx')
# ocx.read()
# ocx.print_properties()
# ocx.write('modified.ocx')
5. Java Class for .OCX File Handling
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.nio.file.*;
import java.time.*;
public class OCXFile {
private String filepath;
private ByteBuffer buffer;
private final java.util.Map<String, Object> properties = new java.util.HashMap<>();
public OCXFile(String filepath) {
this.filepath = filepath;
}
public void read() throws IOException {
try (FileChannel channel = FileChannel.open(Paths.get(filepath), StandardOpenOption.READ)) {
buffer = ByteBuffer.allocate((int) channel.size()).order(ByteOrder.LITTLE_ENDIAN);
channel.read(buffer);
buffer.flip();
parseHeader();
parseRootDirectory();
}
}
private void parseHeader() {
buffer.position(0);
byte[] sig = new byte[8];
buffer.get(sig);
properties.put("Header Signature", bytesToHex(sig));
byte[] clsid = new byte[16];
buffer.get(clsid);
properties.put("CLSID", bytesToHex(clsid));
properties.put("Minor Version", buffer.getShort());
properties.put("Major Version", buffer.getShort());
properties.put("Byte Order", buffer.getShort());
properties.put("Sector Shift", buffer.getShort());
properties.put("Mini Sector Shift", buffer.getShort());
properties.put("Reserved", buffer.getShort());
properties.put("Reserved1", buffer.getInt());
properties.put("Number of Directory Sectors", buffer.getInt());
properties.put("Number of FAT Sectors", buffer.getInt());
properties.put("First Directory Sector Location", buffer.getInt());
properties.put("Transaction Signature Number", buffer.getInt());
properties.put("Mini Stream Cutoff Size", buffer.getInt());
properties.put("First MiniFAT Sector Location", buffer.getInt());
properties.put("Number of MiniFAT Sectors", buffer.getInt());
properties.put("First DIFAT Sector Location", buffer.getInt());
properties.put("Number of DIFAT Sectors", buffer.getInt());
int[] difat = new int[109];
for (int i = 0; i < 109; i++) {
difat[i] = buffer.getInt();
}
properties.put("DIFAT Array", difat);
}
private void parseRootDirectory() {
int sectorSize = 1 << (short) properties.get("Sector Shift");
int dirStart = (int) properties.get("First Directory Sector Location");
int dirOffset = (dirStart + 1) * sectorSize;
buffer.position(dirOffset);
StringBuilder name = new StringBuilder();
for (int i = 0; i < 32; i++) {
char c = buffer.getChar();
if (c == 0) break;
name.append(c);
}
properties.put("Root Name", name.toString());
buffer.position(dirOffset + 66);
properties.put("Root Type", buffer.get());
properties.put("Root Node Color", buffer.get());
properties.put("Root Left Sibling SID", buffer.getInt());
properties.put("Root Right Sibling SID", buffer.getInt());
properties.put("Root Child SID", buffer.getInt());
byte[] rootClsid = new byte[16];
buffer.get(rootClsid);
properties.put("Root CLSID", bytesToHex(rootClsid));
properties.put("Root State Bits", buffer.getInt());
long creation = buffer.getLong();
properties.put("Root Creation Time", fileTimeToInstant(creation));
long mod = buffer.getLong();
properties.put("Root Modification Time", fileTimeToInstant(mod));
properties.put("Root Starting Sector", buffer.getInt());
properties.put("Root Stream Size", buffer.getLong());
}
private String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
private Instant fileTimeToInstant(long filetime) {
return Instant.ofEpochSecond((filetime / 10000000) - 11644473600L);
}
public void printProperties() {
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String newFilepath) throws IOException {
// For demonstration, copies the file; implement modifications as needed.
if (newFilepath == null) newFilepath = filepath;
Files.copy(Paths.get(filepath), Paths.get(newFilepath), StandardCopyOption.REPLACE_EXISTING);
}
// Example usage:
// public static void main(String[] args) throws IOException {
// OCXFile ocx = new OCXFile("example.ocx");
// ocx.read();
// ocx.printProperties();
// ocx.write("modified.ocx");
// }
}
6. JavaScript Class for .OCX File Handling
class OCXFile {
constructor(filepath) {
this.filepath = filepath;
this.properties = {};
}
async read() {
// Note: In Node.js, use fs module; this assumes Node environment.
const fs = require('fs');
const data = fs.readFileSync(this.filepath);
const view = new DataView(data.buffer);
this.parseHeader(view);
this.parseRootDirectory(view);
return this.properties;
}
parseHeader(view) {
let sig = '';
for (let i = 0; i < 8; i++) sig += view.getUint8(i).toString(16).padStart(2, '0');
this.properties['Header Signature'] = `0x${sig}`;
this.properties['CLSID'] = this.getGuid(view, 8);
this.properties['Minor Version'] = view.getUint16(24, true);
this.properties['Major Version'] = view.getUint16(26, true);
this.properties['Byte Order'] = view.getUint16(28, true);
this.properties['Sector Shift'] = view.getUint16(30, true);
this.properties['Mini Sector Shift'] = view.getUint16(32, true);
this.properties['Reserved'] = view.getUint16(34, true);
this.properties['Reserved1'] = view.getUint32(36, true);
this.properties['Number of Directory Sectors'] = view.getUint32(40, true);
this.properties['Number of FAT Sectors'] = view.getUint32(44, true);
this.properties['First Directory Sector Location'] = view.getUint32(48, true);
this.properties['Transaction Signature Number'] = view.getUint32(52, true);
this.properties['Mini Stream Cutoff Size'] = view.getUint32(56, true);
this.properties['First MiniFAT Sector Location'] = view.getUint32(60, true);
this.properties['Number of MiniFAT Sectors'] = view.getUint32(64, true);
this.properties['First DIFAT Sector Location'] = view.getUint32(68, true);
this.properties['Number of DIFAT Sectors'] = view.getUint32(72, true);
let difat = [];
for (let i = 0; i < 109; i++) {
difat.push(view.getUint32(76 + i * 4, true));
}
this.properties['DIFAT Array'] = difat;
}
parseRootDirectory(view) {
const sectorSize = 1 << this.properties['Sector Shift'];
const dirStart = this.properties['First Directory Sector Location'];
const dirOffset = (dirStart + 1) * sectorSize;
let name = '';
for (let i = 0; i < 64; i += 2) {
const char = view.getUint16(dirOffset + i, true);
if (char === 0) break;
name += String.fromCharCode(char);
}
this.properties['Root Name'] = name;
this.properties['Root Type'] = view.getUint8(dirOffset + 66);
this.properties['Root Node Color'] = view.getUint8(dirOffset + 67);
this.properties['Root Left Sibling SID'] = view.getInt32(dirOffset + 68, true);
this.properties['Root Right Sibling SID'] = view.getInt32(dirOffset + 72, true);
this.properties['Root Child SID'] = view.getInt32(dirOffset + 76, true);
this.properties['Root CLSID'] = this.getGuid(view, dirOffset + 80);
this.properties['Root State Bits'] = view.getUint32(dirOffset + 96, true);
const creation = this.getFileTime(view, dirOffset + 100);
this.properties['Root Creation Time'] = creation;
const mod = this.getFileTime(view, dirOffset + 108);
this.properties['Root Modification Time'] = mod;
this.properties['Root Starting Sector'] = view.getUint32(dirOffset + 116, true);
this.properties['Root Stream Size'] = Number(view.getBigUint64(dirOffset + 120, true));
}
getGuid(view, offset) {
let guid = '';
for (let i = 0; i < 16; i++) {
guid += view.getUint8(offset + i).toString(16).padStart(2, '0');
}
return guid;
}
getFileTime(view, offset) {
const low = view.getUint32(offset, true);
const high = view.getUint32(offset + 4, true);
const time = (BigInt(high) << 32n) + BigInt(low);
return new Date(Number(time / 10000n) - 11644473600000).toISOString();
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(newFilepath = this.filepath) {
// For demonstration, copies the file; implement modifications as needed.
const fs = require('fs');
fs.copyFileSync(this.filepath, newFilepath);
}
}
// Example usage:
// const ocx = new OCXFile('example.ocx');
// await ocx.read();
// ocx.printProperties();
// ocx.write('modified.ocx');
7. C++ Class for .OCX File Handling
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <iomanip>
#include <ctime>
#include <cstring>
class OCXFile {
private:
std::string filepath;
std::vector<char> data;
std::map<std::string, std::string> properties;
public:
OCXFile(const std::string& fp) : filepath(fp) {}
void read() {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
std::cerr << "Failed to open file." << std::endl;
return;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
data.resize(size);
file.read(data.data(), size);
parseHeader();
parseRootDirectory();
}
void parseHeader() {
std::stringstream ss;
ss << "0x";
for (int i = 0; i < 8; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (static_cast<unsigned char>(data[i]));
}
properties["Header Signature"] = ss.str();
ss.str("");
for (int i = 8; i < 24; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (static_cast<unsigned char>(data[i]));
}
properties["CLSID"] = ss.str();
unsigned short minorVer = *reinterpret_cast<unsigned short*>(&data[24]);
properties["Minor Version"] = std::to_string(minorVer);
unsigned short majorVer = *reinterpret_cast<unsigned short*>(&data[26]);
properties["Major Version"] = std::to_string(majorVer);
unsigned short byteOrder = *reinterpret_cast<unsigned short*>(&data[28]);
properties["Byte Order"] = std::to_string(byteOrder);
unsigned short sectorShift = *reinterpret_cast<unsigned short*>(&data[30]);
properties["Sector Shift"] = std::to_string(sectorShift);
unsigned short miniShift = *reinterpret_cast<unsigned short*>(&data[32]);
properties["Mini Sector Shift"] = std::to_string(miniShift);
unsigned short reserved = *reinterpret_cast<unsigned short*>(&data[34]);
properties["Reserved"] = std::to_string(reserved);
unsigned int reserved1 = *reinterpret_cast<unsigned int*>(&data[36]);
properties["Reserved1"] = std::to_string(reserved1);
unsigned int dirSects = *reinterpret_cast<unsigned int*>(&data[40]);
properties["Number of Directory Sectors"] = std::to_string(dirSects);
unsigned int fatSects = *reinterpret_cast<unsigned int*>(&data[44]);
properties["Number of FAT Sectors"] = std::to_string(fatSects);
unsigned int firstDir = *reinterpret_cast<unsigned int*>(&data[48]);
properties["First Directory Sector Location"] = std::to_string(firstDir);
unsigned int transSig = *reinterpret_cast<unsigned int*>(&data[52]);
properties["Transaction Signature Number"] = std::to_string(transSig);
unsigned int miniCutoff = *reinterpret_cast<unsigned int*>(&data[56]);
properties["Mini Stream Cutoff Size"] = std::to_string(miniCutoff);
unsigned int firstMiniFat = *reinterpret_cast<unsigned int*>(&data[60]);
properties["First MiniFAT Sector Location"] = std::to_string(firstMiniFat);
unsigned int numMiniFat = *reinterpret_cast<unsigned int*>(&data[64]);
properties["Number of MiniFAT Sectors"] = std::to_string(numMiniFat);
unsigned int firstDifat = *reinterpret_cast<unsigned int*>(&data[68]);
properties["First DIFAT Sector Location"] = std::to_string(firstDifat);
unsigned int numDifat = *reinterpret_cast<unsigned int*>(&data[72]);
properties["Number of DIFAT Sectors"] = std::to_string(numDifat);
ss.str("[");
for (int i = 0; i < 109; ++i) {
unsigned int val = *reinterpret_cast<unsigned int*>(&data[76 + i * 4]);
ss << val << ", ";
}
std::string difatStr = ss.str();
difatStr = difatStr.substr(0, difatStr.size() - 2) + "]";
properties["DIFAT Array"] = difatStr;
}
void parseRootDirectory() {
unsigned short sectorShift = std::stoi(properties["Sector Shift"]);
unsigned int sectorSize = 1 << sectorShift;
unsigned int dirStart = std::stoi(properties["First Directory Sector Location"]);
size_t dirOffset = (dirStart + 1) * sectorSize;
std::string name;
for (size_t i = 0; i < 64; i += 2) {
unsigned short charVal = *reinterpret_cast<unsigned short*>(&data[dirOffset + i]);
if (charVal == 0) break;
name += static_cast<char>(charVal);
}
properties["Root Name"] = name;
unsigned char type = data[dirOffset + 66];
properties["Root Type"] = std::to_string(type);
unsigned char color = data[dirOffset + 67];
properties["Root Node Color"] = std::to_string(color);
int leftSib = *reinterpret_cast<int*>(&data[dirOffset + 68]);
properties["Root Left Sibling SID"] = std::to_string(leftSib);
int rightSib = *reinterpret_cast<int*>(&data[dirOffset + 72]);
properties["Root Right Sibling SID"] = std::to_string(rightSib);
int child = *reinterpret_cast<int*>(&data[dirOffset + 76]);
properties["Root Child SID"] = std::to_string(child);
std::stringstream ss;
for (size_t i = 80; i < 96; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (static_cast<unsigned char>(data[dirOffset + i]));
}
properties["Root CLSID"] = ss.str();
unsigned int stateBits = *reinterpret_cast<unsigned int*>(&data[dirOffset + 96]);
properties["Root State Bits"] = std::to_string(stateBits);
long long creation = *reinterpret_cast<long long*>(&data[dirOffset + 100]);
properties["Root Creation Time"] = filetimeToString(creation);
long long mod = *reinterpret_cast<long long*>(&data[dirOffset + 108]);
properties["Root Modification Time"] = filetimeToString(mod);
unsigned int startSect = *reinterpret_cast<unsigned int*>(&data[dirOffset + 116]);
properties["Root Starting Sector"] = std::to_string(startSect);
long long size = *reinterpret_cast<long long*>(&data[dirOffset + 120]);
properties["Root Stream Size"] = std::to_string(size);
}
std::string filetimeToString(long long filetime) {
filetime /= 10000000; // To seconds
filetime -= 11644473600LL; // Epoch adjustment
std::time_t tt = static_cast<std::time_t>(filetime);
std::tm* tm = std::gmtime(&tt);
std::stringstream ss;
ss << std::put_time(tm, "%Y-%m-%dT%H:%M:%SZ");
return ss.str();
}
void printProperties() {
for (const auto& prop : properties) {
std::cout << prop.first << ": " << prop.second << std::endl;
}
}
void write(const std::string& newFilepath = "") {
std::string outPath = newFilepath.empty() ? filepath : newFilepath;
std::ofstream out(outPath, std::ios::binary);
if (!out) {
std::cerr << "Failed to write file." << std::endl;
return;
}
out.write(data.data(), data.size());
}
};
// Example usage:
// int main() {
// OCXFile ocx("example.ocx");
// ocx.read();
// ocx.printProperties();
// ocx.write("modified.ocx");
// return 0;
// }