Skip to content

Instantly share code, notes, and snippets.

@Upliner
Last active June 5, 2024 08:57
Show Gist options
  • Save Upliner/a77344bc28b08ffecd97439eba071e25 to your computer and use it in GitHub Desktop.
Save Upliner/a77344bc28b08ffecd97439eba071e25 to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
import sys, struct
szmap = {"H":2, "I":4, "Q": 8}
hdrs = (
(b"II\x2a\x00", "<", "I", "H"),
(b"MM\x00\x2a", ">", "I", "H"),
(b"II\x2b\x00\x08\x00\x00\x00", "<", "Q", "Q"),
(b"MM\x00\x2b\x00\x08\x00\x00", ">", "Q", "Q"))
def parse_rational64(e, v, cnt, fmt):
arr = struct.unpack(f"{e}{cnt*2}{fmt}",v)
return tuple(zip(arr[::2], arr[1::2]))
fmts = {
1: (1, lambda e, v, cnt: tuple(v) if cnt < 10 else v), #uint8
2: (1, lambda e, v, cnt: v.decode("latin1")), #string
3: (2, lambda e, v, cnt: struct.unpack(f"{e}{cnt}H", v)), #uint16
4: (4, lambda e, v, cnt: struct.unpack(f"{e}{cnt}I", v)), #uint32
5: (8, lambda e, v, cnt: parse_rational64(e, v, cnt, "I")), #rational64u
6: (1, lambda e, v, cnt: struct.unpack(f"{e}{cnt}b", v)), #int8s
7: (1, lambda e, v, cnt: v), #binary
8: (2, lambda e, v, cnt: struct.unpack(f"{e}{cnt}h", v)), #int16
9: (4, lambda e, v, cnt: struct.unpack(f"{e}{cnt}i", v)), #int32s
10: (8, lambda e, v, cnt: parse_rational64(e, v, cnt, "i")), #rational64s
11: (4, lambda e, v, cnt: struct.unpack(f"{e}{cnt}f", v)), #float
12: (8, lambda e, v, cnt: struct.unpack(f"{e}{cnt}d", v)), #double
13: (4, lambda e, v, cnt: struct.unpack(f"{e}{cnt}I", v)), #ifd32
14: (2, lambda e, v, cnt: v.decode("utf-16")), #unicode
15: (8, lambda e, v, cnt: tuple(complex(tup) for tup in parse_rational64(e, v, cnt, "f"))), #complex
16: (8, lambda e, v, cnt: struct.unpack(f"{e}{cnt}Q", v)), #uint64
17: (8, lambda e, v, cnt: struct.unpack(f"{e}{cnt}q", v)), #int64
18: (8, lambda e, v, cnt: struct.unpack(f"{e}{cnt}Q", v)), #ifd64
129: (1, lambda e, v, cnt: v.decode("utf-8")), #string-utf8
}
subifds = []
def dump_exif_jpeg(data):
offs = 2
while offs < len(data) and data[offs] == 255:
tag = data[offs + 1]
offs += 2
if tag >= 0xd0 and tag <= 0xd8:
continue
if tag == 0xd9 or tag == 0xda:
break
sz = struct.unpack(">H", data[offs:offs+2])[0]
if tag == 0xe1 and data[offs+2:offs+8] == b"Exif\0\0":
dumpb(data[offs+8:offs+sz])
return
offs += sz
print("Exif data is not found in JPEG file")
def dump_exif_png(data):
offs = 8
while offs < len(data)-12:
sz = struct.unpack(">I", data[offs:offs+4])[0]
if data[offs+4:offs+8] == b"eXIf":
dumpb(data[offs+8:offs+8+sz])
return
offs += sz + 12
print("Exif data is not found in PNG file")
def dumpf(fname):
with open(fname, "rb") as f:
data = f.read()
dumpb(data)
def dumpb(data):
for t in hdrs:
if data[:len(t[0])] == t[0]:
dumpb1(data, len(t[0]), t[1], t[2], t[3])
break
else:
if data[:3] == b"\xff\xd8\xff":
dump_exif_jpeg(data)
elif data[:8] == b"\x89PNG\x0d\x0a\x1a\x0a":
dump_exif_png(data)
else:
print("Invalid TIFF Header")
def dumpb1(data, offs, en, ptr, scnt):
dumpifd(data, struct.unpack(f"{en}{ptr}", data[offs:offs+szmap[ptr]])[0], en, ptr, scnt)
def dumpifd(data, offs, en, ptr, scnt):
offs2 = offs+szmap[scnt]
cnt = struct.unpack(f"{en}{scnt}", data[offs:offs2])[0]
offs = offs2
szptr = szmap[ptr]
sz = 4 + szptr
for i in range(cnt):
offs2 = offs+sz
tag, typ, cnt = struct.unpack(f"{en}HH{ptr}", data[offs:offs2])
print(f"{tag:04x}: {fmt_val(data, offs + 4 + szptr, en, ptr, typ, cnt, tag)}")
offs = offs2 + szptr
offs = struct.unpack(f"{en}{ptr}", data[offs:offs+szptr])[0]
if offs:
print(f"\nChained IFD {offs:08x}:")
dumpifd(data, offs, en, ptr, scnt)
while subifds:
subifds2 = subifds.copy()
subifds.clear()
for offs in subifds2:
print(f"\nSubIFD {offs:08x}:")
dumpifd(data, offs, en, ptr, scnt)
def fmt_val(data, offs, en, ptr, typ, cnt, tag=0):
fmt = fmts.get(typ)
if fmt is None:
return "<Invalid format>"
sz = fmt[0] * cnt
szptr = szmap[ptr]
if sz > szptr:
offs = struct.unpack(f"{en}{ptr}",data[offs:offs+szptr])[0]
if cnt > 200:
return f"<fmt {typ}, {cnt} items>"
result = fmt[1](en, data[offs:offs+sz], cnt)
if fmt in (13, 18) or tag in (0x8825, 0x8769, 0xa005):
subifds.extend(result)
result = [f"<SubIFD 0x{i:08x}>" for i in result]
if isinstance(result, (tuple, list)) and len(result) == 1:
result = result[0]
return result
dumpf(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment