1
1
mirror of https://github.com/wader/fq.git synced 2024-11-23 09:56:07 +03:00

apple bookmarkdata decoder initial commit

This commit is contained in:
David McDonald 2022-11-22 23:31:19 -06:00
parent 1dd9c8759a
commit 71b17d0382
2 changed files with 277 additions and 0 deletions

276
format/bookmark/bookmark.go Normal file
View File

@ -0,0 +1,276 @@
package bplist
import (
"embed"
"time"
"github.com/wader/fq/format"
"github.com/wader/fq/pkg/decode"
"github.com/wader/fq/pkg/interp"
"github.com/wader/fq/pkg/scalar"
)
//go:embed bplist.jq bplist.md
var bookmarkFS embed.FS
func init() {
interp.RegisterFormat(decode.Format{
Name: format.BOOKMARK,
ProbeOrder: format.ProbeOrderBinUnique,
Description: "Apple BookmarkData",
Groups: []string{format.PROBE},
DecodeFn: bookmarkDecode,
Functions: []string{},
//Functions: []string{"torepr"},
})
interp.RegisterFS(bookmarkFS)
}
const (
dataTypeString = 0x0101
dataTypeData = 0x0201
dataTypeNumber8 = 0x0301
dataTypeNumber16 = 0x0302
dataTypeNumber32 = 0x0303
dataTypeNumber64 = 0x0304
dataTypeNumber32F = 0x0305
dataTypeNumber64F = 0x0306
dataTypeDate = 0x0400
dataTypeBooleanFalse = 0x0500
dataTypeBooleanTrue = 0x0501
dataTypeArray = 0x0601
dataTypeDictionary = 0x0701
dataTypeUUID = 0x0801
dataTypeURL = 0x0901
dataTypeRelativeURL = 0x0902
)
var dataTypeMap = scalar.UToScalar{
dataTypeString: {Sym: "String", Description: "UTF-8 String"},
dataTypeData: {Sym: "Data", Description: "Raw bytes"},
dataTypeNumber8: {Sym: "Byte", Description: "(signed 8-bit) 1-byte number"},
dataTypeNumber16: {Sym: "Short", Description: "(signed 16-bit) 2-byte number"},
dataTypeNumber32: {Sym: "Int", Description: "(signed 32-bit) 4-byte number"},
dataTypeNumber64: {Sym: "Long", Description: "(signed 64-bit) 8-byte number"},
dataTypeNumber32F: {Sym: "Float", Description: "(32-bit float) IEEE single precision"},
dataTypeNumber64F: {Sym: "Double", Description: "(64-bit float) IEEE double precision"},
dataTypeDate: {Sym: "Date", Description: "Big-endian IEEE double precision seconds since 2001-01-01 00:00:00 UTC"},
dataTypeBooleanFalse: {Sym: "BooleanFalse", Description: "(false)"},
dataTypeBooleanTrue: {Sym: "BooleanTrue", Description: "(true)"},
dataTypeArray: {Sym: "Array", Description: "Array of 4-byte offsets to data items"},
dataTypeDictionary: {Sym: "Dictionary", Description: "Array of pairs of 4-byte (key, value) data item offsets"},
dataTypeUUID: {Sym: "UUID", Description: "Raw bytes"},
dataTypeURL: {Sym: "URL", Description: "UTF-8 string"},
dataTypeRelativeURL: {Sym: "RelativeURL", Description: "4-byte offset to base URL, 4-byte offset to UTF-8 string"},
}
const (
elementTypeTargetURL = 0x1003
elementTypeTargetPath = 0x1004
elementTypeTargetCNIDPath = 0x1005
elementTypeTargetFlags = 0x1010
elementTypeTargetFilename = 0x1020
elementTypeCNID = 0x1030
elementTypeTargetCreationDate = 0x1040
elementTypeUnknown1 = 0x1054
elementTypeUnknown2 = 0x1055
elementTypeUnknown3 = 0x1056
elementTypeUnknown4 = 0x1101
elementTypeUnknown5 = 0x1102
elementTypeTOCPath = 0x2000
elementTypeVolumePath = 0x2002
elementTypeVolumeURL = 0x2005
elementTypeVolumeName = 0x2010
elementTypeVolumeUUID = 0x2011
elementTypeVolumeSize = 0x2012
elementTypeVolumeCreationDate = 0x2013
elementTypeVolumeFlags = 0x2020
elementTypeVolumeIsRoot = 0x2030
elementTypeVolumeBookmark = 0x2040
elementTypeVolumeMountPointURL = 0x2050
elementTypeUnknown6 = 0x2070
elementTypeContainingFolderIndex = 0xc001
elementTypeCreatorUsername = 0xc011
elementTypeCreatorUID = 0xc012
elementTypeFileReferenceFlag = 0xd001
elementTypeCreationOptions = 0xd010
elementTypeURLLengthArray = 0xe003
elementTypeDisplayName = 0xf017
elementTypeIconData = 0xf020
elementTypeIconImageData = 0xf021
elementTypeTypeBindingInfo = 0xf022
elementTypeBookmarkCreationTime = 0xf030
elementTypeSandboxRWExtension = 0xf080
elementTypeSandboxROExtension = 0xf081
)
var elementTypeMap = scalar.UToScalar{
elementTypeTargetURL: {Sym: "Target URL", Description: "A URL"},
elementTypeTargetPath: {Sym: "Target path", Description: "Array of individual path components"},
elementTypeTargetCNIDPath: {Sym: "Target CNID path", Description: "Array of CNIDs"},
elementTypeTargetFlags: {Sym: "Target flags", Description: "Data - see below"},
elementTypeTargetFilename: {Sym: "Target filename", Description: "String"},
elementTypeCNID: {Sym: "Target CNID", Description: "4-byte integer"},
elementTypeTargetCreationDate: {Sym: "Target creation date", Description: "Date"},
elementTypeUnknown1: {Sym: "Unknown", Description: "Unknown"},
elementTypeUnknown2: {Sym: "Unknown", Description: "Unknown"},
elementTypeUnknown3: {Sym: "Unknown", Description: "Unknown"},
elementTypeUnknown4: {Sym: "Unknown", Description: "Unknown"},
elementTypeUnknown5: {Sym: "Unknown", Description: "Unknown"},
elementTypeTOCPath: {Sym: "TOC path", Description: "Array - see below"},
elementTypeVolumePath: {Sym: "Volume path", Description: "Array of individual path components"},
elementTypeVolumeURL: {Sym: "Volume URL", Description: "URL of volume root"},
elementTypeVolumeName: {Sym: "Volume name", Description: "String"},
elementTypeVolumeUUID: {Sym: "Volume UUID", Description: "String (not a UUID!)"},
elementTypeVolumeSize: {Sym: "Volume size", Description: "8-byte integer"},
elementTypeVolumeCreationDate: {Sym: "Volume creation date", Description: "Date"},
elementTypeVolumeFlags: {Sym: "Volume flags", Description: "Data - see below"},
elementTypeVolumeIsRoot: {Sym: "Volume is root", Description: "True if the volume was the filesystem root"},
elementTypeVolumeBookmark: {Sym: "Volume bookmark", Description: "TOC identifier for disk image"},
elementTypeVolumeMountPointURL: {Sym: "Volume mount point", Description: "URL"},
elementTypeUnknown6: {Sym: "Unknown", Description: "Unknown"},
elementTypeContainingFolderIndex: {Sym: "Containing folder index", Description: "Integer index of containing folder in target path array"},
elementTypeCreatorUsername: {Sym: "Creator username", Description: "Name of user that created bookmark"},
elementTypeCreatorUID: {Sym: "Creator UID", Description: "UID of user that created bookmark"},
elementTypeFileReferenceFlag: {Sym: "File reference flag", Description: "True if creating URL was a file reference URL"},
elementTypeCreationOptions: {Sym: "Creation options", Description: "Integer containing flags passed to CFURLCreateBookmarkData"},
elementTypeURLLengthArray: {Sym: "URL length array", Description: "Array of integers - see below"},
elementTypeDisplayName: {Sym: "Display name", Description: "String"},
elementTypeIconData: {Sym: "Icon data", Description: "icns format data"},
elementTypeIconImageData: {Sym: "Icon image", Description: "Data"},
elementTypeTypeBindingInfo: {Sym: "Type binding info", Description: "dnib byte array"},
elementTypeBookmarkCreationTime: {Sym: "Bookmark creation time", Description: "64-bit float seconds since January 1st 2001"},
elementTypeSandboxRWExtension: {Sym: "Sandbox RW extension", Description: "Looks like a hash with data and an access right"},
elementTypeSandboxROExtension: {Sym: "Sandbox RO extension", Description: "As above"},
}
var cocoaTimeEpochDate = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.UTC)
type tocHeader struct {
tocSize uint64
nextTOCOffset uint64
numEntries uint64
}
func decodeTOCHeader(d *decode.D) *tocHeader {
var hdr *tocHeader
d.FieldStruct("first_toc", func(d *decode.D) {
hdr.tocSize = d.FieldU32("toc_size")
d.FieldU32("magic", d.AssertU(0xfffffffe))
d.FieldU32("identifier")
hdr.nextTOCOffset = d.FieldU32("next_toc_offset")
hdr.numEntries = d.FieldU32("num_entries_in_toc")
})
return hdr
}
type tocEntry struct {
key uint64
recordOffset uint64
}
func decodeTOCEntry(d *decode.D) *tocEntry {
var entry *tocEntry
entry.key = d.FieldU32("key")
entry.recordOffset = d.FieldU32("offset_to_record")
d.FieldU32("reserved")
return entry
}
const (
arrayEntrySize = 4
dictEntrySize = 4
)
func decodeRecord(d *decode.D, offset uint64) {
d.FieldStruct("record", func(d *decode.D) {
n := int(d.FieldU32("length"))
typ := d.FieldU32("type", dataTypeMap)
switch typ {
case dataTypeString:
d.FieldUTF8("data", n)
case dataTypeData:
d.FieldRawLen("data", int64(n*8))
case dataTypeNumber8:
d.FieldS8("data")
case dataTypeNumber16:
d.FieldS16("data")
case dataTypeNumber32:
d.FieldS32("data")
case dataTypeNumber64:
d.FieldS64("data")
case dataTypeNumber32F:
d.FieldF32("data")
case dataTypeNumber64F:
d.FieldF64("data")
case dataTypeDate:
d.FieldF64("data")
case dataTypeBooleanFalse:
case dataTypeBooleanTrue:
case dataTypeArray:
d.FieldStructNArray("data", "element", int64(n/arrayEntrySize), func(d *decode.D) {
offset := d.FieldU32("offset")
decodeRecord(d, offset)
})
case dataTypeDictionary:
d.FieldStructNArray("data", "element", int64(n/dictEntrySize), func(d *decode.D) {
keyOffset := d.FieldU32("key_offset")
d.FieldStruct("key", func(d *decode.D) {
decodeRecord(d, keyOffset)
})
valueOffset := d.FieldU32("value_offset")
d.FieldStruct("key", func(d *decode.D) {
decodeRecord(d, valueOffset)
})
})
case dataTypeUUID:
d.FieldRawLen("data", int64(n*8))
case dataTypeURL:
d.FieldUTF8("data", n)
case dataTypeRelativeURL:
baseOffset := d.FieldU32("base_url_offset")
d.FieldStruct("base_url", func(d *decode.D) {
decodeRecord(d, baseOffset)
})
suffixOffset := d.FieldU32("suffix_offset")
d.FieldStruct("suffix", func(d *decode.D) {
decodeRecord(d, suffixOffset)
})
}
})
}
func bookmarkDecode(d *decode.D, _ any) any {
d.FieldStruct("header", func(d *decode.D) {
d.FieldUTF8("magic", 4, d.AssertStr("book", "alis"))
d.FieldU32("total_size")
d.FieldU32("unknown")
d.FieldU32("header_size", d.AssertU(48))
})
tocOffset := d.FieldU32("first_toc_offset")
for tocOffset != 0 {
d.SeekAbs(int64(tocOffset), func(d *decode.D) {
tocHdr := decodeTOCHeader(d)
tocOffset = tocHdr.nextTOCOffset
d.FieldStructNArray("entries", "entry", int64(tocHdr.numEntries), func(d *decode.D) {
var entry *tocEntry
entry.key = d.FieldU32("key", elementTypeMap)
entry.recordOffset = d.FieldU32("offset_to_record")
d.FieldU32("reserved")
decodeRecord(d, entry.recordOffset)
})
})
}
return nil
}

View File

@ -49,6 +49,7 @@ const (
BITCOIN_BLOCK = "bitcoin_block"
BITCOIN_SCRIPT = "bitcoin_script"
BITCOIN_TRANSACTION = "bitcoin_transaction"
BOOKMARK = "apple_bookmarkdata"
BPLIST = "bplist"
BSD_LOOPBACK_FRAME = "bsd_loopback_frame"
BSON = "bson"