Task 123: .DAA File Format
Task 123: .DAA File Format
The Direct Access Archive (DAA) file format is a proprietary disk image format developed by PowerISO Computing. It supports compression, password protection, encryption, and multi-volume splitting, and is essentially a chunked, compressed ISO image. The specifications are not officially published, but reverse engineering from open sources reveals the structure as a header followed by a list of chunk lengths and zlib-compressed chunks (for version 1.0), with optional LZMA in other versions.
- List of properties intrinsic to the file format:
- Magic sequence (typically "DAA" or "DAA..." as the signature bytes).
- File format version (e.g., 0x00000100 for version 1.0).
- Offset to list of compressed chunk lengths (field located at file offset 0x4C, assumed 8 bytes little-endian).
- Offset to first compressed chunk (field located at file offset 0x5E, assumed 8 bytes little-endian).
- List of compressed chunk lengths (each encoded as 3 bytes in MSB-LSB-middle order; e.g., for value 0x697, encoded as 0x00 0x97 0x06).
- Compressed chunks (zlib-compressed data without header; each decompresses to 65536 bytes except the last).
- Compression type (zlib/DEFLATE for version 1.0, potentially LZMA for others).
- Encryption flag and type (AES for password-protected files).
- Password protection support (hash or key in header for authentication).
- Multi-volume splitting support (for large files split into .daa.001, .daa.002, etc.).
- Uncompressed size (derived from chunk count and chunk size).
- Compressed size (sum of chunk lengths).
- Two direct download links for .DAA files (note: .DAA files are uncommon in public repositories due to their proprietary nature; these are sample links from public sources found during search, but verify legality and safety before downloading):
- https://isc.sans.edu/diaryimages/images/2019-08-12-ISC-diary-Malicious-DAA-sample.daa (sample from SANS analysis, but not directly accessible; refer to diary for context).
- http://example.com/test.daa (hypothetical; actual public samples are rare—search for legal disk image samples on archives like archive.org).
- HTML/JavaScript for a Ghost blog embed (this is a self-contained snippet to embed in a blog post; it creates a drag-and-drop area, parses the .DAA file, and dumps the properties to the screen):
Drag and drop .DAA file here
- Python class:
import zlib
import struct
class DAAFile:
def __init__(self, filepath):
self.filepath = filepath
self.magic = None
self.version = None
self.offset_to_list = None
self.offset_to_data = None
self.num_chunks = None
self.chunk_lengths = []
self.uncompressed_size = None
self.compressed_chunks = []
self.decompressed_data = None
def read(self):
with open(self.filepath, 'rb') as f:
data = f.read()
self.magic = data[0:4].decode('utf-8', errors='ignore')
self.version = struct.unpack('<I', data[4:8])[0]
self.offset_to_list = struct.unpack('<Q', data[0x4C:0x4C+8])[0]
self.offset_to_data = struct.unpack('<Q', data[0x5E:0x5E+8])[0]
list_size = self.offset_to_data - self.offset_to_list
self.num_chunks = list_size // 3
for i in range(self.num_chunks):
pos = self.offset_to_list + i * 3
b1, b2, b3 = data[pos:pos+3]
length = (b1 << 16) | (b3 << 8) | b2
self.chunk_lengths.append(length)
pos = self.offset_to_data
for length in self.chunk_lengths:
chunk = data[pos:pos+length]
self.compressed_chunks.append(chunk)
pos += length
self.uncompressed_size = (self.num_chunks - 1) * 65536 + (len(zlib.decompress(self.compressed_chunks[-1], wbits=-15)) if self.num_chunks > 0 else 0)
def decode(self):
self.decompressed_data = b''
for chunk in self.compressed_chunks:
decompressed = zlib.decompress(chunk, wbits=-15)
self.decompressed_data += decompressed
def print_properties(self):
print(f"Magic: {self.magic}")
print(f"Version: 0x{self.version:x}")
print(f"Offset to chunk lengths list: 0x{self.offset_to_list:x}")
print(f"Offset to first chunk: 0x{self.offset_to_data:x}")
print(f"Number of chunks: {self.num_chunks}")
print(f"Chunk lengths: {self.chunk_lengths}")
print(f"Compression type: zlib")
print(f"Encryption: {'Yes' if 'encrypted' in self.magic.lower() else 'No'} (basic check)")
print(f"Multi-volume: {'Yes' if self.filepath.endswith('.001') else 'No'} (basic check)")
print(f"Uncompressed size: {self.uncompressed_size} bytes")
def write(self, new_filepath):
# For write, reverse the read: compress data in chunks, update lengths, write header and data.
# Assume self.decompressed_data is set; split into chunks, compress, etc.
if not self.decompressed_data:
raise ValueError("No data to write")
chunks = [self.decompressed_data[i:i+65536] for i in range(0, len(self.decompressed_data), 65536)]
self.chunk_lengths = []
self.compressed_chunks = []
for chunk in chunks:
compressed = zlib.compress(chunk, level=9)[2:-4] # Remove zlib header and checksum
self.chunk_lengths.append(len(compressed))
self.compressed_chunks.append(compressed)
self.num_chunks = len(chunks)
self.uncompressed_size = len(self.decompressed_data)
# Rebuild list bytes
list_bytes = b''
for length in self.chunk_lengths:
b1 = (length >> 16) & 0xFF
b2 = length & 0xFF
b3 = (length >> 8) & 0xFF
list_bytes += bytes([b1, b2, b3])
# Assume header is 0x66 bytes, update offsets
self.offset_to_list = 0x66
self.offset_to_data = self.offset_to_list + len(list_bytes)
header = b'DAA\x00' + struct.pack('<I', self.version) + b'\x00' * (0x4C - 8) + struct.pack('<Q', self.offset_to_list) + b'\x00' * (0x5E - 0x4C - 8) + struct.pack('<Q', self.offset_to_data) + b'\x00' * (0x66 - 0x5E - 8)
with open(new_filepath, 'wb') as f:
f.write(header + list_bytes + b''.join(self.compressed_chunks))
# Example usage
# daa = DAAFile('example.daa')
# daa.read()
# daa.decode()
# daa.print_properties()
# daa.write('new.daa')
- Java class:
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.Inflater;
import java.util.ArrayList;
import java.util.List;
public class DAAFile {
private String filepath;
private String magic;
private int version;
private long offsetToList;
private long offsetToData;
private int numChunks;
private List<Integer> chunkLengths = new ArrayList<>();
private long uncompressedSize;
private List<byte[]> compressedChunks = new ArrayList<>();
private byte[] decompressedData;
public DAAFile(String filepath) {
this.filepath = filepath;
}
public void read() throws IOException {
RandomAccessFile raf = new RandomAccessFile(filepath, "r");
byte[] data = new byte[(int) raf.length()];
raf.readFully(data);
raf.close();
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
magic = new String(data, 0, 4);
version = bb.getInt(4);
offsetToList = bb.getLong(0x4C);
offsetToData = bb.getLong(0x5E);
long listSize = offsetToData - offsetToList;
numChunks = (int) (listSize / 3);
for (int i = 0; i < numChunks; i++) {
int pos = (int) offsetToList + i * 3;
int b1 = data[pos] & 0xFF;
int b2 = data[pos + 1] & 0xFF;
int b3 = data[pos + 2] & 0xFF;
int length = (b1 << 16) | (b3 << 8) | b2;
chunkLengths.add(length);
}
int pos = (int) offsetToData;
for (int length : chunkLengths) {
byte[] chunk = new byte[length];
System.arraycopy(data, pos, chunk, 0, length);
compressedChunks.add(chunk);
pos += length;
}
uncompressedSize = (numChunks - 1L) * 65536 + (decompressChunk(compressedChunks.get(compressedChunks.size() - 1)).length if numChunks > 0 else 0);
}
private byte[] decompressChunk(byte[] chunk) throws IOException {
Inflater inflater = new Inflater(true); // No header
byte[] output = new byte[65536];
inflater.setInput(chunk);
int len = inflater.inflate(output);
inflater.end();
byte[] result = new byte[len];
System.arraycopy(output, 0, result, 0, len);
return result;
}
public void decode() throws IOException {
List<byte[]> decompressed = new ArrayList<>();
for (byte[] chunk : compressedChunks) {
decompressed.add(decompressChunk(chunk));
}
int total = decompressed.stream().mapToInt(b -> b.length).sum();
decompressedData = new byte[total];
int pos = 0;
for (byte[] d : decompressed) {
System.arraycopy(d, 0, decompressedData, pos, d.length);
pos += d.length;
}
}
public void printProperties() {
System.out.println("Magic: " + magic);
System.out.println("Version: 0x" + Integer.toHexString(version));
System.out.println("Offset to chunk lengths list: 0x" + Long.toHexString(offsetToList));
System.out.println("Offset to first chunk: 0x" + Long.toHexString(offsetToData));
System.out.println("Number of chunks: " + numChunks);
System.out.println("Chunk lengths: " + chunkLengths);
System.out.println("Compression type: zlib");
System.out.println("Encryption: " + (magic.toLowerCase().contains("encrypted") ? "Yes" : "No") + " (basic check)");
System.out.println("Multi-volume: " + (filepath.endsWith(".001") ? "Yes" : "No") + " (basic check)");
System.out.println("Uncompressed size: " + uncompressedSize + " bytes");
}
public void write(String newFilepath) throws IOException {
// Similar to Python: compress decompressedData, update lengths, write.
if (decompressedData == null) {
throw new IOException("No data to write");
}
List<byte[]> chunks = new ArrayList<>();
for (int i = 0; i < decompressedData.length; i += 65536) {
int end = Math.min(i + 65536, decompressedData.length);
byte[] chunk = new byte[end - i];
System.arraycopy(decompressedData, i, chunk, 0, chunk.length);
chunks.add(chunk);
}
chunkLengths.clear();
compressedChunks.clear();
for (byte[] chunk : chunks) {
byte[] compressed = compressChunk(chunk);
chunkLengths.add(compressed.length);
compressedChunks.add(compressed);
}
numChunks = chunks.size();
uncompressedSize = decompressedData.length;
// Build list bytes
ByteBuffer listBb = ByteBuffer.allocate(numChunks * 3);
for (int length : chunkLengths) {
int b1 = (length >> 16) & 0xFF;
int b2 = length & 0xFF;
int b3 = (length >> 8) & 0xFF;
listBb.put((byte) b1);
listBb.put((byte) b2);
listBb.put((byte) b3);
}
// Header
offsetToList = 0x66;
offsetToData = offsetToList + numChunks * 3;
ByteBuffer header = ByteBuffer.allocate(0x66).order(ByteOrder.LITTLE_ENDIAN);
header.position(0);
header.put("DAA\0".getBytes());
header.putInt(version);
header.position(0x4C);
header.putLong(offsetToList);
header.position(0x5E);
header.putLong(offsetToData);
// Write
RandomAccessFile raf = new RandomAccessFile(newFilepath, "rw");
raf.write(header.array());
raf.write(listBb.array());
for (byte[] chunk : compressedChunks) {
raf.write(chunk);
}
raf.close();
}
private byte[] compressChunk(byte[] chunk) throws IOException {
// Use Deflater with no header
java.util.zip.Deflater deflater = new java.util.zip.Deflater(9, true);
deflater.setInput(chunk);
deflater.finish();
byte[] output = new byte[65536];
int len = deflater.deflate(output);
deflater.end();
byte[] result = new byte[len];
System.arraycopy(output, 0, result, 0, len);
return result;
}
// Example usage
// DAAFile daa = new DAAFile("example.daa");
// daa.read();
// daa.decode();
// daa.printProperties();
// daa.write("new.daa");
}
- JavaScript class:
class DAAFile {
constructor(filepath) {
this.filepath = filepath;
this.magic = null;
this.version = null;
this.offsetToList = null;
this.offsetToData = null;
this.numChunks = null;
this.chunkLengths = [];
this.uncompressedSize = null;
this.compressedChunks = [];
this.decompressedData = null;
}
async read() {
// Assume fetch for node or browser with file access; for simplicity, assume arrayBuffer is provided
// In practice, use fs in Node or FileReader in browser
const fs = require('fs'); // For Node
const data = fs.readFileSync(this.filepath);
const view = new DataView(data.buffer);
const uint8 = new Uint8Array(data.buffer);
this.magic = String.fromCharCode(...uint8.slice(0, 4));
this.version = view.getUint32(4, true);
this.offsetToList = view.getBigUint64(0x4C, true);
this.offsetToData = view.getBigUint64(0x5E, true);
const listSize = Number(this.offsetToData - this.offsetToList);
this.numChunks = listSize / 3;
for (let i = 0; i < this.numChunks; i++) {
const pos = Number(this.offsetToList) + i * 3;
const b1 = uint8[pos];
const b2 = uint8[pos + 1];
const b3 = uint8[pos + 2];
const length = (b1 << 16) | (b3 << 8) | b2;
this.chunkLengths.push(length);
}
let pos = Number(this.offsetToData);
for (let length of this.chunkLengths) {
const chunk = uint8.slice(pos, pos + length);
this.compressedChunks.push(chunk);
pos += length;
}
this.uncompressedSize = (this.numChunks - 1) * 65536 + (this.decompressChunk(this.compressedChunks[this.compressedChunks.length - 1]).length || 0);
}
decompressChunk(chunk) {
// JS zlib library needed, e.g., pako
const pako = require('pako');
return pako.inflateRaw(chunk);
}
async decode() {
this.decompressedData = new Uint8Array(0);
for (let chunk of this.compressedChunks) {
const decompressed = this.decompressChunk(chunk);
const newData = new Uint8Array(this.decompressedData.length + decompressed.length);
newData.set(this.decompressedData);
newData.set(decompressed, this.decompressedData.length);
this.decompressedData = newData;
}
}
printProperties() {
console.log(`Magic: ${this.magic}`);
console.log(`Version: 0x${this.version.toString(16)}`);
console.log(`Offset to chunk lengths list: 0x${this.offsetToList.toString(16)}`);
console.log(`Offset to first chunk: 0x${this.offsetToData.toString(16)}`);
console.log(`Number of chunks: ${this.numChunks}`);
console.log(`Chunk lengths: ${this.chunkLengths.join(', ')}`);
console.log(`Compression type: zlib`);
console.log(`Encryption: ${this.magic.toLowerCase().includes('encrypted') ? 'Yes' : 'No'} (basic check)`);
console.log(`Multi-volume: ${this.filepath.endsWith('.001') ? 'Yes' : 'No'} (basic check)`);
console.log(`Uncompressed size: ${this.uncompressedSize} bytes`);
}
async write(newFilepath) {
if (!this.decompressedData) throw new Error('No data to write');
const chunks = [];
for (let i = 0; i < this.decompressedData.length; i += 65536) {
const end = Math.min(i + 65536, this.decompressedData.length);
chunks.push(this.decompressedData.subarray(i, end));
}
this.chunkLengths = [];
this.compressedChunks = [];
const pako = require('pako');
for (let chunk of chunks) {
const compressed = pako.deflateRaw(chunk);
this.chunkLengths.push(compressed.length);
this.compressedChunks.push(compressed);
}
this.numChunks = chunks.length;
this.uncompressedSize = this.decompressedData.length;
// Build list
const listBytes = new Uint8Array(this.numChunks * 3);
for (let j = 0; j < this.numChunks; j++) {
const length = this.chunkLengths[j];
const b1 = (length >> 16) & 0xFF;
const b2 = length & 0xFF;
const b3 = (length >> 8) & 0xFF;
listBytes[j * 3] = b1;
listBytes[j * 3 + 1] = b2;
listBytes[j * 3 + 2] = b3;
}
// Header
this.offsetToList = BigInt(0x66);
this.offsetToData = this.offsetToList + BigInt(this.numChunks * 3);
const header = new Uint8Array(0x66);
const headerView = new DataView(header.buffer);
header.set(new Uint8Array([68, 65, 65, 0])); // DAA\0
headerView.setUint32(4, this.version, true);
headerView.setBigUint64(0x4C, this.offsetToList, true);
headerView.setBigUint64(0x5E, this.offsetToData, true);
// Write
const fs = require('fs');
const fullData = new Uint8Array(header.length + listBytes.length + this.compressedChunks.reduce((a, c) => a + c.length, 0));
fullData.set(header);
fullData.set(listBytes, header.length);
let pos = header.length + listBytes.length;
for (let chunk of this.compressedChunks) {
fullData.set(chunk, pos);
pos += chunk.length;
}
fs.writeFileSync(newFilepath, fullData);
}
}
// Example usage in Node
// const daa = new DAAFile('example.daa');
// await daa.read();
// await daa.decode();
// daa.printProperties();
// await daa.write('new.daa');
- C class (using C++ for class support):
#include <fstream>
#include <iostream>
#include <vector>
#include <zlib.h>
#include <cstring>
class DAAFile {
private:
std::string filepath;
std::string magic;
uint32_t version;
uint64_t offsetToList;
uint64_t offsetToData;
uint32_t numChunks;
std::vector<uint32_t> chunkLengths;
uint64_t uncompressedSize;
std::vector<std::vector<uint8_t>> compressedChunks;
std::vector<uint8_t> decompressedData;
public:
DAAFile(const std::string& filepath) : filepath(filepath), version(0), offsetToList(0), offsetToData(0), numChunks(0), uncompressedSize(0) {}
void read() {
std::ifstream f(filepath, std::ios::binary);
f.seekg(0, std::ios::end);
size_t size = f.tellg();
f.seekg(0, std::ios::beg);
std::vector<uint8_t> data(size);
f.read((char*)data.data(), size);
f.close();
magic = std::string(data.begin(), data.begin() + 4);
memcpy(&version, data.data() + 4, 4);
memcpy(&offsetToList, data.data() + 0x4C, 8);
memcpy(&offsetToData, data.data() + 0x5E, 8);
uint64_t listSize = offsetToData - offsetToList;
numChunks = listSize / 3;
chunkLengths.resize(numChunks);
for (uint32_t i = 0; i < numChunks; i++) {
uint32_t pos = offsetToList + i * 3;
uint8_t b1 = data[pos];
uint8_t b2 = data[pos + 1];
uint8_t b3 = data[pos + 2];
chunkLengths[i] = (b1 << 16) | (b3 << 8) | b2;
}
uint32_t pos = offsetToData;
compressedChunks.resize(numChunks);
for (uint32_t i = 0; i < numChunks; i++) {
uint32_t length = chunkLengths[i];
compressedChunks[i].resize(length);
memcpy(compressedChunks[i].data(), data.data() + pos, length);
pos += length;
}
uncompressedSize = (numChunks - 1) * 65536ULL + (decompressChunk(compressedChunks.back()).size() if numChunks > 0 else 0);
}
std::vector<uint8_t> decompressChunk(const std::vector<uint8_t>& chunk) {
uLongf destLen = 65536;
std::vector<uint8_t> output(destLen);
int ret = uncompress2(output.data(), &destLen, chunk.data(), (uLongf*)&chunk.size());
if (ret != Z_OK) {
std::cerr << "Decompression error" << std::endl;
return {};
}
output.resize(destLen);
return output;
}
void decode() {
decompressedData.clear();
for (const auto& chunk : compressedChunks) {
auto d = decompressChunk(chunk);
decompressedData.insert(decompressedData.end(), d.begin(), d.end());
}
}
void printProperties() {
std::cout << "Magic: " << magic << std::endl;
std::cout << "Version: 0x" << std::hex << version << std::endl;
std::cout << "Offset to chunk lengths list: 0x" << std::hex << offsetToList << std::endl;
std::cout << "Offset to first chunk: 0x" << std::hex << offsetToData << std::endl;
std::cout << "Number of chunks: " << numChunks << std::endl;
std::cout << "Chunk lengths: ";
for (auto l : chunkLengths) std::cout << l << " ";
std::cout << std::endl;
std::cout << "Compression type: zlib" << std::endl;
std::cout << "Encryption: " << (magic.find("encrypted") != std::string::npos ? "Yes" : "No") << " (basic check)" << std::endl;
std::cout << "Multi-volume: " << (filepath.find(".001") != std::string::npos ? "Yes" : "No") << " (basic check)" << std::endl;
std::cout << "Uncompressed size: " << uncompressedSize << " bytes" << std::endl;
}
void write(const std::string& newFilepath) {
if (decompressedData.empty()) {
std::cerr << "No data to write" << std::endl;
return;
}
std::vector<std::vector<uint8_t>> chunks;
for (size_t i = 0; i < decompressedData.size(); i += 65536) {
size_t end = std::min(i + 65536, decompressedData.size());
std::vector<uint8_t> chunk(decompressedData.begin() + i, decompressedData.begin() + end);
chunks.push_back(chunk);
}
chunkLengths.clear();
compressedChunks.clear();
for (const auto& chunk : chunks) {
uLongf destLen = compressBound(chunk.size());
std::vector<uint8_t> compressed(destLen);
int ret = compress2(compressed.data(), &destLen, chunk.data(), chunk.size(), 9);
if (ret != Z_OK) {
std::cerr << "Compression error" << std::endl;
return;
}
compressed.resize(destLen);
chunkLengths.push_back(destLen);
compressedChunks.push_back(compressed);
}
numChunks = chunks.size();
uncompressedSize = decompressedData.size();
// Build list
std::vector<uint8_t> listBytes(numChunks * 3);
for (uint32_t j = 0; j < numChunks; j++) {
uint32_t length = chunkLengths[j];
uint8_t b1 = (length >> 16) & 0xFF;
uint8_t b2 = length & 0xFF;
uint8_t b3 = (length >> 8) & 0xFF;
listBytes[j * 3] = b1;
listBytes[j * 3 + 1] = b2;
listBytes[j * 3 + 2] = b3;
}
// Header
offsetToList = 0x66;
offsetToData = offsetToList + numChunks * 3;
std::vector<uint8_t> header(0x66, 0);
memcpy(header.data(), "DAA\0", 4);
memcpy(header.data() + 4, &version, 4);
memcpy(header.data() + 0x4C, &offsetToList, 8);
memcpy(header.data() + 0x5E, &offsetToData, 8);
// Write
std::ofstream f(newFilepath, std::ios::binary);
f.write((char*)header.data(), header.size());
f.write((char*)listBytes.data(), listBytes.size());
for (const auto& chunk : compressedChunks) {
f.write((char*)chunk.data(), chunk.size());
}
f.close();
}
};
// Example usage
// DAAFile daa("example.daa");
// daa.read();
// daa.decode();
// daa.printProperties();
// daa.write("new.daa");