Skip to content

nyarime/gho

Repository files navigation

gho

Go Reference Go Report Card License: MIT

Pure Go library and CLI tool for parsing Norton Ghost GHO disk image files.

No C dependencies. No CGo. Single binary.

Features

  • Parse GHO file/partition headers and record structure
  • Decompress Fast LZ (Z1) compressed partitions
  • Create GHO images from raw partition data
  • Fixup GHO headers (ghofixup.exe equivalent — CD/span flag modification with PRNG cipher)
  • Extract MBR/Track 0 data and partition table
  • Stream decompression (constant memory, arbitrary image sizes)
  • Supports Ghost 11.x–12.x format

Install

# Library
go get github.com/nyarime/gho

# CLI tool
go install github.com/nyarime/gho/cmd/gho@latest

CLI Usage

# Show image info
gho info disk.gho

# Extract all partitions to a directory
gho extract disk.gho output/

# Create a GHO image from a partition image (+ optional MBR)
gho create output.gho partition.img mbr.bin

# Modify header flags (ghofixup.exe equivalent)
gho fixup disk.gho cd      # Set spanned/CD bit
gho fixup disk.gho cd-     # Clear spanned/CD bit
gho fixup disk.gho span    # Toggle CD flag

Example output:

GHO Image Summary
  File Type:   1 (1=single, 9=span)
  Compression: 2 (Fast/Z1)
  Image ID:    0x12345678
  MBR Partitions: 1
    P0: type=0x83 LBA=2016 size=102240 (49.9 MB)
  Data Partitions: 1
    Partition 0: 3 spans, 42816212 bytes compressed data

Decompressing partition 0...
Saved partition 0: output/partition-0.img (51.4 MB)

Library Usage

package main

import (
    "bytes"
    "fmt"
    "log"

    "github.com/nyarime/gho"
)

func main() {
    img, err := gho.Open("disk.gho")
    if err != nil {
        log.Fatal(err)
    }
    defer img.Close()

    // Print summary
    fmt.Print(img.Summary())

    // Access MBR partition table
    for i, p := range img.MBRPartitions() {
        fmt.Printf("Partition %d: type=%#x, LBA=%d, size=%d sectors\n",
            i, p.Type, p.LBAStart, p.LBASize)
    }

    // Decompress partition 0 to memory
    var buf bytes.Buffer
    if err := img.DecompressPartition(0, &buf); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Decompressed: %d bytes\n", buf.Len())
}

Create a GHO Image

w, _ := gho.Create("output.gho", gho.CompressionNone)
w.WriteTrack0(mbrData, 63)  // MBR + boot sectors
w.WritePartition(partFile)    // raw partition data (io.Reader)
w.Close()

Modify Header Flags (ghofixup)

// Set CD/spanned bit (like ghofixup.exe cd)
gho.ModifyHeader("disk.gho", gho.FixupCD)

// Toggle span flag
gho.ModifyHeader("disk.gho", gho.FixupSpan)

GHO Format

Norton Ghost GHO files store disk/partition images with the following structure:

┌──────────────────────────────────────┐
│  File Header (512 bytes)             │  Magic: FE EF, compression type, ID
├──────────────────────────────────────┤
│  Record: Track 0 (type 0x0006)       │  6-byte header + MBR + boot sectors
├──────────────────────────────────────┤
│  Record: Partition (type 0x0603)     │  20-byte partition descriptor
├──────────────────────────────────────┤
│  FEEF Partition Header (512 bytes)   │  Per-partition compression settings
├──────────────────────────────────────┤
│  Compressed Blocks                   │  [2B len][block data]...
│  ├─ Block: 2B stored_len + data      │  32KB decompressed per block
│  ├─ Block: ...                       │
│  └─ Block: ...                       │
├──────────────────────────────────────┤
│  Record: Continuation (type 0x0703)  │  Additional data spans
├──────────────────────────────────────┤
│  (optional FEEF + more blocks)       │
├──────────────────────────────────────┤
│  Record: End (type 0x0023)           │  End of image marker
└──────────────────────────────────────┘

Compression: Each block is 32KB decompressed. The first byte of block data indicates the type:

  • 0x01 → Uncompressed (raw data at offset 4)
  • Other → Fast LZ compressed (custom LZ77 variant with 4096-entry hash table)

Records: Every record has a 10-byte header: [4B type][4B magic 0x012F18D8][2B body_len]

Fast LZ Algorithm

The Fast LZ decompressor was reverse-engineered from Norton Ghost 11.5.1. It's a custom LZ77 variant using:

  • 16-bit control words (bit 0 = literal, bit 1 = match reference)
  • 4096-entry hash table with the hash function: h = ((-24993 * (b2 ^ (16 * (b1 ^ (16 * b0))))) >> 4) & 0xFFF
  • 2-byte match tokens encoding hash index + extra length
  • Minimum match length of 3 bytes

Supported Formats

Ghost Version Compression Status
11.x–12.x None (Z0) ✅ Read + Write
11.x–12.x Fast LZ (Z1)
11.x–12.x High/zlib (Z3–Z9) ✅ Read + Write
Encrypted images CRC-16 cipher ✅ Read + Write
Span files (.ghs) Multi-file ✅ Read

Contributing

Contributions welcome! Areas that need work:

  • Span file writing: Creating multi-file (.ghs) span images
  • Full disk images: Currently supports partition-level images; whole-disk with MBR rebuild is planned

License

MIT — see LICENSE.

Credits

Reverse-engineered from Norton Ghost 11.5.1 by Nyarime.

Part of the Nyarc firmware analysis toolkit.

About

Pure Go Norton Ghost GHO disk image parser — no CGo, single binary

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages