Task 336: .JUMP File Format
Task 336: .JUMP File Format
File Format Specifications for the .JUMP File Format
Based on extensive research, there is no standard file format with the exact extension ".JUMP". The closest and most relevant match is the Windows Jump List format, which is used by Windows operating systems (from Windows 7 onward) to store quick access lists for applications. These files are typically saved with extensions .automaticDestinations-ms
(for automatic lists) or .customDestinations-ms
(for custom lists), but they are commonly referred to as "jump list files" in forensic and technical contexts. The task appears to refer to this format, as it aligns with the term "JUMP" and has detailed specifications available.
The Windows Jump List format is designed to track user interactions with applications, files, and tasks. Automatic Jump Lists are generated by Windows, while Custom Jump Lists are defined by application developers. The format for automatic Jump Lists is based on the Compound File Binary (CFB) format (also known as OLE Compound File), which is like a mini-file system containing streams and storage. Custom Jump Lists are simpler, consisting of concatenated LNK (Windows Shortcut) binary segments.
I will describe the specifications for the automatic Jump List format, as it is the more comprehensive and commonly analyzed one. The custom format is a sequence of MS-SHLLINK (LNK) structures, which are documented in the Microsoft [MS-SHLLINK] specification.
Automatic Jump List File Format Specifications
- File Extension:
.automaticDestinations-ms
(e.g.,1bc392b8e104a00e.automaticDestinations-ms
, where the prefix is the AppID for the application). - Overall Structure: OLE Compound File (CFB) with multiple streams. Key streams include numbered streams (e.g., "0", "1", "2") containing LNK data, and a "DestList" stream that acts as an MRU/MFU (Most Recently/Frequently Used) list.
- Header: Standard OLE Compound File header (512 bytes, starting with magic bytes
D0 CF 11 E0 A1 B1 1A E1
). - Streams:
- Numbered streams: Each is a full LNK file (MS-SHLLINK binary format) representing a jump list entry (e.g., a recently opened file).
- "DestList" stream: Contains a header and entries linking to the numbered streams.
- DestList Stream Structure:
- 32-byte header.
- Variable number of entries, each 114 bytes + variable-length Unicode string (UTF-16LE).
- LNK Stream Structure: Each numbered stream follows the MS-SHLLINK format, which includes headers, link target ID lists, link info, data strings, and extra data blocks.
List of all the properties of this file format intrinsic to its file system:
- File System Metadata (extrinsic to the file itself, but intrinsic as part of Windows NTFS):
- File creation timestamp (FILETIME structure, 8 bytes).
- File modification timestamp (FILETIME, 8 bytes).
- File access timestamp (FILETIME, 8 bytes).
- File size (in bytes).
- AppID (hex string in filename, identifying the application, e.g., "1bc392b8e104a00e" for Remote Desktop).
- OLE Compound File Properties:
- Magic bytes (8 bytes: D0 CF 11 E0 A1 B1 1A E1).
- Minor version (2 bytes).
- Major version (2 bytes).
- Byte order (2 bytes, usually FE FF for little-endian).
- Sector shift (2 bytes).
- Mini sector shift (2 bytes).
- Number of directory sectors (4 bytes).
- Number of FAT sectors (4 bytes).
- First directory sector location (4 bytes).
- Transaction signature number (4 bytes).
- Mini stream cutoff size (4 bytes).
- First mini FAT sector location (4 bytes).
- Number of mini FAT sectors (4 bytes).
- First DIFAT sector location (4 bytes).
- Number of DIFAT sectors (4 bytes).
- DIFAT array (436 bytes).
- DestList Stream Properties (MRU/MFU list):
- Header (32 bytes, including version, entry count, pinned entry count, etc.).
- For each entry:
- Checksum (8 bytes).
- Pin status (4 bytes, -1 if pinned).
- Frequency count (4 bytes, number of times accessed).
- NetBIOS name (16 bytes, padded with zeros).
- Stream number (8 bytes, linking to the numbered LNK stream).
- Last modification time (FILETIME, 8 bytes).
- Path string size (2 bytes, number of UTF-16 characters).
- Path string (variable length, UTF-16LE, full path to the target file/folder).
- LNK Stream Properties (for each numbered stream, from MS-SHLLINK):
- Shell link header (76 bytes, including link flags, file attributes, creation/mod/access times, file size, icon index, show command, hotkey).
- Link target ID list (variable, shell item IDs for the target).
- Link info (variable, volume ID, local base path, common network relative link).
- Name string (variable, UTF-16LE).
- Relative path (variable, UTF-16LE).
- Working directory (variable, UTF-16LE).
- Command line arguments (variable, UTF-16LE).
- Icon location (variable, UTF-16LE).
- Extra data blocks (variable, e.g., tracker data with machine ID, MAC address, birth timestamps).
- Pinned status (derived from DestList).
- Target arguments (for tasks).
- Target file size.
- Application (derived from AppID).
Two direct download links for files of format .JUMP:
After extensive searching, no direct download links for sample .JUMP or .automaticDestinations-ms files were found. These files are system-generated and contain sensitive user data, so they are not typically shared publicly to avoid privacy issues. Forensic tools like JLECmd (from GitHub) can be used to generate or parse them from your own system, but no samples are available in repositories or forensic datasets I could access. If this is a custom or fictional format, no public samples exist.
Ghost blog embedded HTML JavaScript for drag and drop .JUMP file dump:
Here's an embedded HTML with JavaScript that can be placed in a Ghost blog post. It allows drag-and-drop of a .JUMP file (assuming automatic Jump List format). It uses a simple binary parser to extract and dump properties to the screen (note: full OLE parsing in JS is complex; this is a simplified version using DataView for DestList; full LNK parsing is sketched but not complete for brevity).
- Python class for opening, decoding, reading, writing, and printing .JUMP properties:
Usingolefile
library to parse OLE, and manual parsing for DestList and LNK (simplified LNK parsing for brevity).
import olefile
from struct import unpack
import datetime
class JumpFile:
def __init__(self, filename):
self.filename = filename
self.ole = None
self.properties = {}
self.dest_list = []
def open(self):
self.ole = olefile.OleFileIO(self.filename)
self.read_properties()
def read_properties(self):
# Read file system properties
self.properties['filename'] = self.filename
self.properties['creation_time'] = self.ole.getctime()
self.properties['modification_time'] = self.ole.getmtime()
# Parse DestList stream
if 'DestList' in self.ole.listdir():
dest_list_stream = self.ole.openstream('DestList')
data = dest_list_stream.read()
offset = 32 # Skip header
while offset + 114 < len(data):
netbios = data[offset + 0x48:offset + 0x48 + 16].decode('utf-8', errors='ignore').rstrip('\0')
stream_num = unpack('<Q', data[offset + 0x58:offset + 0x58 + 8])[0]
last_mod = unpack('<Q', data[offset + 0x64:offset + 0x64 + 8])[0]
last_mod_time = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=last_mod / 10)
path_size = unpack('<H', data[offset + 0x70:offset + 0x70 + 2])[0]
path = data[offset + 0x72:offset + 0x72 + path_size * 2].decode('utf-16le')
self.dest_list.append({
'netbios': netbios,
'stream_num': stream_num,
'last_mod': last_mod_time,
'path': path
})
offset += 114 + path_size * 2
# Parse LNK streams (simplified, add full LNK parser if needed)
def print_properties(self):
print("File Properties:")
for k, v in self.properties.items():
print(f"{k}: {v}")
print("\nDestList Entries:")
for entry in self.dest_list:
print(entry)
def write(self, new_filename):
# Simplified write: copy and modify (full write would require recreating OLE)
with open(new_filename, 'wb') as f:
with open(self.filename, 'rb') as original:
f.write(original.read())
print("File written (copy)")
def close(self):
self.ole.close()
# Usage
if __name__ == '__main__':
j = JumpFile('example.JUMP')
j.open()
j.print_properties()
j.write('new.JUMP')
j.close()
- Java class for opening, decoding, reading, writing, and printing .JUMP properties:
Using Apache POI for OLE parsing (assume POI dependency).
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.Entry;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
public class JumpFile {
private String filename;
private POIFSFileSystem ole;
private Map<String, Object> properties = new HashMap<>();
private ArrayList<Map<String, Object>> destList = new ArrayList<>();
public JumpFile(String filename) {
this.filename = filename;
}
public void open() throws IOException {
ole = new POIFSFileSystem(new FileInputStream(filename));
readProperties();
}
private void readProperties() throws IOException {
properties.put("filename", filename);
properties.put("creation_time", new Date(ole.getRoot().getCreationTime()));
properties.put("modification_time", new Date(ole.getRoot().getModifyTime()));
// Parse DestList
DirectoryNode root = ole.getRoot();
Iterator<Entry> entries = root.iterator();
while (entries.hasNext()) {
Entry entry = entries.next();
if (entry.getName().equals("DestList")) {
byte[] data = new byte[entry.getSize()];
ole.createDocumentInputStream(entry.getName()).read(data);
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
int offset = 32;
while (offset + 114 < data.length) {
bb.position(offset + 0x48);
String netbios = new String(data, offset + 0x48, 16, "UTF-8").trim();
long streamNum = bb.getLong(offset + 0x58);
long lastModRaw = bb.getLong(offset + 0x64);
Date lastMod = new Date((lastModRaw / 10000) - 11644473600000L); // FILETIME to Date
short pathSize = bb.getShort(offset + 0x70);
String path = new String(data, offset + 0x72, pathSize * 2, "UTF-16LE");
Map<String, Object> entryMap = new HashMap<>();
entryMap.put("netbios", netbios);
entryMap.put("stream_num", streamNum);
entryMap.put("last_mod", lastMod);
entryMap.put("path", path);
destList.add(entryMap);
offset += 114 + pathSize * 2;
}
}
}
// Parse LNK streams (simplified)
}
public void printProperties() {
System.out.println("File Properties:");
properties.forEach((k, v) -> System.out.println(k + ": " + v));
System.out.println("\nDestList Entries:");
destList.forEach(System.out::println);
}
public void write(String newFilename) throws IOException {
// Simplified copy write
ole.writeFilesystem(new FileOutputStream(newFilename));
System.out.println("File written");
}
public void close() throws IOException {
ole.close();
}
public static void main(String[] args) throws IOException {
JumpFile j = new JumpFile("example.JUMP");
j.open();
j.printProperties();
j.write("new.JUMP");
j.close();
}
}
- JavaScript class for opening, decoding, reading, writing, and printing .JUMP properties:
(Node.js style, using fs for read/write; binary parsing with Buffer).
const fs = require('fs');
class JumpFile {
constructor(filename) {
this.filename = filename;
this.properties = {};
this.destList = [];
}
open() {
const data = fs.readFileSync(this.filename);
const buffer = Buffer.from(data);
// Check OLE magic
if (buffer.readUInt32LE(0) !== 0xE011CFD0 || buffer.readUInt32LE(4) !== 0xE11AB1A1) {
console.log('Invalid OLE file');
return;
}
this.properties.filename = this.filename;
this.properties.creation_time = fs.statSync(this.filename).birthtime;
this.properties.modification_time = fs.statSync(this.filename).mtime;
// Assume DestList location (simplified; use ole-js library for full)
const destListOffset = 512; // Placeholder
let offset = destListOffset + 32;
while (offset + 114 < buffer.length) {
const netbios = buffer.slice(offset + 0x48, offset + 0x48 + 16).toString('utf8').replace(/\0/g, '');
const streamNum = buffer.readBigUInt64LE(offset + 0x58);
const lastModRaw = buffer.readBigUInt64LE(offset + 0x64);
const lastMod = new Date(Number(lastModRaw / 10000n) - 11644473600000);
const pathSize = buffer.readUInt16LE(offset + 0x70);
const path = buffer.slice(offset + 0x72, offset + 0x72 + pathSize * 2).toString('utf16le');
this.destList.push({ netbios, streamNum, lastMod, path });
offset += 114 + pathSize * 2;
}
// LNK parsing skipped for brevity
}
printProperties() {
console.log('File Properties:');
console.log(this.properties);
console.log('\nDestList Entries:');
console.log(this.destList);
}
write(newFilename) {
fs.copyFileSync(this.filename, newFilename);
console.log('File written (copy)');
}
}
// Usage
const j = new JumpFile('example.JUMP');
j.open();
j.printProperties();
j.write('new.JUMP');
- C class for opening, decoding, reading, writing, and printing .JUMP properties:
(Using C++; assume libole2 or manual, but simplified manual parsing for brevity).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdint.h>
struct DestEntry {
char netbios[17];
uint64_t stream_num;
time_t last_mod;
char* path;
};
class JumpFile {
private:
char* filename;
FILE* fp;
struct stat file_stat;
DestEntry* dest_list;
int dest_count;
public:
JumpFile(const char* fn) {
filename = strdup(fn);
dest_list = NULL;
dest_count = 0;
}
~JumpFile() {
free(filename);
for (int i = 0; i < dest_count; i++) free(dest_list[i].path);
free(dest_list);
}
void open() {
fp = fopen(filename, "rb");
if (!fp) {
printf("Error opening file\n");
return;
}
stat(filename, &file_stat);
// Read data (simplified, load whole file)
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
fseek(fp, 0, SEEK_SET);
uint8_t* data = (uint8_t*)malloc(size);
fread(data, 1, size, fp);
// Check OLE
if (*(uint32_t*)data != 0xE011CFD0 || *(uint32_t*)(data+4) != 0xE11AB1A1) {
printf("Invalid OLE\n");
free(data);
return;
}
// Assume DestList at offset
int offset = 512 + 32; // Placeholder
while (offset + 114 < size) {
dest_list = (DestEntry*)realloc(dest_list, sizeof(DestEntry) * (++dest_count));
strncpy(dest_list[dest_count-1].netbios, (char*)(data + offset + 0x48), 16);
dest_list[dest_count-1].netbios[16] = '\0';
dest_list[dest_count-1].stream_num = *(uint64_t*)(data + offset + 0x58);
uint64_t last_mod_raw = *(uint64_t*)(data + offset + 0x64);
dest_list[dest_count-1].last_mod = (time_t)((last_mod_raw / 10000000) - 11644473600LL);
uint16_t path_size = *(uint16_t*)(data + offset + 0x70);
dest_list[dest_count-1].path = (char*)malloc(path_size * 2 + 1);
memcpy(dest_list[dest_count-1].path, data + offset + 0x72, path_size * 2);
dest_list[dest_count-1].path[path_size * 2] = '\0';
offset += 114 + path_size * 2;
}
free(data);
}
void printProperties() {
printf("Filename: %s\n", filename);
printf("Creation Time: %s", ctime(&file_stat.st_ctime));
printf("Modification Time: %s", ctime(&file_stat.st_mtime));
printf("\nDestList Entries:\n");
for (int i = 0; i < dest_count; i++) {
printf("NetBIOS: %s\nStream Num: %llu\nLast Mod: %sPath: %ls\n\n",
dest_list[i].netbios, dest_list[i].stream_num, ctime(&dest_list[i].last_mod), (wchar_t*)dest_list[i].path);
}
}
void write(const char* new_fn) {
FILE* out = fopen(new_fn, "wb");
fseek(fp, 0, SEEK_SET);
uint8_t buf[1024];
size_t read;
while ((read = fread(buf, 1, 1024, fp)) > 0) {
fwrite(buf, 1, read, out);
}
fclose(out);
printf("File written\n");
}
void close() {
if (fp) fclose(fp);
}
};
int main() {
JumpFile j("example.JUMP");
j.open();
j.printProperties();
j.write("new.JUMP");
j.close();
return 0;
}