Skip to content

Instantly share code, notes, and snippets.

@koseki
Last active June 12, 2021 12:22
Show Gist options
  • Select an option

  • Save koseki/9737a4490128bcb8b426c22a0b7c3885 to your computer and use it in GitHub Desktop.

Select an option

Save koseki/9737a4490128bcb8b426c22a0b7c3885 to your computer and use it in GitHub Desktop.

Revisions

  1. koseki revised this gist Jun 12, 2021. 1 changed file with 27 additions and 26 deletions.
    53 changes: 27 additions & 26 deletions dump-rpm-headers.py
    Original file line number Diff line number Diff line change
    @@ -139,30 +139,30 @@ class HeaderTagValue(Enum):
    RPMTAG_PLATFORM = 1132

    # https://github.com/rpm-software-management/rpm/blob/847c6f062c267c4be643be9c202141bd330a7891/lib/rpmtag.h
    RPMTAG_PATCHESNAME = 1133
    RPMTAG_PATCHESFLAGS = 1134
    RPMTAG_PATCHESVERSION = 1135
    RPMTAG_CACHECTIME = 1136
    RPMTAG_CACHEPKGPATH = 1137
    RPMTAG_CACHEPKGSIZE = 1138
    RPMTAG_CACHEPKGMTIME = 1139
    RPMTAG_FILECOLORS = 1140
    RPMTAG_FILECLASS = 1141
    RPMTAG_CLASSDICT = 1142
    RPMTAG_FILEDEPENDSX = 1143
    RPMTAG_FILEDEPENDSN = 1144
    RPMTAG_DEPENDSDICT = 1145
    RPMTAG_SOURCEPKGID = 1146
    RPMTAG_FILECONTEXTS = 1147
    RPMTAG_FSCONTEXTS = 1148
    RPMTAG_RECONTEXTS = 1149
    RPMTAG_POLICIES = 1150
    RPMTAG_PRETRANS = 1151
    RPMTAG_POSTTRANS = 1152
    RPMTAG_PRETRANSPROG = 1153
    RPMTAG_POSTTRANSPROG = 1154
    RPMTAG_DISTTAG = 1155
    RPMTAG_OLDSUGGESTSNAME = 1156
    RPMTAG_PATCHESNAME = 1133
    RPMTAG_PATCHESFLAGS = 1134
    RPMTAG_PATCHESVERSION = 1135
    RPMTAG_CACHECTIME = 1136
    RPMTAG_CACHEPKGPATH = 1137
    RPMTAG_CACHEPKGSIZE = 1138
    RPMTAG_CACHEPKGMTIME = 1139
    RPMTAG_FILECOLORS = 1140
    RPMTAG_FILECLASS = 1141
    RPMTAG_CLASSDICT = 1142
    RPMTAG_FILEDEPENDSX = 1143
    RPMTAG_FILEDEPENDSN = 1144
    RPMTAG_DEPENDSDICT = 1145
    RPMTAG_SOURCEPKGID = 1146
    RPMTAG_FILECONTEXTS = 1147
    RPMTAG_FSCONTEXTS = 1148
    RPMTAG_RECONTEXTS = 1149
    RPMTAG_POLICIES = 1150
    RPMTAG_PRETRANS = 1151
    RPMTAG_POSTTRANS = 1152
    RPMTAG_PRETRANSPROG = 1153
    RPMTAG_POSTTRANSPROG = 1154
    RPMTAG_DISTTAG = 1155
    RPMTAG_OLDSUGGESTSNAME = 1156

    RPMTAG_FILEDIGESTALGO = 5011
    RPMTAG_BUGURL = 5012
    @@ -183,7 +183,8 @@ class RPMLead(BigEndianStructure):

    class RPMHeader(BigEndianStructure):
    _fields_ = (
    ('magic', c_char * 4),
    ('magic', c_char * 3),
    ('version', c_char),
    ('reserved', c_char * 4),
    ('nindex', c_int),
    ('hsize', c_int),
    @@ -291,4 +292,4 @@ def dump_header_structure(source, label, tag_value):
    print(f"[{index.position:>3}] {index.tag_name}\n{v}")
    print("")

    main(argv[1])
    main(argv[1])
  2. koseki created this gist May 4, 2021.
    294 changes: 294 additions & 0 deletions dump-rpm-headers.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,294 @@
    #! /usr/bin/env python

    import struct
    from ctypes import *
    import io
    import re

    from enum import Enum
    from sys import argv

    """
    Linux Standard Base Core Specification, Generic Part - 25.2. Package File Format
    https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/pkgformat.html
    """

    class IndexTypeValue(Enum):
    NULL = 0
    CHAR = 1
    INT8 = 2
    INT16 = 3
    INT32 = 4
    INT64 = 5
    STRING = 6
    BIN = 7
    STRING_ARRAY = 8
    I18NSTRING = 9

    def ctype_size(self):
    return {
    self.NULL: 1,
    self.CHAR: 1,
    self.INT8: 1,
    self.INT16: 2,
    self.INT32: 4,
    self.INT64: 8,
    self.STRING: 1,
    self.BIN: 1,
    self.STRING_ARRAY: 1,
    self.I18NSTRING: 1,
    }[self]

    def format_char(self):
    format_chars = {
    self.INT8: 'C',
    self.INT16: 'H',
    self.INT32: 'I',
    self.INT64: 'L',
    }
    if self in format_chars:
    return format_chars[self]
    return None


    class SignatureTagValue(Enum):
    RPMTAG_HEADERSIGNATURES = 62
    RPMTAG_HEADERIMMUTABLE = 63
    RPMTAG_HEADERI18NTABLE = 100

    RPMSIGTAG_SIZE = 1000
    RPMSIGTAG_PAYLOADSIZE = 1007
    RPMSIGTAG_SHA1 = 269
    RPMSIGTAG_MD5 = 1004
    RPMSIGTAG_DSA = 267
    RPMSIGTAG_RSA = 268
    RPMSIGTAG_PGP = 1002
    RPMSIGTAG_GPG = 1005

    class HeaderTagValue(Enum):
    RPMTAG_HEADERSIGNATURES = 62
    RPMTAG_HEADERIMMUTABLE = 63
    RPMTAG_HEADERI18NTABLE = 100

    RPMTAG_NAME = 1000
    RPMTAG_VERSION = 1001
    RPMTAG_RELEASE = 1002
    RPMTAG_SUMMARY = 1004
    RPMTAG_DESCRIPTION = 1005
    RPMTAG_SIZE = 1009
    RPMTAG_DISTRIBUTION = 1010
    RPMTAG_VENDOR = 1011
    RPMTAG_LICENSE = 1014
    RPMTAG_PACKAGER = 1015
    RPMTAG_GROUP = 1016
    RPMTAG_URL = 1020
    RPMTAG_OS = 1021
    RPMTAG_ARCH = 1022
    RPMTAG_SOURCERPM = 1044
    RPMTAG_ARCHIVESIZE = 1046
    RPMTAG_RPMVERSION = 1064
    RPMTAG_COOKIE = 1094
    RPMTAG_DISTURL = 1123
    RPMTAG_PAYLOADFORMAT = 1124
    RPMTAG_PAYLOADCOMPRESSOR = 1125
    RPMTAG_PAYLOADFLAGS = 1126
    RPMTAG_PREIN = 1023
    RPMTAG_POSTIN = 1024
    RPMTAG_PREUN = 1025
    RPMTAG_POSTUN = 1026
    RPMTAG_PREINPROG = 1085
    RPMTAG_POSTINPROG = 1086
    RPMTAG_PREUNPROG = 1087
    RPMTAG_POSTUNPROG = 1088
    RPMTAG_OLDFILENAMES = 1027
    RPMTAG_FILESIZES = 1028
    RPMTAG_FILEMODES = 1030
    RPMTAG_FILERDEVS = 1033
    RPMTAG_FILEMTIMES = 1034
    RPMTAG_FILEMD5S = 1035
    RPMTAG_FILELINKTOS = 1036
    RPMTAG_FILEFLAGS = 1037
    RPMTAG_FILEUSERNAME = 1039
    RPMTAG_FILEGROUPNAME = 1040
    RPMTAG_FILEDEVICES = 1095
    RPMTAG_FILEINODES = 1096
    RPMTAG_FILELANGS = 1097
    RPMTAG_DIRINDEXES = 1116
    RPMTAG_BASENAMES = 1117
    RPMTAG_DIRNAMES = 1118
    RPMTAG_PROVIDENAME = 1047
    RPMTAG_REQUIREFLAGS = 1048
    RPMTAG_REQUIRENAME = 1049
    RPMTAG_REQUIREVERSION = 1050
    RPMTAG_CONFLICTFLAGS = 1053
    RPMTAG_CONFLICTNAME = 1054
    RPMTAG_CONFLICTVERSION = 1055
    RPMTAG_OBSOLETENAME = 1090
    RPMTAG_PROVIDEFLAGS = 1112
    RPMTAG_PROVIDEVERSION = 1113
    RPMTAG_OBSOLETEFLAGS = 1114
    RPMTAG_OBSOLETEVERSION = 1115
    RPMTAG_BUILDTIME = 1006
    RPMTAG_BUILDHOST = 1007
    RPMTAG_FILEVERIFYFLAGS = 1045
    RPMTAG_CHANGELOGTIME = 1080
    RPMTAG_CHANGELOGNAME = 1081
    RPMTAG_CHANGELOGTEXT = 1082
    RPMTAG_OPTFLAGS = 1122
    RPMTAG_RHNPLATFORM = 1131
    RPMTAG_PLATFORM = 1132

    # https://github.com/rpm-software-management/rpm/blob/847c6f062c267c4be643be9c202141bd330a7891/lib/rpmtag.h
    RPMTAG_PATCHESNAME = 1133
    RPMTAG_PATCHESFLAGS = 1134
    RPMTAG_PATCHESVERSION = 1135
    RPMTAG_CACHECTIME = 1136
    RPMTAG_CACHEPKGPATH = 1137
    RPMTAG_CACHEPKGSIZE = 1138
    RPMTAG_CACHEPKGMTIME = 1139
    RPMTAG_FILECOLORS = 1140
    RPMTAG_FILECLASS = 1141
    RPMTAG_CLASSDICT = 1142
    RPMTAG_FILEDEPENDSX = 1143
    RPMTAG_FILEDEPENDSN = 1144
    RPMTAG_DEPENDSDICT = 1145
    RPMTAG_SOURCEPKGID = 1146
    RPMTAG_FILECONTEXTS = 1147
    RPMTAG_FSCONTEXTS = 1148
    RPMTAG_RECONTEXTS = 1149
    RPMTAG_POLICIES = 1150
    RPMTAG_PRETRANS = 1151
    RPMTAG_POSTTRANS = 1152
    RPMTAG_PRETRANSPROG = 1153
    RPMTAG_POSTTRANSPROG = 1154
    RPMTAG_DISTTAG = 1155
    RPMTAG_OLDSUGGESTSNAME = 1156

    RPMTAG_FILEDIGESTALGO = 5011
    RPMTAG_BUGURL = 5012


    class RPMLead(BigEndianStructure):
    _fields_ = (
    ('magic', c_char * 4),
    ('major', c_char),
    ('minor', c_char),
    ('type', c_short),
    ('archnum', c_short),
    ('name', c_char * 66),
    ('osnum', c_short),
    ('signature_type', c_short),
    ('reserved', c_char * 16),
    )

    class RPMHeader(BigEndianStructure):
    _fields_ = (
    ('magic', c_char * 4),
    ('reserved', c_char * 4),
    ('nindex', c_int),
    ('hsize', c_int),
    )

    class RPMHeaderIndex(BigEndianStructure):
    position = -1
    tag_name = "Unknown"
    _fields_ = (
    ('tag', c_int),
    ('type', c_int),
    ('offset', c_int),
    ('count', c_int),
    )

    def read_string(source:io.IOBase):
    result = []
    while True:
    chunk = source.read(128)
    i = chunk.find(b'\0')
    if i == -1:
    result.append(chunk)
    else:
    result.append(chunk[:i])
    source.seek(i - 128 + 1, io.SEEK_CUR)
    break
    if len(chunk) < 128:
    break
    return b''.join(result)

    def read_string_array(source:io.IOBase, length:int):
    result = []
    for i in range(length):
    result.append(read_string(source))
    return result

    def main(rpmfile):
    source = open(rpmfile, 'rb')

    print("==== Lead Section ====")

    lead = RPMLead()
    source.readinto(lead)

    for field in lead._fields_:
    v = getattr(lead, field[0])
    print(f"{field[0]:>10}: {v}")

    dump_header_structure(source, "Signature Section", SignatureTagValue)
    dump_header_structure(source, "Header Section", HeaderTagValue)

    source.close()

    def dump_header_structure(source, label, tag_value):
    print(f"==== {label} - Header ====")

    header = RPMHeader()
    source.readinto(header)

    for field in header._fields_:
    v = getattr(header, field[0])
    print(f"{field[0]:>10}: {v}")

    print(f"==== {label} - Index Entries ====")

    index_entries = []
    for i in range(header.nindex):
    e = RPMHeaderIndex()
    source.readinto(e)
    e.position = i
    index_entries.append(e)

    try:
    e.tag_name = tag_value(e.tag).name
    except:
    pass

    t = IndexTypeValue(e.type).name
    print(f"[{i:>3}] tag:{e.tag:>5} type: {t:<12} offset:{e.offset:>4} count:{e.count:>4} # {e.tag_name}")

    print(f"==== {label} - Data ====")

    index_entries = sorted(index_entries, key=lambda e: e.offset)

    start = source.tell()
    for index in index_entries:
    index_type = IndexTypeValue(index.type)
    size = index_type.ctype_size()

    source.seek(start + index.offset)

    if index_type == IndexTypeValue.STRING:
    v = read_string(source)
    elif index_type == IndexTypeValue.STRING_ARRAY:
    v = read_string_array(source, index.count)
    v = ' ' + '\n '.join(map(str, v))
    else:
    v = source.read(size * index.count)
    format_char = index_type.format_char()
    if format_char:
    v = ' ' + '\n '.join(map(str, struct.unpack('>' + format_char * index.count, v)))
    else:
    v = re.sub('([0-9a-f]{4})', '\\1 ', re.sub('(.{32})', '\\1\n', v.hex()))
    v = v.strip()
    print(f"[{index.position:>3}] {index.tag_name}\n{v}")
    print("")

    main(argv[1])