Skip to content

Instantly share code, notes, and snippets.

@jvehent
Created April 17, 2018 19:33
Show Gist options
  • Save jvehent/f7d5bf53cfe57bafb579b0a8c3c46fee to your computer and use it in GitHub Desktop.
Save jvehent/f7d5bf53cfe57bafb579b0a8c3c46fee to your computer and use it in GitHub Desktop.
$ go run main.go
Header: MAR ID="MAR1", Offset to Index=45801756
Signatures Header: FileSize=45804305, NumSignatures=1
* Signature 0 Entry Header: Algorithm="RSA-PKCS1-SHA384", Size=512
* Signature 0 Data (len=512): 89F1914AA881AF7876F5EA22E4B047475AC90B270BC68CBFADB3B94E7942107C45F438B1C0391BEF0941D15F37C662F524EB2DC74634764CFC479F15B1C75ECD7A7423BF613F07BF648ADE38F3C19B2B8D567912E7D2A51D2F0592B59EE9185016446B55446450D9F472A34D05FEF0FD1A79E4AA0D8EEDAB57500013DEBCABD9CEE4BF3772D8262383C4CCCA721C6CB38A87C5EDA7157E3E14F4E413CA1668BCEE5F70F36AF4E962449DC22223CBC16C5255974836D413C1F62CABAEE12559A9EC0723FBCF00A27C8A13D04CE4F25E94A67C25328C0A86F948CBA00FA189FC0EF2007D8947B97749DF693C3966226210E7A9849784F7D58723ED5D5514D5F0CD8E0414ED4EC08C16344F8DB652B13AA3A060420F2D7D14FABD8FC080E1589782D15DCCC8F51C705773DE939F8F5EFE0B74319078D328CE100C2BB99C5F4DBD0702EDC4E9C607CB75DA3A28689FA54DBCE8D5A41462309AEC8729D6D9B867C68B3A6C155338C05BD8FB8E680AEA63E1A918E4EA74E3E0BDF2FEA754E70E5A9C3F5B5AE6B979A6791BECB77A4FB9F4CDDC87817BDD4DDB7D2F9BCA2B345FB5DFCAE3AE4CC5BBCE1780188B1D5230FC5F3CB5B07DFDEE6F77F558FC499185329BC85233C6ABF6A58DD1CD59D0BCF1AA58C664CB5CDE039A3D103218C74A3993A5D5EFB1FDD2056D432A4047E3F88963EABA231A9831D8165434B2C950146356C08D
Additional Sections: 1
* Additional Section Entry 0: BlockSize=104, BlockID="Product Information"
* Additional Section Entry 0 Data (len=96): firefox-mozilla-central61.0a1
Content: read 45802404 bytes
Index Size: 402653185
* Index Entry 0: FileName="ef/channel-prefs.js", Offset To Content=2758042982, Size=1635085428, Flags=1932488818
* Index Entry 1: FileName="crashreporter.ini", Offset To Content=35882384, Size=1656, Flags=420
* Index Entry 2: FileName="crashreporter", Offset To Content=35884040, Size=101212, Flags=493
* Index Entry 3: FileName="chrome.manifest", Offset To Content=35985252, Size=32, Flags=420
* Index Entry 4: FileName="browser/omni.ja", Offset To Content=35985284, Size=7998140, Flags=420
* Index Entry 5: FileName="browser/features/webcompat@mozilla.org.xpi", Offset To Content=43983424, Size=3420, Flags=420
* Index Entry 6: FileName="browser/features/webcompat-reporter@mozilla.org.xpi", Offset To Content=43986844, Size=4424, Flags=420
* Index Entry 7: FileName="browser/features/screenshots@mozilla.org.xpi", Offset To Content=43991268, Size=335020, Flags=420
* Index Entry 8: FileName="browser/features/presentation@mozilla.org.xpi", Offset To Content=44326288, Size=4632, Flags=420
* Index Entry 9: FileName="browser/features/onboarding@mozilla.org.xpi", Offset To Content=44330920, Size=124828, Flags=420
* Index Entry 10: FileName="browser/features/formautofill@mozilla.org.xpi", Offset To Content=44455748, Size=90692, Flags=420
* Index Entry 11: FileName="browser/features/followonsearch@mozilla.com.xpi", Offset To Content=44546440, Size=5284, Flags=420
* Index Entry 12: FileName="browser/features/firefox@getpocket.com.xpi", Offset To Content=44551724, Size=548704, Flags=420
* Index Entry 13: FileName="browser/features/aushelper@mozilla.org.xpi", Offset To Content=45100428, Size=2932, Flags=420
* Index Entry 14: FileName="browser/features/activity-stream@mozilla.org.xpi", Offset To Content=45103360, Size=648432, Flags=420
* Index Entry 15: FileName="browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}.xpi", Offset To Content=45751792, Size=964, Flags=420
* Index Entry 16: FileName="browser/crashreporter-override.ini", Offset To Content=45752756, Size=528, Flags=420
* Index Entry 17: FileName="browser/chrome/icons/default/default64.png", Offset To Content=45753284, Size=5332, Flags=420
* Index Entry 18: FileName="browser/chrome/icons/default/default48.png", Offset To Content=45758616, Size=3648, Flags=420
* Index Entry 19: FileName="browser/chrome/icons/default/default32.png", Offset To Content=45762264, Size=2208, Flags=420
* Index Entry 20: FileName="browser/chrome/icons/default/default16.png", Offset To Content=45764472, Size=852, Flags=420
* Index Entry 21: FileName="browser/chrome/icons/default/default128.png", Offset To Content=45765324, Size=12992, Flags=420
* Index Entry 22: FileName="browser/chrome.manifest", Offset To Content=45778316, Size=32, Flags=420
* Index Entry 23: FileName="browser/blocklist.xml", Offset To Content=45778348, Size=22480, Flags=420
* Index Entry 24: FileName="application.ini", Offset To Content=45800828, Size=532, Flags=420
* Index Entry 25: FileName="Throbber-small.gif", Offset To Content=45801360, Size=396, Flags=420
@jvehent
Copy link
Author

jvehent commented Apr 17, 2018

package main

import (
        "bytes"
        "encoding/binary"
        "fmt"
        "log"
        "os"
)

const (
        HeaderLen                        = 8
        SignaturesHeaderLen              = 12
        SignatureEntryHeaderLen          = 8
        AdditionalSectionsHeaderLen      = 4
        AdditionalSectionsEntryHeaderLen = 8
        IndexHeaderLen                   = 4
        IndexEntryHeaderLen              = 12

        SigAlgRsaPkcs1Sha1   = 1
        SigAlgRsaPkcs1Sha384 = 2

        BlockIDProductInfo = 1
)

type MARHeader struct {
        MarID         [4]byte
        OffsetToIndex uint32
}
type SignaturesHeader struct {
        FileSize      uint64
        NumSignatures uint32
}

type SignatureEntryHeader struct {
        AlgorithmID uint32
        Size        uint32
}

type AdditionalSections struct {
        NumAdditionalSections uint32
}

type AdditionalSectionEntry struct {
        BlockSize uint32
        BlockID   uint32
}

type Content []byte

type IndexHeader struct {
        Size uint32
}

type IndexEntryHeader struct {
        OffsetToContent uint32
        Size            uint32
        Flags           uint32
}
type FileName []byte

func main() {
        var (
                // current position of the cursor in the file
                cursor, c int

                header MARHeader

                sigHeader      SignaturesHeader
                sigEntryHeader SignatureEntryHeader
                sigalg         string

                additionalHeader AdditionalSections
                addSectionEntry  AdditionalSectionEntry
                blockid          string

                idxHeader      IndexHeader
                idxEntryHeader IndexEntryHeader

                i uint32
        )
        file, err := os.Open("firefox-61.0a1.ach.linux-i686.complete.mar")
        if err != nil {
                log.Fatal("Error while opening file", err)
        }
        defer file.Close()

        c, err = parse(file, &header, HeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("Header: MAR ID=%q, Offset to Index=%d\n", header.MarID, header.OffsetToIndex)

        c, err = parse(file, &sigHeader, SignaturesHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nSignatures Header: FileSize=%d, NumSignatures=%d\n", sigHeader.FileSize, sigHeader.NumSignatures)

        for i = 0; i < sigHeader.NumSignatures; i++ {
                c, err = parse(file, &sigEntryHeader, SignatureEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                switch sigEntryHeader.AlgorithmID {
                case SigAlgRsaPkcs1Sha1:
                        sigalg = "RSA-PKCS1-SHA1"
                case SigAlgRsaPkcs1Sha384:
                        sigalg = "RSA-PKCS1-SHA384"
                default:
                        sigalg = fmt.Sprintf("unknown algorithm id %d", sigEntryHeader.AlgorithmID)
                }

                fmt.Printf("* Signature %d Entry Header: Algorithm=%q, Size=%d\n", i, sigalg, sigEntryHeader.Size)

                sigData := make([]byte, sigEntryHeader.Size, sigEntryHeader.Size)
                c, err = parse(file, &sigData, int(sigEntryHeader.Size))
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                fmt.Printf("* Signature %d Data (len=%d): %X\n", i, len(sigData), sigData)
        }

        c, err = parse(file, &additionalHeader, AdditionalSectionsHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nAdditional Sections: %d\n", additionalHeader.NumAdditionalSections)

        for {
                c, err = parse(file, &addSectionEntry, AdditionalSectionsEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c

                break
        }
        for i = 0; i < additionalHeader.NumAdditionalSections; i++ {
                switch addSectionEntry.BlockID {
                case BlockIDProductInfo:
                        blockid = "Product Information"
                default:
                        blockid = fmt.Sprintf("unknown block id %d", addSectionEntry.BlockID)
                }
                fmt.Printf("* Additional Section Entry %d: BlockSize=%d, BlockID=%q\n", i, addSectionEntry.BlockSize, blockid)

                dataSize := addSectionEntry.BlockSize - AdditionalSectionsEntryHeaderLen
                addSectionData := make([]byte, dataSize, dataSize)
                c, err = parse(file, &addSectionData, int(dataSize))
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                fmt.Printf("* Additional Section Entry %d Data (len=%d): %s\n", i, dataSize, addSectionData)
        }

        contentLength := int(sigHeader.FileSize) - (int(sigHeader.FileSize) - int(header.OffsetToIndex) - cursor)
        content := make([]byte, contentLength, contentLength)
        c, err = parse(file, &content, contentLength)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nContent: read %d bytes\n", len(content))

        c, err = parse(file, &idxHeader, IndexHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nIndex Size: %d\n", idxHeader.Size)

        for i = 0; ; i++ {
                c, err = parse(file, &idxEntryHeader, IndexEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                var filename []byte
                oneChar := make([]byte, 1, 1)
                for {
                        c, err = file.Read(oneChar)
                        if err != nil {
                                log.Fatalf("parsing failed at position %d: %v", cursor, err)
                        }
                        cursor += c
                        if bytes.Equal(oneChar, []byte("\x00")) {
                                // null terminator
                                break
                        }
                        filename = append(filename, oneChar...)
                }
                fmt.Printf("* Index Entry %d: FileName=%q, Offset To Content=%d, Size=%d, Flags=%d\n",
                        i, filename, idxEntryHeader.OffsetToContent, idxEntryHeader.Size, idxEntryHeader.Flags)
                if cursor == int(sigHeader.FileSize) {
                        break
                }
        }

}

func parse(file *os.File, data interface{}, number int) (readBytes int, err error) {
        raw := make([]byte, number)
        readBytes, err = file.Read(raw)
        if err != nil {
                return
        }
        buffer := bytes.NewBuffer(raw)
        err = binary.Read(buffer, binary.BigEndian, data)
        return
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment